@kubitnodes/stellaris 0.3.21 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cli/index.js +1007 -598
- package/dist/server/index.js +1252 -671
- package/package.json +3 -1
- package/web/dist/assets/{BackLink-23Q1JvmZ.js → BackLink-DGYs80bb.js} +1 -1
- package/web/dist/assets/Button-f4jgBZY1.js +1 -0
- package/web/dist/assets/ChannelForms-q9zdj4p2.js +1 -0
- package/web/dist/assets/ChannelView-CGs_aWL3.js +2 -0
- package/web/dist/assets/CitizenView-gcIo_sKt.js +2 -0
- package/web/dist/assets/CitizensView-DZ7S8vnJ.js +1 -0
- package/web/dist/assets/{CliIcon-BOSygXeI.js → CliIcon-CLHaA65A.js} +1 -1
- package/web/dist/assets/CronsView-1wns6Uwv.js +1 -0
- package/web/dist/assets/KnowledgeView-Cxfi2-Pt.js +1 -0
- package/web/dist/assets/Markdown-BhtIFcWc.js +30 -0
- package/web/dist/assets/MessageItem-Bk9eBp1U.js +1 -0
- package/web/dist/assets/MetricsView-CTEH6rJn.js +1 -0
- package/web/dist/assets/NeedsYouView-2VNqH2Sb.js +1 -0
- package/web/dist/assets/Overview-CS9HCSmm.js +1 -0
- package/web/dist/assets/{Pane-BendOaje.js → Pane-CJYRVkwh.js} +1 -1
- package/web/dist/assets/{Picker-BuVnakvi.js → Picker-DoxCN6C7.js} +1 -1
- package/web/dist/assets/ProjectView-CJ3r4UdN.js +1 -0
- package/web/dist/assets/ProposalView-DLn9dNPp.js +2 -0
- package/web/dist/assets/ProposalsView-B-cOxTsQ.js +1 -0
- package/web/dist/assets/Quota-Bo7UdWTV.js +1 -0
- package/web/dist/assets/RunnersView-DlQruhwp.js +1 -0
- package/web/dist/assets/SocietyView-Bl4ErnV8.js +1 -0
- package/web/dist/assets/StickyList-MVP34ytV.js +1 -0
- package/web/dist/assets/SubjectThread-BFFnHxIF.js +1 -0
- package/web/dist/assets/TaskFileView-GZ0eQFUb.js +4 -0
- package/web/dist/assets/TaskView-DppFelLW.js +3 -0
- package/web/dist/assets/TasksView-DDNXzeG4.js +1 -0
- package/web/dist/assets/ThreadForms-C846TvtS.js +1 -0
- package/web/dist/assets/ThreadItem-C8cou6wD.js +1 -0
- package/web/dist/assets/ThreadView-DvSGDYoD.js +1 -0
- package/web/dist/assets/citizen-B2Dlbcpe.js +1 -0
- package/web/dist/assets/compose-BmqQmcZH.js +1 -0
- package/web/dist/assets/format-DT52SbbR.js +55 -0
- package/web/dist/assets/governance-Cr5OlQnw.js +1 -0
- package/web/dist/assets/index-BTVy_xFF.js +23 -0
- package/web/dist/assets/index-DzV6CCnG.css +2 -0
- package/web/dist/assets/mentions-D5hNgtg6.js +3 -0
- package/web/dist/assets/{tasks-CyXIxoAy.js → tasks-R3YgpYTe.js} +1 -1
- package/web/dist/assets/useMatch-pEF6RJ6Q.js +1 -0
- package/web/dist/index.html +9 -10
- package/web/dist/assets/ChannelForms-0Cwh59E7.js +0 -1
- package/web/dist/assets/ChannelView-CqWV1zza.js +0 -2
- package/web/dist/assets/CitizenView-s5OdjXmB.js +0 -2
- package/web/dist/assets/CitizensView-r6SZLnVl.js +0 -1
- package/web/dist/assets/CronsView-Y-bAE0l2.js +0 -1
- package/web/dist/assets/KnowledgeView-CI4PJWwm.js +0 -1
- package/web/dist/assets/Markdown-DyckB74r.js +0 -32
- package/web/dist/assets/MessageItem-DI05jbrI.js +0 -1
- package/web/dist/assets/MetricsView-DKh-lEW5.js +0 -1
- package/web/dist/assets/NeedsYouView-C6FfiTsg.js +0 -1
- package/web/dist/assets/Overview-D5RvWBg5.js +0 -1
- package/web/dist/assets/ProjectView-blBEh70W.js +0 -1
- package/web/dist/assets/ProposalView-CMntpKTc.js +0 -2
- package/web/dist/assets/ProposalsView-CVUAV2Mm.js +0 -1
- package/web/dist/assets/RunnersView-B-wuWVgB.js +0 -1
- package/web/dist/assets/SocietyView-CaVbfJWz.js +0 -1
- package/web/dist/assets/SubjectThread-Bd_-sDhJ.js +0 -1
- package/web/dist/assets/TaskFileView-CO4Y97OT.js +0 -4
- package/web/dist/assets/TaskView-DRZoHALo.js +0 -3
- package/web/dist/assets/TasksView-CQVQo3zF.js +0 -1
- package/web/dist/assets/ThreadForms-CUSeWu0c.js +0 -1
- package/web/dist/assets/ThreadItem-BPYYXh3V.js +0 -1
- package/web/dist/assets/ThreadView-BcI2i6FI.js +0 -1
- package/web/dist/assets/citizen-Eff1h9rA.js +0 -1
- package/web/dist/assets/compose-D73Agshx.js +0 -1
- package/web/dist/assets/format-fw0G_Vff.js +0 -55
- package/web/dist/assets/governance-sRsfOw52.js +0 -1
- package/web/dist/assets/index-BkOq4PSU.css +0 -2
- package/web/dist/assets/index-CYiok0pT.js +0 -23
- package/web/dist/assets/link-Cl7vKbOz.js +0 -1
- package/web/dist/assets/mentions-BCdV0bMK.js +0 -1
- package/web/dist/assets/useMatch-D-tEHQEw.js +0 -1
- package/web/dist/assets/useStickyScroll-Bn4YyEXL.js +0 -1
package/dist/server/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { serve } from "@hono/node-server";
|
|
|
9
9
|
import { chmod as chmod2, cp, mkdir as mkdir4, readdir as readdir3, readFile as readFile4, rename as rename3, rm as rm2, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
|
|
10
10
|
import path6 from "node:path";
|
|
11
11
|
import { decodeTime, monotonicFactory } from "ulid";
|
|
12
|
-
import { z as
|
|
12
|
+
import { z as z16 } from "zod";
|
|
13
13
|
|
|
14
14
|
// packages/shared/dist/ids.js
|
|
15
15
|
import { z } from "zod";
|
|
@@ -64,7 +64,8 @@ var VERB_NAMES = [
|
|
|
64
64
|
"create_cron",
|
|
65
65
|
"update_cron",
|
|
66
66
|
"remove_cron",
|
|
67
|
-
"remove_skill"
|
|
67
|
+
"remove_skill",
|
|
68
|
+
"upload_image"
|
|
68
69
|
];
|
|
69
70
|
var VerbNameSchema = z2.enum(VERB_NAMES);
|
|
70
71
|
var CharterTriggerSchema = z2.enum(["user_post", "ops_event", "heartbeat"]);
|
|
@@ -93,6 +94,7 @@ var USER_ROLE = "user";
|
|
|
93
94
|
var USER_NAME = "user";
|
|
94
95
|
var MEMBER_VERBS = [
|
|
95
96
|
"post_message",
|
|
97
|
+
"upload_image",
|
|
96
98
|
"read_inbox",
|
|
97
99
|
"search",
|
|
98
100
|
"open_thread",
|
|
@@ -169,7 +171,7 @@ var SEED_ROLES = [
|
|
|
169
171
|
];
|
|
170
172
|
|
|
171
173
|
// packages/shared/dist/board.js
|
|
172
|
-
import { z as
|
|
174
|
+
import { z as z5 } from "zod";
|
|
173
175
|
|
|
174
176
|
// packages/shared/dist/crons.js
|
|
175
177
|
import { Cron as Pattern } from "croner";
|
|
@@ -303,6 +305,115 @@ function localTime(date, timeZone) {
|
|
|
303
305
|
return `${part("weekday")} ${part("year")}-${part("month")}-${part("day")} ${part("hour")}:${part("minute")}`;
|
|
304
306
|
}
|
|
305
307
|
|
|
308
|
+
// packages/shared/dist/quota.js
|
|
309
|
+
import { z as z4 } from "zod";
|
|
310
|
+
var QUOTA_WARNING_PERCENT = 80;
|
|
311
|
+
var QuotaWindowSchema = z4.object({
|
|
312
|
+
/**
|
|
313
|
+
* The CLI's own name for the window: Claude's `five_hour`, `seven_day`, `seven_day_opus`, and
|
|
314
|
+
* `model:<label>` for a model's weekly window; Codex's `primary` and `secondary`.
|
|
315
|
+
*/
|
|
316
|
+
key: z4.string().min(1).max(80),
|
|
317
|
+
/** What a scoped window counts, as the CLI labels it, such as a model's name. */
|
|
318
|
+
label: z4.string().min(1).max(80).optional(),
|
|
319
|
+
/** The window's length in minutes, when known. */
|
|
320
|
+
minutes: z4.number().int().positive().nullable().default(null),
|
|
321
|
+
/** How much of the window is used, in percent, or null when the CLI did not say. */
|
|
322
|
+
usedPercent: z4.number().nonnegative().nullable().default(null),
|
|
323
|
+
/** When the window starts afresh, when known. */
|
|
324
|
+
resetsAt: IsoDateTimeSchema.nullable().default(null),
|
|
325
|
+
/** When the CLI last reported this window. */
|
|
326
|
+
observedAt: IsoDateTimeSchema
|
|
327
|
+
});
|
|
328
|
+
var QuotaStatusSchema = z4.enum(["allowed", "warning", "limited", "none"]);
|
|
329
|
+
var QuotaCreditsSchema = z4.object({
|
|
330
|
+
hasCredits: z4.boolean(),
|
|
331
|
+
unlimited: z4.boolean(),
|
|
332
|
+
balance: z4.string().max(40).nullable()
|
|
333
|
+
});
|
|
334
|
+
var QuotaExtraUsageSchema = z4.object({
|
|
335
|
+
enabled: z4.boolean(),
|
|
336
|
+
monthlyLimit: z4.number().nonnegative().nullable(),
|
|
337
|
+
usedCredits: z4.number().nonnegative().nullable(),
|
|
338
|
+
currency: z4.string().min(1).max(8).nullable()
|
|
339
|
+
});
|
|
340
|
+
var CliQuotaSchema = z4.object({
|
|
341
|
+
/** When the CLI last reported anything here. */
|
|
342
|
+
observedAt: IsoDateTimeSchema,
|
|
343
|
+
/** Whether it came from a turn's stream or from asking the CLI. */
|
|
344
|
+
source: z4.enum(["turn", "read"]),
|
|
345
|
+
status: QuotaStatusSchema,
|
|
346
|
+
/** While `limited`: when work can go on, when known, and the window that stopped it. */
|
|
347
|
+
limitedUntil: IsoDateTimeSchema.nullable().default(null),
|
|
348
|
+
limitedBy: z4.string().min(1).max(80).nullable().default(null),
|
|
349
|
+
/** The plan's name as the CLI gives it, such as `max` or `pro`. */
|
|
350
|
+
plan: z4.string().min(1).max(40).nullable().default(null),
|
|
351
|
+
windows: z4.array(QuotaWindowSchema).max(24).default([]),
|
|
352
|
+
credits: QuotaCreditsSchema.nullable().default(null),
|
|
353
|
+
extraUsage: QuotaExtraUsageSchema.nullable().default(null)
|
|
354
|
+
});
|
|
355
|
+
function mergeQuota(previous, next) {
|
|
356
|
+
if (previous === void 0) {
|
|
357
|
+
return next;
|
|
358
|
+
}
|
|
359
|
+
const windows = new Map(previous.windows.map((window) => [window.key, window]));
|
|
360
|
+
for (const window of next.windows) {
|
|
361
|
+
windows.set(window.key, window);
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
...next,
|
|
365
|
+
plan: next.plan ?? previous.plan,
|
|
366
|
+
credits: next.credits ?? previous.credits,
|
|
367
|
+
extraUsage: next.extraUsage ?? previous.extraUsage,
|
|
368
|
+
windows: [...windows.values()]
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function windowCurrent(window, now) {
|
|
372
|
+
return window.resetsAt === null || Date.parse(window.resetsAt) > now.getTime();
|
|
373
|
+
}
|
|
374
|
+
function quotaLevel(quota, now) {
|
|
375
|
+
if (quota.status === "none") {
|
|
376
|
+
return "none";
|
|
377
|
+
}
|
|
378
|
+
if (quota.status === "limited" && (quota.limitedUntil === null || Date.parse(quota.limitedUntil) > now.getTime())) {
|
|
379
|
+
return "limited";
|
|
380
|
+
}
|
|
381
|
+
const high = quota.windows.some((window) => windowCurrent(window, now) && window.usedPercent !== null && window.usedPercent >= QUOTA_WARNING_PERCENT);
|
|
382
|
+
return quota.status === "warning" || high ? "warning" : "ok";
|
|
383
|
+
}
|
|
384
|
+
var WINDOW_NAMES = {
|
|
385
|
+
five_hour: "5h",
|
|
386
|
+
seven_day: "7d",
|
|
387
|
+
seven_day_opus: "7d Opus",
|
|
388
|
+
seven_day_sonnet: "7d Sonnet",
|
|
389
|
+
seven_day_oauth_apps: "7d apps",
|
|
390
|
+
seven_day_overage_included: "7d extra",
|
|
391
|
+
overage: "extra usage"
|
|
392
|
+
};
|
|
393
|
+
function windowName(window) {
|
|
394
|
+
if (window.label !== void 0) {
|
|
395
|
+
return window.minutes === 10080 ? `7d ${window.label}` : window.label;
|
|
396
|
+
}
|
|
397
|
+
const named = WINDOW_NAMES[window.key];
|
|
398
|
+
if (named !== void 0) {
|
|
399
|
+
return named;
|
|
400
|
+
}
|
|
401
|
+
if (window.minutes !== null) {
|
|
402
|
+
return window.minutes % 1440 === 0 ? `${window.minutes / 1440}d` : window.minutes % 60 === 0 ? `${window.minutes / 60}h` : `${window.minutes}m`;
|
|
403
|
+
}
|
|
404
|
+
return window.key;
|
|
405
|
+
}
|
|
406
|
+
function readings(quota) {
|
|
407
|
+
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();
|
|
408
|
+
return windows.join(",");
|
|
409
|
+
}
|
|
410
|
+
function quotaChanged(previous, next) {
|
|
411
|
+
if (previous === void 0) {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
return previous.status !== next.status || previous.limitedUntil !== next.limitedUntil || previous.plan !== next.plan || previous.credits?.balance !== next.credits?.balance || previous.extraUsage?.usedCredits !== next.extraUsage?.usedCredits || readings(previous) !== readings(next);
|
|
415
|
+
}
|
|
416
|
+
|
|
306
417
|
// packages/shared/dist/board.js
|
|
307
418
|
var ASK_CHANNEL = "asks";
|
|
308
419
|
var SOCIETY_CHANNELS = ["general", "governance", ASK_CHANNEL];
|
|
@@ -315,24 +426,24 @@ var SOCIETY_SCOPE = "society";
|
|
|
315
426
|
function wakeScope(member, project) {
|
|
316
427
|
return project !== null && member.memberships.includes(project) ? project : SOCIETY_SCOPE;
|
|
317
428
|
}
|
|
318
|
-
var CliKindSchema =
|
|
319
|
-
var ArchivedChannelSchema =
|
|
429
|
+
var CliKindSchema = z5.enum(["claude", "codex"]);
|
|
430
|
+
var ArchivedChannelSchema = z5.object({
|
|
320
431
|
name: NameSchema,
|
|
321
432
|
at: IsoDateTimeSchema,
|
|
322
433
|
by: NameSchema,
|
|
323
|
-
reason:
|
|
434
|
+
reason: z5.string().min(1)
|
|
324
435
|
});
|
|
325
|
-
var SocietySchema =
|
|
326
|
-
name:
|
|
327
|
-
version:
|
|
436
|
+
var SocietySchema = z5.object({
|
|
437
|
+
name: z5.string().min(1),
|
|
438
|
+
version: z5.literal(1),
|
|
328
439
|
createdAt: IsoDateTimeSchema,
|
|
329
440
|
/** The channels that take posts; an archived one moves to `archivedChannels`. */
|
|
330
|
-
channels:
|
|
331
|
-
archivedChannels:
|
|
441
|
+
channels: z5.array(NameSchema),
|
|
442
|
+
archivedChannels: z5.array(ArchivedChannelSchema).default([]),
|
|
332
443
|
/** The time zone crons are read in when they name none, and every prompt tells the time in; the user's to set. */
|
|
333
444
|
timezone: TimeZoneSchema.default(DEFAULT_TIME_ZONE)
|
|
334
445
|
});
|
|
335
|
-
var CompletionEffectSchema =
|
|
446
|
+
var CompletionEffectSchema = z5.enum(["none", "merge", "ghpr"]);
|
|
336
447
|
var GITHUB_REMOTE = [
|
|
337
448
|
/^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?\/?$/,
|
|
338
449
|
/^git@github\.com:([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?$/,
|
|
@@ -348,7 +459,7 @@ function githubRepository(remote) {
|
|
|
348
459
|
return null;
|
|
349
460
|
}
|
|
350
461
|
var PULL_REQUEST_URL = /^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)\/pull\/([1-9]\d*)\/?$/;
|
|
351
|
-
var PullRequestUrlSchema =
|
|
462
|
+
var PullRequestUrlSchema = z5.string().trim().regex(PULL_REQUEST_URL, "a pull request's address, https://github.com/<owner>/<repo>/pull/<number>");
|
|
352
463
|
function parsePullRequestUrl(url) {
|
|
353
464
|
const match = PULL_REQUEST_URL.exec(url.trim());
|
|
354
465
|
return match?.[1] === void 0 || match[2] === void 0 || match[3] === void 0 ? null : { owner: match[1], name: match[2], number: Number(match[3]) };
|
|
@@ -356,72 +467,72 @@ function parsePullRequestUrl(url) {
|
|
|
356
467
|
function sameRepository(a, b) {
|
|
357
468
|
return a.owner.toLowerCase() === b.owner.toLowerCase() && a.name.toLowerCase() === b.name.toLowerCase();
|
|
358
469
|
}
|
|
359
|
-
var MergeSubjectSchema =
|
|
360
|
-
var PullRequestLinkSchema =
|
|
470
|
+
var MergeSubjectSchema = z5.string().trim().min(1).max(300).regex(/^[^\n]+$/, "a merge commit's subject is one line");
|
|
471
|
+
var PullRequestLinkSchema = z5.object({
|
|
361
472
|
url: PullRequestUrlSchema,
|
|
362
|
-
number:
|
|
473
|
+
number: z5.number().int().positive(),
|
|
363
474
|
subject: MergeSubjectSchema.optional(),
|
|
364
|
-
body:
|
|
475
|
+
body: z5.string().max(2e4).optional(),
|
|
365
476
|
linkedBy: NameSchema,
|
|
366
477
|
linkedAt: IsoDateTimeSchema,
|
|
367
478
|
/**
|
|
368
479
|
* Once its `ghpr` task was abandoned, how the board's closing of it on GitHub went: `ok` false
|
|
369
480
|
* leaves it open, for the reason `detail` gives.
|
|
370
481
|
*/
|
|
371
|
-
closed:
|
|
482
|
+
closed: z5.object({ at: IsoDateTimeSchema, ok: z5.boolean(), detail: z5.string() }).optional()
|
|
372
483
|
});
|
|
373
|
-
var DefaultBranchSchema =
|
|
374
|
-
var StageIdSchema =
|
|
484
|
+
var DefaultBranchSchema = z5.string().trim().min(1).max(100).regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/, "a branch name is letters, digits, and . _ / -").refine((branch) => !branch.includes("..") && !branch.includes("//") && !branch.endsWith("/") && !branch.endsWith(".") && !branch.endsWith(".lock"), "not a branch name git takes").refine((branch) => !branch.startsWith("task/") && !branch.startsWith("agent/"), "task/ and agent/ hold the board's own branches");
|
|
485
|
+
var StageIdSchema = z5.string().regex(/^s[1-9][0-9]*$/, "expected a stage id such as s1");
|
|
375
486
|
var stageFields = {
|
|
376
|
-
name:
|
|
487
|
+
name: z5.string().trim().min(1).max(120),
|
|
377
488
|
role: NameSchema.optional(),
|
|
378
489
|
agent: NameSchema.optional(),
|
|
379
|
-
gate:
|
|
490
|
+
gate: z5.boolean().default(false)
|
|
380
491
|
};
|
|
381
492
|
function oneAssignee(stage) {
|
|
382
493
|
return stage.role === void 0 || stage.agent === void 0;
|
|
383
494
|
}
|
|
384
495
|
var ONE_ASSIGNEE = { message: "a stage names a role or an agent, not both" };
|
|
385
|
-
var PlanStageSchema =
|
|
386
|
-
var PlanEditStageSchema =
|
|
387
|
-
var PlanSchema =
|
|
388
|
-
var PlanEditSchema =
|
|
389
|
-
var ProjectSchema =
|
|
496
|
+
var PlanStageSchema = z5.object(stageFields).refine(oneAssignee, ONE_ASSIGNEE);
|
|
497
|
+
var PlanEditStageSchema = z5.object({ id: StageIdSchema.optional(), ...stageFields }).refine(oneAssignee, ONE_ASSIGNEE);
|
|
498
|
+
var PlanSchema = z5.array(PlanStageSchema);
|
|
499
|
+
var PlanEditSchema = z5.array(PlanEditStageSchema);
|
|
500
|
+
var ProjectSchema = z5.object({
|
|
390
501
|
slug: NameSchema,
|
|
391
|
-
name:
|
|
392
|
-
repo:
|
|
393
|
-
defaultBranch:
|
|
502
|
+
name: z5.string().min(1),
|
|
503
|
+
repo: z5.string().nullable(),
|
|
504
|
+
defaultBranch: z5.string().min(1),
|
|
394
505
|
/** The channels that take posts; an archived one moves to `archivedChannels`. */
|
|
395
|
-
channels:
|
|
396
|
-
archivedChannels:
|
|
397
|
-
members:
|
|
398
|
-
approvers:
|
|
399
|
-
requiredCapabilities:
|
|
506
|
+
channels: z5.array(NameSchema),
|
|
507
|
+
archivedChannels: z5.array(ArchivedChannelSchema).default([]),
|
|
508
|
+
members: z5.array(NameSchema),
|
|
509
|
+
approvers: z5.array(NameSchema),
|
|
510
|
+
requiredCapabilities: z5.array(z5.string()),
|
|
400
511
|
createdAt: IsoDateTimeSchema,
|
|
401
512
|
onDone: CompletionEffectSchema.default("none"),
|
|
402
513
|
/** The runner the project lives on, which holds its repository; set on the project's first turn. */
|
|
403
514
|
runner: NameSchema.optional(),
|
|
404
515
|
/** Set once the project is archived: nobody belongs to it, and nothing more is posted, filed, or joined there. */
|
|
405
|
-
archived:
|
|
516
|
+
archived: z5.object({ at: IsoDateTimeSchema, by: NameSchema, reason: z5.string().min(1) }).optional()
|
|
406
517
|
});
|
|
407
|
-
var TaskStepSchema =
|
|
408
|
-
action:
|
|
518
|
+
var TaskStepSchema = z5.object({
|
|
519
|
+
action: z5.enum(["advanced", "returned", "abandoned", "landed", "reopened"]),
|
|
409
520
|
stage: StageIdSchema,
|
|
410
521
|
to: StageIdSchema.nullable()
|
|
411
522
|
});
|
|
412
|
-
var ProposalStepSchema =
|
|
413
|
-
var MessageStepSchema =
|
|
523
|
+
var ProposalStepSchema = z5.object({ action: z5.enum(["approved", "rejected"]) });
|
|
524
|
+
var MessageStepSchema = z5.union([TaskStepSchema, ProposalStepSchema]);
|
|
414
525
|
function isTaskStep(step) {
|
|
415
526
|
return "stage" in step;
|
|
416
527
|
}
|
|
417
|
-
var KnowledgeNoticeSchema =
|
|
418
|
-
kind:
|
|
419
|
-
action:
|
|
528
|
+
var KnowledgeNoticeSchema = z5.object({
|
|
529
|
+
kind: z5.literal("knowledge"),
|
|
530
|
+
action: z5.enum(["written", "updated", "removed"]),
|
|
420
531
|
topic: NameSchema,
|
|
421
532
|
project: NameSchema.nullable()
|
|
422
533
|
});
|
|
423
|
-
var MessageNoticeSchema =
|
|
424
|
-
var MessageFrontmatterSchema =
|
|
534
|
+
var MessageNoticeSchema = z5.discriminatedUnion("kind", [KnowledgeNoticeSchema]);
|
|
535
|
+
var MessageFrontmatterSchema = z5.object({
|
|
425
536
|
id: UlidSchema,
|
|
426
537
|
author: NameSchema,
|
|
427
538
|
channel: ChannelRefSchema,
|
|
@@ -435,7 +546,7 @@ var MessageFrontmatterSchema = z4.object({
|
|
|
435
546
|
/** On a post a verb made about its work, such as a knowledge topic's note: what it records. */
|
|
436
547
|
notice: MessageNoticeSchema.optional(),
|
|
437
548
|
ts: IsoDateTimeSchema,
|
|
438
|
-
mentions:
|
|
549
|
+
mentions: z5.array(NameSchema)
|
|
439
550
|
});
|
|
440
551
|
var MessageRefSchema = MessageFrontmatterSchema.pick({
|
|
441
552
|
id: true,
|
|
@@ -445,16 +556,16 @@ var MessageRefSchema = MessageFrontmatterSchema.pick({
|
|
|
445
556
|
ts: true
|
|
446
557
|
});
|
|
447
558
|
var MESSAGE_LOOKUP_LIMIT = 50;
|
|
448
|
-
var TaskStatusSchema =
|
|
449
|
-
var ThreadStateSchema =
|
|
450
|
-
var ThreadSubjectSchema =
|
|
451
|
-
kind:
|
|
559
|
+
var TaskStatusSchema = z5.enum(["open", "claimed", "done", "abandoned"]);
|
|
560
|
+
var ThreadStateSchema = z5.enum(["open", "closed"]);
|
|
561
|
+
var ThreadSubjectSchema = z5.object({
|
|
562
|
+
kind: z5.enum(["task", "proposal"]),
|
|
452
563
|
id: UlidSchema
|
|
453
564
|
});
|
|
454
|
-
var ThreadFrontmatterSchema =
|
|
565
|
+
var ThreadFrontmatterSchema = z5.object({
|
|
455
566
|
id: UlidSchema,
|
|
456
567
|
channel: ChannelRefSchema,
|
|
457
|
-
title:
|
|
568
|
+
title: z5.string().min(1).max(200),
|
|
458
569
|
subject: ThreadSubjectSchema.optional(),
|
|
459
570
|
state: ThreadStateSchema,
|
|
460
571
|
openedBy: NameSchema,
|
|
@@ -462,28 +573,28 @@ var ThreadFrontmatterSchema = z4.object({
|
|
|
462
573
|
closedBy: NameSchema.optional(),
|
|
463
574
|
closedAt: IsoDateTimeSchema.optional()
|
|
464
575
|
});
|
|
465
|
-
var StageSchema =
|
|
576
|
+
var StageSchema = z5.object({
|
|
466
577
|
id: StageIdSchema,
|
|
467
|
-
name:
|
|
578
|
+
name: z5.string().min(1),
|
|
468
579
|
role: NameSchema.optional(),
|
|
469
580
|
agent: NameSchema.optional(),
|
|
470
|
-
gate:
|
|
581
|
+
gate: z5.boolean().default(false),
|
|
471
582
|
/** Everyone who has held the stage, oldest first. */
|
|
472
|
-
holders:
|
|
583
|
+
holders: z5.array(NameSchema).default([]),
|
|
473
584
|
completedBy: NameSchema.optional(),
|
|
474
585
|
completedAt: IsoDateTimeSchema.optional()
|
|
475
586
|
});
|
|
476
|
-
var TaskReturnSchema =
|
|
587
|
+
var TaskReturnSchema = z5.object({
|
|
477
588
|
from: StageIdSchema,
|
|
478
589
|
by: NameSchema,
|
|
479
590
|
at: IsoDateTimeSchema
|
|
480
591
|
});
|
|
481
|
-
var TaskFrontmatterSchema =
|
|
592
|
+
var TaskFrontmatterSchema = z5.object({
|
|
482
593
|
id: UlidSchema,
|
|
483
594
|
project: NameSchema,
|
|
484
595
|
/** The channel of the project the task is filed in, where its thread hangs. */
|
|
485
596
|
channel: NameSchema.default("general"),
|
|
486
|
-
title:
|
|
597
|
+
title: z5.string().min(1),
|
|
487
598
|
status: TaskStatusSchema,
|
|
488
599
|
createdBy: NameSchema,
|
|
489
600
|
createdAt: IsoDateTimeSchema,
|
|
@@ -492,19 +603,19 @@ var TaskFrontmatterSchema = z4.object({
|
|
|
492
603
|
claimedBy: NameSchema.optional(),
|
|
493
604
|
leaseExpiresAt: IsoDateTimeSchema.optional(),
|
|
494
605
|
parentId: UlidSchema.optional(),
|
|
495
|
-
blockedBy:
|
|
496
|
-
requiredCapabilities:
|
|
497
|
-
stages:
|
|
606
|
+
blockedBy: z5.array(UlidSchema),
|
|
607
|
+
requiredCapabilities: z5.array(z5.string()),
|
|
608
|
+
stages: z5.array(StageSchema).min(1),
|
|
498
609
|
stage: StageIdSchema,
|
|
499
610
|
/** When the current stage last became current or lost its holder: the waiting clock. */
|
|
500
611
|
stageSince: IsoDateTimeSchema,
|
|
501
612
|
/** The highest stage number issued, so a removed stage's id is never reused. */
|
|
502
|
-
stageSeq:
|
|
613
|
+
stageSeq: z5.number().int().positive(),
|
|
503
614
|
onDone: CompletionEffectSchema.default("none"),
|
|
504
615
|
/** The pull request a `ghpr` task lands through. */
|
|
505
616
|
pullRequest: PullRequestLinkSchema.optional(),
|
|
506
617
|
/** The last stage is complete and the completion effect is running. */
|
|
507
|
-
completing:
|
|
618
|
+
completing: z5.boolean().default(false),
|
|
508
619
|
/** The latest send-back while its rework lasts: cleared once the task reaches that stage again. */
|
|
509
620
|
returned: TaskReturnSchema.optional()
|
|
510
621
|
});
|
|
@@ -528,79 +639,90 @@ function mayHoldStage(member, task) {
|
|
|
528
639
|
}
|
|
529
640
|
return !(stage.gate && task.stages.slice(0, index).some((s) => s.holders.includes(member.name)));
|
|
530
641
|
}
|
|
531
|
-
var ModelNameSchema =
|
|
532
|
-
var EffortSchema =
|
|
533
|
-
var EffortOptionSchema =
|
|
642
|
+
var ModelNameSchema = z5.string().trim().min(1).max(120).regex(/^[\w.:/[\]-]+$/, "a model name is one word of letters, digits, and . _ : / - [ ]");
|
|
643
|
+
var EffortSchema = z5.string().trim().min(1).max(32).regex(/^[a-z][a-z0-9_-]*$/, "an effort is one lowercase word, such as high");
|
|
644
|
+
var EffortOptionSchema = z5.object({
|
|
534
645
|
id: EffortSchema,
|
|
535
|
-
description:
|
|
646
|
+
description: z5.string().default("")
|
|
536
647
|
});
|
|
537
|
-
var ModelOptionSchema =
|
|
648
|
+
var ModelOptionSchema = z5.object({
|
|
538
649
|
id: ModelNameSchema,
|
|
539
|
-
name:
|
|
540
|
-
description:
|
|
541
|
-
isDefault:
|
|
542
|
-
efforts:
|
|
650
|
+
name: z5.string(),
|
|
651
|
+
description: z5.string().default(""),
|
|
652
|
+
isDefault: z5.boolean().default(false),
|
|
653
|
+
efforts: z5.array(EffortOptionSchema).default([]),
|
|
543
654
|
defaultEffort: EffortSchema.optional()
|
|
544
655
|
});
|
|
545
|
-
var RunnerModelsSchema =
|
|
656
|
+
var RunnerModelsSchema = z5.object({
|
|
546
657
|
runner: NameSchema,
|
|
547
658
|
cli: CliKindSchema,
|
|
548
|
-
models:
|
|
659
|
+
models: z5.array(ModelOptionSchema)
|
|
549
660
|
});
|
|
550
|
-
var AgentStatusSchema =
|
|
551
|
-
var AgentSchema =
|
|
661
|
+
var AgentStatusSchema = z5.enum(["active", "retired"]);
|
|
662
|
+
var AgentSchema = z5.object({
|
|
552
663
|
name: NameSchema,
|
|
553
664
|
role: NameSchema,
|
|
554
665
|
cli: CliKindSchema.nullable(),
|
|
555
|
-
model:
|
|
666
|
+
model: z5.string().optional(),
|
|
556
667
|
/** The reasoning effort set for the agent's turns; the model's own default applies otherwise. */
|
|
557
|
-
effort:
|
|
668
|
+
effort: z5.string().optional(),
|
|
558
669
|
/** The runner preferred for the agent's turns where nothing else decides; any runner with its CLI otherwise. */
|
|
559
670
|
homeRunner: NameSchema.optional(),
|
|
560
|
-
memberships:
|
|
561
|
-
subscriptions:
|
|
671
|
+
memberships: z5.array(NameSchema),
|
|
672
|
+
subscriptions: z5.array(ChannelRefSchema),
|
|
562
673
|
status: AgentStatusSchema,
|
|
563
674
|
createdAt: IsoDateTimeSchema,
|
|
564
|
-
tokenHash:
|
|
675
|
+
tokenHash: z5.string().min(1),
|
|
565
676
|
retiredAt: IsoDateTimeSchema.optional(),
|
|
566
|
-
retiredReason:
|
|
677
|
+
retiredReason: z5.string().optional()
|
|
567
678
|
});
|
|
568
|
-
var MemberSchema =
|
|
679
|
+
var MemberSchema = z5.object({
|
|
569
680
|
name: NameSchema,
|
|
570
681
|
role: NameSchema,
|
|
571
682
|
cli: CliKindSchema.nullable(),
|
|
572
683
|
homeRunner: NameSchema.optional(),
|
|
573
684
|
status: AgentStatusSchema,
|
|
574
|
-
resident:
|
|
685
|
+
resident: z5.boolean().default(false),
|
|
575
686
|
/** The model configured on the record, if any; the CLI's own default applies otherwise. */
|
|
576
|
-
model:
|
|
687
|
+
model: z5.string().optional(),
|
|
577
688
|
/** The model the CLI reported on the citizen's last turn. */
|
|
578
|
-
lastModel:
|
|
689
|
+
lastModel: z5.string().optional(),
|
|
579
690
|
/** The reasoning effort configured on the record, if any; the model's own default applies otherwise. */
|
|
580
|
-
effort:
|
|
691
|
+
effort: z5.string().optional(),
|
|
581
692
|
/** The citizen's own skills, by name. */
|
|
582
|
-
skills:
|
|
583
|
-
memberships:
|
|
584
|
-
subscriptions:
|
|
585
|
-
claimsHeld:
|
|
586
|
-
tasksDone:
|
|
693
|
+
skills: z5.array(NameSchema).default([]),
|
|
694
|
+
memberships: z5.array(NameSchema),
|
|
695
|
+
subscriptions: z5.array(ChannelRefSchema),
|
|
696
|
+
claimsHeld: z5.number().int().nonnegative().default(0),
|
|
697
|
+
tasksDone: z5.number().int().nonnegative().default(0),
|
|
587
698
|
lastTurnAt: IsoDateTimeSchema.optional(),
|
|
588
|
-
lastTurnOutcome:
|
|
699
|
+
lastTurnOutcome: z5.string().optional(),
|
|
589
700
|
createdAt: IsoDateTimeSchema,
|
|
590
701
|
retiredAt: IsoDateTimeSchema.optional()
|
|
591
702
|
});
|
|
592
|
-
var RunnerOsSchema =
|
|
593
|
-
var
|
|
703
|
+
var RunnerOsSchema = z5.enum(["linux", "windows", "darwin"]);
|
|
704
|
+
var GitIdentityPartSchema = z5.enum(["name", "email"]);
|
|
705
|
+
var RunnerSchema = z5.object({
|
|
594
706
|
name: NameSchema,
|
|
595
707
|
os: RunnerOsSchema,
|
|
596
|
-
clis:
|
|
597
|
-
capabilities:
|
|
708
|
+
clis: z5.array(CliKindSchema),
|
|
709
|
+
capabilities: z5.array(z5.string()),
|
|
598
710
|
/** The release the runner reported when it last registered; absent until it has. */
|
|
599
|
-
version:
|
|
600
|
-
|
|
601
|
-
|
|
711
|
+
version: z5.string().optional(),
|
|
712
|
+
/**
|
|
713
|
+
* What the runner's account lacked of a git identity when it last registered, empty when it had
|
|
714
|
+
* both; absent while the runner has not said, as one from before runners checked never does.
|
|
715
|
+
*/
|
|
716
|
+
gitIdentityMissing: z5.array(GitIdentityPartSchema).optional(),
|
|
717
|
+
status: z5.enum(["connected", "disconnected"]),
|
|
718
|
+
lastSeen: IsoDateTimeSchema.optional(),
|
|
719
|
+
/**
|
|
720
|
+
* Each CLI's plan quota as its login on this runner last reported it, from a turn's stream or a
|
|
721
|
+
* read; absent until one has.
|
|
722
|
+
*/
|
|
723
|
+
quotas: z5.partialRecord(CliKindSchema, CliQuotaSchema).optional()
|
|
602
724
|
});
|
|
603
|
-
var ProposalKindSchema =
|
|
725
|
+
var ProposalKindSchema = z5.enum([
|
|
604
726
|
"role",
|
|
605
727
|
"member",
|
|
606
728
|
"channel",
|
|
@@ -609,42 +731,42 @@ var ProposalKindSchema = z4.enum([
|
|
|
609
731
|
"skill",
|
|
610
732
|
"archive"
|
|
611
733
|
]);
|
|
612
|
-
var ProposalStatusSchema =
|
|
734
|
+
var ProposalStatusSchema = z5.enum([
|
|
613
735
|
"proposed",
|
|
614
736
|
"approved",
|
|
615
737
|
"rejected",
|
|
616
738
|
"provisioned",
|
|
617
739
|
"retired"
|
|
618
740
|
]);
|
|
619
|
-
var MemberProposalSchema =
|
|
741
|
+
var MemberProposalSchema = z5.object({
|
|
620
742
|
name: NameSchema,
|
|
621
743
|
role: NameSchema,
|
|
622
744
|
cli: CliKindSchema,
|
|
623
|
-
model:
|
|
745
|
+
model: z5.string().optional(),
|
|
624
746
|
homeRunner: NameSchema.optional(),
|
|
625
|
-
memberships:
|
|
626
|
-
subscriptions:
|
|
627
|
-
seedInstructions:
|
|
747
|
+
memberships: z5.array(NameSchema).default([]),
|
|
748
|
+
subscriptions: z5.array(ChannelRefSchema).default([]),
|
|
749
|
+
seedInstructions: z5.string().optional()
|
|
628
750
|
});
|
|
629
|
-
var ChannelProposalSchema =
|
|
751
|
+
var ChannelProposalSchema = z5.object({
|
|
630
752
|
project: NameSchema.nullable().default(null),
|
|
631
753
|
name: NameSchema,
|
|
632
|
-
purpose:
|
|
754
|
+
purpose: z5.string().min(1)
|
|
633
755
|
});
|
|
634
|
-
var RoleChangesSchema =
|
|
635
|
-
maxReplicas:
|
|
636
|
-
backlogThreshold:
|
|
637
|
-
wakeTriggers:
|
|
638
|
-
verbs:
|
|
756
|
+
var RoleChangesSchema = z5.strictObject({
|
|
757
|
+
maxReplicas: z5.number().int().min(1).optional(),
|
|
758
|
+
backlogThreshold: z5.number().positive().optional(),
|
|
759
|
+
wakeTriggers: z5.array(CharterTriggerSchema).optional(),
|
|
760
|
+
verbs: z5.array(VerbNameSchema).optional()
|
|
639
761
|
}).refine((changes) => Object.values(changes).some((value) => value !== void 0), {
|
|
640
762
|
message: "a reallocation changes at least one of maxReplicas, backlogThreshold, wakeTriggers, verbs"
|
|
641
763
|
});
|
|
642
|
-
var ReallocationProposalSchema =
|
|
764
|
+
var ReallocationProposalSchema = z5.object({
|
|
643
765
|
role: NameSchema,
|
|
644
766
|
changes: RoleChangesSchema,
|
|
645
|
-
description:
|
|
767
|
+
description: z5.string().min(1)
|
|
646
768
|
});
|
|
647
|
-
var LegacyReallocationSchema =
|
|
769
|
+
var LegacyReallocationSchema = z5.object({ description: z5.string().min(1) });
|
|
648
770
|
function applyRoleChanges(role, changes) {
|
|
649
771
|
return {
|
|
650
772
|
...role,
|
|
@@ -679,18 +801,18 @@ function roleChangeLines(role, changes) {
|
|
|
679
801
|
}
|
|
680
802
|
return lines;
|
|
681
803
|
}
|
|
682
|
-
var RetirementProposalSchema =
|
|
804
|
+
var RetirementProposalSchema = z5.object({
|
|
683
805
|
agent: NameSchema,
|
|
684
|
-
reason:
|
|
806
|
+
reason: z5.string().min(1)
|
|
685
807
|
});
|
|
686
|
-
var ArchiveProposalSchema =
|
|
808
|
+
var ArchiveProposalSchema = z5.object({
|
|
687
809
|
project: NameSchema,
|
|
688
|
-
reason:
|
|
810
|
+
reason: z5.string().min(1)
|
|
689
811
|
});
|
|
690
|
-
var SkillProposalSchema =
|
|
812
|
+
var SkillProposalSchema = z5.object({
|
|
691
813
|
name: NameSchema,
|
|
692
|
-
summary:
|
|
693
|
-
body:
|
|
814
|
+
summary: z5.string().min(1).max(200),
|
|
815
|
+
body: z5.string().min(1).optional()
|
|
694
816
|
});
|
|
695
817
|
var ProposalCharterSchemas = {
|
|
696
818
|
role: RoleCharterSchema,
|
|
@@ -793,23 +915,23 @@ function describeCharter(kind, charter) {
|
|
|
793
915
|
}
|
|
794
916
|
return `${kind} ${JSON.stringify(charter)}`;
|
|
795
917
|
}
|
|
796
|
-
var SkillSchema =
|
|
918
|
+
var SkillSchema = z5.object({
|
|
797
919
|
name: NameSchema,
|
|
798
|
-
summary:
|
|
799
|
-
scope:
|
|
800
|
-
path:
|
|
920
|
+
summary: z5.string().default(""),
|
|
921
|
+
scope: z5.enum(["own", "society"]),
|
|
922
|
+
path: z5.string().min(1)
|
|
801
923
|
});
|
|
802
|
-
var RemovedSkillSchema =
|
|
924
|
+
var RemovedSkillSchema = z5.object({
|
|
803
925
|
name: NameSchema,
|
|
804
|
-
reason:
|
|
926
|
+
reason: z5.string().min(1),
|
|
805
927
|
removedBy: NameSchema,
|
|
806
928
|
removedAt: IsoDateTimeSchema
|
|
807
929
|
});
|
|
808
|
-
var HomeConflictSchema =
|
|
930
|
+
var HomeConflictSchema = z5.object({
|
|
809
931
|
/** The copy, relative to the home. */
|
|
810
|
-
path:
|
|
932
|
+
path: z5.string().min(1),
|
|
811
933
|
/** The file beside it, whose version the board kept. */
|
|
812
|
-
file:
|
|
934
|
+
file: z5.string().min(1),
|
|
813
935
|
/** When the copy reached the board. */
|
|
814
936
|
since: IsoDateTimeSchema
|
|
815
937
|
});
|
|
@@ -817,79 +939,79 @@ var CONFLICT_COPY = /^(.+)\.conflict-[^/.]+$/;
|
|
|
817
939
|
function conflictCopyOf(copy) {
|
|
818
940
|
return CONFLICT_COPY.exec(copy)?.[1] ?? null;
|
|
819
941
|
}
|
|
820
|
-
var CommitIdSchema =
|
|
821
|
-
var HomeFileChangeSchema =
|
|
822
|
-
path:
|
|
823
|
-
status:
|
|
942
|
+
var CommitIdSchema = z5.string().regex(/^[0-9a-f]{40}([0-9a-f]{24})?$/);
|
|
943
|
+
var HomeFileChangeSchema = z5.object({
|
|
944
|
+
path: z5.string().min(1),
|
|
945
|
+
status: z5.enum(["added", "modified", "deleted"]),
|
|
824
946
|
/** Lines added and removed, or null for a binary file. */
|
|
825
|
-
added:
|
|
826
|
-
removed:
|
|
947
|
+
added: z5.number().int().nonnegative().nullable(),
|
|
948
|
+
removed: z5.number().int().nonnegative().nullable()
|
|
827
949
|
});
|
|
828
|
-
var HomeChangeSchema =
|
|
950
|
+
var HomeChangeSchema = z5.object({
|
|
829
951
|
commit: CommitIdSchema,
|
|
830
952
|
at: IsoDateTimeSchema,
|
|
831
|
-
kind:
|
|
832
|
-
author:
|
|
953
|
+
kind: z5.enum(["turn", "board", "merge"]),
|
|
954
|
+
author: z5.string(),
|
|
833
955
|
/** The turn that made it, from the runner's commit message. */
|
|
834
956
|
turnId: UlidSchema.optional(),
|
|
835
|
-
subject:
|
|
836
|
-
files:
|
|
957
|
+
subject: z5.string(),
|
|
958
|
+
files: z5.array(HomeFileChangeSchema)
|
|
837
959
|
});
|
|
838
|
-
var HomeHistorySchema =
|
|
839
|
-
changes:
|
|
960
|
+
var HomeHistorySchema = z5.object({
|
|
961
|
+
changes: z5.array(HomeChangeSchema),
|
|
840
962
|
/** Whether older commits remain past the ones read. */
|
|
841
|
-
more:
|
|
963
|
+
more: z5.boolean()
|
|
842
964
|
});
|
|
843
965
|
var HomeFileDiffSchema = HomeFileChangeSchema.pick({ path: true, status: true }).extend({
|
|
844
|
-
patch:
|
|
966
|
+
patch: z5.string().nullable(),
|
|
845
967
|
/** The patch was cut at its size limit. */
|
|
846
|
-
truncated:
|
|
968
|
+
truncated: z5.boolean()
|
|
847
969
|
});
|
|
848
|
-
var KnowledgeSchema =
|
|
970
|
+
var KnowledgeSchema = z5.object({
|
|
849
971
|
topic: NameSchema,
|
|
850
972
|
project: NameSchema.nullable(),
|
|
851
|
-
revision:
|
|
973
|
+
revision: z5.number().int().min(0),
|
|
852
974
|
updatedBy: NameSchema,
|
|
853
975
|
updatedAt: IsoDateTimeSchema
|
|
854
976
|
});
|
|
855
|
-
var RemovedKnowledgeSchema =
|
|
977
|
+
var RemovedKnowledgeSchema = z5.object({
|
|
856
978
|
topic: NameSchema,
|
|
857
979
|
project: NameSchema.nullable(),
|
|
858
980
|
removedBy: NameSchema,
|
|
859
981
|
removedAt: IsoDateTimeSchema
|
|
860
982
|
});
|
|
861
|
-
var DashboardSchema =
|
|
983
|
+
var DashboardSchema = z5.object({
|
|
862
984
|
project: NameSchema,
|
|
863
|
-
revision:
|
|
985
|
+
revision: z5.number().int().min(0),
|
|
864
986
|
/** Absent until someone updates the dashboard the project began with. */
|
|
865
987
|
updatedBy: NameSchema.optional(),
|
|
866
988
|
updatedAt: IsoDateTimeSchema.optional(),
|
|
867
|
-
body:
|
|
989
|
+
body: z5.string()
|
|
868
990
|
});
|
|
869
|
-
var ProposalOriginSchema =
|
|
991
|
+
var ProposalOriginSchema = z5.object({
|
|
870
992
|
scope: NameSchema,
|
|
871
993
|
thread: UlidSchema.optional(),
|
|
872
994
|
channel: NameSchema.optional()
|
|
873
995
|
});
|
|
874
996
|
var SKILL_FILES_LIMIT = 100;
|
|
875
997
|
var SKILL_BYTES_LIMIT = 10 * 1024 * 1024;
|
|
876
|
-
var SkillFileSchema =
|
|
877
|
-
path:
|
|
878
|
-
size:
|
|
879
|
-
executable:
|
|
998
|
+
var SkillFileSchema = z5.object({
|
|
999
|
+
path: z5.string().min(1),
|
|
1000
|
+
size: z5.number().int().nonnegative(),
|
|
1001
|
+
executable: z5.boolean()
|
|
880
1002
|
});
|
|
881
|
-
var SkillFilesSchema =
|
|
1003
|
+
var SkillFilesSchema = z5.object({
|
|
882
1004
|
/** The commit of the proposer's home the files came from. */
|
|
883
1005
|
commit: CommitIdSchema,
|
|
884
1006
|
takenAt: IsoDateTimeSchema,
|
|
885
|
-
files:
|
|
1007
|
+
files: z5.array(SkillFileSchema)
|
|
886
1008
|
});
|
|
887
1009
|
var SkillFileContentSchema = SkillFileSchema.extend({
|
|
888
|
-
text:
|
|
1010
|
+
text: z5.string().nullable(),
|
|
889
1011
|
/** The text was cut at its size limit. */
|
|
890
|
-
truncated:
|
|
1012
|
+
truncated: z5.boolean()
|
|
891
1013
|
});
|
|
892
|
-
var ProposalFrontmatterSchema =
|
|
1014
|
+
var ProposalFrontmatterSchema = z5.object({
|
|
893
1015
|
id: UlidSchema,
|
|
894
1016
|
kind: ProposalKindSchema,
|
|
895
1017
|
proposedBy: NameSchema,
|
|
@@ -901,146 +1023,191 @@ var ProposalFrontmatterSchema = z4.object({
|
|
|
901
1023
|
skillFiles: SkillFilesSchema.optional(),
|
|
902
1024
|
decidedBy: NameSchema.optional(),
|
|
903
1025
|
decidedAt: IsoDateTimeSchema.optional(),
|
|
904
|
-
reason:
|
|
905
|
-
charter:
|
|
1026
|
+
reason: z5.string().optional(),
|
|
1027
|
+
charter: z5.record(z5.string(), z5.unknown()),
|
|
906
1028
|
/** What approval created, for kinds the board provisions: the agent, channel, role, or retirement. */
|
|
907
1029
|
provisionedAt: IsoDateTimeSchema.optional(),
|
|
908
|
-
provision:
|
|
1030
|
+
provision: z5.record(z5.string(), z5.unknown()).optional()
|
|
909
1031
|
});
|
|
910
|
-
var DecisionSchema =
|
|
1032
|
+
var DecisionSchema = z5.object({
|
|
911
1033
|
id: UlidSchema,
|
|
912
1034
|
proposalId: UlidSchema,
|
|
913
1035
|
decidedBy: NameSchema,
|
|
914
|
-
outcome:
|
|
915
|
-
reason:
|
|
1036
|
+
outcome: z5.enum(["approved", "rejected"]),
|
|
1037
|
+
reason: z5.string().optional(),
|
|
916
1038
|
ts: IsoDateTimeSchema
|
|
917
1039
|
});
|
|
918
1040
|
|
|
919
1041
|
// packages/shared/dist/verbs.js
|
|
920
|
-
import { z as
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1042
|
+
import { z as z7 } from "zod";
|
|
1043
|
+
|
|
1044
|
+
// packages/shared/dist/images.js
|
|
1045
|
+
import { z as z6 } from "zod";
|
|
1046
|
+
var IMAGE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
1047
|
+
var ImageTypeSchema = z6.enum(["png", "jpeg", "gif", "webp"]);
|
|
1048
|
+
var IMAGE_MEDIA_TYPES = {
|
|
1049
|
+
png: "image/png",
|
|
1050
|
+
jpeg: "image/jpeg",
|
|
1051
|
+
gif: "image/gif",
|
|
1052
|
+
webp: "image/webp"
|
|
1053
|
+
};
|
|
1054
|
+
function startsWith(bytes, prefix, at = 0) {
|
|
1055
|
+
for (let index = 0; index < prefix.length; index += 1) {
|
|
1056
|
+
if (bytes[at + index] !== prefix[index]) {
|
|
1057
|
+
return false;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return true;
|
|
1061
|
+
}
|
|
1062
|
+
var ascii = (text) => new TextEncoder().encode(text);
|
|
1063
|
+
function imageTypeOf(bytes) {
|
|
1064
|
+
if (startsWith(bytes, [137, 80, 78, 71, 13, 10, 26, 10])) {
|
|
1065
|
+
return "png";
|
|
1066
|
+
}
|
|
1067
|
+
if (startsWith(bytes, [255, 216, 255])) {
|
|
1068
|
+
return "jpeg";
|
|
1069
|
+
}
|
|
1070
|
+
if (startsWith(bytes, ascii("GIF87a")) || startsWith(bytes, ascii("GIF89a"))) {
|
|
1071
|
+
return "gif";
|
|
1072
|
+
}
|
|
1073
|
+
if (startsWith(bytes, ascii("RIFF")) && startsWith(bytes, ascii("WEBP"), 8)) {
|
|
1074
|
+
return "webp";
|
|
1075
|
+
}
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
var ImagePathSchema = z6.string().min(1).max(4096).refine((value) => !value.includes("\0"), { message: "a path with no NUL character" });
|
|
1079
|
+
var IMAGE_SCHEME = "image:";
|
|
1080
|
+
var UploadedImageSchema = z6.object({
|
|
1081
|
+
id: UlidSchema,
|
|
1082
|
+
type: ImageTypeSchema,
|
|
1083
|
+
size: z6.number().int().positive(),
|
|
1084
|
+
markdown: z6.string()
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
// packages/shared/dist/verbs.js
|
|
1088
|
+
var MomentSchema = z7.iso.datetime({ offset: true });
|
|
1089
|
+
var CronTitleSchema = z7.string().trim().min(1).max(120);
|
|
1090
|
+
var CronNoteSchema = z7.string().trim().min(1).max(4e3);
|
|
924
1091
|
var VerbInputs = {
|
|
925
|
-
post_message:
|
|
1092
|
+
post_message: z7.object({
|
|
926
1093
|
/** Required outside a thread; in one it may be left out, and must otherwise be the thread's. */
|
|
927
1094
|
channel: ChannelRefSchema.optional(),
|
|
928
|
-
body:
|
|
1095
|
+
body: z7.string().min(1),
|
|
929
1096
|
thread_id: UlidSchema.optional()
|
|
930
1097
|
}),
|
|
931
|
-
read_inbox:
|
|
1098
|
+
read_inbox: z7.object({
|
|
932
1099
|
since_cursor: UlidSchema.nullable().optional(),
|
|
933
|
-
limit:
|
|
934
|
-
advance:
|
|
1100
|
+
limit: z7.number().int().positive().max(200).default(50),
|
|
1101
|
+
advance: z7.boolean().default(true)
|
|
935
1102
|
}),
|
|
936
|
-
search:
|
|
937
|
-
query:
|
|
1103
|
+
search: z7.object({
|
|
1104
|
+
query: z7.string().min(1),
|
|
938
1105
|
project: NameSchema.optional(),
|
|
939
1106
|
channel: ChannelRefSchema.optional(),
|
|
940
|
-
limit:
|
|
1107
|
+
limit: z7.number().int().positive().max(100).default(20)
|
|
941
1108
|
}),
|
|
942
1109
|
/** On a task or a proposal, whose id the thread takes, or on a channel with a title. */
|
|
943
|
-
open_thread:
|
|
1110
|
+
open_thread: z7.object({
|
|
944
1111
|
task_id: UlidSchema.optional(),
|
|
945
1112
|
proposal_id: UlidSchema.optional(),
|
|
946
1113
|
channel: ChannelRefSchema.optional(),
|
|
947
|
-
title:
|
|
1114
|
+
title: z7.string().min(1).max(200).optional()
|
|
948
1115
|
}),
|
|
949
|
-
close_thread:
|
|
950
|
-
create_task:
|
|
1116
|
+
close_thread: z7.object({ thread_id: UlidSchema, summary: z7.string().min(1) }),
|
|
1117
|
+
create_task: z7.object({
|
|
951
1118
|
project: NameSchema,
|
|
952
|
-
title:
|
|
953
|
-
body:
|
|
1119
|
+
title: z7.string().min(1).max(200),
|
|
1120
|
+
body: z7.string().default(""),
|
|
954
1121
|
parent_id: UlidSchema.optional(),
|
|
955
|
-
required_capabilities:
|
|
1122
|
+
required_capabilities: z7.array(z7.string()).default([]),
|
|
956
1123
|
/** The plan; without it the task gets one stage, work, that anyone in the project may take. */
|
|
957
|
-
stages:
|
|
1124
|
+
stages: z7.array(PlanStageSchema).min(1).optional(),
|
|
958
1125
|
/** A channel of the project the task belongs to, such as a release's; its thread opens there. */
|
|
959
1126
|
channel: NameSchema.default("general")
|
|
960
1127
|
}),
|
|
961
|
-
claim_task:
|
|
962
|
-
release_task:
|
|
963
|
-
update_task:
|
|
1128
|
+
claim_task: z7.object({ task_id: UlidSchema }),
|
|
1129
|
+
release_task: z7.object({ task_id: UlidSchema }),
|
|
1130
|
+
update_task: z7.object({
|
|
964
1131
|
task_id: UlidSchema,
|
|
965
1132
|
/** Move the task back to this earlier stage. */
|
|
966
1133
|
stage: StageIdSchema.optional(),
|
|
967
|
-
status:
|
|
1134
|
+
status: z7.enum(["abandoned"]).optional(),
|
|
968
1135
|
/** Posted to the task's thread as the caller, marked with the step when the task moved. */
|
|
969
|
-
note:
|
|
970
|
-
blocked_by:
|
|
1136
|
+
note: z7.string().min(1).optional(),
|
|
1137
|
+
blocked_by: z7.array(UlidSchema).optional(),
|
|
971
1138
|
/**
|
|
972
1139
|
* The pull request a `ghpr` task lands through, on its project's repository, with the merge
|
|
973
1140
|
* commit's subject and body; null unlinks it.
|
|
974
1141
|
*/
|
|
975
|
-
pull_request:
|
|
1142
|
+
pull_request: z7.object({
|
|
976
1143
|
url: PullRequestUrlSchema,
|
|
977
1144
|
merge_subject: MergeSubjectSchema.optional(),
|
|
978
|
-
merge_body:
|
|
1145
|
+
merge_body: z7.string().max(2e4).optional()
|
|
979
1146
|
}).nullable().optional()
|
|
980
1147
|
}),
|
|
981
|
-
get_task:
|
|
982
|
-
subscribe:
|
|
983
|
-
unsubscribe:
|
|
984
|
-
propose:
|
|
1148
|
+
get_task: z7.object({ task_id: UlidSchema }),
|
|
1149
|
+
subscribe: z7.object({ channel: ChannelRefSchema }),
|
|
1150
|
+
unsubscribe: z7.object({ channel: ChannelRefSchema }),
|
|
1151
|
+
propose: z7.object({
|
|
985
1152
|
kind: ProposalKindSchema,
|
|
986
|
-
charter:
|
|
987
|
-
rationale:
|
|
1153
|
+
charter: z7.record(z7.string(), z7.unknown()),
|
|
1154
|
+
rationale: z7.string().default("")
|
|
988
1155
|
}),
|
|
989
|
-
approve:
|
|
990
|
-
reject:
|
|
991
|
-
create_project:
|
|
1156
|
+
approve: z7.object({ proposal_id: UlidSchema, reason: z7.string().optional() }),
|
|
1157
|
+
reject: z7.object({ proposal_id: UlidSchema, reason: z7.string().min(1) }),
|
|
1158
|
+
create_project: z7.object({
|
|
992
1159
|
slug: NameSchema,
|
|
993
|
-
name:
|
|
994
|
-
repo:
|
|
1160
|
+
name: z7.string().min(1).optional(),
|
|
1161
|
+
repo: z7.string().min(1).nullable().default(null),
|
|
995
1162
|
default_branch: DefaultBranchSchema.default("main"),
|
|
996
1163
|
on_done: CompletionEffectSchema.optional()
|
|
997
1164
|
}),
|
|
998
|
-
join_project:
|
|
999
|
-
leave_project:
|
|
1000
|
-
write_knowledge:
|
|
1165
|
+
join_project: z7.object({ project: NameSchema, agent: NameSchema.optional() }),
|
|
1166
|
+
leave_project: z7.object({ project: NameSchema, agent: NameSchema.optional() }),
|
|
1167
|
+
write_knowledge: z7.object({
|
|
1001
1168
|
/** A project you belong to, or null for society knowledge, which the steward and the user curate. */
|
|
1002
1169
|
project: NameSchema.nullable().default(null),
|
|
1003
1170
|
topic: NameSchema,
|
|
1004
|
-
body:
|
|
1171
|
+
body: z7.string().min(1),
|
|
1005
1172
|
/** The `revision` in the frontmatter of the topic you read, 0 when it names none or for a new topic. */
|
|
1006
|
-
revision:
|
|
1173
|
+
revision: z7.number().int().min(0)
|
|
1007
1174
|
}),
|
|
1008
|
-
remove_knowledge:
|
|
1175
|
+
remove_knowledge: z7.object({
|
|
1009
1176
|
/** As for write_knowledge: a project you belong to, or null for society knowledge. */
|
|
1010
1177
|
project: NameSchema.nullable().default(null),
|
|
1011
1178
|
topic: NameSchema
|
|
1012
1179
|
}),
|
|
1013
|
-
plan_task:
|
|
1180
|
+
plan_task: z7.object({
|
|
1014
1181
|
task_id: UlidSchema,
|
|
1015
|
-
stages:
|
|
1182
|
+
stages: z7.array(PlanEditStageSchema),
|
|
1016
1183
|
on_done: CompletionEffectSchema.optional()
|
|
1017
1184
|
}),
|
|
1018
1185
|
/** The note is posted to the task's thread as the caller, marked with the step. */
|
|
1019
|
-
advance_task:
|
|
1020
|
-
configure_project:
|
|
1186
|
+
advance_task: z7.object({ task_id: UlidSchema, note: z7.string().min(1).optional() }),
|
|
1187
|
+
configure_project: z7.object({
|
|
1021
1188
|
project: NameSchema,
|
|
1022
1189
|
on_done: CompletionEffectSchema.optional(),
|
|
1023
1190
|
/** A new display name; the slug, which everything refers to the project by, never changes. */
|
|
1024
|
-
name:
|
|
1191
|
+
name: z7.string().trim().min(1).max(120).optional(),
|
|
1025
1192
|
/** The branch tasks start from and land on from now; tasks in play keep their branches. */
|
|
1026
1193
|
default_branch: DefaultBranchSchema.optional()
|
|
1027
1194
|
}),
|
|
1028
|
-
archive_project:
|
|
1029
|
-
create_channel:
|
|
1195
|
+
archive_project: z7.object({ project: NameSchema, reason: z7.string().min(1) }),
|
|
1196
|
+
create_channel: z7.object({
|
|
1030
1197
|
/** A project, or null for a channel of the society. */
|
|
1031
1198
|
project: NameSchema.nullable().default(null),
|
|
1032
1199
|
name: NameSchema,
|
|
1033
|
-
purpose:
|
|
1200
|
+
purpose: z7.string().min(1)
|
|
1034
1201
|
}),
|
|
1035
|
-
archive_channel:
|
|
1036
|
-
update_dashboard:
|
|
1202
|
+
archive_channel: z7.object({ channel: ChannelRefSchema, reason: z7.string().min(1) }),
|
|
1203
|
+
update_dashboard: z7.object({
|
|
1037
1204
|
project: NameSchema,
|
|
1038
1205
|
/** The whole dashboard as markdown; the board keeps its frontmatter. */
|
|
1039
|
-
body:
|
|
1206
|
+
body: z7.string().min(1),
|
|
1040
1207
|
/** The `revision` in the frontmatter of the dashboard you read, 0 when it names none. */
|
|
1041
|
-
revision:
|
|
1208
|
+
revision: z7.number().int().min(0)
|
|
1042
1209
|
}),
|
|
1043
|
-
create_cron:
|
|
1210
|
+
create_cron: z7.object({
|
|
1044
1211
|
title: CronTitleSchema,
|
|
1045
1212
|
/** What to do when it fires; quoted in the prompt of every turn it starts. */
|
|
1046
1213
|
note: CronNoteSchema,
|
|
@@ -1059,10 +1226,10 @@ var VerbInputs = {
|
|
|
1059
1226
|
/** An open channel of that place other than general, whose conversation it fires in. */
|
|
1060
1227
|
channel: NameSchema.optional()
|
|
1061
1228
|
}),
|
|
1062
|
-
update_cron:
|
|
1229
|
+
update_cron: z7.object({
|
|
1063
1230
|
cron_id: UlidSchema,
|
|
1064
1231
|
/** Pause it, or resume it, which starts its schedule afresh from now. */
|
|
1065
|
-
paused:
|
|
1232
|
+
paused: z7.boolean().optional(),
|
|
1066
1233
|
title: CronTitleSchema.optional(),
|
|
1067
1234
|
note: CronNoteSchema.optional(),
|
|
1068
1235
|
/** A new expression, or `at` for one time; either starts the schedule afresh. */
|
|
@@ -1071,8 +1238,12 @@ var VerbInputs = {
|
|
|
1071
1238
|
timezone: TimeZoneSchema.nullable().optional(),
|
|
1072
1239
|
at: MomentSchema.optional()
|
|
1073
1240
|
}),
|
|
1074
|
-
remove_cron:
|
|
1075
|
-
remove_skill:
|
|
1241
|
+
remove_cron: z7.object({ cron_id: UlidSchema, reason: z7.string().trim().min(1) }),
|
|
1242
|
+
remove_skill: z7.object({ name: NameSchema, reason: z7.string().trim().min(1) }),
|
|
1243
|
+
upload_image: z7.object({
|
|
1244
|
+
/** The image file on the machine your turn runs on: absolute, or relative to your working directory. */
|
|
1245
|
+
path: ImagePathSchema
|
|
1246
|
+
})
|
|
1076
1247
|
};
|
|
1077
1248
|
var VERB_DESCRIPTIONS = {
|
|
1078
1249
|
post_message: "Post a markdown message to a channel, or into a thread when thread_id is given; a thread's message goes to the thread's channel.",
|
|
@@ -1083,7 +1254,7 @@ var VERB_DESCRIPTIONS = {
|
|
|
1083
1254
|
create_task: "Create a task in a project with its plan: stages of {name, role or agent, gate}. Without stages it gets one stage, work, that anyone in the project may take. File it in a channel of the project, such as a release's, or general by default; its thread opens there with it, under the task's id, for everything said about the work.",
|
|
1084
1255
|
claim_task: "Hold the task's current stage. Claims are leases renewed by every turn that touches the task.",
|
|
1085
1256
|
release_task: "Let go of the stage you hold so someone else can take it.",
|
|
1086
|
-
update_task: "Move a task back to an earlier stage, abandon it, set the tasks it is blocked by (blocked_by: its stage wakes nobody until they have all ended), or link its pull request (pull_request: url, with the merge commit's merge_subject and merge_body), which a ghpr task needs before its last stage completes. A note is posted to the task's thread.",
|
|
1257
|
+
update_task: "Move a task back to an earlier stage (the current stage's holder, the user, the steward, or the concierge; anyone else asks the concierge in the task's thread), abandon it, set the tasks it is blocked by (blocked_by: its stage wakes nobody until they have all ended), or link its pull request (pull_request: url, with the merge commit's merge_subject and merge_body), which a ghpr task needs before its last stage completes. A note is posted to the task's thread.",
|
|
1087
1258
|
get_task: "Read a task with its plan, its brief, and the messages of its thread.",
|
|
1088
1259
|
subscribe: "Subscribe to a channel. Subscriptions feed your digest; they never wake you.",
|
|
1089
1260
|
unsubscribe: "Unsubscribe from a channel.",
|
|
@@ -1105,11 +1276,12 @@ var VERB_DESCRIPTIONS = {
|
|
|
1105
1276
|
create_cron: "Set a cron that wakes you later: a title, a note on what to do then, and either cron, a five-field expression (minute hour day-of-month month day-of-week) read in timezone or the society's, or at, one time. It fires in this turn's conversation unless you name a project, thread_id, or channel, at most every 15 minutes, and every fire is a turn. Only the user, the steward, and the concierge set one that wakes another citizen (agent).",
|
|
1106
1277
|
update_cron: "Pause or resume a cron, or change its title, note, or schedule; resuming or rescheduling starts it afresh from now. Yours, or anyone's for the user, the steward, and the concierge.",
|
|
1107
1278
|
remove_cron: "End a cron for good, with a reason. Yours, or anyone's for the user, the steward, and the concierge. A cron also ends with its conversation, and a one-time cron once it fires.",
|
|
1108
|
-
remove_skill: "Remove a skill from the society's skills, with a reason, when it is wrong, superseded, or no longer used: no skills index lists it from the next turn, and a notice in general says so with the reason. Its proposer's own copy stays in its home. Steward and user only; the board keeps its text aside for the user to restore."
|
|
1279
|
+
remove_skill: "Remove a skill from the society's skills, with a reason, when it is wrong, superseded, or no longer used: no skills index lists it from the next turn, and a notice in general says so with the reason. Its proposer's own copy stays in its home. Steward and user only; the board keeps its text aside for the user to restore.",
|
|
1280
|
+
upload_image: "Upload an image file, such as a screenshot or a chart, from the machine your turn runs on: a PNG, JPEG, GIF, or WebP of at most 8 MB, in your worktree, your home, or the system temp folder. Returns its id and the markdown that shows it in a post, ``; write what it shows as the alt text."
|
|
1109
1281
|
};
|
|
1110
1282
|
|
|
1111
1283
|
// packages/shared/dist/events.js
|
|
1112
|
-
import { z as
|
|
1284
|
+
import { z as z8 } from "zod";
|
|
1113
1285
|
var BOARD_EVENT_TYPES = [
|
|
1114
1286
|
"society.initialized",
|
|
1115
1287
|
"project.added",
|
|
@@ -1154,6 +1326,7 @@ var BOARD_EVENT_TYPES = [
|
|
|
1154
1326
|
"user.signed_out",
|
|
1155
1327
|
"user.token_rotated",
|
|
1156
1328
|
"runner.changed",
|
|
1329
|
+
"runner.quota",
|
|
1157
1330
|
"ops.signal",
|
|
1158
1331
|
"paused.changed",
|
|
1159
1332
|
"wake.requested",
|
|
@@ -1172,30 +1345,39 @@ var BOARD_EVENT_TYPES = [
|
|
|
1172
1345
|
"cron.updated",
|
|
1173
1346
|
"cron.fired",
|
|
1174
1347
|
"cron.skipped",
|
|
1175
|
-
"cron.ended"
|
|
1348
|
+
"cron.ended",
|
|
1349
|
+
"image.uploaded"
|
|
1176
1350
|
];
|
|
1177
|
-
var BoardEventTypeSchema =
|
|
1178
|
-
var BoardEventSchema =
|
|
1351
|
+
var BoardEventTypeSchema = z8.enum(BOARD_EVENT_TYPES);
|
|
1352
|
+
var BoardEventSchema = z8.object({
|
|
1179
1353
|
id: UlidSchema,
|
|
1180
1354
|
ts: IsoDateTimeSchema,
|
|
1181
1355
|
type: BoardEventTypeSchema,
|
|
1182
1356
|
actor: NameSchema,
|
|
1183
|
-
payload:
|
|
1357
|
+
payload: z8.record(z8.string(), z8.unknown())
|
|
1184
1358
|
});
|
|
1185
|
-
var UsageSchema =
|
|
1186
|
-
inputTokens:
|
|
1187
|
-
outputTokens:
|
|
1188
|
-
cacheReadTokens:
|
|
1189
|
-
cacheWriteTokens:
|
|
1359
|
+
var UsageSchema = z8.object({
|
|
1360
|
+
inputTokens: z8.number().int().nonnegative(),
|
|
1361
|
+
outputTokens: z8.number().int().nonnegative(),
|
|
1362
|
+
cacheReadTokens: z8.number().int().nonnegative().default(0),
|
|
1363
|
+
cacheWriteTokens: z8.number().int().nonnegative().default(0)
|
|
1190
1364
|
});
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1365
|
+
function addUsage(a, b) {
|
|
1366
|
+
return {
|
|
1367
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
1368
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
1369
|
+
cacheReadTokens: a.cacheReadTokens + b.cacheReadTokens,
|
|
1370
|
+
cacheWriteTokens: a.cacheWriteTokens + b.cacheWriteTokens
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
var TurnStatusSchema = z8.object({
|
|
1374
|
+
summary: z8.string().min(1),
|
|
1375
|
+
claimsHeld: z8.array(UlidSchema).default([]),
|
|
1376
|
+
blockedOn: z8.array(z8.string()).default([]),
|
|
1377
|
+
needsUserDecision: z8.boolean().default(false),
|
|
1378
|
+
memoryUpdated: z8.boolean().default(false)
|
|
1197
1379
|
});
|
|
1198
|
-
var TurnExitReasonSchema =
|
|
1380
|
+
var TurnExitReasonSchema = z8.enum([
|
|
1199
1381
|
"completed",
|
|
1200
1382
|
"timeout",
|
|
1201
1383
|
"error",
|
|
@@ -1203,50 +1385,50 @@ var TurnExitReasonSchema = z6.enum([
|
|
|
1203
1385
|
"blocked",
|
|
1204
1386
|
"stopped"
|
|
1205
1387
|
]);
|
|
1206
|
-
var AgentEventSchema =
|
|
1207
|
-
|
|
1208
|
-
type:
|
|
1388
|
+
var AgentEventSchema = z8.discriminatedUnion("type", [
|
|
1389
|
+
z8.object({
|
|
1390
|
+
type: z8.literal("turn_started"),
|
|
1209
1391
|
agent: NameSchema,
|
|
1210
|
-
session:
|
|
1392
|
+
session: z8.string(),
|
|
1211
1393
|
runner: NameSchema,
|
|
1212
1394
|
/** The model the CLI reports running, when it reports one. */
|
|
1213
|
-
model:
|
|
1395
|
+
model: z8.string().optional()
|
|
1214
1396
|
}),
|
|
1215
1397
|
/**
|
|
1216
1398
|
* The CLI took a steer into the running turn. Adapters name the steer; the server adds the
|
|
1217
1399
|
* messages it delivered and the text the CLI was given.
|
|
1218
1400
|
*/
|
|
1219
|
-
|
|
1220
|
-
type:
|
|
1221
|
-
steer:
|
|
1222
|
-
messages:
|
|
1223
|
-
text:
|
|
1401
|
+
z8.object({
|
|
1402
|
+
type: z8.literal("steered"),
|
|
1403
|
+
steer: z8.string().min(1),
|
|
1404
|
+
messages: z8.array(UlidSchema).optional(),
|
|
1405
|
+
text: z8.string().optional()
|
|
1224
1406
|
}),
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
type:
|
|
1229
|
-
name:
|
|
1230
|
-
ok:
|
|
1407
|
+
z8.object({ type: z8.literal("text"), delta: z8.string() }),
|
|
1408
|
+
z8.object({ type: z8.literal("tool_call"), name: z8.string(), input: z8.unknown() }),
|
|
1409
|
+
z8.object({
|
|
1410
|
+
type: z8.literal("tool_result"),
|
|
1411
|
+
name: z8.string(),
|
|
1412
|
+
ok: z8.boolean(),
|
|
1231
1413
|
/** What the tool returned, cut to `OUTPUT_LIMIT` characters by `capOutput`. */
|
|
1232
|
-
output:
|
|
1414
|
+
output: z8.string().optional()
|
|
1233
1415
|
}),
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
type:
|
|
1416
|
+
z8.object({ type: z8.literal("approval_requested"), kind: z8.string(), detail: z8.unknown() }),
|
|
1417
|
+
z8.object({
|
|
1418
|
+
type: z8.literal("turn_completed"),
|
|
1237
1419
|
usage: UsageSchema,
|
|
1238
|
-
costUsd:
|
|
1420
|
+
costUsd: z8.number().nonnegative(),
|
|
1239
1421
|
status: TurnStatusSchema.nullable(),
|
|
1240
1422
|
exitReason: TurnExitReasonSchema
|
|
1241
1423
|
}),
|
|
1242
|
-
|
|
1424
|
+
z8.object({ type: z8.literal("error"), message: z8.string() })
|
|
1243
1425
|
]);
|
|
1244
|
-
var TranscriptEntrySchema =
|
|
1426
|
+
var TranscriptEntrySchema = z8.object({
|
|
1245
1427
|
ts: IsoDateTimeSchema,
|
|
1246
1428
|
event: AgentEventSchema
|
|
1247
1429
|
});
|
|
1248
|
-
var LiveTurnEventSchema =
|
|
1249
|
-
seq:
|
|
1430
|
+
var LiveTurnEventSchema = z8.object({
|
|
1431
|
+
seq: z8.number().int().positive(),
|
|
1250
1432
|
ts: IsoDateTimeSchema,
|
|
1251
1433
|
agent: NameSchema,
|
|
1252
1434
|
project: NameSchema,
|
|
@@ -1258,10 +1440,10 @@ var LiveTurnEventSchema = z6.object({
|
|
|
1258
1440
|
});
|
|
1259
1441
|
|
|
1260
1442
|
// packages/shared/dist/turns.js
|
|
1261
|
-
import { z as
|
|
1443
|
+
import { z as z10 } from "zod";
|
|
1262
1444
|
|
|
1263
1445
|
// packages/shared/dist/runner.js
|
|
1264
|
-
import { z as
|
|
1446
|
+
import { z as z9 } from "zod";
|
|
1265
1447
|
var RUNNER_PROTOCOL = 1;
|
|
1266
1448
|
var PATH_TOKENS = {
|
|
1267
1449
|
home: "{{stellaris:home}}",
|
|
@@ -1272,67 +1454,77 @@ var SERVER_TOKEN = "{{stellaris:server}}";
|
|
|
1272
1454
|
var WORKSPACE_TOKEN = "{{stellaris:workspace}}";
|
|
1273
1455
|
var HOME_SCRATCH = "scratch";
|
|
1274
1456
|
var HOME_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
1275
|
-
var RunnerHelloSchema =
|
|
1276
|
-
protocol:
|
|
1277
|
-
version:
|
|
1457
|
+
var RunnerHelloSchema = z9.object({
|
|
1458
|
+
protocol: z9.number().int().positive(),
|
|
1459
|
+
version: z9.string().min(1),
|
|
1278
1460
|
os: RunnerOsSchema,
|
|
1279
|
-
clis:
|
|
1461
|
+
clis: z9.array(CliKindSchema),
|
|
1280
1462
|
/** The CLIs whose adapter can keep a session warm between turns. */
|
|
1281
|
-
residentClis:
|
|
1463
|
+
residentClis: z9.array(CliKindSchema).default([]),
|
|
1282
1464
|
/** The CLIs whose adapter can take input into a running turn; the server steers only these. */
|
|
1283
|
-
steerableClis:
|
|
1465
|
+
steerableClis: z9.array(CliKindSchema).default([]),
|
|
1284
1466
|
/** The CLIs whose adapter can stop a running turn; the server stops only these. */
|
|
1285
|
-
stoppableClis:
|
|
1467
|
+
stoppableClis: z9.array(CliKindSchema).default([]),
|
|
1286
1468
|
/** Whether the runner answers file reads on its projects' branches; one from before never does. */
|
|
1287
|
-
branchReads:
|
|
1469
|
+
branchReads: z9.boolean().default(false),
|
|
1288
1470
|
/** Whether the runner lists what a branch changed; one from before never does. */
|
|
1289
|
-
branchChanges:
|
|
1290
|
-
|
|
1471
|
+
branchChanges: z9.boolean().default(false),
|
|
1472
|
+
/** Whether the runner answers a read of a CLI's quota; one from before never does. */
|
|
1473
|
+
quotaReads: z9.boolean().default(false),
|
|
1474
|
+
/** Whether the runner reads an image a turn running there uploads; one from before never does. */
|
|
1475
|
+
imageReads: z9.boolean().default(false),
|
|
1476
|
+
capabilities: z9.array(z9.string()).default([]),
|
|
1477
|
+
/**
|
|
1478
|
+
* What the runner's account lacks of the git identity citizens' own commits carry, checked
|
|
1479
|
+
* before each hello outside any repository; empty when it has both. Absent from a runner from
|
|
1480
|
+
* before the check, which no default fills in, so the board marks only what a runner reported.
|
|
1481
|
+
*/
|
|
1482
|
+
gitIdentityMissing: z9.array(GitIdentityPartSchema).optional(),
|
|
1291
1483
|
/** Turns the runner runs at once, or null for no limit. */
|
|
1292
|
-
slots:
|
|
1484
|
+
slots: z9.number().int().positive().nullable(),
|
|
1293
1485
|
/** Turns the runner is still running, when it registers again; the server fails the rest of its turns. */
|
|
1294
|
-
turns:
|
|
1486
|
+
turns: z9.array(UlidSchema).default([])
|
|
1295
1487
|
});
|
|
1296
|
-
var RunnerWelcomeSchema =
|
|
1297
|
-
var RunnerEnrollRequestSchema =
|
|
1298
|
-
protocol:
|
|
1299
|
-
version:
|
|
1300
|
-
hostname:
|
|
1488
|
+
var RunnerWelcomeSchema = z9.object({ name: NameSchema, version: z9.string() });
|
|
1489
|
+
var RunnerEnrollRequestSchema = z9.object({
|
|
1490
|
+
protocol: z9.number().int().positive(),
|
|
1491
|
+
version: z9.string().min(1).max(64),
|
|
1492
|
+
hostname: z9.string().min(1).max(253),
|
|
1301
1493
|
os: RunnerOsSchema,
|
|
1302
|
-
clis:
|
|
1303
|
-
capabilities:
|
|
1494
|
+
clis: z9.array(CliKindSchema).max(8),
|
|
1495
|
+
capabilities: z9.array(z9.string().min(1).max(64)).max(32).default([])
|
|
1304
1496
|
});
|
|
1305
|
-
var RunnerEnrollmentSchema =
|
|
1306
|
-
deviceCode:
|
|
1307
|
-
userCode:
|
|
1497
|
+
var RunnerEnrollmentSchema = z9.object({
|
|
1498
|
+
deviceCode: z9.string().min(1),
|
|
1499
|
+
userCode: z9.string().min(1),
|
|
1308
1500
|
expiresAt: IsoDateTimeSchema,
|
|
1309
|
-
intervalMs:
|
|
1501
|
+
intervalMs: z9.number().int().positive()
|
|
1310
1502
|
});
|
|
1311
|
-
var RunnerEnrollPollSchema =
|
|
1312
|
-
var RunnerEnrollStatusSchema =
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1503
|
+
var RunnerEnrollPollSchema = z9.object({ deviceCode: z9.string().min(1) });
|
|
1504
|
+
var RunnerEnrollStatusSchema = z9.discriminatedUnion("status", [
|
|
1505
|
+
z9.object({ status: z9.literal("pending") }),
|
|
1506
|
+
z9.object({ status: z9.literal("approved"), name: NameSchema, token: z9.string().min(1) }),
|
|
1507
|
+
z9.object({ status: z9.literal("denied") })
|
|
1316
1508
|
]);
|
|
1317
|
-
var PendingEnrollmentSchema =
|
|
1318
|
-
userCode:
|
|
1319
|
-
hostname:
|
|
1509
|
+
var PendingEnrollmentSchema = z9.object({
|
|
1510
|
+
userCode: z9.string().min(1),
|
|
1511
|
+
hostname: z9.string(),
|
|
1320
1512
|
os: RunnerOsSchema,
|
|
1321
|
-
clis:
|
|
1322
|
-
capabilities:
|
|
1323
|
-
version:
|
|
1513
|
+
clis: z9.array(CliKindSchema),
|
|
1514
|
+
capabilities: z9.array(z9.string()),
|
|
1515
|
+
version: z9.string(),
|
|
1324
1516
|
requestedAt: IsoDateTimeSchema,
|
|
1325
1517
|
expiresAt: IsoDateTimeSchema
|
|
1326
1518
|
});
|
|
1327
|
-
var ProjectRepoSchema =
|
|
1519
|
+
var ProjectRepoSchema = z9.object({
|
|
1328
1520
|
slug: NameSchema,
|
|
1329
|
-
origin:
|
|
1330
|
-
defaultBranch:
|
|
1521
|
+
origin: z9.string().nullable(),
|
|
1522
|
+
defaultBranch: z9.string().min(1),
|
|
1331
1523
|
/**
|
|
1332
1524
|
* The board lands finished tasks on the default branch, itself or through their pull requests,
|
|
1333
1525
|
* so the runner lets nothing else move it.
|
|
1334
1526
|
*/
|
|
1335
|
-
boardLands:
|
|
1527
|
+
boardLands: z9.boolean()
|
|
1336
1528
|
});
|
|
1337
1529
|
function projectRepo(project) {
|
|
1338
1530
|
return {
|
|
@@ -1342,29 +1534,29 @@ function projectRepo(project) {
|
|
|
1342
1534
|
boardLands: project.onDone !== "none"
|
|
1343
1535
|
};
|
|
1344
1536
|
}
|
|
1345
|
-
var TurnWorkspaceSchema =
|
|
1346
|
-
|
|
1347
|
-
kind:
|
|
1537
|
+
var TurnWorkspaceSchema = z9.discriminatedUnion("kind", [
|
|
1538
|
+
z9.object({
|
|
1539
|
+
kind: z9.literal("home"),
|
|
1348
1540
|
thread: UlidSchema.optional(),
|
|
1349
1541
|
channel: NameSchema.optional()
|
|
1350
1542
|
}),
|
|
1351
|
-
|
|
1352
|
-
kind:
|
|
1543
|
+
z9.object({
|
|
1544
|
+
kind: z9.literal("project"),
|
|
1353
1545
|
repo: ProjectRepoSchema,
|
|
1354
|
-
branches:
|
|
1546
|
+
branches: z9.array(z9.string().min(1)),
|
|
1355
1547
|
thread: UlidSchema.optional(),
|
|
1356
1548
|
channel: NameSchema.optional()
|
|
1357
1549
|
}),
|
|
1358
|
-
|
|
1550
|
+
z9.object({ kind: z9.literal("task"), repo: ProjectRepoSchema, taskId: UlidSchema })
|
|
1359
1551
|
]);
|
|
1360
|
-
var TurnJobSchema =
|
|
1552
|
+
var TurnJobSchema = z9.object({
|
|
1361
1553
|
turnId: UlidSchema,
|
|
1362
1554
|
agent: NameSchema,
|
|
1363
1555
|
role: NameSchema,
|
|
1364
1556
|
cli: CliKindSchema,
|
|
1365
|
-
model:
|
|
1557
|
+
model: z9.string().optional(),
|
|
1366
1558
|
/** The reasoning effort set on the agent; absent for the model's own default. */
|
|
1367
|
-
effort:
|
|
1559
|
+
effort: z9.string().optional(),
|
|
1368
1560
|
/** A project slug, or the society scope. */
|
|
1369
1561
|
scope: NameSchema,
|
|
1370
1562
|
/** The thread whose conversation the turn is in; absent for the home conversation. */
|
|
@@ -1375,125 +1567,130 @@ var TurnJobSchema = z7.object({
|
|
|
1375
1567
|
* The session to resume, or null to start one: the CLI's adapter picks a fresh session's id, and
|
|
1376
1568
|
* the outcome reports it.
|
|
1377
1569
|
*/
|
|
1378
|
-
session:
|
|
1570
|
+
session: z9.string().min(1).nullable(),
|
|
1379
1571
|
/** The running total the session reported after its last turn, which a resumed session starts from. */
|
|
1380
|
-
costSoFarUsd:
|
|
1572
|
+
costSoFarUsd: z9.number().nonnegative(),
|
|
1381
1573
|
/** The rendered charter, norms, memory core, and skills index, with `PATH_TOKENS` for places. */
|
|
1382
|
-
instructions:
|
|
1383
|
-
prompt:
|
|
1384
|
-
mcp:
|
|
1385
|
-
limits:
|
|
1386
|
-
timeoutMs:
|
|
1387
|
-
maxTurns:
|
|
1574
|
+
instructions: z9.string(),
|
|
1575
|
+
prompt: z9.string(),
|
|
1576
|
+
mcp: z9.object({ url: z9.string().min(1), token: z9.string().min(1) }),
|
|
1577
|
+
limits: z9.object({
|
|
1578
|
+
timeoutMs: z9.number().int().positive().nullable(),
|
|
1579
|
+
maxTurns: z9.number().int().positive().nullable()
|
|
1388
1580
|
}),
|
|
1389
1581
|
/** Present when the runner keeps the conversation's session warm between turns, under `key`. */
|
|
1390
|
-
resident:
|
|
1582
|
+
resident: z9.object({ key: z9.string().min(1), idleMs: z9.number().int().positive() }).optional(),
|
|
1391
1583
|
workspace: TurnWorkspaceSchema
|
|
1392
1584
|
});
|
|
1393
|
-
var TurnWorkSchema =
|
|
1394
|
-
var TurnEventsSchema =
|
|
1395
|
-
var TurnOutcomeSchema =
|
|
1585
|
+
var TurnWorkSchema = z9.object({ branch: z9.string().min(1), head: z9.string().min(1) });
|
|
1586
|
+
var TurnEventsSchema = z9.object({ entries: z9.array(TranscriptEntrySchema) });
|
|
1587
|
+
var TurnOutcomeSchema = z9.object({
|
|
1396
1588
|
exitReason: TurnExitReasonSchema,
|
|
1397
1589
|
status: TurnStatusSchema.nullable(),
|
|
1398
|
-
error:
|
|
1590
|
+
error: z9.string().nullable(),
|
|
1399
1591
|
usage: UsageSchema,
|
|
1400
|
-
costUsd:
|
|
1592
|
+
costUsd: z9.number().nonnegative(),
|
|
1401
1593
|
/** The session's running total after this turn, for CLIs that report one. */
|
|
1402
|
-
sessionCostUsd:
|
|
1594
|
+
sessionCostUsd: z9.number().nonnegative().optional(),
|
|
1403
1595
|
/** The session the CLI used, which differs from the job's for CLIs that assign their own ids. */
|
|
1404
|
-
session:
|
|
1596
|
+
session: z9.string().min(1),
|
|
1405
1597
|
/** The model the CLI reported, when it reported one. */
|
|
1406
|
-
model:
|
|
1598
|
+
model: z9.string().nullable(),
|
|
1407
1599
|
work: TurnWorkSchema.nullable(),
|
|
1408
1600
|
/**
|
|
1409
1601
|
* Whether the turn's own workspace, a thread's, a channel's, or a task's, holds work on no
|
|
1410
1602
|
* branch after the hand-back: uncommitted changes, or commits no branch contains.
|
|
1411
1603
|
*/
|
|
1412
|
-
leftovers:
|
|
1604
|
+
leftovers: z9.boolean().default(false),
|
|
1605
|
+
/**
|
|
1606
|
+
* The CLI's plan quota as the turn's stream last reported it, when it reported any; a runner from
|
|
1607
|
+
* before never sends it.
|
|
1608
|
+
*/
|
|
1609
|
+
quota: CliQuotaSchema.optional()
|
|
1413
1610
|
});
|
|
1414
|
-
var TurnAckSchema =
|
|
1415
|
-
var LandRequestSchema =
|
|
1611
|
+
var TurnAckSchema = z9.object({ dropWorkspace: z9.boolean() });
|
|
1612
|
+
var LandRequestSchema = z9.object({
|
|
1416
1613
|
repo: ProjectRepoSchema,
|
|
1417
|
-
branch:
|
|
1614
|
+
branch: z9.string().min(1),
|
|
1418
1615
|
/**
|
|
1419
1616
|
* For a `ghpr` task, the pull request to merge in place of the branch, which must hold the
|
|
1420
1617
|
* branch's files, and the merge commit's subject and body, the pull request's title and an
|
|
1421
1618
|
* empty body when left out.
|
|
1422
1619
|
*/
|
|
1423
|
-
pullRequest:
|
|
1424
|
-
url:
|
|
1425
|
-
subject:
|
|
1426
|
-
body:
|
|
1620
|
+
pullRequest: z9.object({
|
|
1621
|
+
url: z9.string().min(1),
|
|
1622
|
+
subject: z9.string().min(1).optional(),
|
|
1623
|
+
body: z9.string().optional()
|
|
1427
1624
|
}).optional()
|
|
1428
1625
|
});
|
|
1429
|
-
var CloseRequestSchema =
|
|
1626
|
+
var CloseRequestSchema = z9.object({
|
|
1430
1627
|
repo: ProjectRepoSchema,
|
|
1431
|
-
url:
|
|
1432
|
-
comment:
|
|
1628
|
+
url: z9.string().min(1),
|
|
1629
|
+
comment: z9.string().min(1)
|
|
1433
1630
|
});
|
|
1434
|
-
var MergeOutcomeSchema =
|
|
1435
|
-
var ModelListSchema =
|
|
1631
|
+
var MergeOutcomeSchema = z9.object({ ok: z9.boolean(), detail: z9.string() });
|
|
1632
|
+
var ModelListSchema = z9.array(ModelOptionSchema);
|
|
1436
1633
|
var BRANCH_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
1437
|
-
var BranchPathSchema =
|
|
1438
|
-
var BranchNameSchema =
|
|
1439
|
-
var BranchReadSchema =
|
|
1634
|
+
var BranchPathSchema = z9.string().max(4096).refine((value) => value === "" || value.split("/").every((segment) => segment !== "" && segment !== "." && segment !== ".." && !segment.includes("\0")), { message: "a path inside the branch, with no empty, `.`, or `..` segment" });
|
|
1635
|
+
var BranchNameSchema = z9.string().regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/);
|
|
1636
|
+
var BranchReadSchema = z9.object({
|
|
1440
1637
|
repo: ProjectRepoSchema,
|
|
1441
1638
|
branch: BranchNameSchema,
|
|
1442
1639
|
path: BranchPathSchema
|
|
1443
1640
|
});
|
|
1444
|
-
var BranchCommitSchema =
|
|
1445
|
-
id:
|
|
1641
|
+
var BranchCommitSchema = z9.object({
|
|
1642
|
+
id: z9.string().min(1),
|
|
1446
1643
|
at: IsoDateTimeSchema,
|
|
1447
|
-
author:
|
|
1644
|
+
author: z9.string()
|
|
1448
1645
|
});
|
|
1449
|
-
var BranchFileSchema =
|
|
1450
|
-
|
|
1451
|
-
kind:
|
|
1646
|
+
var BranchFileSchema = z9.discriminatedUnion("kind", [
|
|
1647
|
+
z9.object({
|
|
1648
|
+
kind: z9.literal("file"),
|
|
1452
1649
|
path: BranchPathSchema,
|
|
1453
1650
|
commit: BranchCommitSchema,
|
|
1454
|
-
size:
|
|
1455
|
-
content:
|
|
1651
|
+
size: z9.number().int().nonnegative(),
|
|
1652
|
+
content: z9.string().nullable()
|
|
1456
1653
|
}),
|
|
1457
|
-
|
|
1458
|
-
kind:
|
|
1654
|
+
z9.object({
|
|
1655
|
+
kind: z9.literal("dir"),
|
|
1459
1656
|
path: BranchPathSchema,
|
|
1460
1657
|
commit: BranchCommitSchema,
|
|
1461
|
-
entries:
|
|
1462
|
-
name:
|
|
1463
|
-
kind:
|
|
1464
|
-
size:
|
|
1658
|
+
entries: z9.array(z9.object({
|
|
1659
|
+
name: z9.string().min(1),
|
|
1660
|
+
kind: z9.enum(["file", "dir"]),
|
|
1661
|
+
size: z9.number().int().nonnegative().nullable()
|
|
1465
1662
|
}))
|
|
1466
1663
|
})
|
|
1467
1664
|
]);
|
|
1468
|
-
var BranchChangesReadSchema =
|
|
1665
|
+
var BranchChangesReadSchema = z9.object({
|
|
1469
1666
|
repo: ProjectRepoSchema,
|
|
1470
1667
|
branch: BranchNameSchema
|
|
1471
1668
|
});
|
|
1472
|
-
var BranchFileChangeSchema =
|
|
1473
|
-
path:
|
|
1474
|
-
status:
|
|
1669
|
+
var BranchFileChangeSchema = z9.object({
|
|
1670
|
+
path: z9.string().min(1),
|
|
1671
|
+
status: z9.enum(["added", "modified", "deleted"]),
|
|
1475
1672
|
/** Lines added and removed, or null for a binary file. */
|
|
1476
|
-
added:
|
|
1477
|
-
removed:
|
|
1673
|
+
added: z9.number().int().nonnegative().nullable(),
|
|
1674
|
+
removed: z9.number().int().nonnegative().nullable(),
|
|
1478
1675
|
/** Absent when the file changed only through a merge of another branch. */
|
|
1479
|
-
lastChange:
|
|
1676
|
+
lastChange: z9.object({ author: z9.string(), at: IsoDateTimeSchema }).optional()
|
|
1480
1677
|
});
|
|
1481
|
-
var BranchChangesSchema =
|
|
1678
|
+
var BranchChangesSchema = z9.object({
|
|
1482
1679
|
head: BranchCommitSchema,
|
|
1483
|
-
files:
|
|
1484
|
-
total:
|
|
1680
|
+
files: z9.array(BranchFileChangeSchema),
|
|
1681
|
+
total: z9.number().int().nonnegative()
|
|
1485
1682
|
});
|
|
1486
|
-
var BranchDiffReadSchema =
|
|
1683
|
+
var BranchDiffReadSchema = z9.object({
|
|
1487
1684
|
repo: ProjectRepoSchema,
|
|
1488
1685
|
branch: BranchNameSchema,
|
|
1489
1686
|
path: BranchPathSchema.min(1)
|
|
1490
1687
|
});
|
|
1491
|
-
var BranchFileDiffSchema =
|
|
1688
|
+
var BranchFileDiffSchema = z9.object({
|
|
1492
1689
|
head: BranchCommitSchema,
|
|
1493
1690
|
path: BranchPathSchema,
|
|
1494
1691
|
status: BranchFileChangeSchema.shape.status,
|
|
1495
|
-
patch:
|
|
1496
|
-
truncated:
|
|
1692
|
+
patch: z9.string().nullable(),
|
|
1693
|
+
truncated: z9.boolean()
|
|
1497
1694
|
});
|
|
1498
1695
|
function patchHunks(patch, limit2) {
|
|
1499
1696
|
if (/^Binary files .* differ$/m.test(patch)) {
|
|
@@ -1507,9 +1704,23 @@ function patchHunks(patch, limit2) {
|
|
|
1507
1704
|
const cut = hunks.lastIndexOf("\n", limit2);
|
|
1508
1705
|
return { patch: hunks.slice(0, cut === -1 ? limit2 : cut), truncated: true };
|
|
1509
1706
|
}
|
|
1510
|
-
var
|
|
1511
|
-
|
|
1512
|
-
|
|
1707
|
+
var TurnImageReadSchema = z9.object({
|
|
1708
|
+
turnId: UlidSchema,
|
|
1709
|
+
path: ImagePathSchema
|
|
1710
|
+
});
|
|
1711
|
+
var ImageRefusalSchema = z9.enum(["missing", "outside", "not_image", "too_big"]);
|
|
1712
|
+
var TurnImageSchema = z9.discriminatedUnion("kind", [
|
|
1713
|
+
z9.object({
|
|
1714
|
+
kind: z9.literal("image"),
|
|
1715
|
+
type: ImageTypeSchema,
|
|
1716
|
+
size: z9.number().int().positive(),
|
|
1717
|
+
content: z9.string()
|
|
1718
|
+
}),
|
|
1719
|
+
z9.object({ kind: z9.literal("refused"), why: ImageRefusalSchema, detail: z9.string() })
|
|
1720
|
+
]);
|
|
1721
|
+
var WorkspaceConversationSchema = z9.discriminatedUnion("kind", [
|
|
1722
|
+
z9.object({ kind: z9.literal("thread"), id: UlidSchema }),
|
|
1723
|
+
z9.object({ kind: z9.literal("channel"), scope: NameSchema, name: NameSchema })
|
|
1513
1724
|
]);
|
|
1514
1725
|
function workspaceConversationOf(turn) {
|
|
1515
1726
|
if (turn.thread !== void 0) {
|
|
@@ -1517,60 +1728,70 @@ function workspaceConversationOf(turn) {
|
|
|
1517
1728
|
}
|
|
1518
1729
|
return turn.channel === void 0 ? null : { kind: "channel", scope: turn.scope, name: turn.channel };
|
|
1519
1730
|
}
|
|
1520
|
-
var HeldWorkspaceSchema =
|
|
1731
|
+
var HeldWorkspaceSchema = z9.object({
|
|
1521
1732
|
agent: NameSchema,
|
|
1522
1733
|
scope: NameSchema,
|
|
1523
1734
|
conversation: WorkspaceConversationSchema
|
|
1524
1735
|
});
|
|
1525
|
-
var HeldWorkspacesSchema =
|
|
1526
|
-
var SweepResultSchema =
|
|
1527
|
-
var TurnSteerSchema =
|
|
1528
|
-
var RunnerMessageSchema =
|
|
1529
|
-
|
|
1530
|
-
|
|
1736
|
+
var HeldWorkspacesSchema = z9.object({ workspaces: z9.array(HeldWorkspaceSchema) });
|
|
1737
|
+
var SweepResultSchema = z9.object({ kept: z9.array(HeldWorkspaceSchema) });
|
|
1738
|
+
var TurnSteerSchema = z9.object({ id: z9.uuid(), text: z9.string().min(1) });
|
|
1739
|
+
var RunnerMessageSchema = z9.discriminatedUnion("type", [
|
|
1740
|
+
z9.object({ type: z9.literal("turn"), job: TurnJobSchema }),
|
|
1741
|
+
z9.object({ type: z9.literal("land"), request: z9.string().min(1), land: LandRequestSchema }),
|
|
1531
1742
|
/** Answered with a `MergeOutcome`. */
|
|
1532
|
-
|
|
1533
|
-
|
|
1743
|
+
z9.object({ type: z9.literal("close"), request: z9.string().min(1), close: CloseRequestSchema }),
|
|
1744
|
+
z9.object({ type: z9.literal("models"), request: z9.string().min(1), cli: CliKindSchema }),
|
|
1745
|
+
/**
|
|
1746
|
+
* Answered with a `CliQuota` read from the CLI without a turn, or null when its adapter cannot
|
|
1747
|
+
* read one. Sent only to runners whose hello says `quotaReads`.
|
|
1748
|
+
*/
|
|
1749
|
+
z9.object({ type: z9.literal("quota"), request: z9.string().min(1), cli: CliKindSchema }),
|
|
1534
1750
|
/** Answered with a `BranchFile`, or null when the branch or the path does not exist. */
|
|
1535
|
-
|
|
1751
|
+
z9.object({ type: z9.literal("file"), request: z9.string().min(1), read: BranchReadSchema }),
|
|
1536
1752
|
/** Answered with `BranchChanges`, or null when the repository or the branch does not exist. */
|
|
1537
|
-
|
|
1538
|
-
type:
|
|
1539
|
-
request:
|
|
1753
|
+
z9.object({
|
|
1754
|
+
type: z9.literal("changes"),
|
|
1755
|
+
request: z9.string().min(1),
|
|
1540
1756
|
read: BranchChangesReadSchema
|
|
1541
1757
|
}),
|
|
1542
1758
|
/** Answered with a `BranchFileDiff`, or null when the branch does not exist or left the path alone. */
|
|
1543
|
-
|
|
1759
|
+
z9.object({ type: z9.literal("diff"), request: z9.string().min(1), read: BranchDiffReadSchema }),
|
|
1760
|
+
/**
|
|
1761
|
+
* Answered with a `TurnImage`, or null when the turn is not running there. Sent only to runners
|
|
1762
|
+
* whose hello says `imageReads`.
|
|
1763
|
+
*/
|
|
1764
|
+
z9.object({ type: z9.literal("image"), request: z9.string().min(1), read: TurnImageReadSchema }),
|
|
1544
1765
|
/** Answered with whether the CLI accepted it; that it took it comes as a `steered` event. */
|
|
1545
|
-
|
|
1546
|
-
type:
|
|
1547
|
-
request:
|
|
1766
|
+
z9.object({
|
|
1767
|
+
type: z9.literal("steer"),
|
|
1768
|
+
request: z9.string().min(1),
|
|
1548
1769
|
turnId: UlidSchema,
|
|
1549
1770
|
steer: TurnSteerSchema
|
|
1550
1771
|
}),
|
|
1551
1772
|
/** Answered with whether the turn was there to stop; it ends `stopped`. */
|
|
1552
|
-
|
|
1773
|
+
z9.object({ type: z9.literal("stop"), request: z9.string().min(1), turnId: UlidSchema }),
|
|
1553
1774
|
/**
|
|
1554
1775
|
* A conversation ended: the runner removes every citizen's workspace for it but those a turn
|
|
1555
1776
|
* still uses, and answers with a `SweepResult` naming those it kept for holding work on no branch.
|
|
1556
1777
|
*/
|
|
1557
|
-
|
|
1558
|
-
type:
|
|
1559
|
-
request:
|
|
1778
|
+
z9.object({
|
|
1779
|
+
type: z9.literal("sweep"),
|
|
1780
|
+
request: z9.string().min(1),
|
|
1560
1781
|
conversation: WorkspaceConversationSchema
|
|
1561
1782
|
})
|
|
1562
1783
|
]);
|
|
1563
|
-
var RunnerAnswerSchema =
|
|
1564
|
-
|
|
1565
|
-
|
|
1784
|
+
var RunnerAnswerSchema = z9.discriminatedUnion("ok", [
|
|
1785
|
+
z9.object({ ok: z9.literal(true), value: z9.unknown() }),
|
|
1786
|
+
z9.object({ ok: z9.literal(false), error: z9.string() })
|
|
1566
1787
|
]);
|
|
1567
|
-
var WarmSessionsSchema =
|
|
1568
|
-
var FileManifestSchema =
|
|
1788
|
+
var WarmSessionsSchema = z9.object({ warm: z9.array(z9.string()) });
|
|
1789
|
+
var FileManifestSchema = z9.object({ files: z9.record(z9.string(), z9.string()) });
|
|
1569
1790
|
function fileVersion(sha, executable) {
|
|
1570
1791
|
return executable ? `${sha}+x` : sha;
|
|
1571
1792
|
}
|
|
1572
|
-
var FileReadSchema =
|
|
1573
|
-
var FileContentsSchema =
|
|
1793
|
+
var FileReadSchema = z9.object({ paths: z9.array(z9.string()) });
|
|
1794
|
+
var FileContentsSchema = z9.object({ files: z9.record(z9.string(), z9.string()) });
|
|
1574
1795
|
function isSafeRelativePath(file) {
|
|
1575
1796
|
if (file.length === 0 || file.startsWith("/") || file.includes("\\") || file.includes("\0")) {
|
|
1576
1797
|
return false;
|
|
@@ -1579,7 +1800,7 @@ function isSafeRelativePath(file) {
|
|
|
1579
1800
|
}
|
|
1580
1801
|
|
|
1581
1802
|
// packages/shared/dist/turns.js
|
|
1582
|
-
var TriggerKindSchema =
|
|
1803
|
+
var TriggerKindSchema = z10.enum([
|
|
1583
1804
|
"mention",
|
|
1584
1805
|
"stage",
|
|
1585
1806
|
"task_done",
|
|
@@ -1593,11 +1814,11 @@ var TriggerKindSchema = z8.enum([
|
|
|
1593
1814
|
"closing",
|
|
1594
1815
|
"cron"
|
|
1595
1816
|
]);
|
|
1596
|
-
var TriggerSchema =
|
|
1817
|
+
var TriggerSchema = z10.object({
|
|
1597
1818
|
kind: TriggerKindSchema,
|
|
1598
1819
|
from: NameSchema.optional(),
|
|
1599
|
-
fromUser:
|
|
1600
|
-
reason:
|
|
1820
|
+
fromUser: z10.boolean().default(false),
|
|
1821
|
+
reason: z10.string().default(""),
|
|
1601
1822
|
taskId: UlidSchema.optional(),
|
|
1602
1823
|
messageId: UlidSchema.optional(),
|
|
1603
1824
|
/** The channel the waking message was posted in. */
|
|
@@ -1605,17 +1826,17 @@ var TriggerSchema = z8.object({
|
|
|
1605
1826
|
/** The cron that came due, for a `cron` wake. */
|
|
1606
1827
|
cronId: UlidSchema.optional()
|
|
1607
1828
|
});
|
|
1608
|
-
var TurnThreadSchema =
|
|
1609
|
-
var TurnDispatchSchema =
|
|
1829
|
+
var TurnThreadSchema = z10.object({ id: UlidSchema, task: z10.boolean() });
|
|
1830
|
+
var TurnDispatchSchema = z10.object({
|
|
1610
1831
|
agent: NameSchema,
|
|
1611
1832
|
project: NameSchema,
|
|
1612
1833
|
thread: TurnThreadSchema.optional(),
|
|
1613
1834
|
channel: NameSchema.optional(),
|
|
1614
1835
|
trigger: TriggerSchema,
|
|
1615
|
-
priority:
|
|
1616
|
-
onboarding:
|
|
1836
|
+
priority: z10.number().int().min(0).max(2),
|
|
1837
|
+
onboarding: z10.boolean().default(false),
|
|
1617
1838
|
/** Every cron that came due for this turn, whatever wake it merged into; its prompt quotes each. */
|
|
1618
|
-
crons:
|
|
1839
|
+
crons: z10.array(UlidSchema).optional()
|
|
1619
1840
|
});
|
|
1620
1841
|
function conversationPart(conversation) {
|
|
1621
1842
|
return conversation.thread ?? (conversation.channel === void 0 ? void 0 : `#${conversation.channel}`);
|
|
@@ -1639,7 +1860,7 @@ function channelConversation(scope, channel) {
|
|
|
1639
1860
|
const name = at < 0 ? channel : channel.slice(at + 1);
|
|
1640
1861
|
return place === scope && name !== "general" ? name : void 0;
|
|
1641
1862
|
}
|
|
1642
|
-
var TurnRecordSchema =
|
|
1863
|
+
var TurnRecordSchema = z10.object({
|
|
1643
1864
|
/** Assigned by the board when the turn begins; the turn's transcript is filed under it. */
|
|
1644
1865
|
id: UlidSchema.optional(),
|
|
1645
1866
|
agent: NameSchema,
|
|
@@ -1650,31 +1871,31 @@ var TurnRecordSchema = z8.object({
|
|
|
1650
1871
|
channel: NameSchema.optional(),
|
|
1651
1872
|
runner: NameSchema,
|
|
1652
1873
|
cli: CliKindSchema.nullable(),
|
|
1653
|
-
session:
|
|
1874
|
+
session: z10.string().nullable(),
|
|
1654
1875
|
trigger: TriggerSchema,
|
|
1655
1876
|
startedAt: IsoDateTimeSchema,
|
|
1656
1877
|
endedAt: IsoDateTimeSchema.nullable(),
|
|
1657
1878
|
exitReason: TurnExitReasonSchema.nullable(),
|
|
1658
1879
|
status: TurnStatusSchema.nullable(),
|
|
1659
|
-
error:
|
|
1880
|
+
error: z10.string().nullable(),
|
|
1660
1881
|
usage: UsageSchema.nullable(),
|
|
1661
|
-
costUsd:
|
|
1882
|
+
costUsd: z10.number().nonnegative().default(0),
|
|
1662
1883
|
/**
|
|
1663
1884
|
* The running total the CLI reported for the session after this turn, when it reports one. A
|
|
1664
1885
|
* resumed session starts from it, so the next turn's own cost is measured from here.
|
|
1665
1886
|
*/
|
|
1666
|
-
sessionCostUsd:
|
|
1667
|
-
toolCalls:
|
|
1887
|
+
sessionCostUsd: z10.number().nonnegative().optional(),
|
|
1888
|
+
toolCalls: z10.number().int().nonnegative().default(0),
|
|
1668
1889
|
/** The model the CLI reported for this turn, else the model configured for the agent, else null. */
|
|
1669
|
-
model:
|
|
1890
|
+
model: z10.string().nullable().default(null),
|
|
1670
1891
|
/** The task branch the turn left work on, with its commit, as the runner reported it. */
|
|
1671
1892
|
work: TurnWorkSchema.optional(),
|
|
1672
1893
|
/** Who stopped the turn, for one that ended `stopped`. */
|
|
1673
1894
|
stoppedBy: NameSchema.optional(),
|
|
1674
1895
|
/** The crons whose fire started the turn, which record how it ended. */
|
|
1675
|
-
crons:
|
|
1896
|
+
crons: z10.array(UlidSchema).optional()
|
|
1676
1897
|
});
|
|
1677
|
-
var RunningTurnSchema =
|
|
1898
|
+
var RunningTurnSchema = z10.object({
|
|
1678
1899
|
turnId: UlidSchema,
|
|
1679
1900
|
agent: NameSchema,
|
|
1680
1901
|
scope: NameSchema,
|
|
@@ -1682,49 +1903,49 @@ var RunningTurnSchema = z8.object({
|
|
|
1682
1903
|
channel: NameSchema.optional(),
|
|
1683
1904
|
cli: CliKindSchema,
|
|
1684
1905
|
askedIn: ChannelRefSchema.optional(),
|
|
1685
|
-
steerable:
|
|
1686
|
-
stoppable:
|
|
1906
|
+
steerable: z10.boolean(),
|
|
1907
|
+
stoppable: z10.boolean()
|
|
1687
1908
|
});
|
|
1688
|
-
var SessionsFileSchema =
|
|
1689
|
-
claude:
|
|
1690
|
-
codex:
|
|
1909
|
+
var SessionsFileSchema = z10.object({
|
|
1910
|
+
claude: z10.string().optional(),
|
|
1911
|
+
codex: z10.string().optional(),
|
|
1691
1912
|
runner: NameSchema.optional()
|
|
1692
1913
|
});
|
|
1693
|
-
var WakeRequestSchema =
|
|
1914
|
+
var WakeRequestSchema = z10.object({
|
|
1694
1915
|
agent: NameSchema,
|
|
1695
1916
|
project: NameSchema,
|
|
1696
|
-
reason:
|
|
1917
|
+
reason: z10.string().default("manual wake"),
|
|
1697
1918
|
/** A manual wake, or a reflection turn requested ahead of the cadence. */
|
|
1698
|
-
kind:
|
|
1919
|
+
kind: z10.enum(["manual", "reflection"]).default("manual")
|
|
1699
1920
|
});
|
|
1700
|
-
var TurnHistoryEntrySchema =
|
|
1921
|
+
var TurnHistoryEntrySchema = z10.object({
|
|
1701
1922
|
/** The id of the event that ended the turn. */
|
|
1702
1923
|
id: UlidSchema,
|
|
1703
1924
|
/** The turn's own id, when it has a transcript to open; turns before transcripts have none. */
|
|
1704
1925
|
turnId: UlidSchema.optional(),
|
|
1705
1926
|
ts: IsoDateTimeSchema,
|
|
1706
|
-
outcome:
|
|
1927
|
+
outcome: z10.enum(["completed", "failed"]),
|
|
1707
1928
|
/** A project slug, or the society scope. */
|
|
1708
1929
|
project: NameSchema,
|
|
1709
1930
|
/** The thread whose conversation the turn was in; absent for the home conversation. */
|
|
1710
1931
|
thread: UlidSchema.optional(),
|
|
1711
1932
|
/** The channel, never general, whose conversation the turn was in. */
|
|
1712
1933
|
channel: NameSchema.optional(),
|
|
1713
|
-
trigger:
|
|
1714
|
-
exitReason:
|
|
1715
|
-
costUsd:
|
|
1934
|
+
trigger: z10.string(),
|
|
1935
|
+
exitReason: z10.string().nullable(),
|
|
1936
|
+
costUsd: z10.number(),
|
|
1716
1937
|
/** The tokens the turn used; absent for turns logged before turns recorded them. */
|
|
1717
1938
|
usage: UsageSchema.optional(),
|
|
1718
|
-
model:
|
|
1719
|
-
summary:
|
|
1720
|
-
error:
|
|
1939
|
+
model: z10.string().nullable(),
|
|
1940
|
+
summary: z10.string().nullable(),
|
|
1941
|
+
error: z10.string().nullable(),
|
|
1721
1942
|
/** When the log recorded the turn's start; absent when the start is missing from the log. */
|
|
1722
1943
|
startedAt: IsoDateTimeSchema.optional(),
|
|
1723
|
-
toolCalls:
|
|
1944
|
+
toolCalls: z10.number().int().nonnegative().optional()
|
|
1724
1945
|
});
|
|
1725
1946
|
|
|
1726
1947
|
// packages/shared/dist/ops.js
|
|
1727
|
-
import { z as
|
|
1948
|
+
import { z as z11 } from "zod";
|
|
1728
1949
|
var OPS_SIGNAL_KINDS = [
|
|
1729
1950
|
"waiting_stage",
|
|
1730
1951
|
"backlog",
|
|
@@ -1740,7 +1961,7 @@ var OPS_SIGNAL_KINDS = [
|
|
|
1740
1961
|
"stuck_workspace",
|
|
1741
1962
|
"cron_failing"
|
|
1742
1963
|
];
|
|
1743
|
-
var OpsSignalKindSchema =
|
|
1964
|
+
var OpsSignalKindSchema = z11.enum(OPS_SIGNAL_KINDS);
|
|
1744
1965
|
var WAKING_SIGNAL_KINDS = [
|
|
1745
1966
|
"backlog",
|
|
1746
1967
|
"role_gap",
|
|
@@ -1750,15 +1971,15 @@ var WAKING_SIGNAL_KINDS = [
|
|
|
1750
1971
|
"blocked_capability",
|
|
1751
1972
|
"scaled"
|
|
1752
1973
|
];
|
|
1753
|
-
var OpsSignalSchema =
|
|
1974
|
+
var OpsSignalSchema = z11.object({
|
|
1754
1975
|
kind: OpsSignalKindSchema,
|
|
1755
1976
|
/** Identifies the condition, for example `backlog:demo:engineer`, so a persisting one is not logged again every pass. */
|
|
1756
|
-
key:
|
|
1977
|
+
key: z11.string().min(1),
|
|
1757
1978
|
/** One line a reader can act on. */
|
|
1758
|
-
summary:
|
|
1979
|
+
summary: z11.string().min(1),
|
|
1759
1980
|
/** The counter or timer value behind the signal. */
|
|
1760
|
-
value:
|
|
1761
|
-
threshold:
|
|
1981
|
+
value: z11.number(),
|
|
1982
|
+
threshold: z11.number().optional(),
|
|
1762
1983
|
project: NameSchema.optional(),
|
|
1763
1984
|
agent: NameSchema.optional(),
|
|
1764
1985
|
role: NameSchema.optional(),
|
|
@@ -1766,8 +1987,8 @@ var OpsSignalSchema = z9.object({
|
|
|
1766
1987
|
});
|
|
1767
1988
|
|
|
1768
1989
|
// packages/shared/dist/metrics.js
|
|
1769
|
-
import { z as
|
|
1770
|
-
var MetricsWindowSchema =
|
|
1990
|
+
import { z as z12 } from "zod";
|
|
1991
|
+
var MetricsWindowSchema = z12.enum(["24h", "7d", "all"]);
|
|
1771
1992
|
var METRICS_WINDOW_MS = {
|
|
1772
1993
|
"24h": 24 * 36e5,
|
|
1773
1994
|
"7d": 7 * 24 * 36e5,
|
|
@@ -1778,9 +1999,9 @@ function isBoardAction(tool) {
|
|
|
1778
1999
|
const verb = /^mcp__board__(.+)$/.exec(tool)?.[1];
|
|
1779
2000
|
return verb !== void 0 && !READ_VERBS.some((read2) => read2 === verb);
|
|
1780
2001
|
}
|
|
1781
|
-
var CountSchema =
|
|
1782
|
-
var MsSchema =
|
|
1783
|
-
var MetricsSchema =
|
|
2002
|
+
var CountSchema = z12.number().int().nonnegative();
|
|
2003
|
+
var MsSchema = z12.number().nonnegative();
|
|
2004
|
+
var MetricsSchema = z12.object({
|
|
1784
2005
|
window: MetricsWindowSchema,
|
|
1785
2006
|
since: IsoDateTimeSchema.nullable(),
|
|
1786
2007
|
until: IsoDateTimeSchema,
|
|
@@ -1788,12 +2009,12 @@ var MetricsSchema = z10.object({
|
|
|
1788
2009
|
* Finished turns that changed nothing on the board. Reflections are left out, since they write
|
|
1789
2010
|
* memory, not the board; turns without a transcript are counted apart, their actions unknown.
|
|
1790
2011
|
*/
|
|
1791
|
-
idle:
|
|
2012
|
+
idle: z12.object({
|
|
1792
2013
|
turns: CountSchema,
|
|
1793
2014
|
idle: CountSchema,
|
|
1794
2015
|
unknown: CountSchema,
|
|
1795
|
-
byTrigger:
|
|
1796
|
-
byAgent:
|
|
2016
|
+
byTrigger: z12.array(z12.object({ trigger: z12.string(), turns: CountSchema, idle: CountSchema })),
|
|
2017
|
+
byAgent: z12.array(z12.object({
|
|
1797
2018
|
agent: NameSchema,
|
|
1798
2019
|
role: NameSchema.nullable(),
|
|
1799
2020
|
turns: CountSchema,
|
|
@@ -1801,33 +2022,33 @@ var MetricsSchema = z10.object({
|
|
|
1801
2022
|
}))
|
|
1802
2023
|
}),
|
|
1803
2024
|
/** Work sent back to an earlier stage: over the tasks finished in the window, and by where. */
|
|
1804
|
-
sentBack:
|
|
2025
|
+
sentBack: z12.object({
|
|
1805
2026
|
finished: CountSchema,
|
|
1806
2027
|
sendBacks: CountSchema,
|
|
1807
2028
|
tasksSentBack: CountSchema,
|
|
1808
|
-
byStage:
|
|
1809
|
-
bySender:
|
|
2029
|
+
byStage: z12.array(z12.object({ project: NameSchema, stage: z12.string(), count: CountSchema })),
|
|
2030
|
+
bySender: z12.array(z12.object({ agent: NameSchema, count: CountSchema }))
|
|
1810
2031
|
}),
|
|
1811
2032
|
/** Posts in the threads of the tasks finished in the window, the board's own notices left out. */
|
|
1812
|
-
messages:
|
|
2033
|
+
messages: z12.object({
|
|
1813
2034
|
finished: CountSchema,
|
|
1814
2035
|
messages: CountSchema,
|
|
1815
|
-
byProject:
|
|
1816
|
-
busiest:
|
|
2036
|
+
byProject: z12.array(z12.object({ project: NameSchema, finished: CountSchema, messages: CountSchema })),
|
|
2037
|
+
busiest: z12.array(z12.object({
|
|
1817
2038
|
taskId: UlidSchema,
|
|
1818
|
-
title:
|
|
2039
|
+
title: z12.string(),
|
|
1819
2040
|
project: NameSchema,
|
|
1820
2041
|
messages: CountSchema
|
|
1821
2042
|
}))
|
|
1822
2043
|
}),
|
|
1823
2044
|
/** Time from a mention to the start of the turn it woke, in the conversation it was made in. */
|
|
1824
|
-
latency:
|
|
2045
|
+
latency: z12.object({
|
|
1825
2046
|
mentions: CountSchema,
|
|
1826
2047
|
/** Mentions no turn has followed yet. */
|
|
1827
2048
|
unanswered: CountSchema,
|
|
1828
2049
|
medianMs: MsSchema.nullable(),
|
|
1829
2050
|
slowestMs: MsSchema.nullable(),
|
|
1830
|
-
byAgent:
|
|
2051
|
+
byAgent: z12.array(z12.object({
|
|
1831
2052
|
agent: NameSchema,
|
|
1832
2053
|
mentions: CountSchema,
|
|
1833
2054
|
medianMs: MsSchema,
|
|
@@ -1835,61 +2056,73 @@ var MetricsSchema = z10.object({
|
|
|
1835
2056
|
}))
|
|
1836
2057
|
}),
|
|
1837
2058
|
/** The user's decisions: proposals decided, questions answered, and stages passed or sent back. */
|
|
1838
|
-
decisions:
|
|
2059
|
+
decisions: z12.object({
|
|
1839
2060
|
total: CountSchema,
|
|
1840
|
-
byDay:
|
|
1841
|
-
day:
|
|
2061
|
+
byDay: z12.array(z12.object({
|
|
2062
|
+
day: z12.string(),
|
|
1842
2063
|
proposals: CountSchema,
|
|
1843
2064
|
answers: CountSchema,
|
|
1844
2065
|
stages: CountSchema
|
|
1845
2066
|
}))
|
|
1846
2067
|
}),
|
|
1847
2068
|
/** Tasks that needed a capability no connected runner offered, and whether they still do. */
|
|
1848
|
-
blocked:
|
|
2069
|
+
blocked: z12.array(z12.object({
|
|
1849
2070
|
taskId: UlidSchema,
|
|
1850
|
-
title:
|
|
2071
|
+
title: z12.string().nullable(),
|
|
1851
2072
|
project: NameSchema.nullable(),
|
|
1852
|
-
summary:
|
|
2073
|
+
summary: z12.string(),
|
|
1853
2074
|
firstAt: IsoDateTimeSchema,
|
|
1854
2075
|
lastAt: IsoDateTimeSchema,
|
|
1855
|
-
holds:
|
|
2076
|
+
holds: z12.boolean()
|
|
1856
2077
|
}))
|
|
1857
2078
|
});
|
|
2079
|
+
var CitizenTokensSchema = z12.object({
|
|
2080
|
+
agent: NameSchema,
|
|
2081
|
+
turns: CountSchema,
|
|
2082
|
+
withUsage: CountSchema,
|
|
2083
|
+
usage: UsageSchema.nullable()
|
|
2084
|
+
});
|
|
2085
|
+
var TokenUseSchema = z12.object({
|
|
2086
|
+
window: MetricsWindowSchema,
|
|
2087
|
+
since: IsoDateTimeSchema.nullable(),
|
|
2088
|
+
until: IsoDateTimeSchema,
|
|
2089
|
+
citizens: z12.array(CitizenTokensSchema)
|
|
2090
|
+
});
|
|
1858
2091
|
|
|
1859
2092
|
// packages/shared/dist/config.js
|
|
1860
|
-
import { z as
|
|
1861
|
-
var LogLevelSchema =
|
|
1862
|
-
var GithubSignInSchema =
|
|
1863
|
-
clientId:
|
|
1864
|
-
clientSecret:
|
|
2093
|
+
import { z as z13 } from "zod";
|
|
2094
|
+
var LogLevelSchema = z13.enum(["trace", "debug", "info", "warn", "error", "fatal"]);
|
|
2095
|
+
var GithubSignInSchema = z13.object({
|
|
2096
|
+
clientId: z13.string().min(1),
|
|
2097
|
+
clientSecret: z13.string().min(1),
|
|
1865
2098
|
/** Logins, lowercased; GitHub logins are case-insensitive. */
|
|
1866
|
-
users:
|
|
2099
|
+
users: z13.array(z13.string().min(1)).min(1)
|
|
1867
2100
|
});
|
|
1868
|
-
var ServerConfigSchema =
|
|
1869
|
-
dataDir:
|
|
1870
|
-
host:
|
|
1871
|
-
port:
|
|
2101
|
+
var ServerConfigSchema = z13.object({
|
|
2102
|
+
dataDir: z13.string().min(1),
|
|
2103
|
+
host: z13.string().min(1),
|
|
2104
|
+
port: z13.number().int().positive().max(65535),
|
|
1872
2105
|
/**
|
|
1873
2106
|
* An address every runner's agents reach this server at, for example https://board.example, or
|
|
1874
2107
|
* null for each runner to use the address it reaches the server at itself.
|
|
1875
2108
|
*/
|
|
1876
|
-
publicUrl:
|
|
2109
|
+
publicUrl: z13.string().min(1).nullable(),
|
|
1877
2110
|
logLevel: LogLevelSchema,
|
|
1878
2111
|
/** Turns running at once across every runner, or null for no limit beyond each runner's own. */
|
|
1879
|
-
concurrency:
|
|
2112
|
+
concurrency: z13.number().int().positive().nullable(),
|
|
1880
2113
|
/** How long one turn may run before it is stopped, or null for no limit. */
|
|
1881
|
-
turnTimeoutMs:
|
|
2114
|
+
turnTimeoutMs: z13.number().int().positive().nullable(),
|
|
1882
2115
|
/** Rounds of tool calls one Claude turn may take, or null for no limit. */
|
|
1883
|
-
toolRounds:
|
|
2116
|
+
toolRounds: z13.number().int().positive().nullable(),
|
|
1884
2117
|
/** How long a resident session stays warm after its last turn. */
|
|
1885
|
-
residentIdleMs:
|
|
2118
|
+
residentIdleMs: z13.number().int().positive(),
|
|
1886
2119
|
/** Signing in to the board with GitHub, or null when only a token lets anyone in. */
|
|
1887
2120
|
github: GithubSignInSchema.nullable(),
|
|
1888
2121
|
/**
|
|
1889
2122
|
* Reverse proxies in front of the server whose X-Forwarded-For entries name the client, for the
|
|
1890
2123
|
* rate limits on what anyone may ask without a token: 0 when clients connect directly.
|
|
1891
2124
|
*/
|
|
1892
|
-
trustedProxies:
|
|
2125
|
+
trustedProxies: z13.number().int().min(0).max(16)
|
|
1893
2126
|
});
|
|
1894
2127
|
function limit(value, fallback) {
|
|
1895
2128
|
if (value === void 0 || value.trim() === "") {
|
|
@@ -1927,25 +2160,73 @@ function loadServerConfig(env) {
|
|
|
1927
2160
|
trustedProxies: Number(env["STELLARIS_TRUSTED_PROXIES"] ?? "0")
|
|
1928
2161
|
});
|
|
1929
2162
|
}
|
|
1930
|
-
var RunnerConfigSchema =
|
|
2163
|
+
var RunnerConfigSchema = z13.object({
|
|
1931
2164
|
/** The board server, for example http://127.0.0.1:4700. */
|
|
1932
|
-
serverUrl:
|
|
2165
|
+
serverUrl: z13.string().url(),
|
|
1933
2166
|
/**
|
|
1934
2167
|
* The runner's token, from `runner add`, or null for the one it saved when it was enrolled, or
|
|
1935
2168
|
* to enroll it.
|
|
1936
2169
|
*/
|
|
1937
|
-
token:
|
|
2170
|
+
token: z13.string().min(1).nullable(),
|
|
1938
2171
|
/** The runner's own data directory: the board mirror, agent homes, repositories, worktrees. */
|
|
1939
|
-
dataDir:
|
|
2172
|
+
dataDir: z13.string().min(1),
|
|
1940
2173
|
logLevel: LogLevelSchema,
|
|
1941
2174
|
/** Turns this machine runs at once, or null for no limit. */
|
|
1942
|
-
slots:
|
|
1943
|
-
clis:
|
|
1944
|
-
capabilities:
|
|
2175
|
+
slots: z13.number().int().positive().nullable(),
|
|
2176
|
+
clis: z13.array(CliKindSchema).min(1),
|
|
2177
|
+
capabilities: z13.array(z13.string())
|
|
1945
2178
|
});
|
|
1946
2179
|
|
|
1947
2180
|
// packages/shared/dist/version.js
|
|
1948
|
-
var STELLARIS_VERSION = "0.3.
|
|
2181
|
+
var STELLARIS_VERSION = "0.3.23";
|
|
2182
|
+
|
|
2183
|
+
// packages/shared/dist/mentions.js
|
|
2184
|
+
import { parse, postprocess, preprocess } from "micromark";
|
|
2185
|
+
import { gfm } from "micromark-extension-gfm";
|
|
2186
|
+
var MENTION_PATTERN = /(^|[^\w@])@([a-z0-9][a-z0-9-]{0,31})(?![\w-])/g;
|
|
2187
|
+
var CODE = /* @__PURE__ */ new Set(["codeText", "codeFenced", "codeIndented"]);
|
|
2188
|
+
var SLOW = {
|
|
2189
|
+
disable: {
|
|
2190
|
+
null: [
|
|
2191
|
+
"wwwAutolink",
|
|
2192
|
+
"protocolAutolink",
|
|
2193
|
+
"emailAutolink",
|
|
2194
|
+
"gfmFootnoteCall",
|
|
2195
|
+
"gfmPotentialFootnoteCall"
|
|
2196
|
+
]
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
function codeRanges(markdown) {
|
|
2200
|
+
const shift = markdown.startsWith("\uFEFF") ? 1 : 0;
|
|
2201
|
+
const events = postprocess(parse({ extensions: [gfm(), SLOW] }).document().write(preprocess()(markdown, void 0, true)));
|
|
2202
|
+
return events.flatMap(([kind, token]) => kind === "enter" && CODE.has(token.type) ? [[token.start.offset + shift, token.end.offset + shift]] : []);
|
|
2203
|
+
}
|
|
2204
|
+
function addMentions(found, text) {
|
|
2205
|
+
for (const match of text.matchAll(MENTION_PATTERN)) {
|
|
2206
|
+
const name = match[2];
|
|
2207
|
+
if (name !== void 0) {
|
|
2208
|
+
found.add(name);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
function mentionsIn(markdown) {
|
|
2213
|
+
if (markdown.search(MENTION_PATTERN) === -1) {
|
|
2214
|
+
return [];
|
|
2215
|
+
}
|
|
2216
|
+
const found = /* @__PURE__ */ new Set();
|
|
2217
|
+
let from = 0;
|
|
2218
|
+
for (const [start, end] of codeRanges(markdown)) {
|
|
2219
|
+
addMentions(found, markdown.slice(from, start));
|
|
2220
|
+
from = end;
|
|
2221
|
+
}
|
|
2222
|
+
addMentions(found, markdown.slice(from));
|
|
2223
|
+
return [...found].toSorted();
|
|
2224
|
+
}
|
|
2225
|
+
function mentionsAnywhere(text) {
|
|
2226
|
+
const found = /* @__PURE__ */ new Set();
|
|
2227
|
+
addMentions(found, text);
|
|
2228
|
+
return [...found].toSorted();
|
|
2229
|
+
}
|
|
1949
2230
|
|
|
1950
2231
|
// packages/board-core/dist/errors.js
|
|
1951
2232
|
var BoardError = class extends Error {
|
|
@@ -2591,33 +2872,59 @@ function filesOf(lines, status) {
|
|
|
2591
2872
|
return [...files.values()];
|
|
2592
2873
|
}
|
|
2593
2874
|
|
|
2875
|
+
// packages/board-core/dist/mentions.js
|
|
2876
|
+
import vm from "node:vm";
|
|
2877
|
+
var MENTION_PARSE_MS = 500;
|
|
2878
|
+
var context = vm.createContext({});
|
|
2879
|
+
var call = new vm.Script("work()");
|
|
2880
|
+
function within(work, ms) {
|
|
2881
|
+
let result;
|
|
2882
|
+
context["work"] = () => {
|
|
2883
|
+
result = work();
|
|
2884
|
+
};
|
|
2885
|
+
try {
|
|
2886
|
+
call.runInContext(context, { timeout: ms });
|
|
2887
|
+
} catch (error) {
|
|
2888
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") {
|
|
2889
|
+
return void 0;
|
|
2890
|
+
}
|
|
2891
|
+
throw error;
|
|
2892
|
+
} finally {
|
|
2893
|
+
context["work"] = void 0;
|
|
2894
|
+
}
|
|
2895
|
+
return result;
|
|
2896
|
+
}
|
|
2897
|
+
function postMentions(body) {
|
|
2898
|
+
return within(() => mentionsIn(body), MENTION_PARSE_MS) ?? mentionsAnywhere(body);
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2594
2901
|
// packages/board-core/dist/metrics.js
|
|
2595
|
-
import { z as
|
|
2596
|
-
var TurnPayload =
|
|
2597
|
-
turnId:
|
|
2598
|
-
project:
|
|
2599
|
-
thread:
|
|
2600
|
-
channel:
|
|
2601
|
-
trigger:
|
|
2902
|
+
import { z as z14 } from "zod";
|
|
2903
|
+
var TurnPayload = z14.object({
|
|
2904
|
+
turnId: z14.string().optional(),
|
|
2905
|
+
project: z14.string(),
|
|
2906
|
+
thread: z14.string().optional(),
|
|
2907
|
+
channel: z14.string().optional(),
|
|
2908
|
+
trigger: z14.string()
|
|
2602
2909
|
});
|
|
2603
|
-
var TaskPayload =
|
|
2604
|
-
taskId:
|
|
2605
|
-
project:
|
|
2606
|
-
from:
|
|
2910
|
+
var TaskPayload = z14.object({
|
|
2911
|
+
taskId: z14.string(),
|
|
2912
|
+
project: z14.string(),
|
|
2913
|
+
from: z14.string().optional()
|
|
2607
2914
|
});
|
|
2608
|
-
var MessagePayload =
|
|
2609
|
-
id:
|
|
2610
|
-
channel:
|
|
2611
|
-
thread:
|
|
2612
|
-
mentions:
|
|
2915
|
+
var MessagePayload = z14.object({
|
|
2916
|
+
id: z14.string().optional(),
|
|
2917
|
+
channel: z14.string(),
|
|
2918
|
+
thread: z14.string().nullable().optional(),
|
|
2919
|
+
mentions: z14.array(z14.string()).default([])
|
|
2613
2920
|
});
|
|
2614
|
-
var SteeredPayload =
|
|
2615
|
-
var SignalPayload =
|
|
2616
|
-
kind:
|
|
2617
|
-
key:
|
|
2618
|
-
summary:
|
|
2619
|
-
taskId:
|
|
2620
|
-
project:
|
|
2921
|
+
var SteeredPayload = z14.object({ messages: z14.array(z14.string()) });
|
|
2922
|
+
var SignalPayload = z14.object({
|
|
2923
|
+
kind: z14.string(),
|
|
2924
|
+
key: z14.string(),
|
|
2925
|
+
summary: z14.string(),
|
|
2926
|
+
taskId: z14.string().optional(),
|
|
2927
|
+
project: z14.string().optional()
|
|
2621
2928
|
});
|
|
2622
2929
|
function median(values) {
|
|
2623
2930
|
const sorted = values.toSorted((a, b) => a - b);
|
|
@@ -2906,6 +3213,59 @@ function computeMetrics(input) {
|
|
|
2906
3213
|
};
|
|
2907
3214
|
}
|
|
2908
3215
|
|
|
3216
|
+
// packages/board-core/dist/turn-history.js
|
|
3217
|
+
import { z as z15 } from "zod";
|
|
3218
|
+
var TurnHistoryPayloadSchema = z15.object({
|
|
3219
|
+
project: NameSchema,
|
|
3220
|
+
thread: UlidSchema.optional(),
|
|
3221
|
+
channel: NameSchema.optional(),
|
|
3222
|
+
trigger: z15.string().default("manual"),
|
|
3223
|
+
exitReason: z15.string().nullable().default(null),
|
|
3224
|
+
costUsd: z15.number().default(0),
|
|
3225
|
+
usage: UsageSchema.optional(),
|
|
3226
|
+
model: z15.string().nullable().default(null),
|
|
3227
|
+
summary: z15.string().nullable().default(null),
|
|
3228
|
+
error: z15.string().nullable().default(null),
|
|
3229
|
+
toolCalls: z15.number().int().nonnegative().optional(),
|
|
3230
|
+
turnId: UlidSchema.optional()
|
|
3231
|
+
});
|
|
3232
|
+
function tokenUse(events, window, now) {
|
|
3233
|
+
const span = METRICS_WINDOW_MS[window];
|
|
3234
|
+
const since = span === null ? null : now.getTime() - span;
|
|
3235
|
+
const rows = /* @__PURE__ */ new Map();
|
|
3236
|
+
for (const event of events) {
|
|
3237
|
+
if (event.type !== "turn.completed" && event.type !== "turn.failed") {
|
|
3238
|
+
continue;
|
|
3239
|
+
}
|
|
3240
|
+
if (since !== null && Date.parse(event.ts) < since) {
|
|
3241
|
+
continue;
|
|
3242
|
+
}
|
|
3243
|
+
const turn = TurnHistoryPayloadSchema.safeParse(event.payload);
|
|
3244
|
+
if (!turn.success) {
|
|
3245
|
+
continue;
|
|
3246
|
+
}
|
|
3247
|
+
const row = rows.get(event.actor) ?? {
|
|
3248
|
+
agent: event.actor,
|
|
3249
|
+
turns: 0,
|
|
3250
|
+
withUsage: 0,
|
|
3251
|
+
usage: null
|
|
3252
|
+
};
|
|
3253
|
+
const usage = turn.data.usage;
|
|
3254
|
+
rows.set(event.actor, {
|
|
3255
|
+
agent: event.actor,
|
|
3256
|
+
turns: row.turns + 1,
|
|
3257
|
+
withUsage: row.withUsage + (usage === void 0 ? 0 : 1),
|
|
3258
|
+
usage: usage === void 0 ? row.usage : row.usage === null ? usage : addUsage(row.usage, usage)
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
3261
|
+
return {
|
|
3262
|
+
window,
|
|
3263
|
+
since: since === null ? null : new Date(since).toISOString(),
|
|
3264
|
+
until: now.toISOString(),
|
|
3265
|
+
citizens: [...rows.values()].toSorted((a, b) => a.agent.localeCompare(b.agent))
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
|
|
2909
3269
|
// packages/board-core/dist/mutex.js
|
|
2910
3270
|
var Mutex = class {
|
|
2911
3271
|
tail = Promise.resolve();
|
|
@@ -3112,6 +3472,13 @@ var BoardPaths = class {
|
|
|
3112
3472
|
eventLog() {
|
|
3113
3473
|
return path5.join(this.events(), "log.jsonl");
|
|
3114
3474
|
}
|
|
3475
|
+
/** Images citizens uploaded, by id: outside the projection, which every runner mirrors. */
|
|
3476
|
+
images() {
|
|
3477
|
+
return path5.join(this.dataDir, "images");
|
|
3478
|
+
}
|
|
3479
|
+
image(id) {
|
|
3480
|
+
return path5.join(this.images(), id);
|
|
3481
|
+
}
|
|
3115
3482
|
state() {
|
|
3116
3483
|
return path5.join(this.dataDir, "state");
|
|
3117
3484
|
}
|
|
@@ -3147,41 +3514,27 @@ function namesList(names) {
|
|
|
3147
3514
|
return names.length <= 1 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} and ${names.at(-1) ?? ""}`;
|
|
3148
3515
|
}
|
|
3149
3516
|
var SKIPPED_IN_HOME = /* @__PURE__ */ new Set([".git", "turns", ".claude", ".codex"]);
|
|
3150
|
-
var RunnerTokensSchema =
|
|
3151
|
-
runners:
|
|
3517
|
+
var RunnerTokensSchema = z16.object({
|
|
3518
|
+
runners: z16.record(z16.string(), z16.object({ tokenHash: z16.string().min(1) }))
|
|
3152
3519
|
});
|
|
3153
|
-
var SignInsSchema =
|
|
3154
|
-
signIns:
|
|
3155
|
-
tokenHash:
|
|
3156
|
-
login:
|
|
3520
|
+
var SignInsSchema = z16.object({
|
|
3521
|
+
signIns: z16.array(z16.object({
|
|
3522
|
+
tokenHash: z16.string().min(1),
|
|
3523
|
+
login: z16.string().min(1),
|
|
3157
3524
|
/** The account's picture at the identity provider; sign-ins from before it have none. */
|
|
3158
|
-
avatarUrl:
|
|
3525
|
+
avatarUrl: z16.url().optional(),
|
|
3159
3526
|
createdAt: IsoDateTimeSchema,
|
|
3160
3527
|
expiresAt: IsoDateTimeSchema
|
|
3161
3528
|
})).default([])
|
|
3162
3529
|
});
|
|
3163
|
-
var
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
costUsd: z13.number().default(0),
|
|
3170
|
-
usage: UsageSchema.optional(),
|
|
3171
|
-
model: z13.string().nullable().default(null),
|
|
3172
|
-
summary: z13.string().nullable().default(null),
|
|
3173
|
-
error: z13.string().nullable().default(null),
|
|
3174
|
-
toolCalls: z13.number().int().nonnegative().optional(),
|
|
3175
|
-
turnId: UlidSchema.optional()
|
|
3176
|
-
});
|
|
3177
|
-
var TurnStartPayloadSchema = z13.object({ project: NameSchema, turnId: UlidSchema.optional() });
|
|
3178
|
-
var CursorsSchema = z13.object({
|
|
3179
|
-
digest: z13.string().nullable(),
|
|
3180
|
-
scopes: z13.record(z13.string(), z13.string()).default({}),
|
|
3181
|
-
threadsFrom: z13.record(z13.string(), z13.string().nullable()).default({}),
|
|
3182
|
-
channelsFrom: z13.record(z13.string(), z13.string().nullable()).default({}),
|
|
3530
|
+
var TurnStartPayloadSchema = z16.object({ project: NameSchema, turnId: UlidSchema.optional() });
|
|
3531
|
+
var CursorsSchema = z16.object({
|
|
3532
|
+
digest: z16.string().nullable(),
|
|
3533
|
+
scopes: z16.record(z16.string(), z16.string()).default({}),
|
|
3534
|
+
threadsFrom: z16.record(z16.string(), z16.string().nullable()).default({}),
|
|
3535
|
+
channelsFrom: z16.record(z16.string(), z16.string().nullable()).default({}),
|
|
3183
3536
|
/** How far a work role has read news, which goes to whichever of its general conversations reads first. */
|
|
3184
|
-
news:
|
|
3537
|
+
news: z16.string().optional()
|
|
3185
3538
|
});
|
|
3186
3539
|
var NO_CURSORS = { digest: null, scopes: {}, threadsFrom: {}, channelsFrom: {} };
|
|
3187
3540
|
function conversationKey(scope, part) {
|
|
@@ -3220,9 +3573,8 @@ function byId(a, b) {
|
|
|
3220
3573
|
function turnTime(record) {
|
|
3221
3574
|
return record.endedAt ?? record.startedAt;
|
|
3222
3575
|
}
|
|
3223
|
-
var PausedSchema =
|
|
3576
|
+
var PausedSchema = z16.object({ paused: z16.boolean() });
|
|
3224
3577
|
var DEFAULT_LEASE_MS = 30 * 60 * 1e3;
|
|
3225
|
-
var MENTION_PATTERN = /(^|[^\w@])@([a-z0-9][a-z0-9-]{0,31})(?![\w-])/g;
|
|
3226
3578
|
function withoutMentions(text) {
|
|
3227
3579
|
return text.replaceAll(MENTION_PATTERN, "$1$2");
|
|
3228
3580
|
}
|
|
@@ -3246,7 +3598,7 @@ var OPS_WAKE_TRIGGER = "ops_event";
|
|
|
3246
3598
|
var FRONT_DESK_TRIGGER = "user_post";
|
|
3247
3599
|
var OPS_CHANNELS = ["governance"];
|
|
3248
3600
|
var FRONT_DESK_CHANNELS = [ASK_CHANNEL];
|
|
3249
|
-
var AlignmentsSchema =
|
|
3601
|
+
var AlignmentsSchema = z16.object({ applied: z16.array(z16.string()).default([]) });
|
|
3250
3602
|
var FOLLOW_DECISIONS = "ops-readers-follow-decisions";
|
|
3251
3603
|
var THREAD_RECORDS = "threads-as-records";
|
|
3252
3604
|
var DIGEST_CURSORS = "digest-replaces-inbox";
|
|
@@ -3260,10 +3612,11 @@ var CHANNEL_CONVERSATIONS = "channel-conversations";
|
|
|
3260
3612
|
var DASHBOARD_UPDATES = "dashboard-updates-granted";
|
|
3261
3613
|
var CRON_VERBS = "cron-verbs-granted";
|
|
3262
3614
|
var KNOWLEDGE_NOTICES = "knowledge-notices";
|
|
3263
|
-
var
|
|
3615
|
+
var IMAGE_UPLOADS = "image-uploads-granted";
|
|
3616
|
+
var KnowledgeEventSchema = z16.object({
|
|
3264
3617
|
topic: NameSchema,
|
|
3265
3618
|
project: NameSchema.nullable(),
|
|
3266
|
-
replaced:
|
|
3619
|
+
replaced: z16.boolean().optional()
|
|
3267
3620
|
});
|
|
3268
3621
|
var CRON_VERB_NAMES = ["create_cron", "update_cron", "remove_cron"];
|
|
3269
3622
|
var OLD_DASHBOARD_LINE = "Agents may edit this file. It is rendered by the board UI.";
|
|
@@ -3381,16 +3734,6 @@ function unanswered(messages) {
|
|
|
3381
3734
|
}
|
|
3382
3735
|
return waiting;
|
|
3383
3736
|
}
|
|
3384
|
-
function extractMentions(body) {
|
|
3385
|
-
const found = /* @__PURE__ */ new Set();
|
|
3386
|
-
for (const match of body.matchAll(MENTION_PATTERN)) {
|
|
3387
|
-
const name = match[2];
|
|
3388
|
-
if (name !== void 0) {
|
|
3389
|
-
found.add(name);
|
|
3390
|
-
}
|
|
3391
|
-
}
|
|
3392
|
-
return [...found].toSorted();
|
|
3393
|
-
}
|
|
3394
3737
|
function snippetAround(text, query, radius = 80) {
|
|
3395
3738
|
const index = text.toLowerCase().indexOf(query.toLowerCase());
|
|
3396
3739
|
if (index === -1) {
|
|
@@ -3405,6 +3748,7 @@ var Board = class _Board {
|
|
|
3405
3748
|
mutex = new Mutex();
|
|
3406
3749
|
/** The turns in flight, which the server hosting them supplies; none until it does. */
|
|
3407
3750
|
turnsInFlight = () => [];
|
|
3751
|
+
imageSource = null;
|
|
3408
3752
|
newId = monotonicFactory();
|
|
3409
3753
|
events;
|
|
3410
3754
|
actionCounts = /* @__PURE__ */ new Map();
|
|
@@ -3469,7 +3813,8 @@ var Board = class _Board {
|
|
|
3469
3813
|
CHANNEL_CONVERSATIONS,
|
|
3470
3814
|
DASHBOARD_UPDATES,
|
|
3471
3815
|
CRON_VERBS,
|
|
3472
|
-
KNOWLEDGE_NOTICES
|
|
3816
|
+
KNOWLEDGE_NOTICES,
|
|
3817
|
+
IMAGE_UPLOADS
|
|
3473
3818
|
].filter((name) => !applied.includes(name));
|
|
3474
3819
|
if (pending.length === 0) {
|
|
3475
3820
|
return;
|
|
@@ -3499,13 +3844,13 @@ var Board = class _Board {
|
|
|
3499
3844
|
await this.openAsksChannel();
|
|
3500
3845
|
}
|
|
3501
3846
|
if (pending.includes(KNOWLEDGE_REMOVAL)) {
|
|
3502
|
-
await this.
|
|
3847
|
+
await this.grantBeside("write_knowledge", "remove_knowledge");
|
|
3503
3848
|
}
|
|
3504
3849
|
if (pending.includes(CHANNEL_CONVERSATIONS)) {
|
|
3505
3850
|
await this.startChannelCursors();
|
|
3506
3851
|
}
|
|
3507
3852
|
if (pending.includes(DASHBOARD_UPDATES)) {
|
|
3508
|
-
await this.
|
|
3853
|
+
await this.grantBeside("write_knowledge", "update_dashboard");
|
|
3509
3854
|
await this.rewordDashboards();
|
|
3510
3855
|
}
|
|
3511
3856
|
if (pending.includes(CRON_VERBS)) {
|
|
@@ -3514,6 +3859,9 @@ var Board = class _Board {
|
|
|
3514
3859
|
if (pending.includes(KNOWLEDGE_NOTICES)) {
|
|
3515
3860
|
await this.noticeKnowledgeNotes();
|
|
3516
3861
|
}
|
|
3862
|
+
if (pending.includes(IMAGE_UPLOADS)) {
|
|
3863
|
+
await this.grantBeside("post_message", "upload_image");
|
|
3864
|
+
}
|
|
3517
3865
|
await writeJson(file, { applied: [...applied, ...pending] });
|
|
3518
3866
|
}
|
|
3519
3867
|
/**
|
|
@@ -3555,12 +3903,12 @@ var Board = class _Board {
|
|
|
3555
3903
|
}
|
|
3556
3904
|
}
|
|
3557
3905
|
/**
|
|
3558
|
-
* Grants `verb` once to every charter granting `
|
|
3559
|
-
*
|
|
3906
|
+
* Grants `verb` once to every charter granting `beside`, the work roles' charters in practice;
|
|
3907
|
+
* seed roles have it from their seed.
|
|
3560
3908
|
*/
|
|
3561
|
-
async
|
|
3909
|
+
async grantBeside(beside, verb) {
|
|
3562
3910
|
for (const charter of await this.listRoles()) {
|
|
3563
|
-
if (charter.verbs.includes(
|
|
3911
|
+
if (charter.verbs.includes(beside) && !charter.verbs.includes(verb)) {
|
|
3564
3912
|
await this.writeRoleUnlocked(USER_NAME, { ...charter, verbs: [...charter.verbs, verb] }, { verbsAdded: [verb] });
|
|
3565
3913
|
}
|
|
3566
3914
|
}
|
|
@@ -3718,7 +4066,7 @@ var Board = class _Board {
|
|
|
3718
4066
|
}
|
|
3719
4067
|
}
|
|
3720
4068
|
for (const file of await listFiles(this.paths.tasks(slug))) {
|
|
3721
|
-
const raw = await readMarkdown(path6.join(this.paths.tasks(slug), file),
|
|
4069
|
+
const raw = await readMarkdown(path6.join(this.paths.tasks(slug), file), z16.record(z16.string(), z16.unknown()));
|
|
3722
4070
|
const legacy = raw.data["thread"];
|
|
3723
4071
|
if (legacy === void 0) {
|
|
3724
4072
|
continue;
|
|
@@ -3796,7 +4144,7 @@ var Board = class _Board {
|
|
|
3796
4144
|
* turns, so it follows nothing now.
|
|
3797
4145
|
*/
|
|
3798
4146
|
async renameInboxCursors() {
|
|
3799
|
-
const Legacy =
|
|
4147
|
+
const Legacy = z16.object({ inbox: z16.string().nullable() });
|
|
3800
4148
|
for (const agent of await this.listAgents()) {
|
|
3801
4149
|
const file = this.paths.agentCursors(agent.name);
|
|
3802
4150
|
if (await exists(file)) {
|
|
@@ -3842,7 +4190,7 @@ ${charter.purpose}
|
|
|
3842
4190
|
*/
|
|
3843
4191
|
async alignSeedRole(seed) {
|
|
3844
4192
|
const file = this.paths.role(seed.name);
|
|
3845
|
-
const raw = await readMarkdown(file,
|
|
4193
|
+
const raw = await readMarkdown(file, z16.record(z16.string(), z16.unknown()));
|
|
3846
4194
|
const current = RoleCharterSchema.parse(raw.data);
|
|
3847
4195
|
const verbsAdded = seed.verbs.filter((verb) => !current.verbs.includes(verb));
|
|
3848
4196
|
const fieldsAligned = RoleCharterSchema.keyof().options.filter((key) => key === "verbs" || seed[key] === void 0 ? false : seed.name === USER_ROLE ? JSON.stringify(seed[key]) !== JSON.stringify(current[key]) : !(key in raw.data));
|
|
@@ -4159,7 +4507,7 @@ ${charter.purpose}
|
|
|
4159
4507
|
/** The society's norms, the one knowledge topic every turn loads. Empty until the steward writes it. */
|
|
4160
4508
|
async readSocietyNorms() {
|
|
4161
4509
|
const file = this.paths.societyKnowledgeFile("norms");
|
|
4162
|
-
return await exists(file) ? (await readMarkdown(file,
|
|
4510
|
+
return await exists(file) ? (await readMarkdown(file, z16.record(z16.string(), z16.unknown()))).body : "";
|
|
4163
4511
|
}
|
|
4164
4512
|
/** Knowledge topics of a project, or of the society when the project is null. */
|
|
4165
4513
|
async listKnowledge(project) {
|
|
@@ -4179,7 +4527,7 @@ ${charter.purpose}
|
|
|
4179
4527
|
*/
|
|
4180
4528
|
async knowledgeAt(file, project, topic) {
|
|
4181
4529
|
const { data, body } = await readLooseMarkdown(file);
|
|
4182
|
-
const revision =
|
|
4530
|
+
const revision = z16.number().int().min(0).safeParse(data["revision"]);
|
|
4183
4531
|
const updatedBy = NameSchema.safeParse(data["updatedBy"]);
|
|
4184
4532
|
const updatedAt = IsoDateTimeSchema.safeParse(data["updatedAt"]);
|
|
4185
4533
|
return {
|
|
@@ -4512,8 +4860,11 @@ ${DASHBOARD_LINE}
|
|
|
4512
4860
|
throw new BoardError("NOT_FOUND", `runner ${name} not found`);
|
|
4513
4861
|
}
|
|
4514
4862
|
const doc = await readMarkdown(file, RunnerSchema);
|
|
4863
|
+
const { gitIdentityMissing: reported, ...rest } = doc.data;
|
|
4864
|
+
const gitIdentityMissing = patch.gitIdentityMissing === void 0 ? reported : patch.gitIdentityMissing ?? void 0;
|
|
4515
4865
|
const next = RunnerSchema.parse({
|
|
4516
|
-
...
|
|
4866
|
+
...rest,
|
|
4867
|
+
...gitIdentityMissing === void 0 ? {} : { gitIdentityMissing: [...gitIdentityMissing] },
|
|
4517
4868
|
...patch.status === void 0 ? {} : { status: patch.status },
|
|
4518
4869
|
...patch.os === void 0 ? {} : { os: patch.os },
|
|
4519
4870
|
...patch.clis === void 0 ? {} : { clis: [...patch.clis] },
|
|
@@ -4541,6 +4892,36 @@ ${DASHBOARD_LINE}
|
|
|
4541
4892
|
return next;
|
|
4542
4893
|
});
|
|
4543
4894
|
}
|
|
4895
|
+
/**
|
|
4896
|
+
* Records a CLI's plan quota as a runner reported it, from a turn's stream or a read, merged over
|
|
4897
|
+
* what the record held. The record is written on every report, so it says when the numbers were
|
|
4898
|
+
* last seen; `runner.quota` is appended only when what a reader would see changes, since every
|
|
4899
|
+
* turn reports.
|
|
4900
|
+
*/
|
|
4901
|
+
async recordQuota(name, cli, quota) {
|
|
4902
|
+
return this.mutex.run(async () => {
|
|
4903
|
+
const file = this.paths.runner(name);
|
|
4904
|
+
if (!await exists(file)) {
|
|
4905
|
+
throw new BoardError("NOT_FOUND", `runner ${name} not found`);
|
|
4906
|
+
}
|
|
4907
|
+
const doc = await readMarkdown(file, RunnerSchema);
|
|
4908
|
+
const previous = doc.data.quotas?.[cli];
|
|
4909
|
+
const merged = mergeQuota(previous, quota);
|
|
4910
|
+
const next = RunnerSchema.parse({
|
|
4911
|
+
...doc.data,
|
|
4912
|
+
quotas: { ...doc.data.quotas, [cli]: merged }
|
|
4913
|
+
});
|
|
4914
|
+
await writeMarkdown(file, next, doc.body);
|
|
4915
|
+
if (quotaChanged(previous, merged)) {
|
|
4916
|
+
await this.events.append("runner.quota", SYSTEM_ACTOR.name, {
|
|
4917
|
+
name,
|
|
4918
|
+
cli,
|
|
4919
|
+
status: merged.status
|
|
4920
|
+
});
|
|
4921
|
+
}
|
|
4922
|
+
return next;
|
|
4923
|
+
});
|
|
4924
|
+
}
|
|
4544
4925
|
/**
|
|
4545
4926
|
* Logs an operations signal as an `ops.signal` event. No channel carries it: the scheduler wakes
|
|
4546
4927
|
* its readers from the event, the runner lists it in their prompts, and the interface's log shows
|
|
@@ -5109,6 +5490,60 @@ ${current.body}`);
|
|
|
5109
5490
|
return updated;
|
|
5110
5491
|
});
|
|
5111
5492
|
}
|
|
5493
|
+
/**
|
|
5494
|
+
* Keeps an image a citizen's turn names by its path, read on the runner the turn runs on, which
|
|
5495
|
+
* refuses a file outside the turn's worktree, the citizen's home, and the system temp folder.
|
|
5496
|
+
* Only the bytes of a PNG, JPEG, GIF, or WebP of at most `IMAGE_LIMIT_BYTES` are kept, checked
|
|
5497
|
+
* here again whatever the runner said, under a new id outside the projection, so no runner
|
|
5498
|
+
* mirrors them and no prompt carries them: a post shows the image by its `image:` address.
|
|
5499
|
+
*/
|
|
5500
|
+
async uploadImage(actor, input) {
|
|
5501
|
+
const args = VerbInputs.upload_image.parse(input);
|
|
5502
|
+
await this.authorize(actor, "upload_image");
|
|
5503
|
+
if (actor.scope === void 0) {
|
|
5504
|
+
throw new BoardError("INVALID_STATE", "upload_image reads the file on the machine a turn runs on, so only a turn can call it");
|
|
5505
|
+
}
|
|
5506
|
+
if (this.imageSource === null) {
|
|
5507
|
+
throw new BoardError("NOT_FOUND", "this board has no runners to read an image from");
|
|
5508
|
+
}
|
|
5509
|
+
const bytes = await this.imageSource(actor, args.path);
|
|
5510
|
+
const type = imageTypeOf(bytes);
|
|
5511
|
+
if (type === null) {
|
|
5512
|
+
throw new BoardError("VALIDATION", `${args.path} is not a PNG, JPEG, GIF, or WebP image by its first bytes`);
|
|
5513
|
+
}
|
|
5514
|
+
if (bytes.byteLength > IMAGE_LIMIT_BYTES) {
|
|
5515
|
+
throw new BoardError("VALIDATION", `${args.path} is ${bytes.byteLength} bytes, over the ${IMAGE_LIMIT_BYTES} an image may be`);
|
|
5516
|
+
}
|
|
5517
|
+
return this.mutex.run(async () => {
|
|
5518
|
+
const id = this.newId();
|
|
5519
|
+
await writeFileAtomic(this.paths.image(id), bytes);
|
|
5520
|
+
await this.events.append("image.uploaded", actor.name, {
|
|
5521
|
+
id,
|
|
5522
|
+
type,
|
|
5523
|
+
size: bytes.byteLength
|
|
5524
|
+
});
|
|
5525
|
+
const name = path6.basename(args.path).replaceAll(/[[\]\\\r\n]/g, "");
|
|
5526
|
+
return {
|
|
5527
|
+
id,
|
|
5528
|
+
type,
|
|
5529
|
+
size: bytes.byteLength,
|
|
5530
|
+
markdown: ``
|
|
5531
|
+
};
|
|
5532
|
+
});
|
|
5533
|
+
}
|
|
5534
|
+
/** An uploaded image's bytes and kind, which its first bytes tell. */
|
|
5535
|
+
async readImage(id) {
|
|
5536
|
+
const file = this.paths.image(UlidSchema.parse(id));
|
|
5537
|
+
if (!await exists(file)) {
|
|
5538
|
+
throw new BoardError("NOT_FOUND", `image ${id} not found`);
|
|
5539
|
+
}
|
|
5540
|
+
const bytes = await readFile4(file);
|
|
5541
|
+
const type = imageTypeOf(bytes);
|
|
5542
|
+
if (type === null) {
|
|
5543
|
+
throw new BoardError("NOT_FOUND", `image ${id} is not an image the board shows`);
|
|
5544
|
+
}
|
|
5545
|
+
return { type, bytes };
|
|
5546
|
+
}
|
|
5112
5547
|
/** Every cron, ended ones included, oldest first. */
|
|
5113
5548
|
async listCrons() {
|
|
5114
5549
|
const crons = [];
|
|
@@ -5500,6 +5935,13 @@ ${current.body}`);
|
|
|
5500
5935
|
watchTurns(source) {
|
|
5501
5936
|
this.turnsInFlight = source;
|
|
5502
5937
|
}
|
|
5938
|
+
/**
|
|
5939
|
+
* Where `upload_image` reads a file from: the runner the calling turn runs on, through the server
|
|
5940
|
+
* that hosts the runners. Without one, as in the admin CLI, nothing can be uploaded.
|
|
5941
|
+
*/
|
|
5942
|
+
readImagesWith(source) {
|
|
5943
|
+
this.imageSource = source;
|
|
5944
|
+
}
|
|
5503
5945
|
/**
|
|
5504
5946
|
* Refuses to end conversations while another citizen's turn is in one of them: `cut` picks the
|
|
5505
5947
|
* turns that would lose theirs. The caller's own turn is exempt, since ending a conversation
|
|
@@ -6329,6 +6771,8 @@ ${rationale}`}`);
|
|
|
6329
6771
|
return this.updateCron(actor, VerbInputs.update_cron.parse(input));
|
|
6330
6772
|
case "remove_cron":
|
|
6331
6773
|
return this.removeCron(actor, VerbInputs.remove_cron.parse(input));
|
|
6774
|
+
case "upload_image":
|
|
6775
|
+
return this.uploadImage(actor, VerbInputs.upload_image.parse(input));
|
|
6332
6776
|
default:
|
|
6333
6777
|
throw new BoardError("VALIDATION", `unknown verb ${String(verb)}`);
|
|
6334
6778
|
}
|
|
@@ -6375,7 +6819,7 @@ ${rationale}`}`);
|
|
|
6375
6819
|
}
|
|
6376
6820
|
async readAgentRoleBody(agent) {
|
|
6377
6821
|
const file = this.paths.agentRole(agent);
|
|
6378
|
-
return await exists(file) ? (await readMarkdown(file,
|
|
6822
|
+
return await exists(file) ? (await readMarkdown(file, z16.record(z16.string(), z16.unknown()))).body : "";
|
|
6379
6823
|
}
|
|
6380
6824
|
/** The steps of one finished turn, as the runner handed them over when it ended. */
|
|
6381
6825
|
/**
|
|
@@ -6406,6 +6850,14 @@ ${rationale}`}`);
|
|
|
6406
6850
|
}
|
|
6407
6851
|
return computeMetrics({ events, tasks, citizens, actions, activeSignals, window, now });
|
|
6408
6852
|
}
|
|
6853
|
+
/**
|
|
6854
|
+
* Each citizen's finished turns over a window and the tokens of those that counted them, from
|
|
6855
|
+
* the event log alone, which holds every turn's end whatever runner it ran on.
|
|
6856
|
+
*/
|
|
6857
|
+
async tokenUse(window) {
|
|
6858
|
+
const events = await this.events.readSince(null, Number.MAX_SAFE_INTEGER);
|
|
6859
|
+
return tokenUse(events, window, this.now());
|
|
6860
|
+
}
|
|
6409
6861
|
/** The board actions a finished turn took, from its transcript; null when it kept none. */
|
|
6410
6862
|
async turnActions(agent, turnId) {
|
|
6411
6863
|
const known = this.actionCounts.get(turnId);
|
|
@@ -6775,7 +7227,7 @@ ${rationale}`}`);
|
|
|
6775
7227
|
return { project: slug, revision: 0, body: "" };
|
|
6776
7228
|
}
|
|
6777
7229
|
const { data, body } = await readLooseMarkdown(file);
|
|
6778
|
-
const revision =
|
|
7230
|
+
const revision = z16.number().int().min(0).safeParse(data["revision"]);
|
|
6779
7231
|
const updatedBy = NameSchema.safeParse(data["updatedBy"]);
|
|
6780
7232
|
const updatedAt = IsoDateTimeSchema.safeParse(data["updatedAt"]);
|
|
6781
7233
|
return {
|
|
@@ -7049,7 +7501,7 @@ ${charter.purpose}
|
|
|
7049
7501
|
|
|
7050
7502
|
${charter.purpose}
|
|
7051
7503
|
|
|
7052
|
-
Route every lesson with one question: about me, my craft, or the user, it goes in memory/core.md; about this codebase, it goes in the project's knowledge
|
|
7504
|
+
Route every lesson with one question: about me, my craft, or the user, it goes in memory/core.md; about how this codebase behaves, it goes in the repo's AGENTS.md or README; about this project as a place (runners, access, flow, where results live), it goes in the project's knowledge; something everyone should know, post it.
|
|
7053
7505
|
` + (seed.length === 0 ? "" : `
|
|
7054
7506
|
${SEED_INSTRUCTIONS_HEADING}
|
|
7055
7507
|
|
|
@@ -7066,7 +7518,7 @@ ${seed}
|
|
|
7066
7518
|
continue;
|
|
7067
7519
|
}
|
|
7068
7520
|
const file = this.paths.agentRole(agent.name);
|
|
7069
|
-
const body = await exists(file) ? (await readMarkdown(file,
|
|
7521
|
+
const body = await exists(file) ? (await readMarkdown(file, z16.record(z16.string(), z16.unknown()))).body : "";
|
|
7070
7522
|
const at = body.indexOf(`
|
|
7071
7523
|
${SEED_INSTRUCTIONS_HEADING}
|
|
7072
7524
|
`);
|
|
@@ -7904,7 +8356,7 @@ ${charter.purpose}
|
|
|
7904
8356
|
id: this.newId(),
|
|
7905
8357
|
...head,
|
|
7906
8358
|
ts: this.now().toISOString(),
|
|
7907
|
-
mentions:
|
|
8359
|
+
mentions: postMentions(body)
|
|
7908
8360
|
});
|
|
7909
8361
|
const { author } = head;
|
|
7910
8362
|
await writeMarkdown(this.paths.messageFile(dir, frontmatter.id, author), frontmatter, body);
|
|
@@ -8282,7 +8734,7 @@ function decideWake(input) {
|
|
|
8282
8734
|
}
|
|
8283
8735
|
|
|
8284
8736
|
// packages/scheduler/dist/scheduler.js
|
|
8285
|
-
import { z as
|
|
8737
|
+
import { z as z17 } from "zod";
|
|
8286
8738
|
function besideFields(beside) {
|
|
8287
8739
|
if (beside === void 0) {
|
|
8288
8740
|
return {};
|
|
@@ -8309,21 +8761,21 @@ var DEFAULT_TIMINGS = Object.freeze({
|
|
|
8309
8761
|
scaleCooldownMs: 36e5,
|
|
8310
8762
|
reflectionMs: 24 * 36e5
|
|
8311
8763
|
});
|
|
8312
|
-
var SchedulerTimingsSchema =
|
|
8313
|
-
pollMs:
|
|
8314
|
-
debounceMs:
|
|
8315
|
-
userDebounceMs:
|
|
8316
|
-
heartbeatMs:
|
|
8317
|
-
waitingStageMs:
|
|
8318
|
-
leaseSweepMs:
|
|
8319
|
-
opsIntervalMs:
|
|
8320
|
-
signalRepeatMs:
|
|
8321
|
-
costReportMs:
|
|
8322
|
-
staleThreadMs:
|
|
8323
|
-
idleMemberMs:
|
|
8324
|
-
homeConflictMs:
|
|
8325
|
-
scaleCooldownMs:
|
|
8326
|
-
reflectionMs:
|
|
8764
|
+
var SchedulerTimingsSchema = z17.object({
|
|
8765
|
+
pollMs: z17.number().positive(),
|
|
8766
|
+
debounceMs: z17.number().nonnegative(),
|
|
8767
|
+
userDebounceMs: z17.number().nonnegative(),
|
|
8768
|
+
heartbeatMs: z17.number().positive(),
|
|
8769
|
+
waitingStageMs: z17.number().positive(),
|
|
8770
|
+
leaseSweepMs: z17.number().positive(),
|
|
8771
|
+
opsIntervalMs: z17.number().positive(),
|
|
8772
|
+
signalRepeatMs: z17.number().positive(),
|
|
8773
|
+
costReportMs: z17.number().positive(),
|
|
8774
|
+
staleThreadMs: z17.number().positive(),
|
|
8775
|
+
idleMemberMs: z17.number().positive(),
|
|
8776
|
+
homeConflictMs: z17.number().positive(),
|
|
8777
|
+
scaleCooldownMs: z17.number().positive(),
|
|
8778
|
+
reflectionMs: z17.number().positive()
|
|
8327
8779
|
}).partial();
|
|
8328
8780
|
function parseTimings(input) {
|
|
8329
8781
|
const parsed = SchedulerTimingsSchema.parse(input);
|
|
@@ -8335,32 +8787,32 @@ function parseTimings(input) {
|
|
|
8335
8787
|
}
|
|
8336
8788
|
return overrides;
|
|
8337
8789
|
}
|
|
8338
|
-
var PendingSchema =
|
|
8790
|
+
var PendingSchema = z17.object({
|
|
8339
8791
|
dispatch: TurnDispatchSchema,
|
|
8340
|
-
readyAt:
|
|
8341
|
-
stageWakes:
|
|
8342
|
-
holder:
|
|
8792
|
+
readyAt: z17.number(),
|
|
8793
|
+
stageWakes: z17.array(TriggerSchema).optional(),
|
|
8794
|
+
holder: z17.boolean().optional(),
|
|
8343
8795
|
newestMessage: UlidSchema.optional(),
|
|
8344
|
-
onlyMessages:
|
|
8796
|
+
onlyMessages: z17.boolean().optional(),
|
|
8345
8797
|
steeredThrough: UlidSchema.optional(),
|
|
8346
|
-
onlyCrons:
|
|
8798
|
+
onlyCrons: z17.boolean().optional()
|
|
8347
8799
|
});
|
|
8348
|
-
var StateSchema =
|
|
8349
|
-
cursor:
|
|
8350
|
-
lastHeartbeat:
|
|
8800
|
+
var StateSchema = z17.object({
|
|
8801
|
+
cursor: z17.string().nullable(),
|
|
8802
|
+
lastHeartbeat: z17.record(z17.string(), z17.string()),
|
|
8351
8803
|
/** Waiting stages already signalled, as `task:stage`, so each is logged once while it waits. */
|
|
8352
|
-
waitingSeen:
|
|
8804
|
+
waitingSeen: z17.record(z17.string(), z17.string()).default({}),
|
|
8353
8805
|
/** Queued dispatches survive a restart; the cursor has already moved past the events that made them. */
|
|
8354
|
-
pending:
|
|
8355
|
-
opsReported:
|
|
8356
|
-
releaseCounts:
|
|
8357
|
-
lastOps:
|
|
8358
|
-
lastScaled:
|
|
8359
|
-
lastCostReport:
|
|
8360
|
-
costSinceReport:
|
|
8361
|
-
turnsSinceReport:
|
|
8806
|
+
pending: z17.record(z17.string(), PendingSchema).default({}),
|
|
8807
|
+
opsReported: z17.record(z17.string(), z17.string()).default({}),
|
|
8808
|
+
releaseCounts: z17.record(z17.string(), z17.number().int().nonnegative()).default({}),
|
|
8809
|
+
lastOps: z17.string().nullable().default(null),
|
|
8810
|
+
lastScaled: z17.record(z17.string(), z17.string()).default({}),
|
|
8811
|
+
lastCostReport: z17.string().nullable().default(null),
|
|
8812
|
+
costSinceReport: z17.number().nonnegative().default(0),
|
|
8813
|
+
turnsSinceReport: z17.number().int().nonnegative().default(0),
|
|
8362
8814
|
/** When each member last reflected, or was first seen; the cadence counts from here. */
|
|
8363
|
-
lastReflection:
|
|
8815
|
+
lastReflection: z17.record(z17.string(), z17.string()).default({})
|
|
8364
8816
|
});
|
|
8365
8817
|
function messageOf(trigger) {
|
|
8366
8818
|
return trigger.kind === "mention" || trigger.kind === "user_post" ? trigger.messageId : void 0;
|
|
@@ -9933,11 +10385,11 @@ var TURN_CONTRACT = [
|
|
|
9933
10385
|
"- 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.",
|
|
9934
10386
|
"- Silence is allowed. If the digest needs no reply, post nothing.",
|
|
9935
10387
|
"- To come back to something later, set a cron with create_cron: a note on what to do then, and a five-field cron expression or one time with at. It wakes you in this conversation unless you name another, at most every fifteen minutes, and every fire is a paid turn, so end a cron with remove_cron once it has done its job. Never wait by holding a stage, sleeping, or leaving a process running: a turn's board token ends with the turn, so nothing it leaves behind can act on the board. The society's crons are under society/crons/ in the board projection.",
|
|
9936
|
-
"- 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.",
|
|
10388
|
+
"- 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. A name in a code span or a code block is no mention, so code keeps its @ as written. @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.",
|
|
9937
10389
|
"- A thread is a conversation off a channel that reaches only its participants and whoever is mentioned in it. Every task has one, opened with it under the task's id on the channel of its project it was filed in, general unless it was filed in another: talk about a task there, with post_message and its thread_id, and the notes you give advance_task and update_task are posted there as you. Its participants are the task's creator, its stage holders and assignees, whoever may take the stage that waits, and anyone who posted; it closes when the task ends. Every proposal has one too, in governance, opened with its pitch and closed by its decision. Open other threads with open_thread on a channel with a title, for any other topic; close_thread posts your summary to its channel.",
|
|
9938
|
-
"- To show an image in a post, such as a screenshot or a chart,
|
|
9939
|
-
"- Route every lesson: about you, your craft, or the user, write it to memory/core.md in your home directory, and keep that file short by moving detail to memory/<topic>.md, your archive; a durable fact about a project
|
|
9940
|
-
'- A procedure you have followed twice is a skill: write it to skills/<name>/SKILL.md in your home, frontmatter with `name` and a one-line `description` and then the steps, and your skills index lists it from the next turn. Files the steps use, such as scripts/ and references/, go in the same folder, named in SKILL.md by paths relative to it. Propose it with kind "skill" when
|
|
10390
|
+
"- To show an image in a post, such as a screenshot or a chart, save it anywhere you can read it, such as a folder from `mktemp -d`, call upload_image with its path, and put the markdown it returns in the post, ``, with what the image shows as its alt text: the board keeps the image, so it shows in any conversation after the file is gone. It takes a PNG, JPEG, GIF, or WebP of at most 8 MB from your worktree, your home, or the system temp folder. An image committed on a task's branch, linked by its path in the task's worktree, still shows in place, and so does one at an https address. Never put an image or any other file into a message as base64 or a data: URL: the board does not show it, and every reader of the conversation gets it in full in their prompt.",
|
|
10391
|
+
"- Route every lesson: about you, your craft, or the user, write it to memory/core.md in your home directory, and keep that file short by moving detail to memory/<topic>.md, your archive; how a codebase behaves (architecture, conventions, gotchas, how to build, test, and verify it) goes in its AGENTS.md or README, committed on a task's branch like any change to the code, or proposed in the task's thread or the project channel when it is outside the task at hand; a durable fact about a project as a place (runners, access, flow, where results live) goes through `write_knowledge` on that project; something the whole project should know now, post it to the project channel, and anything about one task, in the task's thread.",
|
|
10392
|
+
'- A procedure you have followed twice is a candidate for a skill, which it is when it makes a citizen better at a kind of work on any project: write it to skills/<name>/SKILL.md in your home, frontmatter with `name` and a one-line `description` and then the steps, and your skills index lists it from the next turn. Files the steps use, such as scripts/ and references/, go in the same folder, named in SKILL.md by paths relative to it. Propose it with kind "skill" when more than one citizen would use it and nothing in it is tied to one place; a skill only you use stays yours, however many projects you use it on.',
|
|
9941
10393
|
"- To take a published skill for yourself, install it in a temporary folder and copy it into your home: in a folder from `mktemp -d`, `npx -y skills add <source> --skill <name> -a universal --copy -y` writes `.agents/skills/<name>/` and a lock file there, and you copy that skill folder to skills/<name>/ in your home, named in lowercase letters, digits, and hyphens. Never install one inside a worktree, where it would be committed into the project, or with -g, which writes to the runner's own home, shared by every citizen on it and listed in no skills index.",
|
|
9942
10394
|
"- Report memoryUpdated: true in the status object whenever you changed memory/core.md or a skill, so a warm session restarts with the new instructions.",
|
|
9943
10395
|
"- Other turns of yours may be running at the same time and share your home. Change a file there with your edit tool, which refuses a file changed since you read it, and never rewrite one whole from an earlier read, with a shell redirect or a script, or you erase what another of your turns wrote.",
|
|
@@ -9948,7 +10400,7 @@ var PLANNING = [
|
|
|
9948
10400
|
"- A task is a plan of stages between open and done. Hold the current stage with claim_task and finish it with advance_task; the next stage becomes current, and past the last one the task is done.",
|
|
9949
10401
|
"- Name stages by what gets done. Plan the next few steps rather than everything, and reshape with plan_task as the work teaches you.",
|
|
9950
10402
|
"- Assign a stage to a role when anyone in it could do it, to a citizen only when it must be them, and to nobody when anyone in the project could. A citizen named on a stage must be a member of the task's project; add them first. A stage wakes everyone it is open to when it comes up, so the narrower the assignee, the fewer turns it costs.",
|
|
9951
|
-
"- Reshape rather than force: another round is an inserted stage, and a wait on something outside the society is a stage named for what it waits on. When a check finds work unfinished,
|
|
10403
|
+
"- Reshape rather than force: another round is an inserted stage, and a wait on something outside the society is a stage named for what it waits on. When a check finds work unfinished, the holder of the current stage sends the task back to an earlier stage with update_task, and so may the user, the steward, and the concierge; anyone else asks the concierge in the task's thread.",
|
|
9952
10404
|
"- A task that cannot start until other tasks finish names them with update_task blocked_by. While any of them is in play, its stage wakes nobody, neither when it comes up nor on a heartbeat; once the last has ended, done or abandoned, the stage wakes as if it had just come up, or wakes its holder. Nobody needs to clear the block or mention anyone when they end; change blocked_by only when the plan changes.",
|
|
9953
10405
|
"- A gated stage is an independent check: nobody who held an earlier stage of the task may hold it, and only the user, the steward, and the concierge may add, remove, move, reassign, or ungate it. Ask them in the task's thread when a gate should change.",
|
|
9954
10406
|
"- Commit your work on the task's branch before you advance, and say what you did in the advance note; it is the handover the next holder reads in the task's thread."
|
|
@@ -9967,15 +10419,15 @@ var GOVERNANCE = [
|
|
|
9967
10419
|
"- Prefer scaling an existing role over inventing one, with a reallocation of its replicas, backlog threshold, wakes, or verbs; a new role is justified by work a project needs that no existing role covers. A role that needs a tool the board lacks is an engineering task, not a hiring request.",
|
|
9968
10420
|
"- Operations signals are counters and timers the board logs, listed in the prompt of a role that reads them: stages waiting for a holder, backlog per role, stages waiting on a role nobody fills, churn, stale threads, idle members, missing capabilities, replicas added, spend. They are not posts in any channel; interpreting them is your judgment."
|
|
9969
10421
|
].join("\n");
|
|
9970
|
-
function renderOnboardingPreamble(
|
|
10422
|
+
function renderOnboardingPreamble(context2) {
|
|
9971
10423
|
return [
|
|
9972
|
-
`This is your first turn as ${
|
|
10424
|
+
`This is your first turn as ${context2.agentName}.`,
|
|
9973
10425
|
"",
|
|
9974
|
-
`Your role in one paragraph: ${
|
|
10426
|
+
`Your role in one paragraph: ${context2.roleSummary}`,
|
|
9975
10427
|
"",
|
|
9976
|
-
`You are working on project "${
|
|
9977
|
-
...(
|
|
9978
|
-
`You follow its general channel. It also has ${(
|
|
10428
|
+
`You are working on project "${context2.project}" in the worktree at ${context2.worktree}.`,
|
|
10429
|
+
...(context2.otherChannels ?? []).length === 0 ? [] : [
|
|
10430
|
+
`You follow its general channel. It also has ${(context2.otherChannels ?? []).map((ref) => `#${ref}`).join(", ")}, each a workstream such as a release: subscribe to one when your work there needs its posts. Its tasks and mentions reach you either way.`
|
|
9979
10431
|
],
|
|
9980
10432
|
"Your memory is empty. Write durable lessons about yourself, your craft, or the user to memory/core.md in your home directory.",
|
|
9981
10433
|
"Write profile.md in your home directory: one paragraph on what you do well and what to send your way. The roster the front desk routes with is built from it.",
|
|
@@ -9986,7 +10438,7 @@ function renderOnboardingPreamble(context) {
|
|
|
9986
10438
|
function renderSkillsIndex(skills) {
|
|
9987
10439
|
if (skills.length === 0) {
|
|
9988
10440
|
return [
|
|
9989
|
-
"None yet.
|
|
10441
|
+
"None yet. Something you have done the same way twice is a candidate for the first; the turn contract says when it is one."
|
|
9990
10442
|
];
|
|
9991
10443
|
}
|
|
9992
10444
|
return [
|
|
@@ -10015,9 +10467,9 @@ var REFLECTION_INTRO = "This turn is for your memory; take no new work, and answ
|
|
|
10015
10467
|
var RECONCILE = "- Reconcile first: merge each copy listed under Edits to reconcile in your home into the file beside it, then delete the copy.";
|
|
10016
10468
|
var REFLECTION_STEPS = [
|
|
10017
10469
|
"- Consolidate memory/core.md: keep it to lessons that still hold, about sixty lines at most, and move detail worth keeping to memory/<topic>.md in your home, which the board's search covers for you alone.",
|
|
10018
|
-
'- Extract a skill: a procedure you have followed twice goes to skills/<name>/SKILL.md, frontmatter with `name` and a one-line `description` and then the steps, with any script or reference it uses in the same folder. Propose it with kind "skill" when
|
|
10470
|
+
'- Extract a skill: a procedure you have followed twice is a candidate, and one that makes a citizen better at a kind of work on any project goes to skills/<name>/SKILL.md, frontmatter with `name` and a one-line `description` and then the steps, with any script or reference it uses in the same folder. Propose it with kind "skill" when more than one citizen would use it and nothing in it is tied to one place; one only you use stays yours.',
|
|
10019
10471
|
"- Refresh profile.md: one paragraph on what you do well and what to send your way; the roster the front desk routes with is built from it.",
|
|
10020
|
-
"- Share: a durable fact about this scope
|
|
10472
|
+
"- Share: how a codebase behaves goes in its AGENTS.md or README through a task like any change to the code, so propose it in that task's thread or the project channel; a durable fact about this scope as a place (runners, access, flow, where results live) goes through write_knowledge; a norm the whole society should follow goes to the steward as a plain post in general.",
|
|
10021
10473
|
"- Report memoryUpdated: true in the status object when core.md or a skill changed."
|
|
10022
10474
|
];
|
|
10023
10475
|
var MAX_BODY_CHARS = 1500;
|
|
@@ -10061,10 +10513,25 @@ function signalLine(ts, signal) {
|
|
|
10061
10513
|
].filter((fact) => fact.length > 0);
|
|
10062
10514
|
return `- [${ts}] ${signal.kind}: ${signal.summary} (${facts.join("; ")})`;
|
|
10063
10515
|
}
|
|
10064
|
-
function
|
|
10516
|
+
function quotaPhrase(cli, quota, now) {
|
|
10517
|
+
const level = quotaLevel(quota, now);
|
|
10518
|
+
if (level === "none") {
|
|
10519
|
+
return `${cli} reports no plan limits`;
|
|
10520
|
+
}
|
|
10521
|
+
if (level === "limited") {
|
|
10522
|
+
return `${cli} limited until ${quota.limitedUntil ?? "an unknown time"}`;
|
|
10523
|
+
}
|
|
10524
|
+
const windows = quota.windows.map((window) => !windowCurrent(window, now) ? `${windowName(window)} reset since` : `${windowName(window)} ${window.usedPercent === null ? "?" : Math.floor(window.usedPercent)}%`);
|
|
10525
|
+
return `${cli} ${windows.length === 0 ? "allowed" : windows.join(", ")}${level === "warning" ? " (near a limit)" : ""} as of ${quota.observedAt}`;
|
|
10526
|
+
}
|
|
10527
|
+
function runnerLine(runner, projects, now) {
|
|
10065
10528
|
const living = projects.filter((project) => project.runner === runner.name).map((project) => project.slug);
|
|
10066
10529
|
const state = runner.status === "connected" ? "connected" : `away${runner.lastSeen === void 0 ? "" : ` since ${runner.lastSeen}`}`;
|
|
10067
|
-
|
|
10530
|
+
const quotas = runner.clis.flatMap((cli) => {
|
|
10531
|
+
const quota = runner.quotas?.[cli];
|
|
10532
|
+
return quota === void 0 ? [] : [quotaPhrase(cli, quota, now)];
|
|
10533
|
+
});
|
|
10534
|
+
return `- ${runner.name}: ${state}; ${runner.os}; CLIs ${runner.clis.length === 0 ? "none" : runner.clis.join(", ")}; offers ${runner.capabilities.length === 0 ? "nothing beyond its CLIs" : runner.capabilities.join(", ")}; ${living.length === 0 ? "no projects" : `projects ${living.join(", ")}`}${quotas.length === 0 ? "" : `; quota ${quotas.join("; ")}`}`;
|
|
10068
10535
|
}
|
|
10069
10536
|
function profileLine(profile) {
|
|
10070
10537
|
const line = profile.split("\n").map((entry) => entry.trim()).find((entry) => entry.length > 0 && !entry.startsWith("#"));
|
|
@@ -10214,19 +10681,19 @@ function buildTurnPrompt(input) {
|
|
|
10214
10681
|
}
|
|
10215
10682
|
const machines = input.runners;
|
|
10216
10683
|
if (machines !== void 0 && machines !== null) {
|
|
10217
|
-
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 never moves. 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.", "");
|
|
10684
|
+
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 never moves. 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.", "");
|
|
10218
10685
|
if (machines.runners.length === 0) {
|
|
10219
10686
|
lines.push("None registered yet.");
|
|
10220
10687
|
}
|
|
10221
10688
|
for (const runner of machines.runners) {
|
|
10222
|
-
lines.push(runnerLine(runner, machines.projects));
|
|
10689
|
+
lines.push(runnerLine(runner, machines.projects, input.clock?.now ?? /* @__PURE__ */ new Date()));
|
|
10223
10690
|
}
|
|
10224
10691
|
}
|
|
10225
10692
|
const knowledge = input.knowledge;
|
|
10226
10693
|
if (knowledge !== void 0 && knowledge !== null) {
|
|
10227
10694
|
lines.push("", `## Knowledge of ${dispatch.project}`, "");
|
|
10228
10695
|
if (knowledge.topics.length === 0) {
|
|
10229
|
-
lines.push("None yet. A durable fact about this
|
|
10696
|
+
lines.push("None yet. A durable fact about this place, such as its runners, access, flow, or where results live, is worth a write_knowledge call.");
|
|
10230
10697
|
} else {
|
|
10231
10698
|
lines.push(`Topics under ${knowledge.dir}; open one with your file tools when it is relevant.`, "");
|
|
10232
10699
|
for (const topic of knowledge.topics) {
|
|
@@ -10752,6 +11219,11 @@ var TurnHost = class {
|
|
|
10752
11219
|
}
|
|
10753
11220
|
}
|
|
10754
11221
|
});
|
|
11222
|
+
if (outcome.quota !== void 0) {
|
|
11223
|
+
await this.board.recordQuota(turn.runner, job.cli, outcome.quota).catch((error) => {
|
|
11224
|
+
this.log.warn({ runner: turn.runner, cli: job.cli, error: String(error) }, "could not record a quota");
|
|
11225
|
+
});
|
|
11226
|
+
}
|
|
10755
11227
|
if (recorded) {
|
|
10756
11228
|
this.log.info({
|
|
10757
11229
|
agent: job.agent,
|
|
@@ -10997,6 +11469,12 @@ var DEFAULT_GRACE_MS = 2 * 6e4;
|
|
|
10997
11469
|
var DEFAULT_REQUEST_TIMEOUT_MS = 10 * 6e4;
|
|
10998
11470
|
var CONTROL_TIMEOUT_MS = 15e3;
|
|
10999
11471
|
var FILE_TIMEOUT_MS = 6e4;
|
|
11472
|
+
var IMAGE_REFUSALS = {
|
|
11473
|
+
missing: "NOT_FOUND",
|
|
11474
|
+
outside: "FORBIDDEN",
|
|
11475
|
+
not_image: "VALIDATION",
|
|
11476
|
+
too_big: "VALIDATION"
|
|
11477
|
+
};
|
|
11000
11478
|
var SWEEP_TIMEOUT_MS = 2 * 6e4;
|
|
11001
11479
|
var DEFAULT_ACK_TIMEOUT_MS = 3e4;
|
|
11002
11480
|
var RunnerHub = class {
|
|
@@ -11081,7 +11559,8 @@ var RunnerHub = class {
|
|
|
11081
11559
|
os: hello.os,
|
|
11082
11560
|
clis: hello.clis,
|
|
11083
11561
|
capabilities: hello.capabilities,
|
|
11084
|
-
version: hello.version
|
|
11562
|
+
version: hello.version,
|
|
11563
|
+
gitIdentityMissing: hello.gitIdentityMissing ?? null
|
|
11085
11564
|
});
|
|
11086
11565
|
this.runnerChanged(name);
|
|
11087
11566
|
this.log.info({ runner: name, version: hello.version, clis: hello.clis, slots: hello.slots }, "runner registered");
|
|
@@ -11504,6 +11983,33 @@ var RunnerHub = class {
|
|
|
11504
11983
|
}
|
|
11505
11984
|
return diff;
|
|
11506
11985
|
}
|
|
11986
|
+
/**
|
|
11987
|
+
* The image file a citizen's turn names for `upload_image`, read on the runner that turn runs on,
|
|
11988
|
+
* which refuses a file outside the turn's worktree, its citizen's home, and the system temp
|
|
11989
|
+
* folder, and anything but a PNG, JPEG, GIF, or WebP within the size limit; each refusal comes
|
|
11990
|
+
* back as a `BoardError` in the runner's words.
|
|
11991
|
+
*/
|
|
11992
|
+
async readTurnImage(actor, file) {
|
|
11993
|
+
const turn = this.host.runningTurns().find((each) => each.agent === actor.name && each.scope === actor.scope && each.thread === actor.thread && each.channel === actor.channel);
|
|
11994
|
+
if (turn === void 0) {
|
|
11995
|
+
throw new BoardError("INVALID_STATE", `${actor.name} has no turn running in this conversation to read ${file} from`);
|
|
11996
|
+
}
|
|
11997
|
+
const seat = this.seats.get(turn.runner);
|
|
11998
|
+
if (seat === void 0 || seat.send === null) {
|
|
11999
|
+
throw new RunnerAwayError(`runner ${turn.runner}, where the turn runs, is not connected`);
|
|
12000
|
+
}
|
|
12001
|
+
if (!seat.hello.imageReads) {
|
|
12002
|
+
throw new BoardError("INVALID_STATE", `runner ${seat.name}, where the turn runs, cannot read images yet; upgrade it`);
|
|
12003
|
+
}
|
|
12004
|
+
const image = TurnImageSchema.nullable().parse(await this.request(seat, (request) => ({ type: "image", request, read: { turnId: turn.turnId, path: file } }), FILE_TIMEOUT_MS));
|
|
12005
|
+
if (image === null) {
|
|
12006
|
+
throw new BoardError("INVALID_STATE", `the turn is no longer running on runner ${seat.name}`);
|
|
12007
|
+
}
|
|
12008
|
+
if (image.kind === "refused") {
|
|
12009
|
+
throw new BoardError(IMAGE_REFUSALS[image.why], image.detail);
|
|
12010
|
+
}
|
|
12011
|
+
return Buffer.from(image.content, "base64");
|
|
12012
|
+
}
|
|
11507
12013
|
/** The models a CLI offers, asked of a connected runner that has it; none when no runner has it. */
|
|
11508
12014
|
/**
|
|
11509
12015
|
* The connected runner a model list for `cli` comes from: the first of `prefer` that has the CLI,
|
|
@@ -11523,6 +12029,26 @@ var RunnerHub = class {
|
|
|
11523
12029
|
}
|
|
11524
12030
|
return ModelListSchema.parse(await this.request(seat, (request) => ({ type: "models", request, cli })));
|
|
11525
12031
|
}
|
|
12032
|
+
/**
|
|
12033
|
+
* A CLI's plan quota, read on a runner without a turn and recorded on the runner's record.
|
|
12034
|
+
* Refused for a runner not connected with the CLI, and for one from before quota reads, which
|
|
12035
|
+
* would skip the request and leave the reader waiting.
|
|
12036
|
+
*/
|
|
12037
|
+
async quota(runner, cli) {
|
|
12038
|
+
const seat = this.seats.get(runner);
|
|
12039
|
+
if (seat === void 0 || seat.send === null || !seat.hello.clis.includes(cli)) {
|
|
12040
|
+
throw new RunnerAwayError(`runner ${runner} is not connected with ${cli}`);
|
|
12041
|
+
}
|
|
12042
|
+
if (!seat.hello.quotaReads) {
|
|
12043
|
+
throw new BoardError("INVALID_STATE", `runner ${runner} cannot read quotas yet; upgrade it`);
|
|
12044
|
+
}
|
|
12045
|
+
const quota = CliQuotaSchema.nullable().parse(await this.request(seat, (request) => ({ type: "quota", request, cli }), FILE_TIMEOUT_MS));
|
|
12046
|
+
if (quota === null) {
|
|
12047
|
+
throw new BoardError("NOT_FOUND", `${cli} on runner ${runner} cannot read its quota`);
|
|
12048
|
+
}
|
|
12049
|
+
await this.board.recordQuota(runner, cli, quota);
|
|
12050
|
+
return quota;
|
|
12051
|
+
}
|
|
11526
12052
|
/** Calls `listener` whenever a runner registers, connects, or goes away. */
|
|
11527
12053
|
onRunnerChange(listener) {
|
|
11528
12054
|
this.runnerListeners.add(listener);
|
|
@@ -11755,20 +12281,20 @@ async function handleMcpRequest(board2, actor, request, version) {
|
|
|
11755
12281
|
import { Hono as Hono3 } from "hono";
|
|
11756
12282
|
import { compress } from "hono/compress";
|
|
11757
12283
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
11758
|
-
import { z as
|
|
12284
|
+
import { z as z19, ZodError } from "zod";
|
|
11759
12285
|
|
|
11760
12286
|
// apps/server/dist/github-auth.js
|
|
11761
12287
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
11762
12288
|
import { Hono } from "hono";
|
|
11763
|
-
import { z as
|
|
12289
|
+
import { z as z18 } from "zod";
|
|
11764
12290
|
var SIGN_IN_TTL_MS = 30 * 24 * 36e5;
|
|
11765
12291
|
var STATE_TTL_MS = 10 * 6e4;
|
|
11766
12292
|
var MAX_STATES = 1e3;
|
|
11767
|
-
var TokenAnswerSchema =
|
|
11768
|
-
var GithubUserSchema =
|
|
11769
|
-
login:
|
|
11770
|
-
id:
|
|
11771
|
-
avatar_url:
|
|
12293
|
+
var TokenAnswerSchema = z18.object({ access_token: z18.string().min(1) });
|
|
12294
|
+
var GithubUserSchema = z18.object({
|
|
12295
|
+
login: z18.string().min(1),
|
|
12296
|
+
id: z18.number().int(),
|
|
12297
|
+
avatar_url: z18.string().optional()
|
|
11772
12298
|
});
|
|
11773
12299
|
function githubAvatar(url) {
|
|
11774
12300
|
try {
|
|
@@ -12191,7 +12717,7 @@ async function isFile(file) {
|
|
|
12191
12717
|
return false;
|
|
12192
12718
|
}
|
|
12193
12719
|
}
|
|
12194
|
-
function
|
|
12720
|
+
function within2(root, pathname) {
|
|
12195
12721
|
let decoded;
|
|
12196
12722
|
try {
|
|
12197
12723
|
decoded = decodeURIComponent(pathname);
|
|
@@ -12208,7 +12734,7 @@ function spaHandler(staticDir) {
|
|
|
12208
12734
|
if (pathname.startsWith("/api/")) {
|
|
12209
12735
|
return c.json({ error: "NOT_FOUND", message: `no route ${pathname}` }, 404);
|
|
12210
12736
|
}
|
|
12211
|
-
const file =
|
|
12737
|
+
const file = within2(root, pathname);
|
|
12212
12738
|
const headers = { "x-content-type-options": "nosniff" };
|
|
12213
12739
|
if (file !== null && await isFile(file)) {
|
|
12214
12740
|
return c.body(await readFile5(file), 200, {
|
|
@@ -12243,19 +12769,19 @@ var ERROR_STATUS = {
|
|
|
12243
12769
|
INVALID_STATE: 409
|
|
12244
12770
|
};
|
|
12245
12771
|
var EVENT_PAGE = 100;
|
|
12246
|
-
var RetireBodySchema =
|
|
12247
|
-
var RunnerBodySchema =
|
|
12248
|
-
var EnrollmentApprovalSchema =
|
|
12249
|
-
var HomeRunnerBodySchema =
|
|
12250
|
-
var ModelBodySchema =
|
|
12251
|
-
var EffortBodySchema =
|
|
12252
|
-
var HistoryLimitSchema =
|
|
12253
|
-
var MessageIdsSchema =
|
|
12254
|
-
var TimezoneBodySchema =
|
|
12255
|
-
var ChannelBodySchema =
|
|
12772
|
+
var RetireBodySchema = z19.object({ reason: z19.string().min(1) });
|
|
12773
|
+
var RunnerBodySchema = z19.object({ name: NameSchema });
|
|
12774
|
+
var EnrollmentApprovalSchema = z19.object({ name: NameSchema });
|
|
12775
|
+
var HomeRunnerBodySchema = z19.object({ runner: NameSchema.nullable() });
|
|
12776
|
+
var ModelBodySchema = z19.object({ model: ModelNameSchema.nullable() });
|
|
12777
|
+
var EffortBodySchema = z19.object({ effort: EffortSchema.nullable() });
|
|
12778
|
+
var HistoryLimitSchema = z19.coerce.number().int().min(1).max(500);
|
|
12779
|
+
var MessageIdsSchema = z19.string().transform((ids) => ids.split(",").filter((id) => id !== "")).pipe(z19.array(UlidSchema).min(1).max(MESSAGE_LOOKUP_LIMIT));
|
|
12780
|
+
var TimezoneBodySchema = z19.object({ timezone: TimeZoneSchema });
|
|
12781
|
+
var ChannelBodySchema = z19.object({
|
|
12256
12782
|
project: NameSchema.nullable().default(null),
|
|
12257
12783
|
name: NameSchema,
|
|
12258
|
-
purpose:
|
|
12784
|
+
purpose: z19.string().min(1)
|
|
12259
12785
|
});
|
|
12260
12786
|
function bearer2(c) {
|
|
12261
12787
|
const header = c.req.header("authorization") ?? "";
|
|
@@ -12263,7 +12789,7 @@ function bearer2(c) {
|
|
|
12263
12789
|
return match?.[1]?.trim() ?? null;
|
|
12264
12790
|
}
|
|
12265
12791
|
function createApp(deps) {
|
|
12266
|
-
const { board: board2, version, turns: turns2, scheduler: scheduler2, models } = deps;
|
|
12792
|
+
const { board: board2, version, turns: turns2, scheduler: scheduler2, models, quotas } = deps;
|
|
12267
12793
|
const eventStreamPingMs = deps.eventStreamPingMs ?? 15e3;
|
|
12268
12794
|
const app2 = new Hono3();
|
|
12269
12795
|
app2.get("/health", (c) => c.json({ ok: true, version }));
|
|
@@ -12333,6 +12859,17 @@ function createApp(deps) {
|
|
|
12333
12859
|
api.get("/agents/:name/history/:commit", async (c) => c.json(await board2.homeChange(c.req.param("name"), c.req.param("commit"))));
|
|
12334
12860
|
api.get("/roles", async (c) => c.json(await board2.listRoles()));
|
|
12335
12861
|
api.get("/runners", async (c) => c.json(await board2.listRunners()));
|
|
12862
|
+
api.post("/runners/:name/quota/:cli", async (c) => {
|
|
12863
|
+
if (c.get("actor").role !== USER_ROLE) {
|
|
12864
|
+
throw new BoardError("FORBIDDEN", "only the user reads a runner's quota");
|
|
12865
|
+
}
|
|
12866
|
+
const runner = NameSchema.parse(c.req.param("name"));
|
|
12867
|
+
const cli = CliKindSchema.parse(c.req.param("cli"));
|
|
12868
|
+
if (quotas === void 0) {
|
|
12869
|
+
throw new RunnerAwayError("this server asks no runner for quotas");
|
|
12870
|
+
}
|
|
12871
|
+
return c.json(await quotas.read(runner, cli));
|
|
12872
|
+
});
|
|
12336
12873
|
api.post("/runners", async (c) => {
|
|
12337
12874
|
const body = RunnerBodySchema.parse(await c.req.json());
|
|
12338
12875
|
return c.json(await board2.addRunner(c.get("actor"), body.name));
|
|
@@ -12369,6 +12906,10 @@ function createApp(deps) {
|
|
|
12369
12906
|
const window = MetricsWindowSchema.parse(c.req.query("window") ?? "7d");
|
|
12370
12907
|
return c.json(await board2.metrics(window, new Set(scheduler2?.activeSignals ?? [])));
|
|
12371
12908
|
});
|
|
12909
|
+
api.get("/tokens", async (c) => {
|
|
12910
|
+
const window = MetricsWindowSchema.parse(c.req.query("window") ?? "24h");
|
|
12911
|
+
return c.json(await board2.tokenUse(window));
|
|
12912
|
+
});
|
|
12372
12913
|
api.get("/society/knowledge", async (c) => c.json(await board2.listKnowledge(null)));
|
|
12373
12914
|
api.get("/skills", async (c) => c.json(await board2.listSocietySkills()));
|
|
12374
12915
|
api.post("/agents/:name/retire", async (c) => {
|
|
@@ -12412,7 +12953,7 @@ function createApp(deps) {
|
|
|
12412
12953
|
});
|
|
12413
12954
|
api.put("/roles/:name", async (c) => {
|
|
12414
12955
|
const charter = RoleCharterSchema.parse({
|
|
12415
|
-
...
|
|
12956
|
+
...z19.record(z19.string(), z19.unknown()).parse(await c.req.json()),
|
|
12416
12957
|
name: c.req.param("name")
|
|
12417
12958
|
});
|
|
12418
12959
|
return c.json(await board2.setRoleCharter(c.get("actor"), charter));
|
|
@@ -12451,6 +12992,15 @@ function createApp(deps) {
|
|
|
12451
12992
|
return c.json(await deps.runners.branchDiff(task.project, `task/${task.id}`, BranchPathSchema.parse(c.req.param("path"))));
|
|
12452
12993
|
});
|
|
12453
12994
|
api.get("/tasks/:id/files/:path{.+}", (c) => taskFile(c, c.req.param("path")));
|
|
12995
|
+
api.get("/images/:id", async (c) => {
|
|
12996
|
+
const { type, bytes } = await board2.readImage(UlidSchema.parse(c.req.param("id")));
|
|
12997
|
+
return c.body(new Uint8Array(bytes), 200, {
|
|
12998
|
+
"content-type": IMAGE_MEDIA_TYPES[type],
|
|
12999
|
+
"cache-control": "private, max-age=31536000, immutable",
|
|
13000
|
+
"x-content-type-options": "nosniff",
|
|
13001
|
+
"content-security-policy": "default-src 'none'; sandbox"
|
|
13002
|
+
});
|
|
13003
|
+
});
|
|
12454
13004
|
api.get("/threads", async (c) => c.json(await board2.listThreads()));
|
|
12455
13005
|
api.get("/requests", async (c) => c.json(await board2.listRequests()));
|
|
12456
13006
|
api.get("/threads/:id", async (c) => {
|
|
@@ -12762,6 +13312,35 @@ var ModelCatalog = class {
|
|
|
12762
13312
|
}
|
|
12763
13313
|
};
|
|
12764
13314
|
|
|
13315
|
+
// apps/server/dist/quotas.js
|
|
13316
|
+
var MINUTE = 6e4;
|
|
13317
|
+
var QuotaReads = class {
|
|
13318
|
+
runners;
|
|
13319
|
+
floorMs;
|
|
13320
|
+
now;
|
|
13321
|
+
kept = /* @__PURE__ */ new Map();
|
|
13322
|
+
constructor(runners2, floorMs = MINUTE, now = Date.now) {
|
|
13323
|
+
this.runners = runners2;
|
|
13324
|
+
this.floorMs = floorMs;
|
|
13325
|
+
this.now = now;
|
|
13326
|
+
}
|
|
13327
|
+
read(runner, cli) {
|
|
13328
|
+
const key = `${runner}/${cli}`;
|
|
13329
|
+
const kept = this.kept.get(key);
|
|
13330
|
+
if (kept !== void 0 && this.now() - kept.at < this.floorMs) {
|
|
13331
|
+
return kept.quota;
|
|
13332
|
+
}
|
|
13333
|
+
const quota = this.runners.quota(runner, cli);
|
|
13334
|
+
this.kept.set(key, { at: this.now(), quota });
|
|
13335
|
+
quota.catch(() => {
|
|
13336
|
+
if (this.kept.get(key)?.quota === quota) {
|
|
13337
|
+
this.kept.delete(key);
|
|
13338
|
+
}
|
|
13339
|
+
});
|
|
13340
|
+
return quota;
|
|
13341
|
+
}
|
|
13342
|
+
};
|
|
13343
|
+
|
|
12765
13344
|
// apps/server/dist/turn-hub.js
|
|
12766
13345
|
var TurnHub = class {
|
|
12767
13346
|
capacity;
|
|
@@ -12844,6 +13423,7 @@ var host = new TurnHost({
|
|
|
12844
13423
|
});
|
|
12845
13424
|
var runners = new RunnerHub({ board, host, version: VERSION, log });
|
|
12846
13425
|
board.watchTurns(() => host.inFlight());
|
|
13426
|
+
board.readImagesWith((actor, file) => runners.readTurnImage(actor, file));
|
|
12847
13427
|
var concurrency = config.concurrency ?? Number.POSITIVE_INFINITY;
|
|
12848
13428
|
var timings = parseTimings(JSON.parse(process.env["STELLARIS_TIMINGS"] ?? "{}"));
|
|
12849
13429
|
var scheduler = new Scheduler({ board, runner: runners, log, concurrency, timings });
|
|
@@ -12869,6 +13449,7 @@ var app = createApp({
|
|
|
12869
13449
|
turns,
|
|
12870
13450
|
scheduler: view,
|
|
12871
13451
|
models: new ModelCatalog(runners),
|
|
13452
|
+
quotas: new QuotaReads(runners),
|
|
12872
13453
|
runners,
|
|
12873
13454
|
webDir,
|
|
12874
13455
|
signIn: { github: config.github, log },
|