@kubitnodes/stellaris 0.3.9 → 0.3.10

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 (32) hide show
  1. package/dist/cli/index.js +27 -11
  2. package/dist/server/index.js +100 -69
  3. package/package.json +1 -1
  4. package/web/dist/assets/{BackLink-1EUHNOu3.js → BackLink-B3tz8OFm.js} +1 -1
  5. package/web/dist/assets/{ChannelView-A47Rvstg.js → ChannelView-Dkzr1zkF.js} +1 -1
  6. package/web/dist/assets/{CitizenView-B_jG9N5w.js → CitizenView-BLgyEvYz.js} +1 -1
  7. package/web/dist/assets/{CitizensView-DdIHzcx3.js → CitizensView-DttP5FqR.js} +1 -1
  8. package/web/dist/assets/{KnowledgeView-CXRQ4igk.js → KnowledgeView-DFAC95aF.js} +1 -1
  9. package/web/dist/assets/{Markdown-B2tp7ki-.js → Markdown-4W4xjIq7.js} +1 -1
  10. package/web/dist/assets/{MessageItem-CWZ0X2Vr.js → MessageItem-Bfpve1_Y.js} +1 -1
  11. package/web/dist/assets/{MetricsView-7X3Cmybh.js → MetricsView-Bp486U0v.js} +1 -1
  12. package/web/dist/assets/{NeedsYouView-Pi1jBF8V.js → NeedsYouView-CJ4COcNn.js} +1 -1
  13. package/web/dist/assets/{Overview-C1leALP3.js → Overview-BnO1K52h.js} +1 -1
  14. package/web/dist/assets/{Pane-DxXjKLAj.js → Pane-xzIpwnBD.js} +1 -1
  15. package/web/dist/assets/{ProjectView-DFReelVz.js → ProjectView-w0mcF0KW.js} +1 -1
  16. package/web/dist/assets/{ProposalView-3L5osVUL.js → ProposalView-CyJCwUYK.js} +1 -1
  17. package/web/dist/assets/{ProposalsView-Uc6fp4bD.js → ProposalsView-XrraDhwt.js} +1 -1
  18. package/web/dist/assets/{RunnersView-D4pd3ArT.js → RunnersView-H3kCdSQB.js} +1 -1
  19. package/web/dist/assets/{SocietyView--buUCciX.js → SocietyView-Cf8ENDAw.js} +1 -1
  20. package/web/dist/assets/{SubjectThread-C811fit3.js → SubjectThread-ClMmgVVd.js} +1 -1
  21. package/web/dist/assets/{TaskFileView-dvFssKHp.js → TaskFileView-CRwwL5K8.js} +1 -1
  22. package/web/dist/assets/{TaskView-C-VIKoFp.js → TaskView-A9IgTxQ0.js} +1 -1
  23. package/web/dist/assets/{TasksView-BXqfwU5R.js → TasksView-CnhTIldG.js} +1 -1
  24. package/web/dist/assets/{ThreadCard-Bn7HfgDI.js → ThreadCard-fvf8cY_l.js} +1 -1
  25. package/web/dist/assets/{ThreadForms-CzYPjU1-.js → ThreadForms-D09Ykgj9.js} +1 -1
  26. package/web/dist/assets/{ThreadView-BioBQeqV.js → ThreadView-BqNSssCf.js} +1 -1
  27. package/web/dist/assets/{citizen-BqHxQY9q.js → citizen-C27Wvimj.js} +1 -1
  28. package/web/dist/assets/{format-Cc6KjiYZ.js → format-lz-5NQWT.js} +1 -1
  29. package/web/dist/assets/{index-WB0PqHlY.js → index-CoKyPk79.js} +3 -3
  30. package/web/dist/assets/{tasks-CbQrkZ1C.js → tasks-DK1cnXqs.js} +1 -1
  31. package/web/dist/assets/{useStickyScroll-NvlGfk6Z.js → useStickyScroll-CfP3Yl_4.js} +1 -1
  32. package/web/dist/index.html +3 -3
package/dist/cli/index.js CHANGED
@@ -866,11 +866,12 @@ var ProjectRepoSchema = z6.object({
866
866
  defaultBranch: z6.string().min(1)
867
867
  });
868
868
  var TurnWorkspaceSchema = z6.discriminatedUnion("kind", [
869
- z6.object({ kind: z6.literal("home") }),
869
+ z6.object({ kind: z6.literal("home"), thread: UlidSchema.optional() }),
870
870
  z6.object({
871
871
  kind: z6.literal("project"),
872
872
  repo: ProjectRepoSchema,
873
- branches: z6.array(z6.string().min(1))
873
+ branches: z6.array(z6.string().min(1)),
874
+ thread: UlidSchema.optional()
874
875
  }),
875
876
  z6.object({ kind: z6.literal("task"), repo: ProjectRepoSchema, taskId: UlidSchema })
876
877
  ]);
@@ -921,7 +922,7 @@ var TurnOutcomeSchema = z6.object({
921
922
  model: z6.string().nullable(),
922
923
  work: TurnWorkSchema.nullable()
923
924
  });
924
- var TurnAckSchema = z6.object({ dropWorktree: z6.boolean() });
925
+ var TurnAckSchema = z6.object({ dropWorkspace: z6.boolean() });
925
926
  var LandRequestSchema = z6.object({
926
927
  repo: ProjectRepoSchema,
927
928
  branch: z6.string().min(1)
@@ -1300,7 +1301,7 @@ var RunnerConfigSchema = z10.object({
1300
1301
  });
1301
1302
 
1302
1303
  // packages/shared/dist/version.js
1303
- var STELLARIS_VERSION = "0.3.9";
1304
+ var STELLARIS_VERSION = "0.3.10";
1304
1305
 
1305
1306
  // packages/board-core/dist/errors.js
1306
1307
  var BoardError = class extends Error {
@@ -1345,15 +1346,28 @@ async function writeJson(file, value) {
1345
1346
  await writeFileAtomic(file, `${JSON.stringify(value, null, 2)}
1346
1347
  `);
1347
1348
  }
1349
+ function parseMatter(raw) {
1350
+ return matter(raw, {});
1351
+ }
1348
1352
  async function readMarkdown(file, schema) {
1349
1353
  const raw = await readFile(file, "utf8");
1350
- const parsed = matter(raw);
1354
+ const parsed = parseMatter(raw);
1351
1355
  return { data: schema.parse(parsed.data), body: parsed.content.replace(/^\n/, "") };
1352
1356
  }
1357
+ async function readLooseMarkdown(file) {
1358
+ const raw = await readFile(file, "utf8");
1359
+ try {
1360
+ const parsed = parseMatter(raw);
1361
+ return { data: parsed.data, body: parsed.content.replace(/^\n/, ""), error: null };
1362
+ } catch (error) {
1363
+ const reason = (error instanceof Error ? error.message : String(error)).split("\n")[0] ?? "";
1364
+ return { data: {}, body: raw, error: reason.replace(/:$/, "") };
1365
+ }
1366
+ }
1353
1367
  async function writeMarkdown(file, data, body) {
1354
1368
  const normalized = body.endsWith("\n") || body.length === 0 ? body : `${body}
1355
1369
  `;
1356
- await writeFileAtomic(file, matter.stringify(normalized, data));
1370
+ await writeFileAtomic(file, matter.stringify({ content: normalized }, data, {}));
1357
1371
  }
1358
1372
  function isMissing(error) {
1359
1373
  return typeof error === "object" && error !== null && error.code === "ENOENT";
@@ -3205,7 +3219,7 @@ ${charter.purpose}
3205
3219
  }
3206
3220
  async readProfile(name) {
3207
3221
  const file = this.paths.agentProfile(name);
3208
- return await exists(file) ? (await readMarkdown(file, z12.record(z12.string(), z12.unknown()))).body : "";
3222
+ return await exists(file) ? (await readLooseMarkdown(file)).body : "";
3209
3223
  }
3210
3224
  /** The society's norms, the one knowledge topic every turn loads. Empty until the steward writes it. */
3211
3225
  async readSocietyNorms() {
@@ -3252,10 +3266,11 @@ ${charter.purpose}
3252
3266
  if (!name.success) {
3253
3267
  continue;
3254
3268
  }
3255
- const doc = await readMarkdown(file, z12.record(z12.string(), z12.unknown()));
3269
+ const doc = await readLooseMarkdown(file);
3256
3270
  skills.push({
3257
3271
  name: name.data,
3258
- summary: skillSummary(doc.data, doc.body),
3272
+ // The index is where its writer sees that the file needs fixing.
3273
+ summary: doc.error === null ? skillSummary(doc.data, doc.body) : `its frontmatter does not parse (${doc.error}), so fix the file`,
3259
3274
  scope,
3260
3275
  path: file
3261
3276
  });
@@ -4917,7 +4932,7 @@ ${rationale}`}`);
4917
4932
  }
4918
4933
  async readMemoryCore(agent2) {
4919
4934
  const file = this.paths.agentMemoryCore(agent2);
4920
- return await exists(file) ? (await readMarkdown(file, z12.record(z12.string(), z12.unknown()))).body : "";
4935
+ return await exists(file) ? (await readLooseMarkdown(file)).body : "";
4921
4936
  }
4922
4937
  /**
4923
4938
  * Moves a conversation's cursor forward to `cursor` once a turn there has delivered up to it. It
@@ -5132,7 +5147,8 @@ ${rationale}`}`);
5132
5147
  if (!await exists(file)) {
5133
5148
  return { data: { project: slug }, body: "" };
5134
5149
  }
5135
- return readMarkdown(file, z12.record(z12.string(), z12.unknown()));
5150
+ const { data, body } = await readLooseMarkdown(file);
5151
+ return { data, body };
5136
5152
  }
5137
5153
  async listRunners() {
5138
5154
  const runners = [];
@@ -899,11 +899,12 @@ var ProjectRepoSchema = z6.object({
899
899
  defaultBranch: z6.string().min(1)
900
900
  });
901
901
  var TurnWorkspaceSchema = z6.discriminatedUnion("kind", [
902
- z6.object({ kind: z6.literal("home") }),
902
+ z6.object({ kind: z6.literal("home"), thread: UlidSchema.optional() }),
903
903
  z6.object({
904
904
  kind: z6.literal("project"),
905
905
  repo: ProjectRepoSchema,
906
- branches: z6.array(z6.string().min(1))
906
+ branches: z6.array(z6.string().min(1)),
907
+ thread: UlidSchema.optional()
907
908
  }),
908
909
  z6.object({ kind: z6.literal("task"), repo: ProjectRepoSchema, taskId: UlidSchema })
909
910
  ]);
@@ -954,7 +955,7 @@ var TurnOutcomeSchema = z6.object({
954
955
  model: z6.string().nullable(),
955
956
  work: TurnWorkSchema.nullable()
956
957
  });
957
- var TurnAckSchema = z6.object({ dropWorktree: z6.boolean() });
958
+ var TurnAckSchema = z6.object({ dropWorkspace: z6.boolean() });
958
959
  var LandRequestSchema = z6.object({
959
960
  repo: ProjectRepoSchema,
960
961
  branch: z6.string().min(1)
@@ -1381,7 +1382,7 @@ var RunnerConfigSchema = z10.object({
1381
1382
  });
1382
1383
 
1383
1384
  // packages/shared/dist/version.js
1384
- var STELLARIS_VERSION = "0.3.9";
1385
+ var STELLARIS_VERSION = "0.3.10";
1385
1386
 
1386
1387
  // packages/board-core/dist/errors.js
1387
1388
  var BoardError = class extends Error {
@@ -1429,15 +1430,28 @@ async function writeJson(file, value) {
1429
1430
  await writeFileAtomic(file, `${JSON.stringify(value, null, 2)}
1430
1431
  `);
1431
1432
  }
1433
+ function parseMatter(raw) {
1434
+ return matter(raw, {});
1435
+ }
1432
1436
  async function readMarkdown(file, schema) {
1433
1437
  const raw = await readFile(file, "utf8");
1434
- const parsed = matter(raw);
1438
+ const parsed = parseMatter(raw);
1435
1439
  return { data: schema.parse(parsed.data), body: parsed.content.replace(/^\n/, "") };
1436
1440
  }
1441
+ async function readLooseMarkdown(file) {
1442
+ const raw = await readFile(file, "utf8");
1443
+ try {
1444
+ const parsed = parseMatter(raw);
1445
+ return { data: parsed.data, body: parsed.content.replace(/^\n/, ""), error: null };
1446
+ } catch (error) {
1447
+ const reason = (error instanceof Error ? error.message : String(error)).split("\n")[0] ?? "";
1448
+ return { data: {}, body: raw, error: reason.replace(/:$/, "") };
1449
+ }
1450
+ }
1437
1451
  async function writeMarkdown(file, data, body) {
1438
1452
  const normalized2 = body.endsWith("\n") || body.length === 0 ? body : `${body}
1439
1453
  `;
1440
- await writeFileAtomic(file, matter.stringify(normalized2, data));
1454
+ await writeFileAtomic(file, matter.stringify({ content: normalized2 }, data, {}));
1441
1455
  }
1442
1456
  function isMissing(error) {
1443
1457
  return typeof error === "object" && error !== null && error.code === "ENOENT";
@@ -3289,7 +3303,7 @@ ${charter.purpose}
3289
3303
  }
3290
3304
  async readProfile(name) {
3291
3305
  const file = this.paths.agentProfile(name);
3292
- return await exists(file) ? (await readMarkdown(file, z12.record(z12.string(), z12.unknown()))).body : "";
3306
+ return await exists(file) ? (await readLooseMarkdown(file)).body : "";
3293
3307
  }
3294
3308
  /** The society's norms, the one knowledge topic every turn loads. Empty until the steward writes it. */
3295
3309
  async readSocietyNorms() {
@@ -3336,10 +3350,11 @@ ${charter.purpose}
3336
3350
  if (!name.success) {
3337
3351
  continue;
3338
3352
  }
3339
- const doc = await readMarkdown(file, z12.record(z12.string(), z12.unknown()));
3353
+ const doc = await readLooseMarkdown(file);
3340
3354
  skills.push({
3341
3355
  name: name.data,
3342
- summary: skillSummary(doc.data, doc.body),
3356
+ // The index is where its writer sees that the file needs fixing.
3357
+ summary: doc.error === null ? skillSummary(doc.data, doc.body) : `its frontmatter does not parse (${doc.error}), so fix the file`,
3343
3358
  scope,
3344
3359
  path: file
3345
3360
  });
@@ -5001,7 +5016,7 @@ ${rationale}`}`);
5001
5016
  }
5002
5017
  async readMemoryCore(agent) {
5003
5018
  const file = this.paths.agentMemoryCore(agent);
5004
- return await exists(file) ? (await readMarkdown(file, z12.record(z12.string(), z12.unknown()))).body : "";
5019
+ return await exists(file) ? (await readLooseMarkdown(file)).body : "";
5005
5020
  }
5006
5021
  /**
5007
5022
  * Moves a conversation's cursor forward to `cursor` once a turn there has delivered up to it. It
@@ -5216,7 +5231,8 @@ ${rationale}`}`);
5216
5231
  if (!await exists(file)) {
5217
5232
  return { data: { project: slug }, body: "" };
5218
5233
  }
5219
- return readMarkdown(file, z12.record(z12.string(), z12.unknown()));
5234
+ const { data, body } = await readLooseMarkdown(file);
5235
+ return { data, body };
5220
5236
  }
5221
5237
  async listRunners() {
5222
5238
  const runners2 = [];
@@ -6503,7 +6519,10 @@ var Scheduler = class {
6503
6519
  now;
6504
6520
  log;
6505
6521
  pending = /* @__PURE__ */ new Map();
6506
- /** Turns in flight by session key, each with the lane it holds (see `laneOf`). */
6522
+ /**
6523
+ * Turns in flight by session key. A conversation runs one turn at a time, and different
6524
+ * conversations side by side, since each thread's works in a place of its own.
6525
+ */
6507
6526
  running = /* @__PURE__ */ new Map();
6508
6527
  /** Steers under way, by session key; a pass does not wait for a runner to answer one. */
6509
6528
  steering = /* @__PURE__ */ new Map();
@@ -6560,7 +6579,7 @@ var Scheduler = class {
6560
6579
  }
6561
6580
  await this.passing?.catch(() => void 0);
6562
6581
  await Promise.allSettled([
6563
- ...[...this.running.values()].map((turn) => turn.promise),
6582
+ ...this.running.values(),
6564
6583
  ...this.completing,
6565
6584
  ...this.steering.values()
6566
6585
  ]);
@@ -6610,7 +6629,7 @@ var Scheduler = class {
6610
6629
  async drain() {
6611
6630
  await Promise.allSettled([
6612
6631
  ...this.steering.values(),
6613
- ...[...this.running.values()].map((turn) => turn.promise),
6632
+ ...this.running.values(),
6614
6633
  ...this.completing
6615
6634
  ]);
6616
6635
  }
@@ -7228,7 +7247,7 @@ var Scheduler = class {
7228
7247
  continue;
7229
7248
  }
7230
7249
  const key = sessionKey(agent.name, latest.scope);
7231
- if (this.pending.has(key) || this.laneBusy(key)) {
7250
+ if (this.pending.has(key) || this.running.has(key)) {
7232
7251
  continue;
7233
7252
  }
7234
7253
  this.state.lastReflection[agent.name] = iso(now);
@@ -7526,22 +7545,6 @@ var Scheduler = class {
7526
7545
  this.state.costSinceReport = 0;
7527
7546
  this.state.turnsSinceReport = 0;
7528
7547
  }
7529
- /**
7530
- * The lane a turn runs in: a task's conversation has its own, with a worktree of its own; a
7531
- * member's home conversation and its proposal and topic threads in a scope share the scope's
7532
- * worktree, and so one lane, one turn at a time.
7533
- */
7534
- laneOf(dispatch) {
7535
- return dispatch.thread?.task === true ? sessionKey(dispatch.agent, dispatch.project, dispatch.thread.id) : sessionKey(dispatch.agent, dispatch.project);
7536
- }
7537
- laneBusy(lane) {
7538
- for (const turn of this.running.values()) {
7539
- if (turn.lane === lane) {
7540
- return true;
7541
- }
7542
- }
7543
- return false;
7544
- }
7545
7548
  /**
7546
7549
  * Delivers ready message wakes into the turn already running in their conversation, rather than
7547
7550
  * keeping them for a turn of their own. Each stays queued until that turn ends and is checked
@@ -7592,10 +7595,6 @@ var Scheduler = class {
7592
7595
  if (this.running.size >= this.concurrency) {
7593
7596
  break;
7594
7597
  }
7595
- const lane = this.laneOf(item.dispatch);
7596
- if (this.laneBusy(lane)) {
7597
- continue;
7598
- }
7599
7598
  this.pending.delete(key);
7600
7599
  if (await this.alreadyRead(item)) {
7601
7600
  this.log.info({ agent: item.dispatch.agent, project: item.dispatch.project }, "dropping message wake, its conversation has read the message");
@@ -7636,7 +7635,7 @@ var Scheduler = class {
7636
7635
  this.running.delete(key);
7637
7636
  }
7638
7637
  })();
7639
- this.running.set(key, { lane, promise });
7638
+ this.running.set(key, promise);
7640
7639
  }
7641
7640
  }
7642
7641
  /** Starts each queued completion once the turn that finished the task's last stage has ended. */
@@ -7669,7 +7668,7 @@ import { randomUUID } from "node:crypto";
7669
7668
  var TURN_CONTRACT = [
7670
7669
  "- Every turn belongs to one conversation, named at the top of your prompt: your home conversation in a project or the society, for its channels and for work tied to no thread, or the conversation of one thread you take part in, a task's, a proposal's, or a topic's. Each has its own session and its own turns, which may run while this one does. The prompt carries this conversation only: in a thread, its new messages, or the whole thread when the conversation is new, and in a task's thread the task and its stage; at home, the channel messages that concern you. Read anything else you need with get_task, search, and the board's files, and leave other threads' and other projects' work to their own turns.",
7671
7670
  "- Act through the board tools (the `board` MCP server). Holding a stage is a lease; every turn that touches the task renews it.",
7672
- "- Work only inside your worktree. In a task's conversation it is a worktree of its own on the task's branch `task/<id>`: commit your work there. After each turn the runner commits what you left on the branch and lets it go for the next holder. Your home worktree is on your own branch, `agent/<name>`, for work tied to no task.",
7671
+ "- Work only inside your worktree. In a task's conversation it is a worktree of its own on the task's branch `task/<id>`: commit your work there. After each turn the runner commits what you left on the branch and lets it go for the next holder. Your home worktree is on your own branch, `agent/<name>`, for work tied to no task. In a proposal's or a topic's conversation it is a worktree of its own, detached at your own branch's tip, which goes when the thread closes, so work that should last goes on a task's branch.",
7673
7672
  "- Posts that would wake this conversation may arrive while you work, delivered into the turn under a heading that says so and formatted like the digest. They are part of this turn: act on them now, after the step in hand, or not at all, as with the digest. The user may also stop a turn; the next turn is told.",
7674
7673
  "- Silence is allowed. If the digest needs no reply, post nothing.",
7675
7674
  "- An @mention wakes the citizen named, and every wake costs a turn. Address someone with @ only when you need them to act; when you merely refer to citizens, write their names plainly. @user wakes nobody, since the user takes no turns; it is what puts a question before the user, so use it only when you need the user to answer or decide.",
@@ -7734,7 +7733,7 @@ function renderInstructions(input) {
7734
7733
  if (norms.length > 0) {
7735
7734
  sections.push("", "## Society norms", "", norms);
7736
7735
  }
7737
- sections.push("", "## Core memory", "", memory.length === 0 ? "(empty)" : memory, "", "## Skills", "", ...renderSkillsIndex(input.skills ?? []), "", "## Where things are", "", `- Your home directory: ${input.homeDir}. memory/core.md is loaded every turn; memory/<topic>.md is your archive; skills/<name>/SKILL.md are your skills; projects/<slug>/notes.md are your notes. You may read and write all of it, and the board's search covers your archive and skills for you alone. The runner keeps it in git and shares it with the board after every turn, so whatever machine your next turn runs on has it; leave git there to the runner. Files over ${HOME_FILE_LIMIT_BYTES / (1024 * 1024)} MB are not kept.`, `- ${HOME_SCRATCH}/ in your home is your working folder in turns outside any project: it is never kept or shared, so put downloads, environments, and other working files there, and keep the rest of your home for what you mean to remember.`, `- The board projection: ${input.boardDir} (read-only markdown: society and project channels, tasks, threads; shared knowledge under projects/<slug>/knowledge/ and society/knowledge/; the society's skills under society/skills/). Search it with your file tools; act through the board tools.`, "", "## Turn contract", "", TURN_CONTRACT, "", "## Planning", "", PLANNING, "", "## Governance", "", GOVERNANCE);
7736
+ sections.push("", "## Core memory", "", memory.length === 0 ? "(empty)" : memory, "", "## Skills", "", ...renderSkillsIndex(input.skills ?? []), "", "## Where things are", "", `- Your home directory: ${input.homeDir}. memory/core.md is loaded every turn; memory/<topic>.md is your archive; skills/<name>/SKILL.md are your skills; projects/<slug>/notes.md are your notes. You may read and write all of it, and the board's search covers your archive and skills for you alone. The runner keeps it in git and shares it with the board after every turn, so whatever machine your next turn runs on has it; leave git there to the runner. Files over ${HOME_FILE_LIMIT_BYTES / (1024 * 1024)} MB are not kept.`, `- ${HOME_SCRATCH}/ in your home is your working folder in turns outside any project, and a thread's conversation there works in a folder of its own inside it, which goes when the thread closes: none of it is kept or shared, so put downloads, environments, and other working files there, and keep the rest of your home for what you mean to remember.`, `- The board projection: ${input.boardDir} (read-only markdown: society and project channels, tasks, threads; shared knowledge under projects/<slug>/knowledge/ and society/knowledge/; the society's skills under society/skills/). Search it with your file tools; act through the board tools.`, "", "## Turn contract", "", TURN_CONTRACT, "", "## Planning", "", PLANNING, "", "## Governance", "", GOVERNANCE);
7738
7737
  if (input.onboarding !== void 0) {
7739
7738
  sections.push("", "## First turn", "", renderOnboardingPreamble(input.onboarding));
7740
7739
  }
@@ -8066,7 +8065,7 @@ var TurnHost = class {
8066
8065
  const societyScope = dispatch.project === SOCIETY_SCOPE;
8067
8066
  const taskId = thread?.task === true ? thread.id : void 0;
8068
8067
  const project = societyScope ? null : await this.board.readProject(dispatch.project);
8069
- const workspace = await this.workspaceOf(project, taskId);
8068
+ const workspace = await this.workspaceOf(project, thread);
8070
8069
  const sessions = await this.board.readSessions(agent.name, dispatch.project, thread?.id);
8071
8070
  const recorded = sessions.runner === runner.name ? sessions[agent.cli] : void 0;
8072
8071
  const newSession = recorded === void 0;
@@ -8178,7 +8177,8 @@ var TurnHost = class {
8178
8177
  void this.board.heldClaims(agent.name).then((claims) => this.renewLeases(actor, claims.filter((each) => each.project === dispatch.project).map((each) => each.id))).catch(() => void 0);
8179
8178
  }, LEASE_RENEW_MS);
8180
8179
  keepLeases.unref?.();
8181
- const { promise: ended, resolve: done } = Promise.withResolvers();
8180
+ const { promise: ended, resolve: done, reject: fail } = Promise.withResolvers();
8181
+ ended.catch(() => void 0);
8182
8182
  this.open.set(record.id, {
8183
8183
  job,
8184
8184
  runner: runner.name,
@@ -8189,14 +8189,14 @@ var TurnHost = class {
8189
8189
  outstanding: null,
8190
8190
  stopRequestedBy: null,
8191
8191
  held: held.map((each) => each.id),
8192
- taskId,
8193
8192
  costSoFarUsd,
8194
8193
  configuredModel: agent.model ?? null,
8195
8194
  transcript: [],
8196
8195
  coldToken: residentKey === null ? token : null,
8197
8196
  residentKey: residentKey === null ? null : `${runner.name}|${residentKey}`,
8198
8197
  keepLeases,
8199
- done
8198
+ done,
8199
+ fail
8200
8200
  });
8201
8201
  this.log.info({
8202
8202
  agent: agent.name,
@@ -8335,7 +8335,7 @@ var TurnHost = class {
8335
8335
  }
8336
8336
  /**
8337
8337
  * Records a turn's end from its outcome. Returns null for a turn the host does not know, else
8338
- * whether the task the turn worked on has ended, so the runner may drop its worktree.
8338
+ * whether the task or thread the turn worked in has ended, so the runner may drop its workspace.
8339
8339
  */
8340
8340
  async closeTurn(turnId, outcome) {
8341
8341
  const turn = this.open.get(turnId);
@@ -8348,9 +8348,6 @@ var TurnHost = class {
8348
8348
  this.board.revokeTurnToken(turn.coldToken);
8349
8349
  }
8350
8350
  const { job } = turn;
8351
- if (outcome.session !== job.session) {
8352
- await this.board.writeSession(job.agent, job.scope, job.cli, outcome.session, turn.runner, job.thread);
8353
- }
8354
8351
  const finished = {
8355
8352
  ...turn.record,
8356
8353
  session: outcome.session,
@@ -8366,17 +8363,29 @@ var TurnHost = class {
8366
8363
  ...outcome.work === null ? {} : { work: outcome.work },
8367
8364
  ...outcome.exitReason === "stopped" ? { stoppedBy: turn.stopRequestedBy ?? USER_NAME } : {}
8368
8365
  };
8369
- if (outcome.exitReason === "completed" || outcome.exitReason === "blocked" || outcome.exitReason === "stopped") {
8370
- await this.board.setDigestCursor(job.agent, job.scope, turn.delivered, job.thread);
8371
- await this.renewLeases(turn.actor, turn.held);
8372
- if (outcome.status?.needsUserDecision === true) {
8373
- await this.askUser(turn.actor, job.scope, job.thread, turn.record.startedAt, outcome.status.summary);
8366
+ const recorded = await this.recorded(turn, async () => {
8367
+ if (outcome.session !== job.session) {
8368
+ await this.board.writeSession(job.agent, job.scope, job.cli, outcome.session, turn.runner, job.thread);
8369
+ }
8370
+ if (outcome.exitReason === "completed" || outcome.exitReason === "blocked" || outcome.exitReason === "stopped") {
8371
+ await this.board.setDigestCursor(job.agent, job.scope, turn.delivered, job.thread);
8372
+ await this.renewLeases(turn.actor, turn.held);
8373
+ if (outcome.status?.needsUserDecision === true) {
8374
+ await this.askUser(turn.actor, job.scope, job.thread, turn.record.startedAt, outcome.status.summary);
8375
+ }
8374
8376
  }
8377
+ await this.board.finishTurn(finished, turn.transcript);
8378
+ });
8379
+ if (recorded) {
8380
+ this.log.info({
8381
+ agent: job.agent,
8382
+ project: job.scope,
8383
+ thread: job.thread,
8384
+ exitReason: finished.exitReason
8385
+ }, "turn finished");
8386
+ turn.done(finished);
8375
8387
  }
8376
- await this.board.finishTurn(finished, turn.transcript);
8377
- this.log.info({ agent: job.agent, project: job.scope, thread: job.thread, exitReason: finished.exitReason }, "turn finished");
8378
- turn.done(finished);
8379
- return { dropWorktree: await this.taskEnded(turn.taskId) };
8388
+ return { dropWorkspace: await this.conversationEnded(job) };
8380
8389
  }
8381
8390
  /** Ends a turn whose outcome will never come, as when its runner went away, as failed. */
8382
8391
  async abortTurn(turnId, reason) {
@@ -8396,9 +8405,26 @@ var TurnHost = class {
8396
8405
  error: reason,
8397
8406
  toolCalls: turn.transcript.filter((entry) => entry.event.type === "tool_call").length
8398
8407
  };
8399
- await this.board.finishTurn(finished, turn.transcript);
8400
- this.log.warn({ agent: turn.job.agent, project: turn.job.scope, reason }, "turn aborted");
8401
- turn.done(finished);
8408
+ if (await this.recorded(turn, () => this.board.finishTurn(finished, turn.transcript))) {
8409
+ this.log.warn({ agent: turn.job.agent, project: turn.job.scope, reason }, "turn aborted");
8410
+ turn.done(finished);
8411
+ }
8412
+ }
8413
+ /**
8414
+ * Records the end of a turn already taken out of `open`, and says whether it could. The scheduler
8415
+ * holds the turn's conversation until the turn ends, and nothing would end it once it has left
8416
+ * `open`, so a turn whose end could not be recorded ends with the error rather than holding its
8417
+ * conversation for good.
8418
+ */
8419
+ async recorded(turn, record) {
8420
+ try {
8421
+ await record();
8422
+ return true;
8423
+ } catch (error) {
8424
+ this.log.error({ agent: turn.job.agent, project: turn.job.scope, error: String(error) }, "could not record the end of a turn");
8425
+ turn.fail(error);
8426
+ return false;
8427
+ }
8402
8428
  }
8403
8429
  /** Records a turn that could never start, as when no runner could take its agent's CLI. */
8404
8430
  async refuseTurn(dispatch, runner, reason) {
@@ -8451,29 +8477,34 @@ var TurnHost = class {
8451
8477
  this.residentTokens.set(slot, token);
8452
8478
  return token;
8453
8479
  }
8454
- async workspaceOf(project, taskId) {
8480
+ /** Where a conversation works: a task's on its branch, any other thread's in a place of its own. */
8481
+ async workspaceOf(project, thread) {
8482
+ const own = thread === void 0 || thread.task ? {} : { thread: thread.id };
8455
8483
  if (project === null) {
8456
- return { kind: "home" };
8484
+ return { kind: "home", ...own };
8457
8485
  }
8458
8486
  const repo = {
8459
8487
  slug: project.slug,
8460
8488
  origin: project.repo,
8461
8489
  defaultBranch: project.defaultBranch
8462
8490
  };
8463
- if (taskId !== void 0) {
8464
- return { kind: "task", repo, taskId };
8491
+ if (thread?.task === true) {
8492
+ return { kind: "task", repo, taskId: thread.id };
8465
8493
  }
8466
8494
  const branches = (await this.board.listTasks(project.slug)).filter((task) => task.status === "open" || task.status === "claimed").map((task) => `task/${task.id}`);
8467
- return { kind: "project", repo, branches };
8495
+ return { kind: "project", repo, branches, ...own };
8468
8496
  }
8469
- /** Whether a task conversation's task has ended, which frees its worktree. */
8470
- async taskEnded(taskId) {
8471
- if (taskId === void 0) {
8497
+ /** Whether a thread conversation's task or thread has ended, which frees its workspace. */
8498
+ async conversationEnded(job) {
8499
+ if (job.thread === void 0) {
8472
8500
  return false;
8473
8501
  }
8474
8502
  try {
8475
- const { task } = await this.board.findTask(taskId);
8476
- return task.status === "done" || task.status === "abandoned";
8503
+ if (job.workspace.kind === "task") {
8504
+ const { task } = await this.board.findTask(job.thread);
8505
+ return task.status === "done" || task.status === "abandoned";
8506
+ }
8507
+ return (await this.board.readThread(job.thread)).state === "closed";
8477
8508
  } catch {
8478
8509
  return false;
8479
8510
  }
@@ -8676,7 +8707,7 @@ var RunnerHub = class {
8676
8707
  async turnOutcome(runner, turnId, outcome) {
8677
8708
  this.assertOwns(runner, turnId);
8678
8709
  const ack = await this.host.closeTurn(turnId, outcome);
8679
- return ack ?? { dropWorktree: false };
8710
+ return ack ?? { dropWorkspace: false };
8680
8711
  }
8681
8712
  answer(runner, request, answer) {
8682
8713
  const pending = this.pending.get(request);
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "codex",
9
9
  "multi-agent"
10
10
  ],
11
- "version": "0.3.9",
11
+ "version": "0.3.10",
12
12
  "license": "MIT",
13
13
  "homepage": "https://github.com/kr4t0n/stellaris",
14
14
  "repository": {
@@ -1 +1 @@
1
- import{b as e,ln as t,wt as n}from"./format-Cc6KjiYZ.js";import{L as r,S as i,n as a,r as o,t as s}from"./index-WB0PqHlY.js";var c=t(),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:t}){let u=n(),d=r({select:e=>e.location.state.__TSR_index}),f=o(),p=i(),m=e(),h=a(f,d);if(h===null)return t;let g=s(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};
1
+ import{b as e,ln as t,wt as n}from"./format-lz-5NQWT.js";import{L as r,S as i,n as a,r as o,t as s}from"./index-CoKyPk79.js";var c=t(),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:t}){let u=n(),d=r({select:e=>e.location.state.__TSR_index}),f=o(),p=i(),m=e(),h=a(f,d);if(h===null)return t;let g=s(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};
@@ -1 +1 @@
1
- import{D as e,F as t,Ht as n,R as r,b as i,g as a,ln as o,pn as s,un as c,y as l}from"./format-Cc6KjiYZ.js";import{I as u,V as d,c as f,i as p}from"./index-WB0PqHlY.js";import{t as m}from"./MessageItem-CWZ0X2Vr.js";import{n as h,t as g}from"./Pane-DxXjKLAj.js";import{n as _}from"./ThreadCard-Bn7HfgDI.js";import{i as v}from"./ThreadForms-CzYPjU1-.js";import{t as y}from"./useStickyScroll-NvlGfk6Z.js";var b=s(c(),1),x=o();function S(){let{_splat:e=`general`}=d({from:`/c/$`});return(0,x.jsx)(C,{channel:e},e)}function C({channel:o}){let{api:s}=e(),c=n({queryKey:[`channel`,o],queryFn:()=>s.channel(o)}),d=t(),S=a(),C=i(),w=l(3e4),[T,E]=(0,b.useState)(!1),D=(0,b.useMemo)(()=>{let e=(c.data??[]).map(e=>({kind:`message`,at:e.ts,id:e.id,message:e})),t=(d.data??[]).filter(e=>e.channel===o).map(e=>({kind:`thread`,at:e.openedAt,id:e.id,thread:e}));return[...e,...t].toSorted((e,t)=>e.at.localeCompare(t.at)||e.id.localeCompare(t.id))},[c.data,d.data,o]),O=c.data?.at(-1)?.id??null;(0,b.useEffect)(()=>u(o,O),[o,O]);let{ref:k,onScroll:A}=y(),[j,M]=o.split(`/`),N=M??j??o,P=M===void 0?void 0:C.data?.find(e=>e.slug===j),F=M===void 0?`society`:P?.name??j,I=P?.archived!==void 0,L=D.filter(e=>e.kind===`thread`&&e.thread.state===`open`);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(g,{title:`# ${N}`,subtitle:`${F} · ${c.data?.length??0} messages${L.length>0?` · ${L.length} open threads`:``}`,trailing:T||I?null:(0,x.jsx)(f,{onClick:()=>E(!0),children:`New thread`})}),T?(0,x.jsx)(v,{channel:o,onDone:()=>E(!1)}):null,(0,x.jsx)(`div`,{ref:k,onScroll:A,className:`flex-1 overflow-y-auto py-2`,children:c.error instanceof r&&c.error.status===404?(0,x.jsxs)(h,{children:[`There is no channel `,o,`.`]}):c.data===void 0?(0,x.jsx)(h,{children:`Reading the channel…`}):D.length===0?(0,x.jsx)(h,{children:`Nothing has been said here yet.`}):D.map(e=>e.kind===`message`?(0,x.jsx)(m,{message:e.message,members:S.data,now:w},e.id):(0,x.jsx)(_,{thread:e.thread,now:w},e.id))}),c.data===void 0?null:I?(0,x.jsxs)(`p`,{className:`border-t border-line px-4 py-3 text-meta`,children:[F,` is archived, so its channels are read-only.`]}):(0,x.jsx)(p,{target:{channel:o},placeholder:`Message #${N}`})]})}export{S as ChannelView};
1
+ import{D as e,F as t,Ht as n,R as r,b as i,g as a,ln as o,pn as s,un as c,y as l}from"./format-lz-5NQWT.js";import{I as u,V as d,c as f,i as p}from"./index-CoKyPk79.js";import{t as m}from"./MessageItem-Bfpve1_Y.js";import{n as h,t as g}from"./Pane-xzIpwnBD.js";import{n as _}from"./ThreadCard-fvf8cY_l.js";import{i as v}from"./ThreadForms-D09Ykgj9.js";import{t as y}from"./useStickyScroll-CfP3Yl_4.js";var b=s(c(),1),x=o();function S(){let{_splat:e=`general`}=d({from:`/c/$`});return(0,x.jsx)(C,{channel:e},e)}function C({channel:o}){let{api:s}=e(),c=n({queryKey:[`channel`,o],queryFn:()=>s.channel(o)}),d=t(),S=a(),C=i(),w=l(3e4),[T,E]=(0,b.useState)(!1),D=(0,b.useMemo)(()=>{let e=(c.data??[]).map(e=>({kind:`message`,at:e.ts,id:e.id,message:e})),t=(d.data??[]).filter(e=>e.channel===o).map(e=>({kind:`thread`,at:e.openedAt,id:e.id,thread:e}));return[...e,...t].toSorted((e,t)=>e.at.localeCompare(t.at)||e.id.localeCompare(t.id))},[c.data,d.data,o]),O=c.data?.at(-1)?.id??null;(0,b.useEffect)(()=>u(o,O),[o,O]);let{ref:k,onScroll:A}=y(),[j,M]=o.split(`/`),N=M??j??o,P=M===void 0?void 0:C.data?.find(e=>e.slug===j),F=M===void 0?`society`:P?.name??j,I=P?.archived!==void 0,L=D.filter(e=>e.kind===`thread`&&e.thread.state===`open`);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(g,{title:`# ${N}`,subtitle:`${F} · ${c.data?.length??0} messages${L.length>0?` · ${L.length} open threads`:``}`,trailing:T||I?null:(0,x.jsx)(f,{onClick:()=>E(!0),children:`New thread`})}),T?(0,x.jsx)(v,{channel:o,onDone:()=>E(!1)}):null,(0,x.jsx)(`div`,{ref:k,onScroll:A,className:`flex-1 overflow-y-auto py-2`,children:c.error instanceof r&&c.error.status===404?(0,x.jsxs)(h,{children:[`There is no channel `,o,`.`]}):c.data===void 0?(0,x.jsx)(h,{children:`Reading the channel…`}):D.length===0?(0,x.jsx)(h,{children:`Nothing has been said here yet.`}):D.map(e=>e.kind===`message`?(0,x.jsx)(m,{message:e.message,members:S.data,now:w},e.id):(0,x.jsx)(_,{thread:e.thread,now:w},e.id))}),c.data===void 0?null:I?(0,x.jsxs)(`p`,{className:`border-t border-line px-4 py-3 text-meta`,children:[F,` is archived, so its channels are read-only.`]}):(0,x.jsx)(p,{target:{channel:o},placeholder:`Message #${N}`})]})}export{S as ChannelView};
@@ -1,2 +1,2 @@
1
- import{D as e,E as t,H as n,I as r,L as i,P as a,R as o,T as s,_ as c,a as l,cn as u,ct as d,d as f,f as p,g as m,ln as h,o as g,p as _,pn as v,t as y,un as b,w as x,y as S}from"./format-Cc6KjiYZ.js";import{B as C,H as w,R as T,S as E,T as D,V as ee,_ as te,c as O,f as k,g as ne,h as re,i as ie,m as A,p as j,v as M,x as N,y as P}from"./index-WB0PqHlY.js";import{t as F}from"./Markdown-B2tp7ki-.js";import{n as I,t as ae}from"./Pane-DxXjKLAj.js";import{n as oe,r as L}from"./ThreadForms-CzYPjU1-.js";import{t as se}from"./useStickyScroll-NvlGfk6Z.js";import{a as R,c as ce,i as z,l as B,n as le,o as V,s as ue,t as de}from"./citizen-BqHxQY9q.js";var H=v(b(),1),U=h(),fe={completed:`completed`,timeout:`timed out`,error:`failed`,interrupted:`was interrupted`,blocked:`was blocked`,stopped:`was stopped by you`};function W(e){return new Date(e).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1})}function G({label:e,tone:t,children:n}){return(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`p`,{className:`px-0.5 pb-0.5 text-[10px] tracking-wider text-fg-muted uppercase`,children:e}),(0,U.jsx)(`pre`,{className:`max-h-72 overflow-auto rounded-md bg-surface-0/60 p-2 font-mono text-[11px] leading-relaxed whitespace-pre-wrap break-words ${t}`,children:n})]})}function pe({step:e,open:t}){if(e.kind===`say`)return(0,U.jsx)(`li`,{className:`py-1.5 text-sm`,title:W(e.at),children:(0,U.jsx)(F,{text:e.text})});if(e.kind===`error`)return(0,U.jsx)(`li`,{className:`py-1 font-mono text-xs break-words text-red-400`,title:W(e.at),children:e.message});if(e.kind===`steer`)return(0,U.jsx)(`li`,{children:(0,U.jsxs)(`details`,{open:t,className:`group rounded-md open:bg-surface-2/30`,children:[(0,U.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-center gap-2 rounded-md px-1.5 py-1 text-xs hover:bg-surface-2/40`,children:[(0,U.jsx)(`span`,{"aria-hidden":`true`,className:`grid w-3 shrink-0 place-items-center text-sky-300`,children:`↳`}),(0,U.jsxs)(`span`,{className:`shrink-0 text-fg-tertiary`,children:[`Took `,re(e.posts.length)]}),(0,U.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-fg-secondary`,children:e.from.length===0?``:`from ${e.from.join(`, `)}, delivered mid-turn`}),(0,U.jsx)(`span`,{className:`shrink-0 font-mono text-[10px] text-fg-muted`,children:W(e.at)})]}),(0,U.jsx)(`ul`,{className:`space-y-1.5 px-1.5 pb-1.5`,children:e.posts.map(e=>(0,U.jsxs)(`li`,{className:`rounded-md bg-surface-0/40 px-2.5 py-2`,children:[(0,U.jsxs)(`p`,{className:`flex gap-1.5 text-meta`,children:[(0,U.jsx)(`span`,{className:`text-fg-secondary`,children:e.author}),(0,U.jsxs)(`span`,{className:`min-w-0 truncate`,children:[`in `,e.where]}),e.at===``?null:(0,U.jsx)(`span`,{className:`ml-auto shrink-0 font-mono`,children:W(e.at)})]}),(0,U.jsx)(`div`,{className:`mt-1 text-sm`,children:(0,U.jsx)(F,{text:e.body})})]},`${e.at}-${e.author}`))})]})});let n=e.ok===null?(0,U.jsx)(`span`,{"aria-label":`running`,className:`size-1.5 animate-pulse rounded-full bg-amber-300`}):e.ok?(0,U.jsx)(`span`,{"aria-label":`succeeded`,className:`text-[11px] text-emerald-400`,children:`✓`}):(0,U.jsx)(`span`,{"aria-label":`failed`,className:`text-[11px] text-red-400`,children:`✕`});return(0,U.jsx)(`li`,{children:(0,U.jsxs)(`details`,{open:t,className:`group rounded-md open:bg-surface-2/30`,children:[(0,U.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-center gap-2 rounded-md px-1.5 py-1 text-xs hover:bg-surface-2/40`,children:[(0,U.jsx)(`span`,{className:`grid w-3 shrink-0 place-items-center`,children:n}),(0,U.jsx)(`span`,{className:`shrink-0 font-mono text-fg-tertiary`,children:ne(e.name)}),(0,U.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-fg-secondary`,children:e.summary}),(0,U.jsx)(`span`,{className:`shrink-0 font-mono text-[10px] text-fg-muted`,children:W(e.at)})]}),(0,U.jsxs)(`div`,{className:`space-y-1.5 px-1.5 pb-1.5`,children:[(0,U.jsx)(G,{label:`Input`,tone:`text-fg-secondary`,children:e.detail}),e.output===null?null:(0,U.jsx)(G,{label:e.ok===!1?`Returned an error`:`Returned`,tone:e.ok===!1?`text-red-300`:`text-fg-tertiary`,children:e.output})]})]})})}function K({steps:e}){let[t,n]=(0,H.useState)(!1),r=e.filter(e=>e.kind===`tool`).length;return e.length===0?(0,U.jsx)(`p`,{className:`mt-2 text-meta`,children:`No steps yet.`}):(0,U.jsxs)(U.Fragment,{children:[r===0?null:(0,U.jsxs)(`p`,{className:`mt-2 flex items-center gap-2 text-meta`,children:[r===1?`1 tool call`:`${r} tool calls`,(0,U.jsx)(`button`,{type:`button`,onClick:()=>n(!t),className:`rounded-md px-1.5 py-0.5 text-fg-tertiary transition-colors hover:bg-surface-2/60 hover:text-fg-primary`,children:t?`Collapse all`:`Expand all`})]}),(0,U.jsx)(`ol`,{className:`mt-1 space-y-0.5`,children:e.map(e=>(0,U.jsx)(pe,{step:e,open:t},e.seq))})]})}function me({turn:e,running:t,now:n}){return e.end===null?t?(0,U.jsxs)(`p`,{className:`mt-3 flex items-center gap-2 text-meta`,"aria-live":`polite`,children:[(0,U.jsx)(`span`,{className:`size-1.5 animate-pulse rounded-full bg-emerald-400`}),`Working · last step `,y(e.lastAt,n)]}):(0,U.jsx)(`p`,{className:`mt-3 text-meta`,children:`The turn ended without a report.`}):(0,U.jsxs)(`section`,{className:`mt-3 rounded-xl bg-surface-2/40 px-3.5 py-3 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)]`,children:[(0,U.jsxs)(`p`,{className:`text-caps`,children:[`The turn `,fe[e.end.exitReason],` `,y(e.end.at,n),e.fromStart?` · ${j(e.startedAt,Date.parse(e.end.at))}`:``]}),e.end.summary===null?null:(0,U.jsx)(`p`,{className:`mt-1 text-sm text-fg-secondary`,children:e.end.summary})]})}var q=30,he={turn:`turn`,board:`board`,merge:`both kept`},ge={turn:`Changed by a turn`,board:`Written by the board`,merge:`Both versions kept`};function _e(e){return e.status===`added`?`new`:e.status===`deleted`?`deleted`:e.added===null||e.removed===null?`binary`:`+${e.added} −${e.removed}`}function ve(e){return e.files.map(e=>`${e.path} ${_e(e)}`).join(`, `)}function ye(e){return e.startsWith(`@@`)||e.startsWith(`\\`)?`text-fg-muted`:e.startsWith(`+`)?`text-emerald-300`:e.startsWith(`-`)?`text-red-300`:`text-fg-tertiary`}function be({file:e}){return(0,U.jsxs)(`div`,{children:[(0,U.jsxs)(`p`,{className:`text-xs text-fg-secondary`,children:[e.path,e.status===`modified`?``:(0,U.jsxs)(`span`,{className:`text-fg-muted`,children:[` · `,e.status]})]}),e.patch===null?(0,U.jsx)(`p`,{className:`mt-1 text-meta`,children:`A binary file; its contents are not shown.`}):e.patch===``?(0,U.jsx)(`p`,{className:`mt-1 text-meta`,children:`Empty.`}):(0,U.jsx)(`pre`,{className:`mt-1 max-h-96 overflow-auto rounded-md bg-surface-0/60 p-2 font-mono text-[11px] leading-relaxed`,children:e.patch.split(`
1
+ import{D as e,E as t,H as n,I as r,L as i,P as a,R as o,T as s,_ as c,a as l,cn as u,ct as d,d as f,f as p,g as m,ln as h,o as g,p as _,pn as v,t as y,un as b,w as x,y as S}from"./format-lz-5NQWT.js";import{B as C,H as w,R as T,S as E,T as D,V as ee,_ as te,c as O,f as k,g as ne,h as re,i as ie,m as A,p as j,v as M,x as N,y as P}from"./index-CoKyPk79.js";import{t as F}from"./Markdown-4W4xjIq7.js";import{n as I,t as ae}from"./Pane-xzIpwnBD.js";import{n as oe,r as L}from"./ThreadForms-D09Ykgj9.js";import{t as se}from"./useStickyScroll-CfP3Yl_4.js";import{a as R,c as ce,i as z,l as B,n as le,o as V,s as ue,t as de}from"./citizen-C27Wvimj.js";var H=v(b(),1),U=h(),fe={completed:`completed`,timeout:`timed out`,error:`failed`,interrupted:`was interrupted`,blocked:`was blocked`,stopped:`was stopped by you`};function W(e){return new Date(e).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1})}function G({label:e,tone:t,children:n}){return(0,U.jsxs)(`div`,{children:[(0,U.jsx)(`p`,{className:`px-0.5 pb-0.5 text-[10px] tracking-wider text-fg-muted uppercase`,children:e}),(0,U.jsx)(`pre`,{className:`max-h-72 overflow-auto rounded-md bg-surface-0/60 p-2 font-mono text-[11px] leading-relaxed whitespace-pre-wrap break-words ${t}`,children:n})]})}function pe({step:e,open:t}){if(e.kind===`say`)return(0,U.jsx)(`li`,{className:`py-1.5 text-sm`,title:W(e.at),children:(0,U.jsx)(F,{text:e.text})});if(e.kind===`error`)return(0,U.jsx)(`li`,{className:`py-1 font-mono text-xs break-words text-red-400`,title:W(e.at),children:e.message});if(e.kind===`steer`)return(0,U.jsx)(`li`,{children:(0,U.jsxs)(`details`,{open:t,className:`group rounded-md open:bg-surface-2/30`,children:[(0,U.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-center gap-2 rounded-md px-1.5 py-1 text-xs hover:bg-surface-2/40`,children:[(0,U.jsx)(`span`,{"aria-hidden":`true`,className:`grid w-3 shrink-0 place-items-center text-sky-300`,children:`↳`}),(0,U.jsxs)(`span`,{className:`shrink-0 text-fg-tertiary`,children:[`Took `,re(e.posts.length)]}),(0,U.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-fg-secondary`,children:e.from.length===0?``:`from ${e.from.join(`, `)}, delivered mid-turn`}),(0,U.jsx)(`span`,{className:`shrink-0 font-mono text-[10px] text-fg-muted`,children:W(e.at)})]}),(0,U.jsx)(`ul`,{className:`space-y-1.5 px-1.5 pb-1.5`,children:e.posts.map(e=>(0,U.jsxs)(`li`,{className:`rounded-md bg-surface-0/40 px-2.5 py-2`,children:[(0,U.jsxs)(`p`,{className:`flex gap-1.5 text-meta`,children:[(0,U.jsx)(`span`,{className:`text-fg-secondary`,children:e.author}),(0,U.jsxs)(`span`,{className:`min-w-0 truncate`,children:[`in `,e.where]}),e.at===``?null:(0,U.jsx)(`span`,{className:`ml-auto shrink-0 font-mono`,children:W(e.at)})]}),(0,U.jsx)(`div`,{className:`mt-1 text-sm`,children:(0,U.jsx)(F,{text:e.body})})]},`${e.at}-${e.author}`))})]})});let n=e.ok===null?(0,U.jsx)(`span`,{"aria-label":`running`,className:`size-1.5 animate-pulse rounded-full bg-amber-300`}):e.ok?(0,U.jsx)(`span`,{"aria-label":`succeeded`,className:`text-[11px] text-emerald-400`,children:`✓`}):(0,U.jsx)(`span`,{"aria-label":`failed`,className:`text-[11px] text-red-400`,children:`✕`});return(0,U.jsx)(`li`,{children:(0,U.jsxs)(`details`,{open:t,className:`group rounded-md open:bg-surface-2/30`,children:[(0,U.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-center gap-2 rounded-md px-1.5 py-1 text-xs hover:bg-surface-2/40`,children:[(0,U.jsx)(`span`,{className:`grid w-3 shrink-0 place-items-center`,children:n}),(0,U.jsx)(`span`,{className:`shrink-0 font-mono text-fg-tertiary`,children:ne(e.name)}),(0,U.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-fg-secondary`,children:e.summary}),(0,U.jsx)(`span`,{className:`shrink-0 font-mono text-[10px] text-fg-muted`,children:W(e.at)})]}),(0,U.jsxs)(`div`,{className:`space-y-1.5 px-1.5 pb-1.5`,children:[(0,U.jsx)(G,{label:`Input`,tone:`text-fg-secondary`,children:e.detail}),e.output===null?null:(0,U.jsx)(G,{label:e.ok===!1?`Returned an error`:`Returned`,tone:e.ok===!1?`text-red-300`:`text-fg-tertiary`,children:e.output})]})]})})}function K({steps:e}){let[t,n]=(0,H.useState)(!1),r=e.filter(e=>e.kind===`tool`).length;return e.length===0?(0,U.jsx)(`p`,{className:`mt-2 text-meta`,children:`No steps yet.`}):(0,U.jsxs)(U.Fragment,{children:[r===0?null:(0,U.jsxs)(`p`,{className:`mt-2 flex items-center gap-2 text-meta`,children:[r===1?`1 tool call`:`${r} tool calls`,(0,U.jsx)(`button`,{type:`button`,onClick:()=>n(!t),className:`rounded-md px-1.5 py-0.5 text-fg-tertiary transition-colors hover:bg-surface-2/60 hover:text-fg-primary`,children:t?`Collapse all`:`Expand all`})]}),(0,U.jsx)(`ol`,{className:`mt-1 space-y-0.5`,children:e.map(e=>(0,U.jsx)(pe,{step:e,open:t},e.seq))})]})}function me({turn:e,running:t,now:n}){return e.end===null?t?(0,U.jsxs)(`p`,{className:`mt-3 flex items-center gap-2 text-meta`,"aria-live":`polite`,children:[(0,U.jsx)(`span`,{className:`size-1.5 animate-pulse rounded-full bg-emerald-400`}),`Working · last step `,y(e.lastAt,n)]}):(0,U.jsx)(`p`,{className:`mt-3 text-meta`,children:`The turn ended without a report.`}):(0,U.jsxs)(`section`,{className:`mt-3 rounded-xl bg-surface-2/40 px-3.5 py-3 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)]`,children:[(0,U.jsxs)(`p`,{className:`text-caps`,children:[`The turn `,fe[e.end.exitReason],` `,y(e.end.at,n),e.fromStart?` · ${j(e.startedAt,Date.parse(e.end.at))}`:``]}),e.end.summary===null?null:(0,U.jsx)(`p`,{className:`mt-1 text-sm text-fg-secondary`,children:e.end.summary})]})}var q=30,he={turn:`turn`,board:`board`,merge:`both kept`},ge={turn:`Changed by a turn`,board:`Written by the board`,merge:`Both versions kept`};function _e(e){return e.status===`added`?`new`:e.status===`deleted`?`deleted`:e.added===null||e.removed===null?`binary`:`+${e.added} −${e.removed}`}function ve(e){return e.files.map(e=>`${e.path} ${_e(e)}`).join(`, `)}function ye(e){return e.startsWith(`@@`)||e.startsWith(`\\`)?`text-fg-muted`:e.startsWith(`+`)?`text-emerald-300`:e.startsWith(`-`)?`text-red-300`:`text-fg-tertiary`}function be({file:e}){return(0,U.jsxs)(`div`,{children:[(0,U.jsxs)(`p`,{className:`text-xs text-fg-secondary`,children:[e.path,e.status===`modified`?``:(0,U.jsxs)(`span`,{className:`text-fg-muted`,children:[` · `,e.status]})]}),e.patch===null?(0,U.jsx)(`p`,{className:`mt-1 text-meta`,children:`A binary file; its contents are not shown.`}):e.patch===``?(0,U.jsx)(`p`,{className:`mt-1 text-meta`,children:`Empty.`}):(0,U.jsx)(`pre`,{className:`mt-1 max-h-96 overflow-auto rounded-md bg-surface-0/60 p-2 font-mono text-[11px] leading-relaxed`,children:e.patch.split(`
2
2
  `).map((e,t)=>(0,U.jsx)(`span`,{className:`block whitespace-pre-wrap break-words ${ye(e)}`,children:e===``?` `:e},t))}),e.truncated?(0,U.jsx)(`p`,{className:`mt-1 text-meta`,children:`The rest of this file's change is cut.`}):null]})}function xe({name:e,change:t}){let n=f(e,t.commit,!0);return(0,U.jsxs)(`div`,{className:`space-y-3 px-4 pt-1 pb-3`,children:[(0,U.jsxs)(`p`,{className:`text-xs text-fg-secondary`,children:[t.kind===`merge`?`Two turns on different runners changed the same lines at once. The file kept one turn's version, and this copy holds the other's until the citizen reconciles them.`:t.kind===`board`?`Written by the board: ${t.subject.replace(/^home: /,``)}.`:t.turnId===void 0?t.subject:(0,U.jsx)(T,{to:`/citizen/$name`,params:{name:e},search:{tab:`turns`,turn:t.turnId},className:`text-fg-secondary underline decoration-line underline-offset-2 hover:text-fg-primary`,children:`Open the turn that made it`}),(0,U.jsxs)(`span`,{className:`text-fg-muted`,children:[` · `,W(t.at)]})]}),n.data===void 0?(0,U.jsx)(`p`,{className:`text-meta`,children:n.isError?`Could not read the change.`:`Reading the change…`}):n.data.map(e=>(0,U.jsx)(be,{file:e},e.path))]})}function Se({name:e,change:t,now:n}){let[r,i]=(0,H.useState)(!1),a=t.files.length===0?t.subject:ve(t);return(0,U.jsx)(`li`,{className:`border-b border-line/60 last:border-b-0`,children:(0,U.jsxs)(`details`,{className:`group`,onToggle:e=>i(e.currentTarget.open),children:[(0,U.jsx)(`summary`,{"aria-label":`${ge[t.kind]} ${y(t.at,n)}: ${a}`,className:`block cursor-pointer list-none px-4 py-2.5 transition-colors hover:bg-surface-2/30 group-open:bg-surface-2/20`,children:(0,U.jsxs)(`span`,{className:`flex items-center gap-2 text-xs`,children:[(0,U.jsx)(`span`,{className:`shrink-0 text-fg-muted`,children:he[t.kind]}),(0,U.jsx)(`span`,{className:`min-w-0 flex-1 truncate text-fg-secondary`,children:a}),(0,U.jsx)(`span`,{className:`shrink-0 text-fg-muted`,children:y(t.at,n)}),(0,U.jsx)(`span`,{"aria-hidden":`true`,className:`shrink-0 text-[10px] text-fg-muted transition-transform group-open:rotate-90`,children:`▸`})]})}),r?(0,U.jsx)(xe,{name:e,change:t}):null]})})}function Ce({name:e}){let[t,n]=(0,H.useState)(q),r=_(e,t),i=S(3e4);if(r.data===void 0)return(0,U.jsx)(I,{children:r.isError?`Could not read the home's history.`:`Reading the history…`});let{changes:a,more:o}=r.data;return a.length===0?(0,U.jsxs)(I,{children:[e,`'s home has no history yet; it starts with the citizen's first turn.`]}):(0,U.jsxs)(`div`,{className:`flex-1 overflow-y-auto`,children:[(0,U.jsxs)(`p`,{className:`px-4 pt-3 pb-1 text-meta`,children:[`What `,e,`'s turns changed in its memory, skills, and profile, newest first.`]}),(0,U.jsx)(`ol`,{children:a.map(t=>(0,U.jsx)(Se,{name:e,change:t,now:i},t.commit))}),o?(0,U.jsx)(`button`,{type:`button`,onClick:()=>n(t+q),className:`px-4 py-3 text-xs text-fg-tertiary hover:text-fg-primary`,children:`Show earlier changes`}):null]})}function J({title:e,children:t}){return(0,U.jsxs)(`section`,{className:`border-t border-line pt-4 first:border-t-0 first:pt-0`,children:[(0,U.jsx)(`h3`,{className:`text-section`,children:e}),(0,U.jsx)(`div`,{className:`mt-2`,children:t})]})}function we({member:e,charter:t}){let n=c(e.name),r=g(e.name),i=p(e.name),a=S(3e4);return(0,U.jsxs)(`div`,{className:`flex-1 space-y-5 overflow-y-auto px-4 py-4`,children:[i.data===void 0||i.data.length===0?null:(0,U.jsxs)(J,{title:`Edits to reconcile`,children:[(0,U.jsxs)(`p`,{className:`text-sm text-fg-secondary`,children:[`Two turns on different runners changed these files at once. Each file kept one version; the other waits beside it until `,e.name,` merges it in and deletes the copy.`]}),(0,U.jsx)(`ul`,{className:`mt-2 space-y-1.5`,children:i.data.map(e=>(0,U.jsxs)(`li`,{className:`text-sm`,children:[(0,U.jsx)(`span`,{className:`text-fg-primary`,children:e.file}),(0,U.jsxs)(`span`,{className:`text-meta`,children:[` `,`· `,e.path.slice(e.path.lastIndexOf(`/`)+1),` · left`,` `,y(e.since,a)]})]},e.path))})]}),(0,U.jsx)(J,{title:`Profile`,children:e.profile.trim()===``?(0,U.jsx)(`p`,{className:`text-meta`,children:`No profile yet. The citizen writes its own profile.md.`}):(0,U.jsx)(F,{text:B(e.profile,`Profile`)})}),(0,U.jsx)(J,{title:`Core memory`,children:n.data===void 0?(0,U.jsx)(`p`,{className:`text-meta`,children:`Reading…`}):n.data.body.trim()===``?(0,U.jsx)(`p`,{className:`text-meta`,children:`Empty. Every turn loads this file in full.`}):(0,U.jsx)(F,{text:B(n.data.body,`Core memory`)})}),(0,U.jsx)(J,{title:`Own skills`,children:r.data===void 0?(0,U.jsx)(`p`,{className:`text-meta`,children:`Reading…`}):r.data.length===0?(0,U.jsx)(`p`,{className:`text-meta`,children:`None yet.`}):(0,U.jsx)(`ul`,{className:`space-y-1.5`,children:r.data.map(e=>(0,U.jsxs)(`li`,{className:`text-sm`,children:[(0,U.jsx)(`span`,{className:`text-fg-primary`,children:e.name}),e.summary===``?null:(0,U.jsxs)(`span`,{className:`text-fg-secondary`,children:[` · `,e.summary]})]},e.name))})}),t===void 0?null:(0,U.jsxs)(J,{title:`Charter of ${t.name}`,children:[(0,U.jsx)(`p`,{className:`text-sm text-fg-secondary`,children:t.purpose}),(0,U.jsxs)(`p`,{className:`mt-2 text-meta`,children:[`Wakes on mentions, stages, and `,t.wakeTriggers.join(`, `)||`nothing else`,t.resident?` · keeps a warm session`:``,t.reflects?` · reflects`:``]}),(0,U.jsx)(`p`,{className:`mt-2 flex flex-wrap gap-1`,children:t.verbs.map(e=>(0,U.jsx)(`span`,{className:`rounded-md bg-surface-2/70 px-1.5 py-px font-mono text-[11px] text-fg-tertiary`,children:e},e))})]})]})}function Te(e,t){let n=r(e,t?.turnId??null);return{turn:(0,H.useMemo)(()=>t===void 0||n.data===void 0?void 0:te(n.data,e,t.project,t.thread),[t,n.data,e]),loading:t?.turnId!==void 0&&n.isPending,missing:t!==void 0&&(t.turnId===void 0||n.isError)}}var Y=50;function Ee({name:e,entry:t,now:n,opened:r}){let[i,a]=(0,H.useState)(r),o=(0,H.useRef)(null);(0,H.useEffect)(()=>{r&&o.current?.scrollIntoView({block:`start`})},[r]);let s=E(),c=ue(t),l=t.thread===void 0?null:s.get(t.thread)?.title??`a thread`,u=t.error??t.summary??``,d=t.error===null?`text-fg-secondary`:`text-red-300`;return(0,U.jsx)(`li`,{ref:o,className:`border-b border-line/60 last:border-b-0`,children:(0,U.jsxs)(`details`,{className:`group`,open:i,onToggle:e=>a(e.currentTarget.open),children:[(0,U.jsxs)(`summary`,{className:`block cursor-pointer list-none px-4 py-2.5 transition-colors hover:bg-surface-2/30 group-open:bg-surface-2/20`,children:[(0,U.jsxs)(`span`,{className:`flex items-center gap-2 text-xs`,children:[(0,U.jsxs)(`span`,{className:`min-w-0 truncate text-fg-secondary`,children:[z(t.project),l===null?``:` · ${l}`]}),(0,U.jsx)(`span`,{className:`text-fg-muted`,children:t.trigger}),(0,U.jsxs)(`span`,{className:`min-w-0 flex-1 truncate text-fg-muted`,children:[de(t),` `,y(t.ts,n)]}),(0,U.jsx)(`span`,{className:`shrink-0 font-mono text-[11px] text-fg-tertiary`,title:t.usage===void 0?void 0:R(t.usage),children:[c,t.toolCalls===void 0?null:`${t.toolCalls} tools`,t.usage===void 0?null:V(t.usage)].filter(e=>e!==null).join(` · `)}),(0,U.jsx)(`span`,{"aria-hidden":`true`,className:`shrink-0 text-[10px] text-fg-muted transition-transform group-open:rotate-90`,children:`▸`})]}),u.trim()===``?null:(0,U.jsx)(`span`,{className:`mt-1 line-clamp-2 block text-xs leading-relaxed group-open:hidden ${d}`,children:(0,U.jsx)(N,{text:u,links:!1})})]}),i?(0,U.jsx)(De,{name:e,entry:t,text:u,tone:d}):null]})})}function De({name:e,entry:t,text:n,tone:r}){let i=Te(e,t);return(0,U.jsxs)(`div`,{className:`px-4 pt-1 pb-3`,children:[n.trim()===``?null:(0,U.jsx)(`p`,{className:`text-xs leading-relaxed whitespace-pre-wrap ${r}`,children:(0,U.jsx)(N,{text:n})}),(0,U.jsx)(`div`,{className:`mt-2`,children:t.turnId===void 0?(0,U.jsx)(`p`,{className:`text-meta`,children:`This turn ran before turns kept their steps.`}):i.loading?(0,U.jsx)(`p`,{className:`text-meta`,children:`Reading the steps…`}):i.turn===void 0?(0,U.jsx)(`p`,{className:`text-meta`,children:`No steps were kept for this turn.`}):(0,U.jsxs)(U.Fragment,{children:[t.startedAt===void 0?null:(0,U.jsxs)(`p`,{className:`text-caps`,children:[`Started `,W(t.startedAt)]}),(0,U.jsx)(K,{steps:i.turn.steps})]})})]})}function Oe({name:e,opened:t}){let[n,r]=(0,H.useState)(Y),a=i(e,n),o=S(3e4);if(a.data===void 0)return(0,U.jsx)(I,{children:`Reading the turns…`});let s=a.data.toReversed();if(s.length===0)return(0,U.jsxs)(I,{children:[e,` has not finished a turn yet.`]});let c=le(s),l=c.usage===null?``:` · ${V(c.usage)}${c.withUsage<c.turns?` over ${c.withUsage} of them`:``}`;return(0,U.jsxs)(`div`,{className:`flex-1 overflow-y-auto`,children:[(0,U.jsxs)(`p`,{className:`px-4 pt-3 pb-1 text-meta`,title:c.usage===null?void 0:R(c.usage),children:[c.turns===1?`1 turn`:`${c.turns} turns`,c.failed===0?``:`, ${c.failed} not completed`,l]}),t!==null&&!s.some(e=>e.turnId===t)?(0,U.jsx)(`p`,{className:`px-4 pb-1 text-meta`,children:a.data.length===n?`The turn this address opens is further back; show earlier turns to reach it.`:`The turn this address opens is not in this citizen's history.`}):null,(0,U.jsx)(`ol`,{children:s.map(n=>(0,U.jsx)(Ee,{name:e,entry:n,now:o,opened:t!==null&&n.turnId===t},n.id))}),a.data.length===n?(0,U.jsxs)(`button`,{type:`button`,onClick:()=>r(n+Y),className:`px-4 py-3 text-xs text-fg-tertiary hover:text-fg-primary`,children:[`Show `,Y,` earlier turns`]}):null]})}function ke(e){return e===void 0?``:`${e.name}${e.id===null?``:` · ${e.id}`}`}function X({label:e,choices:t,value:n,onChange:r,listClassName:i=`inset-x-0`}){let[a,o]=(0,H.useState)(!1),s=(0,H.useRef)(null),c=(0,H.useRef)(null),l=(0,H.useRef)([]),u=t.findIndex(e=>e.value===n);(0,H.useEffect)(()=>{if(!a)return;l.current[Math.max(u,0)]?.focus();let e=e=>{e.target instanceof Node&&s.current?.contains(e.target)===!0||o(!1)};return window.addEventListener(`pointerdown`,e),()=>window.removeEventListener(`pointerdown`,e)},[a,u]);let d=e=>{if(e.key===`Escape`){e.stopPropagation(),o(!1),c.current?.focus();return}if(e.key!==`ArrowDown`&&e.key!==`ArrowUp`)return;e.preventDefault();let n=(l.current.findIndex(e=>e===document.activeElement)+(e.key===`ArrowDown`?1:-1)+t.length)%t.length;l.current[n]?.focus()},f=t[u];return(0,U.jsxs)(`div`,{ref:s,className:`relative min-w-0 flex-1`,children:[(0,U.jsxs)(`button`,{ref:c,type:`button`,"aria-label":`${e}: ${ke(f)}`,"aria-expanded":a,onClick:()=>o(!a),onKeyDown:a?d:void 0,className:`flex h-7 w-full items-center gap-1.5 rounded-md bg-surface-2/60 px-2.5 text-xs text-fg-secondary transition-colors hover:bg-surface-2 focus-visible:ring-2 focus-visible:ring-fg-primary/30 focus-visible:outline-none`,children:[(0,U.jsxs)(`span`,{className:`min-w-0 flex-1 truncate text-left`,children:[f?.name,f?.id===null||f===void 0?null:(0,U.jsxs)(`span`,{className:`text-fg-tertiary`,children:[` · `,f.id]})]}),(0,U.jsx)(`span`,{"aria-hidden":`true`,className:`text-[10px] text-fg-muted transition-transform ${a?`rotate-180`:``}`,children:`▾`})]}),a?(0,U.jsx)(`div`,{className:`absolute ${i} top-full z-20 mt-1 max-h-80 overflow-y-auto rounded-xl bg-surface-1/95 p-1 shadow-[inset_0_0_0_1px_rgba(255,255,255,0.08),0_16px_32px_-12px_rgba(0,0,0,0.8)] backdrop-blur-xl`,children:t.map((e,t)=>{let i=e.value===n;return(0,U.jsxs)(`button`,{ref:e=>{l.current[t]=e},type:`button`,"aria-current":i?`true`:void 0,onKeyDown:d,onClick:()=>{r(e.value),o(!1),c.current?.focus()},className:`flex w-full items-start gap-2 rounded-lg px-2.5 py-1.5 text-left transition-colors focus-visible:outline-none ${i?`bg-surface-2/80`:`hover:bg-surface-2/50 focus-visible:bg-surface-2/50`}`,children:[(0,U.jsxs)(`span`,{className:`min-w-0 flex-1`,children:[(0,U.jsxs)(`span`,{className:`block truncate text-xs text-fg-secondary`,children:[e.name,e.id===null?null:(0,U.jsxs)(`span`,{className:`text-fg-tertiary`,children:[` · `,e.id]})]}),e.description===``?null:(0,U.jsx)(`span`,{className:`block truncate text-[11px] text-fg-muted`,children:e.description})]}),(0,U.jsx)(`span`,{"aria-hidden":`true`,className:`w-3 shrink-0 pt-px text-[11px] text-fg-primary`,children:i?`✓`:``})]},e.value)})}):null]})}var Z=``,Q=``;function Ae(e,t){return[{value:Z,name:`CLI default`,id:e.find(e=>e.isDefault)?.name??null,description:`Whatever the CLI runs when no model is set.`},...e.map(e=>({value:e.id,name:e.name,id:e.name===e.id?null:e.id,description:e.description})),...t===null?[]:[{value:t,name:t,id:null,description:`Not in the CLI's list.`}]]}function je(e,t){return[{value:Q,name:`Default`,id:e?.defaultEffort??null,description:`Whatever the model runs unset.`},...(e?.efforts??[]).map(e=>({value:e.id,name:e.id,id:null,description:e.description})),...t===null?[]:[{value:t,name:t,id:null,description:`Not in the model's list.`}]]}function Me({member:t,onDone:n}){let{api:r}=e(),i=u(),a=l(t.name),s=t.model??Z,c=t.effort??Q,[d,f]=(0,H.useState)(s),[p,m]=(0,H.useState)(c),h=w({mutationFn:async()=>{d!==s&&await r.setModel(t.name,d===Z?null:d),p!==c&&await r.setEffort(t.name,p===Q?null:p)},onSettled:()=>void i.invalidateQueries({queryKey:[`members`]}),onSuccess:n}),g=a.data?.models??[],_=t.model!==void 0&&!g.some(e=>e.id===t.model)?t.model:null,v=e=>g.find(t=>e===Z?t.isDefault:t.id===e),y=v(d)?.efforts??[],b=p!==Q&&!y.some(e=>e.id===p)?p:null,x=e=>{f(e);let t=v(e)?.efforts??[];p!==Q&&!t.some(e=>e.id===p)&&m(Q)},S=d!==s||p!==c;return(0,U.jsxs)(`form`,{"aria-label":`Model of ${t.name}`,className:`space-y-2 border-b border-line bg-surface-2/20 p-3`,onSubmit:e=>{e.preventDefault(),h.mutate()},children:[(0,U.jsxs)(`div`,{className:`flex items-center gap-2`,children:[a.isPending?(0,U.jsxs)(`p`,{className:`min-w-0 flex-1 truncate text-meta`,children:[`Asking `,t.cli,` for its models…`]}):a.error===null?(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(X,{label:`Model`,choices:Ae(g,_),value:d,onChange:x}),y.length===0&&b===null?null:(0,U.jsx)(`div`,{className:`flex w-32 shrink-0`,children:(0,U.jsx)(X,{label:`Effort`,choices:je(v(d),b),value:p,onChange:m,listClassName:`right-0 w-64`})})]}):(0,U.jsxs)(`p`,{role:`alert`,className:`min-w-0 flex-1 truncate text-xs text-red-400`,children:[t.cli,` did not list its models:`,` `,a.error instanceof o?a.error.message:`the board server did not answer`]}),(0,U.jsx)(O,{onClick:n,children:`Cancel`}),(0,U.jsx)(O,{variant:`primary`,type:`submit`,disabled:h.isPending||!S||a.error!==null,children:h.isPending?`Saving…`:`Save`})]}),a.data===void 0?null:(0,U.jsxs)(`p`,{className:`text-meta`,children:[`As `,a.data.cli,` on `,a.data.runner,` lists them, where this citizen's turns run.`]}),(0,U.jsx)(L,{error:h.error})]})}var $=``;function Ne(e,t){let n=t.cli;return[e.status,e.clis.length===0?`offers no CLI yet`:e.clis.join(`, `),n!==null&&e.clis.length>0&&!e.clis.includes(n)?`has no ${n}`:``,e.capabilities.length===0?``:e.capabilities.join(`, `)].filter(e=>e!==``).join(` · `)}function Pe(e,t){return[{value:$,name:`Any runner`,id:null,description:`Pinned to the least busy runner on its next turn outside a project.`},...e.map(e=>({value:e.name,name:e.name,id:e.os,description:Ne(e,t)}))]}function Fe({member:t,onDone:n}){let{api:r}=e(),i=u(),a=s(),o=t.homeRunner??$,[c,l]=(0,H.useState)(o),d=w({mutationFn:()=>r.setRunner(t.name,c===$?null:c),onSuccess:()=>{i.invalidateQueries({queryKey:[`members`]}),n()}});return(0,U.jsxs)(`form`,{"aria-label":`Runner of ${t.name}`,className:`space-y-2 border-b border-line bg-surface-2/20 p-3`,onSubmit:e=>{e.preventDefault(),d.mutate()},children:[(0,U.jsxs)(`div`,{className:`flex items-center gap-2`,children:[a.data===void 0?(0,U.jsx)(`p`,{className:`min-w-0 flex-1 truncate text-meta`,children:`Reading the runners…`}):(0,U.jsx)(X,{label:`Runner`,choices:Pe(a.data,t),value:c,onChange:l}),(0,U.jsx)(O,{onClick:n,children:`Cancel`}),(0,U.jsx)(O,{variant:`primary`,type:`submit`,disabled:d.isPending||c===o,children:d.isPending?`Saving…`:`Run here`})]}),(0,U.jsx)(`p`,{className:`text-meta`,children:`Its conversations outside projects start afresh there from its next turn; its memory and skills follow it. Project work runs where each project lives.`}),(0,U.jsx)(L,{error:d.error})]})}function Ie(e){return e.thread===void 0?{channel:e.channel??(e.scope===`society`?`general`:`${e.scope}/general`)}:{threadId:e.thread}}function Le({name:t,turn:n}){let{api:r}=e(),i=u(),[a,o]=(0,H.useState)(!1),s=w({mutationFn:()=>r.stopTurn(n.turnId),onSuccess:()=>{o(!1),i.invalidateQueries({queryKey:[`scheduler`]})}});return(0,U.jsxs)(`div`,{className:`border-t border-line`,children:[(0,U.jsx)(ie,{target:Ie(n),initialText:`@${t} `,placeholder:n.steerable?`Message ${t} while it works`:`Message ${t} for its next turn`,className:`px-3 pt-3 pb-2`},n.turnId),n.stoppable?(0,U.jsxs)(`div`,{className:`flex items-center gap-2 px-3 pb-3`,children:[(0,U.jsx)(`p`,{className:`min-w-0 flex-1 text-[11px] leading-snug text-fg-muted`,children:a?`Ends the turn now. What it was shown counts as read; its stages stay held.`:n.steerable?`Posts reach the turn at its next step.`:`This runner delivers posts after the turn ends.`}),a?(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(O,{onClick:()=>{o(!1),s.reset()},children:`Keep it running`}),(0,U.jsx)(O,{variant:`primary`,disabled:s.isPending,onClick:()=>{s.mutate()},children:s.isPending?`Stopping…`:`Stop the turn`})]}):(0,U.jsx)(O,{onClick:()=>o(!0),children:`Stop…`})]}):null,s.error===null?null:(0,U.jsx)(`div`,{className:`px-3 pb-3`,children:(0,U.jsx)(L,{error:s.error})})]})}function Re({member:t,scopes:n,preferred:r,running:i,paused:a,onDone:o}){let{api:s}=e(),c=u(),[l,d]=(0,H.useState)(`manual`),[f,p]=(0,H.useState)(r!==null&&n.includes(r)?r:n[0]??``),[m,h]=(0,H.useState)(``),g=(0,H.useRef)(null);(0,H.useEffect)(()=>g.current?.focus(),[]);let _=w({mutationFn:()=>s.wake({agent:t.name,project:f,kind:l,...m.trim()===``?{}:{reason:m.trim()}}),onSuccess:()=>{c.invalidateQueries({queryKey:[`scheduler`]}),o()}}),v=z(f),y=l===`reflection`?`Asks ${t.name} to reflect at ${v}: consolidate its memory, skills, and profile, and take no new work. It restarts the reflection clock.`:`Starts a turn for ${t.name} at ${v} as soon as the scheduler takes it.`,b=a?` The society is paused, so it waits until you resume.`:i.includes(f)?` ${t.name} is in a turn there now; this one follows it.`:``;return(0,U.jsxs)(`form`,{"aria-label":`Wake ${t.name}`,className:`space-y-2 border-b border-line bg-surface-2/20 p-3`,onSubmit:e=>{e.preventDefault(),_.mutate()},children:[(0,U.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,U.jsxs)(`fieldset`,{className:`flex gap-1`,children:[(0,U.jsx)(`legend`,{className:`sr-only`,children:`Kind of turn`}),[`manual`,`reflection`].map(e=>(0,U.jsxs)(`label`,{className:`cursor-pointer rounded-md px-2.5 py-1 text-xs transition-colors has-focus-visible:ring-2 has-focus-visible:ring-fg-primary/30 ${l===e?`bg-surface-2 text-fg-primary`:`text-fg-tertiary hover:bg-surface-2/60 hover:text-fg-primary`}`,children:[(0,U.jsx)(`input`,{type:`radio`,name:`kind`,value:e,checked:l===e,onChange:()=>d(e),className:`sr-only`}),e===`manual`?`A turn`:`A reflection`]},e))]}),(0,U.jsxs)(`label`,{className:`ml-auto flex items-center gap-2 text-xs text-fg-tertiary`,children:[`at`,(0,U.jsx)(`select`,{value:f,onChange:e=>p(e.target.value),"aria-label":`Where`,className:`rounded-md bg-surface-2/60 px-2 py-1 text-xs text-fg-primary outline-none`,children:n.map(e=>(0,U.jsx)(`option`,{value:e,children:z(e)},e))})]})]}),(0,U.jsx)(`textarea`,{ref:g,value:m,rows:2,placeholder:l===`reflection`?`Anything to reflect on in particular? Optional.`:`What should it look at? It reads this as the reason it was woken.`,"aria-label":`Reason`,onChange:e=>h(e.target.value),className:`${oe} resize-none`}),(0,U.jsxs)(`p`,{className:`text-[11px] leading-relaxed text-fg-muted`,children:[y,b,` Each turn costs money.`]}),(0,U.jsxs)(`div`,{className:`flex items-center justify-end gap-2`,children:[(0,U.jsx)(O,{onClick:o,children:`Cancel`}),(0,U.jsx)(O,{variant:`primary`,type:`submit`,disabled:_.isPending||f===``,children:_.isPending?`Waking…`:l===`reflection`?`Ask for a reflection`:`Wake ${t.name}`})]}),(0,U.jsx)(L,{error:_.error})]})}var ze={working:`bg-emerald-500/15 text-emerald-300`,queued:`bg-amber-500/15 text-amber-300`,idle:`bg-surface-2 text-fg-tertiary`};function Be({project:e,name:t}){let n=(a(e).data??[]).filter(e=>e.status===`claimed`&&e.claimedBy===t);return n.length===0?null:(0,U.jsx)(`ul`,{className:`mt-1.5 space-y-1`,children:n.map(e=>(0,U.jsx)(`li`,{children:(0,U.jsxs)(T,{to:`/task/$taskId`,params:{taskId:e.id},className:`flex items-center gap-2 rounded-md text-xs text-fg-secondary hover:text-fg-primary`,children:[(0,U.jsx)(`span`,{"aria-hidden":`true`,className:`text-fg-muted`,children:`◇`}),(0,U.jsx)(`span`,{className:`min-w-0 flex-1 truncate`,children:e.title}),(0,U.jsx)(`span`,{className:`max-w-[40%] shrink-0 truncate font-mono text-[11px] text-fg-muted`,children:d(e)?.name??e.stage})]})},e.id))})}function Ve(){let{name:e}=ee({from:`/citizen/$name`}),{scope:t,tab:n,turn:r}=C({from:`/citizen/$name`});return(0,U.jsx)(Ue,{name:e,chosen:t??null,tab:n??`now`,turn:r??null},e)}var He=[{tab:`now`,label:`Now`},{tab:`turns`,label:`Turns`},{tab:`memory`,label:`Memory`},{tab:`history`,label:`History`}];function Ue({name:e,chosen:r,tab:i,turn:a}){let o=m(),s=x(),c=t(),l=P(),[u,d]=(0,H.useState)(null),f=o.data?.find(t=>t.name===e);if(f===void 0)return(0,U.jsx)(I,{children:o.data===void 0?`Reading the roster…`:`There is no citizen ${e}.`});let p=n(c.data?.running??[]).filter(t=>t.agent===e),h=[...new Set(p.map(e=>e.scope))],g=p.map(e=>A(e.agent,e.scope,e.thread)),_=n(c.data?.pending??[]).find(t=>t.agent===e),v=h.length>0?`working`:_===void 0?`idle`:`queued`,y=M(l,e)[0],b=y?.model??f.lastModel,S=b??f.model??`CLI default`,C=f.model!==void 0&&f.model!==b?f.model:null,w=s.data?.find(e=>e.name===f.role),E=ce(f),ee=f.status===`active`&&f.cli!==null;return(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(ae,{leading:f.cli===null?null:(0,U.jsx)(`span`,{className:`grid size-9 shrink-0 place-items-center rounded-lg bg-surface-2/70`,children:(0,U.jsx)(D,{cli:f.cli,size:20})}),title:e,subtitle:(0,U.jsxs)(U.Fragment,{children:[f.role,` · `,S,C===null?``:` · set to ${C}`,f.effort===void 0?``:` · ${f.effort} effort`,f.homeRunner===void 0?``:` · on ${f.homeRunner}`,f.status===`retired`?` · retired`:``]}),trailing:(0,U.jsxs)(U.Fragment,{children:[ee&&u===null?(0,U.jsxs)(U.Fragment,{children:[(0,U.jsx)(O,{onClick:()=>d(`model`),children:`Model…`}),(0,U.jsx)(O,{onClick:()=>d(`runner`),children:`Runner…`}),(0,U.jsx)(O,{onClick:()=>d(`wake`),children:`Wake…`})]}):null,(0,U.jsx)(`span`,{className:`shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium ${ze[v]}`,children:v})]})}),u===`wake`?(0,U.jsx)(Re,{member:f,scopes:E,preferred:r?.split(`/`)[0]??y?.scope??null,running:h,paused:c.data?.paused??!1,onDone:()=>d(null)}):u===`model`?(0,U.jsx)(Me,{member:f,onDone:()=>d(null)}):u===`runner`?(0,U.jsx)(Fe,{member:f,onDone:()=>d(null)}):null,(0,U.jsx)(`nav`,{"aria-label":`Citizen views`,className:`flex gap-1 border-b border-line px-3 py-2`,children:He.map(t=>(0,U.jsx)(T,{to:`/citizen/$name`,params:{name:e},search:{...r===null?{}:{scope:r},...t.tab===`now`?{}:{tab:t.tab}},replace:!0,"aria-current":t.tab===i?`page`:void 0,className:`rounded-md px-2.5 py-1 text-xs transition-colors ${t.tab===i?`bg-surface-2 text-fg-primary`:`text-fg-tertiary hover:bg-surface-2/60 hover:text-fg-primary`}`,children:t.label},t.tab))}),i===`turns`?(0,U.jsx)(Oe,{name:e,opened:a},a):i===`memory`?(0,U.jsx)(we,{member:f,charter:w}):i===`history`?(0,U.jsx)(Ce,{name:e}):(0,U.jsx)(Ge,{name:e,chosen:r,lastTurnAt:f.lastTurnAt,state:v,runningScopes:h,runningKeys:g,runningTurns:(c.data?.turns??[]).filter(t=>t.agent===e),queuedScope:_?.scope??null})]})}function We(e){return e.toSorted((e,t)=>e.startedAt.localeCompare(t.startedAt))[0]}function Ge({name:e,chosen:t,lastTurnAt:n,state:r,runningScopes:a,runningKeys:o,runningTurns:s,queuedScope:c}){let l=P(),u=S(5e3),{ref:d,onScroll:f}=se(),p=M(l,e),m=i(e,1),h=Te(e,p.length===0?m.data?.at(-1):void 0),g=E(),_=e=>e.end===null&&o.includes(A(e.agent,e.scope,e.thread)),v=t?.split(`/`)[0]??null,b=(t?.includes(`/`)===!0?p.find(e=>k(e)===t):void 0)??We(p.filter(e=>e.scope===v&&_(e)))??p.find(e=>k(e)===t)??We(p.filter(_))??p[0]??h.turn,x=e=>e.thread===void 0?z(e.scope):`${z(e.scope)} · ${g.get(e.thread)?.title??`a thread`}`,C=b!==void 0&&_(b),w=b?.scope??a[0]??c,D=(b===void 0?void 0:s.find(e=>e.scope===b.scope&&e.thread===b.thread))??(C?void 0:s[0]);return(0,U.jsxs)(U.Fragment,{children:[(0,U.jsxs)(`section`,{className:`border-b border-line px-4 py-3 text-xs text-fg-tertiary`,children:[(0,U.jsx)(`p`,{children:C?`In a turn at ${z(b.scope)} for ${b.fromStart?``:`at least `}${j(b.startedAt,u)}`:a.length>0?`In a turn at ${a.map(z).join(` and `)}`:c===null?n===void 0?`No turns yet`:`Last turn ${y(n,u)}`:`Queued for a turn at ${z(c)}`}),w===null||w===`society`?null:(0,U.jsx)(Be,{project:w,name:e})]}),(0,U.jsx)(`div`,{ref:d,onScroll:f,className:`flex-1 overflow-y-auto px-4 py-3`,children:b===void 0?(0,U.jsx)(I,{children:r===`working`?`The turn has not reported a step yet.`:h.loading?`Reading the last turn…`:n===void 0?`${e} has not taken a turn yet.`:`${e}'s last turn kept no steps; Turns lists every finished one.`}):(0,U.jsxs)(U.Fragment,{children:[p.length>1?(0,U.jsx)(`div`,{className:`mb-3 flex flex-wrap gap-1.5`,"aria-label":`Turns by conversation`,children:p.toSorted((e,t)=>k(e).localeCompare(k(t))).map(t=>(0,U.jsxs)(T,{to:`/citizen/$name`,params:{name:e},search:{scope:k(t)},replace:!0,"aria-current":t===b?`true`:void 0,className:`inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-[11px] transition-colors ${t===b?`bg-surface-2 text-fg-primary`:`text-fg-tertiary hover:bg-surface-2/60 hover:text-fg-primary`}`,children:[_(t)?(0,U.jsx)(`span`,{className:`size-1.5 animate-pulse rounded-full bg-emerald-400`}):null,x(t)]},k(t)))}):null,(0,U.jsxs)(`p`,{className:`text-caps`,children:[C?`This turn`:`Last turn`,` · `,x(b),` · started`,` `,b.fromStart?W(b.startedAt):`before the earliest step shown`]}),b.fromStart?null:(0,U.jsxs)(`p`,{className:`mt-1 text-meta`,children:[`Earlier steps of this turn are no longer in the server's live buffer`,C?``:`; Turns has the whole turn`,`.`]}),(0,U.jsx)(K,{steps:b.steps}),(0,U.jsx)(me,{turn:b,running:C,now:u})]})}),D===void 0?null:(0,U.jsx)(Le,{name:e,turn:D})]})}export{Ve as CitizenView};
@@ -1 +1 @@
1
- import{E as e,H as t,b as n,g as r,ln as i}from"./format-Cc6KjiYZ.js";import{R as a,T as o}from"./index-WB0PqHlY.js";import{n as s,t as c}from"./Pane-DxXjKLAj.js";import{i as l,r as u}from"./citizen-BqHxQY9q.js";var d=i();function f(e){let t=e.lastModel??e.model??`CLI default`,n=e.model!==void 0&&e.model!==t?`${t} · set to ${e.model}`:t;return e.effort===void 0?n:`${n} · ${e.effort}`}function p(e,t){return e.name.localeCompare(t.name)}function m(e,t){return e.role.localeCompare(t.role)||p(e,t)}function h(){let i=r(),h=e(),g=n();if(i.data===void 0)return(0,d.jsx)(s,{children:`Reading the roster…`});let _=i.data.filter(e=>e.cli!==null),v=_.filter(e=>e.status===`active`).toSorted(m),y=_.filter(e=>e.status!==`active`).toSorted(p),b=new Set(v.map(e=>e.role)).size,x=t(h.data?.running??[]),S=t(h.data?.pending??[]),C=new Map((g.data??[]).flatMap(e=>e.runner===void 0?[]:[[e.slug,e.runner]])),w=e=>{let t=[...new Set(x.filter(t=>t.agent===e.name).map(e=>e.scope))];return t.length>0?{label:`working`,detail:`in a turn at ${t.map(l).join(` and `)}`,tone:`text-emerald-300`}:S.some(t=>t.agent===e.name)?{label:`queued`,detail:`queued for a turn`,tone:`text-amber-300`}:{label:`resting`,detail:`resting`,tone:`text-fg-muted`}};return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(c,{title:`Citizens`,subtitle:`${v.length} active in ${b===1?`1 role`:`${b} roles`}${y.length===0?``:` · ${y.length} retired`}`}),(0,d.jsxs)(`div`,{className:`flex-1 overflow-y-auto px-2 py-2`,children:[v.length===0?(0,d.jsx)(`p`,{className:`px-2 py-2 text-meta`,children:`No citizens yet. Ask the concierge for one.`}):null,(0,d.jsx)(`ul`,{className:`grid grid-cols-[auto_auto_auto_minmax(0,max-content)_auto_minmax(3rem,1fr)_auto] gap-x-3`,children:v.map(e=>{let t=w(e),n=f(e),r=e.memberships.length===0?`no projects`:e.memberships.join(`, `),i=u(e,C);return(0,d.jsx)(`li`,{className:`col-span-full grid grid-cols-subgrid`,children:(0,d.jsxs)(a,{to:`/citizen/$name`,params:{name:e.name},className:`col-span-full grid grid-cols-subgrid items-center rounded-lg px-2 py-1.5 text-sm transition-colors hover:bg-surface-2/50`,children:[(0,d.jsx)(`span`,{className:`grid size-4 place-items-center`,children:e.cli===null?null:(0,d.jsx)(o,{cli:e.cli,size:13})}),(0,d.jsx)(`span`,{className:`text-fg-primary`,children:e.name}),(0,d.jsx)(`span`,{className:`text-fg-secondary`,children:e.role}),(0,d.jsx)(`span`,{className:`truncate text-xs text-fg-muted`,title:n,children:n}),(0,d.jsx)(`span`,{className:`text-xs text-fg-muted`,title:i.detail,children:i.label}),(0,d.jsx)(`span`,{className:`truncate text-xs text-fg-muted`,title:r,children:r}),(0,d.jsx)(`span`,{className:`text-right text-xs ${t.tone}`,title:t.detail,children:t.label})]})},e.name)})}),y.length===0?null:(0,d.jsxs)(`p`,{className:`mt-2 border-t border-line px-2 pt-2 text-meta`,children:[`Retired: `,y.map(e=>`${e.name} (${e.role})`).join(`, `)]})]})]})}export{h as CitizensView};
1
+ import{E as e,H as t,b as n,g as r,ln as i}from"./format-lz-5NQWT.js";import{R as a,T as o}from"./index-CoKyPk79.js";import{n as s,t as c}from"./Pane-xzIpwnBD.js";import{i as l,r as u}from"./citizen-C27Wvimj.js";var d=i();function f(e){let t=e.lastModel??e.model??`CLI default`,n=e.model!==void 0&&e.model!==t?`${t} · set to ${e.model}`:t;return e.effort===void 0?n:`${n} · ${e.effort}`}function p(e,t){return e.name.localeCompare(t.name)}function m(e,t){return e.role.localeCompare(t.role)||p(e,t)}function h(){let i=r(),h=e(),g=n();if(i.data===void 0)return(0,d.jsx)(s,{children:`Reading the roster…`});let _=i.data.filter(e=>e.cli!==null),v=_.filter(e=>e.status===`active`).toSorted(m),y=_.filter(e=>e.status!==`active`).toSorted(p),b=new Set(v.map(e=>e.role)).size,x=t(h.data?.running??[]),S=t(h.data?.pending??[]),C=new Map((g.data??[]).flatMap(e=>e.runner===void 0?[]:[[e.slug,e.runner]])),w=e=>{let t=[...new Set(x.filter(t=>t.agent===e.name).map(e=>e.scope))];return t.length>0?{label:`working`,detail:`in a turn at ${t.map(l).join(` and `)}`,tone:`text-emerald-300`}:S.some(t=>t.agent===e.name)?{label:`queued`,detail:`queued for a turn`,tone:`text-amber-300`}:{label:`resting`,detail:`resting`,tone:`text-fg-muted`}};return(0,d.jsxs)(d.Fragment,{children:[(0,d.jsx)(c,{title:`Citizens`,subtitle:`${v.length} active in ${b===1?`1 role`:`${b} roles`}${y.length===0?``:` · ${y.length} retired`}`}),(0,d.jsxs)(`div`,{className:`flex-1 overflow-y-auto px-2 py-2`,children:[v.length===0?(0,d.jsx)(`p`,{className:`px-2 py-2 text-meta`,children:`No citizens yet. Ask the concierge for one.`}):null,(0,d.jsx)(`ul`,{className:`grid grid-cols-[auto_auto_auto_minmax(0,max-content)_auto_minmax(3rem,1fr)_auto] gap-x-3`,children:v.map(e=>{let t=w(e),n=f(e),r=e.memberships.length===0?`no projects`:e.memberships.join(`, `),i=u(e,C);return(0,d.jsx)(`li`,{className:`col-span-full grid grid-cols-subgrid`,children:(0,d.jsxs)(a,{to:`/citizen/$name`,params:{name:e.name},className:`col-span-full grid grid-cols-subgrid items-center rounded-lg px-2 py-1.5 text-sm transition-colors hover:bg-surface-2/50`,children:[(0,d.jsx)(`span`,{className:`grid size-4 place-items-center`,children:e.cli===null?null:(0,d.jsx)(o,{cli:e.cli,size:13})}),(0,d.jsx)(`span`,{className:`text-fg-primary`,children:e.name}),(0,d.jsx)(`span`,{className:`text-fg-secondary`,children:e.role}),(0,d.jsx)(`span`,{className:`truncate text-xs text-fg-muted`,title:n,children:n}),(0,d.jsx)(`span`,{className:`text-xs text-fg-muted`,title:i.detail,children:i.label}),(0,d.jsx)(`span`,{className:`truncate text-xs text-fg-muted`,title:r,children:r}),(0,d.jsx)(`span`,{className:`text-right text-xs ${t.tone}`,title:t.detail,children:t.label})]})},e.name)})}),y.length===0?null:(0,d.jsxs)(`p`,{className:`mt-2 border-t border-line px-2 pt-2 text-meta`,children:[`Retired: `,y.map(e=>`${e.name} (${e.role})`).join(`, `)]})]})]})}export{h as CitizensView};