@kubitnodes/stellaris 0.3.13 → 0.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1101 -539
- package/dist/server/index.js +1262 -592
- package/package.json +2 -1
- package/web/dist/assets/{BackLink-d0xz7ZLb.js → BackLink-hLg9AYSp.js} +1 -1
- package/web/dist/assets/{ChannelForms-BIRwT9CY.js → ChannelForms-b6eKogvP.js} +1 -1
- package/web/dist/assets/ChannelView-CHvr0c9I.js +1 -0
- package/web/dist/assets/CitizenView-DlqilbFH.js +2 -0
- package/web/dist/assets/{CitizensView-Bv04iH-y.js → CitizensView-Dq3V4mpg.js} +1 -1
- package/web/dist/assets/{CliIcon-tYTwI_RQ.js → CliIcon-DxDvRRcX.js} +1 -1
- package/web/dist/assets/CronsView-CBiPaq-C.js +1 -0
- package/web/dist/assets/KnowledgeView-ndWDlRoP.js +1 -0
- package/web/dist/assets/{Markdown-BLiIulgZ.js → Markdown-BMm7unnl.js} +4 -4
- package/web/dist/assets/{MessageItem-CHrmjF7P.js → MessageItem-Dk1m5NaR.js} +1 -1
- package/web/dist/assets/{MetricsView-DG1alTgs.js → MetricsView-B31t_Q2z.js} +1 -1
- package/web/dist/assets/{NeedsYouView-Doc0fKPi.js → NeedsYouView-d8_ydJI9.js} +1 -1
- package/web/dist/assets/{Overview-ChsOgIEW.js → Overview-D-6YP-tf.js} +1 -1
- package/web/dist/assets/{Pane-MJgGXQBb.js → Pane-CODhA5zE.js} +1 -1
- package/web/dist/assets/Picker-DB-wxloY.js +1 -0
- package/web/dist/assets/ProjectView-CH-nqrAo.js +1 -0
- package/web/dist/assets/ProposalView-D-E0Hzdr.js +1 -0
- package/web/dist/assets/{ProposalsView-CSA8neXR.js → ProposalsView-Bmuq8Qnh.js} +1 -1
- package/web/dist/assets/{RunnersView-Dx21LDFu.js → RunnersView-N4iUU4FM.js} +1 -1
- package/web/dist/assets/{SocietyView-CdVW2DgG.js → SocietyView-fSkmFC3z.js} +1 -1
- package/web/dist/assets/SubjectThread-akZdGCfo.js +1 -0
- package/web/dist/assets/{TaskFileView-BqqzxJKb.js → TaskFileView-DM_JjQQx.js} +2 -2
- package/web/dist/assets/TaskView-JLLJKQ0_.js +3 -0
- package/web/dist/assets/{TasksView-Ba5fPAvy.js → TasksView-CncU8UNJ.js} +1 -1
- package/web/dist/assets/ThreadCard-8OB70j9B.js +1 -0
- package/web/dist/assets/{ThreadForms-ChTFRu-P.js → ThreadForms-RnrV7V5X.js} +1 -1
- package/web/dist/assets/{ThreadView-CnJPNS_8.js → ThreadView-DrwqggME.js} +1 -1
- package/web/dist/assets/citizen-BRSvyK2X.js +1 -0
- package/web/dist/assets/{compose-BgpUWGYE.js → compose-D7DOz6kC.js} +1 -1
- package/web/dist/assets/format-BORdp4w-.js +55 -0
- package/web/dist/assets/{governance-Di4MijNG.js → governance-WrDQawLN.js} +1 -1
- package/web/dist/assets/index-Dw96wkep.css +2 -0
- package/web/dist/assets/index-ww_EHgVF.js +23 -0
- package/web/dist/assets/{link-DAMxBItI.js → link-1v5lugnf.js} +1 -1
- package/web/dist/assets/{tasks-bwj_EdwF.js → tasks-C-9dAYQV.js} +1 -1
- package/web/dist/assets/useMatch-CyeFfbcR.js +1 -0
- package/web/dist/assets/{useStickyScroll-BILPUgGj.js → useStickyScroll-CE7dyz0Q.js} +1 -1
- package/web/dist/index.html +9 -9
- package/web/dist/assets/ChannelView-BvMlzMiN.js +0 -1
- package/web/dist/assets/CitizenView-DNQwjpIp.js +0 -2
- package/web/dist/assets/KnowledgeView-L0pdaM_2.js +0 -1
- package/web/dist/assets/ProjectView-CQTn7yDo.js +0 -1
- package/web/dist/assets/ProposalView-C92OJ_w1.js +0 -1
- package/web/dist/assets/SubjectThread-egXeFhT1.js +0 -1
- package/web/dist/assets/TaskView-rks26vuF.js +0 -3
- package/web/dist/assets/ThreadCard-D6i69_15.js +0 -1
- package/web/dist/assets/citizen-D-dxX00V.js +0 -1
- package/web/dist/assets/format-C3aawMM6.js +0 -55
- package/web/dist/assets/index-VVnGdAXc.css +0 -2
- package/web/dist/assets/index-krHkeIbX.js +0 -23
- package/web/dist/assets/useMatch-B-8IcuU6.js +0 -1
package/dist/server/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { serve } from "@hono/node-server";
|
|
|
9
9
|
import { mkdir as mkdir4, readdir as readdir3, readFile as readFile5, rename as rename3, stat as stat2 } 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 z13 } from "zod";
|
|
13
13
|
|
|
14
14
|
// packages/shared/dist/ids.js
|
|
15
15
|
import { z } from "zod";
|
|
@@ -60,7 +60,10 @@ var VERB_NAMES = [
|
|
|
60
60
|
"remove_knowledge",
|
|
61
61
|
"create_channel",
|
|
62
62
|
"archive_channel",
|
|
63
|
-
"update_dashboard"
|
|
63
|
+
"update_dashboard",
|
|
64
|
+
"create_cron",
|
|
65
|
+
"update_cron",
|
|
66
|
+
"remove_cron"
|
|
64
67
|
];
|
|
65
68
|
var VerbNameSchema = z2.enum(VERB_NAMES);
|
|
66
69
|
var CharterTriggerSchema = z2.enum(["user_post", "ops_event", "heartbeat"]);
|
|
@@ -107,7 +110,10 @@ var MEMBER_VERBS = [
|
|
|
107
110
|
"remove_knowledge",
|
|
108
111
|
"plan_task",
|
|
109
112
|
"advance_task",
|
|
110
|
-
"update_dashboard"
|
|
113
|
+
"update_dashboard",
|
|
114
|
+
"create_cron",
|
|
115
|
+
"update_cron",
|
|
116
|
+
"remove_cron"
|
|
111
117
|
];
|
|
112
118
|
var GOVERNANCE_VERBS = ["approve", "reject"];
|
|
113
119
|
var FRONT_DESK_VERBS = ["create_project"];
|
|
@@ -160,7 +166,141 @@ var SEED_ROLES = [
|
|
|
160
166
|
];
|
|
161
167
|
|
|
162
168
|
// packages/shared/dist/board.js
|
|
169
|
+
import { z as z4 } from "zod";
|
|
170
|
+
|
|
171
|
+
// packages/shared/dist/crons.js
|
|
172
|
+
import { Cron as Pattern } from "croner";
|
|
163
173
|
import { z as z3 } from "zod";
|
|
174
|
+
var CRON_FLOOR_MS = 15 * 6e4;
|
|
175
|
+
var FIRE_SLACK_MS = 6e4;
|
|
176
|
+
var FLOOR_LOOKAHEAD = 200;
|
|
177
|
+
var CRON_FAILING_RUNS = 3;
|
|
178
|
+
var DEFAULT_TIME_ZONE = "UTC";
|
|
179
|
+
function canonicalTimeZone(zone) {
|
|
180
|
+
try {
|
|
181
|
+
return new Intl.DateTimeFormat("en-US", { timeZone: zone }).resolvedOptions().timeZone;
|
|
182
|
+
} catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
var TimeZoneSchema = z3.string().trim().min(1).max(64).transform((zone, ctx) => {
|
|
187
|
+
const canonical = canonicalTimeZone(zone);
|
|
188
|
+
if (canonical === null) {
|
|
189
|
+
ctx.addIssue({
|
|
190
|
+
code: "custom",
|
|
191
|
+
message: `${zone} is not a time zone; name one as IANA does, such as Europe/Berlin, America/New_York, or UTC`
|
|
192
|
+
});
|
|
193
|
+
return z3.NEVER;
|
|
194
|
+
}
|
|
195
|
+
return canonical;
|
|
196
|
+
});
|
|
197
|
+
function pattern(expression, timeZone) {
|
|
198
|
+
return new Pattern(expression, { mode: "5-part", timezone: timeZone, paused: true });
|
|
199
|
+
}
|
|
200
|
+
function cronExpressionError(expression) {
|
|
201
|
+
try {
|
|
202
|
+
pattern(expression, DEFAULT_TIME_ZONE);
|
|
203
|
+
return null;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return error instanceof Error ? error.message : String(error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
var CronExpressionSchema = z3.string().trim().min(1).max(120).superRefine((expression, ctx) => {
|
|
209
|
+
const error = cronExpressionError(expression);
|
|
210
|
+
if (error !== null) {
|
|
211
|
+
ctx.addIssue({
|
|
212
|
+
code: "custom",
|
|
213
|
+
message: `not a five-field cron expression (minute, hour, day of month, month, day of week): ${error}`
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
var CronScheduleSchema = z3.union([
|
|
218
|
+
z3.object({ cron: CronExpressionSchema, timezone: TimeZoneSchema.optional() }),
|
|
219
|
+
z3.object({ at: IsoDateTimeSchema })
|
|
220
|
+
]);
|
|
221
|
+
var CronRunSchema = z3.object({
|
|
222
|
+
turnId: UlidSchema,
|
|
223
|
+
at: IsoDateTimeSchema,
|
|
224
|
+
outcome: z3.enum(["completed", "failed", "stopped"])
|
|
225
|
+
});
|
|
226
|
+
var CronFrontmatterSchema = z3.object({
|
|
227
|
+
id: UlidSchema,
|
|
228
|
+
title: z3.string().trim().min(1).max(120),
|
|
229
|
+
agent: NameSchema,
|
|
230
|
+
scope: NameSchema,
|
|
231
|
+
thread: UlidSchema.optional(),
|
|
232
|
+
channel: NameSchema.optional(),
|
|
233
|
+
schedule: CronScheduleSchema,
|
|
234
|
+
createdBy: NameSchema,
|
|
235
|
+
createdAt: IsoDateTimeSchema,
|
|
236
|
+
/** When the current schedule took effect: set, changed, or resumed. It fires only after this. */
|
|
237
|
+
since: IsoDateTimeSchema,
|
|
238
|
+
paused: z3.object({ at: IsoDateTimeSchema, by: NameSchema }).optional(),
|
|
239
|
+
lastFiredAt: IsoDateTimeSchema.optional(),
|
|
240
|
+
/** When a time came while its previous fire's turn was still queued or running, so it passed. */
|
|
241
|
+
lastSkippedAt: IsoDateTimeSchema.optional(),
|
|
242
|
+
/** How the turn of its latest fire ended. */
|
|
243
|
+
lastRun: CronRunSchema.optional(),
|
|
244
|
+
/** Its turns that failed in a row, up to the latest. */
|
|
245
|
+
failures: z3.number().int().nonnegative().default(0),
|
|
246
|
+
ended: z3.object({ at: IsoDateTimeSchema, by: NameSchema, reason: z3.string().min(1) }).optional()
|
|
247
|
+
});
|
|
248
|
+
function isOneTime(schedule) {
|
|
249
|
+
return "at" in schedule;
|
|
250
|
+
}
|
|
251
|
+
function scheduleZone(schedule, societyZone) {
|
|
252
|
+
return isOneTime(schedule) ? societyZone : schedule.timezone ?? societyZone;
|
|
253
|
+
}
|
|
254
|
+
function fireTimes(schedule, societyZone, after, count) {
|
|
255
|
+
if (isOneTime(schedule)) {
|
|
256
|
+
const at = new Date(schedule.at);
|
|
257
|
+
return at > after && count > 0 ? [at] : [];
|
|
258
|
+
}
|
|
259
|
+
return pattern(schedule.cron, scheduleZone(schedule, societyZone)).nextRuns(count, after);
|
|
260
|
+
}
|
|
261
|
+
function shortestGapMs(schedule, societyZone, after) {
|
|
262
|
+
const times = fireTimes(schedule, societyZone, after, FLOOR_LOOKAHEAD);
|
|
263
|
+
let shortest = null;
|
|
264
|
+
for (let index = 1; index < times.length; index += 1) {
|
|
265
|
+
const gap = (times[index]?.getTime() ?? 0) - (times[index - 1]?.getTime() ?? 0);
|
|
266
|
+
shortest = shortest === null ? gap : Math.min(shortest, gap);
|
|
267
|
+
}
|
|
268
|
+
return shortest;
|
|
269
|
+
}
|
|
270
|
+
function nextFire(cron, societyZone, floorMs = CRON_FLOOR_MS) {
|
|
271
|
+
if (cron.ended !== void 0 || cron.paused !== void 0) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
const fired = cron.lastFiredAt === void 0 ? null : Date.parse(cron.lastFiredAt);
|
|
275
|
+
const since = Date.parse(cron.since);
|
|
276
|
+
if (isOneTime(cron.schedule)) {
|
|
277
|
+
return fired !== null && fired >= since ? null : new Date(cron.schedule.at);
|
|
278
|
+
}
|
|
279
|
+
const from = Math.max(since, fired === null ? since : fired + floorMs - FIRE_SLACK_MS, cron.lastSkippedAt === void 0 ? since : Date.parse(cron.lastSkippedAt));
|
|
280
|
+
return fireTimes(cron.schedule, societyZone, new Date(from), 1)[0] ?? null;
|
|
281
|
+
}
|
|
282
|
+
function describeSchedule(schedule, societyZone) {
|
|
283
|
+
if (isOneTime(schedule)) {
|
|
284
|
+
return `once at ${schedule.at}`;
|
|
285
|
+
}
|
|
286
|
+
return schedule.timezone === void 0 ? `\`${schedule.cron}\` in the society's time zone, ${societyZone}` : `\`${schedule.cron}\` in ${schedule.timezone}`;
|
|
287
|
+
}
|
|
288
|
+
function localTime(date, timeZone) {
|
|
289
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
290
|
+
timeZone,
|
|
291
|
+
weekday: "long",
|
|
292
|
+
year: "numeric",
|
|
293
|
+
month: "2-digit",
|
|
294
|
+
day: "2-digit",
|
|
295
|
+
hour: "2-digit",
|
|
296
|
+
minute: "2-digit",
|
|
297
|
+
hourCycle: "h23"
|
|
298
|
+
}).formatToParts(date);
|
|
299
|
+
const part = (type) => parts.find((each) => each.type === type)?.value ?? "";
|
|
300
|
+
return `${part("weekday")} ${part("year")}-${part("month")}-${part("day")} ${part("hour")}:${part("minute")}`;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// packages/shared/dist/board.js
|
|
164
304
|
var ASK_CHANNEL = "asks";
|
|
165
305
|
var SOCIETY_CHANNELS = ["general", "governance", ASK_CHANNEL];
|
|
166
306
|
var PROJECT_DEFAULT_CHANNELS = ["general"];
|
|
@@ -172,30 +312,32 @@ var SOCIETY_SCOPE = "society";
|
|
|
172
312
|
function wakeScope(member, project) {
|
|
173
313
|
return project !== null && member.memberships.includes(project) ? project : SOCIETY_SCOPE;
|
|
174
314
|
}
|
|
175
|
-
var CliKindSchema =
|
|
176
|
-
var ArchivedChannelSchema =
|
|
315
|
+
var CliKindSchema = z4.enum(["claude", "codex"]);
|
|
316
|
+
var ArchivedChannelSchema = z4.object({
|
|
177
317
|
name: NameSchema,
|
|
178
318
|
at: IsoDateTimeSchema,
|
|
179
319
|
by: NameSchema,
|
|
180
|
-
reason:
|
|
320
|
+
reason: z4.string().min(1)
|
|
181
321
|
});
|
|
182
|
-
var SocietySchema =
|
|
183
|
-
name:
|
|
184
|
-
version:
|
|
322
|
+
var SocietySchema = z4.object({
|
|
323
|
+
name: z4.string().min(1),
|
|
324
|
+
version: z4.literal(1),
|
|
185
325
|
createdAt: IsoDateTimeSchema,
|
|
186
326
|
/** The channels that take posts; an archived one moves to `archivedChannels`. */
|
|
187
|
-
channels:
|
|
188
|
-
archivedChannels:
|
|
327
|
+
channels: z4.array(NameSchema),
|
|
328
|
+
archivedChannels: z4.array(ArchivedChannelSchema).default([]),
|
|
329
|
+
/** The time zone crons are read in when they name none, and every prompt tells the time in; the user's to set. */
|
|
330
|
+
timezone: TimeZoneSchema.default(DEFAULT_TIME_ZONE)
|
|
189
331
|
});
|
|
190
|
-
var CompletionEffectSchema =
|
|
332
|
+
var CompletionEffectSchema = z4.enum(["none", "merge", "ghpr"]);
|
|
191
333
|
var GITHUB_REMOTE = [
|
|
192
334
|
/^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?\/?$/,
|
|
193
335
|
/^git@github\.com:([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?$/,
|
|
194
336
|
/^ssh:\/\/git@github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?\/?$/
|
|
195
337
|
];
|
|
196
338
|
function githubRepository(remote) {
|
|
197
|
-
for (const
|
|
198
|
-
const match = remote === null ? null :
|
|
339
|
+
for (const pattern2 of GITHUB_REMOTE) {
|
|
340
|
+
const match = remote === null ? null : pattern2.exec(remote);
|
|
199
341
|
if (match?.[1] !== void 0 && match[2] !== void 0) {
|
|
200
342
|
return { owner: match[1], name: match[2] };
|
|
201
343
|
}
|
|
@@ -203,7 +345,7 @@ function githubRepository(remote) {
|
|
|
203
345
|
return null;
|
|
204
346
|
}
|
|
205
347
|
var PULL_REQUEST_URL = /^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)\/pull\/([1-9]\d*)\/?$/;
|
|
206
|
-
var PullRequestUrlSchema =
|
|
348
|
+
var PullRequestUrlSchema = z4.string().trim().regex(PULL_REQUEST_URL, "a pull request's address, https://github.com/<owner>/<repo>/pull/<number>");
|
|
207
349
|
function parsePullRequestUrl(url) {
|
|
208
350
|
const match = PULL_REQUEST_URL.exec(url.trim());
|
|
209
351
|
return match?.[1] === void 0 || match[2] === void 0 || match[3] === void 0 ? null : { owner: match[1], name: match[2], number: Number(match[3]) };
|
|
@@ -211,65 +353,65 @@ function parsePullRequestUrl(url) {
|
|
|
211
353
|
function sameRepository(a, b) {
|
|
212
354
|
return a.owner.toLowerCase() === b.owner.toLowerCase() && a.name.toLowerCase() === b.name.toLowerCase();
|
|
213
355
|
}
|
|
214
|
-
var MergeSubjectSchema =
|
|
215
|
-
var PullRequestLinkSchema =
|
|
356
|
+
var MergeSubjectSchema = z4.string().trim().min(1).max(300).regex(/^[^\n]+$/, "a merge commit's subject is one line");
|
|
357
|
+
var PullRequestLinkSchema = z4.object({
|
|
216
358
|
url: PullRequestUrlSchema,
|
|
217
|
-
number:
|
|
359
|
+
number: z4.number().int().positive(),
|
|
218
360
|
subject: MergeSubjectSchema.optional(),
|
|
219
|
-
body:
|
|
361
|
+
body: z4.string().max(2e4).optional(),
|
|
220
362
|
linkedBy: NameSchema,
|
|
221
363
|
linkedAt: IsoDateTimeSchema,
|
|
222
364
|
/**
|
|
223
365
|
* Once its `ghpr` task was abandoned, how the board's closing of it on GitHub went: `ok` false
|
|
224
366
|
* leaves it open, for the reason `detail` gives.
|
|
225
367
|
*/
|
|
226
|
-
closed:
|
|
368
|
+
closed: z4.object({ at: IsoDateTimeSchema, ok: z4.boolean(), detail: z4.string() }).optional()
|
|
227
369
|
});
|
|
228
|
-
var DefaultBranchSchema =
|
|
229
|
-
var StageIdSchema =
|
|
370
|
+
var DefaultBranchSchema = z4.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");
|
|
371
|
+
var StageIdSchema = z4.string().regex(/^s[1-9][0-9]*$/, "expected a stage id such as s1");
|
|
230
372
|
var stageFields = {
|
|
231
|
-
name:
|
|
373
|
+
name: z4.string().trim().min(1).max(120),
|
|
232
374
|
role: NameSchema.optional(),
|
|
233
375
|
agent: NameSchema.optional(),
|
|
234
|
-
gate:
|
|
376
|
+
gate: z4.boolean().default(false)
|
|
235
377
|
};
|
|
236
378
|
function oneAssignee(stage) {
|
|
237
379
|
return stage.role === void 0 || stage.agent === void 0;
|
|
238
380
|
}
|
|
239
381
|
var ONE_ASSIGNEE = { message: "a stage names a role or an agent, not both" };
|
|
240
|
-
var PlanStageSchema =
|
|
241
|
-
var PlanEditStageSchema =
|
|
242
|
-
var PlanSchema =
|
|
243
|
-
var PlanEditSchema =
|
|
244
|
-
var ProjectSchema =
|
|
382
|
+
var PlanStageSchema = z4.object(stageFields).refine(oneAssignee, ONE_ASSIGNEE);
|
|
383
|
+
var PlanEditStageSchema = z4.object({ id: StageIdSchema.optional(), ...stageFields }).refine(oneAssignee, ONE_ASSIGNEE);
|
|
384
|
+
var PlanSchema = z4.array(PlanStageSchema);
|
|
385
|
+
var PlanEditSchema = z4.array(PlanEditStageSchema);
|
|
386
|
+
var ProjectSchema = z4.object({
|
|
245
387
|
slug: NameSchema,
|
|
246
|
-
name:
|
|
247
|
-
repo:
|
|
248
|
-
defaultBranch:
|
|
388
|
+
name: z4.string().min(1),
|
|
389
|
+
repo: z4.string().nullable(),
|
|
390
|
+
defaultBranch: z4.string().min(1),
|
|
249
391
|
/** The channels that take posts; an archived one moves to `archivedChannels`. */
|
|
250
|
-
channels:
|
|
251
|
-
archivedChannels:
|
|
252
|
-
members:
|
|
253
|
-
approvers:
|
|
254
|
-
requiredCapabilities:
|
|
392
|
+
channels: z4.array(NameSchema),
|
|
393
|
+
archivedChannels: z4.array(ArchivedChannelSchema).default([]),
|
|
394
|
+
members: z4.array(NameSchema),
|
|
395
|
+
approvers: z4.array(NameSchema),
|
|
396
|
+
requiredCapabilities: z4.array(z4.string()),
|
|
255
397
|
createdAt: IsoDateTimeSchema,
|
|
256
398
|
onDone: CompletionEffectSchema.default("none"),
|
|
257
399
|
/** The runner the project lives on, which holds its repository; set on the project's first turn. */
|
|
258
400
|
runner: NameSchema.optional(),
|
|
259
401
|
/** Set once the project is archived: nobody belongs to it, and nothing more is posted, filed, or joined there. */
|
|
260
|
-
archived:
|
|
402
|
+
archived: z4.object({ at: IsoDateTimeSchema, by: NameSchema, reason: z4.string().min(1) }).optional()
|
|
261
403
|
});
|
|
262
|
-
var TaskStepSchema =
|
|
263
|
-
action:
|
|
404
|
+
var TaskStepSchema = z4.object({
|
|
405
|
+
action: z4.enum(["advanced", "returned", "abandoned", "landed", "reopened"]),
|
|
264
406
|
stage: StageIdSchema,
|
|
265
407
|
to: StageIdSchema.nullable()
|
|
266
408
|
});
|
|
267
|
-
var ProposalStepSchema =
|
|
268
|
-
var MessageStepSchema =
|
|
409
|
+
var ProposalStepSchema = z4.object({ action: z4.enum(["approved", "rejected"]) });
|
|
410
|
+
var MessageStepSchema = z4.union([TaskStepSchema, ProposalStepSchema]);
|
|
269
411
|
function isTaskStep(step) {
|
|
270
412
|
return "stage" in step;
|
|
271
413
|
}
|
|
272
|
-
var MessageFrontmatterSchema =
|
|
414
|
+
var MessageFrontmatterSchema = z4.object({
|
|
273
415
|
id: UlidSchema,
|
|
274
416
|
author: NameSchema,
|
|
275
417
|
channel: ChannelRefSchema,
|
|
@@ -281,18 +423,18 @@ var MessageFrontmatterSchema = z3.object({
|
|
|
281
423
|
/** On a post a task verb made in the task's thread, or a decision in a proposal's: the step it recorded. */
|
|
282
424
|
step: MessageStepSchema.optional(),
|
|
283
425
|
ts: IsoDateTimeSchema,
|
|
284
|
-
mentions:
|
|
426
|
+
mentions: z4.array(NameSchema)
|
|
285
427
|
});
|
|
286
|
-
var TaskStatusSchema =
|
|
287
|
-
var ThreadStateSchema =
|
|
288
|
-
var ThreadSubjectSchema =
|
|
289
|
-
kind:
|
|
428
|
+
var TaskStatusSchema = z4.enum(["open", "claimed", "done", "abandoned"]);
|
|
429
|
+
var ThreadStateSchema = z4.enum(["open", "closed"]);
|
|
430
|
+
var ThreadSubjectSchema = z4.object({
|
|
431
|
+
kind: z4.enum(["task", "proposal"]),
|
|
290
432
|
id: UlidSchema
|
|
291
433
|
});
|
|
292
|
-
var ThreadFrontmatterSchema =
|
|
434
|
+
var ThreadFrontmatterSchema = z4.object({
|
|
293
435
|
id: UlidSchema,
|
|
294
436
|
channel: ChannelRefSchema,
|
|
295
|
-
title:
|
|
437
|
+
title: z4.string().min(1).max(200),
|
|
296
438
|
subject: ThreadSubjectSchema.optional(),
|
|
297
439
|
state: ThreadStateSchema,
|
|
298
440
|
openedBy: NameSchema,
|
|
@@ -300,28 +442,28 @@ var ThreadFrontmatterSchema = z3.object({
|
|
|
300
442
|
closedBy: NameSchema.optional(),
|
|
301
443
|
closedAt: IsoDateTimeSchema.optional()
|
|
302
444
|
});
|
|
303
|
-
var StageSchema =
|
|
445
|
+
var StageSchema = z4.object({
|
|
304
446
|
id: StageIdSchema,
|
|
305
|
-
name:
|
|
447
|
+
name: z4.string().min(1),
|
|
306
448
|
role: NameSchema.optional(),
|
|
307
449
|
agent: NameSchema.optional(),
|
|
308
|
-
gate:
|
|
450
|
+
gate: z4.boolean().default(false),
|
|
309
451
|
/** Everyone who has held the stage, oldest first. */
|
|
310
|
-
holders:
|
|
452
|
+
holders: z4.array(NameSchema).default([]),
|
|
311
453
|
completedBy: NameSchema.optional(),
|
|
312
454
|
completedAt: IsoDateTimeSchema.optional()
|
|
313
455
|
});
|
|
314
|
-
var TaskReturnSchema =
|
|
456
|
+
var TaskReturnSchema = z4.object({
|
|
315
457
|
from: StageIdSchema,
|
|
316
458
|
by: NameSchema,
|
|
317
459
|
at: IsoDateTimeSchema
|
|
318
460
|
});
|
|
319
|
-
var TaskFrontmatterSchema =
|
|
461
|
+
var TaskFrontmatterSchema = z4.object({
|
|
320
462
|
id: UlidSchema,
|
|
321
463
|
project: NameSchema,
|
|
322
464
|
/** The channel of the project the task is filed in, where its thread hangs. */
|
|
323
465
|
channel: NameSchema.default("general"),
|
|
324
|
-
title:
|
|
466
|
+
title: z4.string().min(1),
|
|
325
467
|
status: TaskStatusSchema,
|
|
326
468
|
createdBy: NameSchema,
|
|
327
469
|
createdAt: IsoDateTimeSchema,
|
|
@@ -330,19 +472,19 @@ var TaskFrontmatterSchema = z3.object({
|
|
|
330
472
|
claimedBy: NameSchema.optional(),
|
|
331
473
|
leaseExpiresAt: IsoDateTimeSchema.optional(),
|
|
332
474
|
parentId: UlidSchema.optional(),
|
|
333
|
-
blockedBy:
|
|
334
|
-
requiredCapabilities:
|
|
335
|
-
stages:
|
|
475
|
+
blockedBy: z4.array(UlidSchema),
|
|
476
|
+
requiredCapabilities: z4.array(z4.string()),
|
|
477
|
+
stages: z4.array(StageSchema).min(1),
|
|
336
478
|
stage: StageIdSchema,
|
|
337
479
|
/** When the current stage last became current or lost its holder: the waiting clock. */
|
|
338
480
|
stageSince: IsoDateTimeSchema,
|
|
339
481
|
/** The highest stage number issued, so a removed stage's id is never reused. */
|
|
340
|
-
stageSeq:
|
|
482
|
+
stageSeq: z4.number().int().positive(),
|
|
341
483
|
onDone: CompletionEffectSchema.default("none"),
|
|
342
484
|
/** The pull request a `ghpr` task lands through. */
|
|
343
485
|
pullRequest: PullRequestLinkSchema.optional(),
|
|
344
486
|
/** The last stage is complete and the completion effect is running. */
|
|
345
|
-
completing:
|
|
487
|
+
completing: z4.boolean().default(false),
|
|
346
488
|
/** The latest send-back while its rework lasts: cleared once the task reaches that stage again. */
|
|
347
489
|
returned: TaskReturnSchema.optional()
|
|
348
490
|
});
|
|
@@ -366,79 +508,79 @@ function mayHoldStage(member, task) {
|
|
|
366
508
|
}
|
|
367
509
|
return !(stage.gate && task.stages.slice(0, index).some((s) => s.holders.includes(member.name)));
|
|
368
510
|
}
|
|
369
|
-
var ModelNameSchema =
|
|
370
|
-
var EffortSchema =
|
|
371
|
-
var EffortOptionSchema =
|
|
511
|
+
var ModelNameSchema = z4.string().trim().min(1).max(120).regex(/^[\w.:/[\]-]+$/, "a model name is one word of letters, digits, and . _ : / - [ ]");
|
|
512
|
+
var EffortSchema = z4.string().trim().min(1).max(32).regex(/^[a-z][a-z0-9_-]*$/, "an effort is one lowercase word, such as high");
|
|
513
|
+
var EffortOptionSchema = z4.object({
|
|
372
514
|
id: EffortSchema,
|
|
373
|
-
description:
|
|
515
|
+
description: z4.string().default("")
|
|
374
516
|
});
|
|
375
|
-
var ModelOptionSchema =
|
|
517
|
+
var ModelOptionSchema = z4.object({
|
|
376
518
|
id: ModelNameSchema,
|
|
377
|
-
name:
|
|
378
|
-
description:
|
|
379
|
-
isDefault:
|
|
380
|
-
efforts:
|
|
519
|
+
name: z4.string(),
|
|
520
|
+
description: z4.string().default(""),
|
|
521
|
+
isDefault: z4.boolean().default(false),
|
|
522
|
+
efforts: z4.array(EffortOptionSchema).default([]),
|
|
381
523
|
defaultEffort: EffortSchema.optional()
|
|
382
524
|
});
|
|
383
|
-
var RunnerModelsSchema =
|
|
525
|
+
var RunnerModelsSchema = z4.object({
|
|
384
526
|
runner: NameSchema,
|
|
385
527
|
cli: CliKindSchema,
|
|
386
|
-
models:
|
|
528
|
+
models: z4.array(ModelOptionSchema)
|
|
387
529
|
});
|
|
388
|
-
var AgentStatusSchema =
|
|
389
|
-
var AgentSchema =
|
|
530
|
+
var AgentStatusSchema = z4.enum(["active", "retired"]);
|
|
531
|
+
var AgentSchema = z4.object({
|
|
390
532
|
name: NameSchema,
|
|
391
533
|
role: NameSchema,
|
|
392
534
|
cli: CliKindSchema.nullable(),
|
|
393
|
-
model:
|
|
535
|
+
model: z4.string().optional(),
|
|
394
536
|
/** The reasoning effort set for the agent's turns; the model's own default applies otherwise. */
|
|
395
|
-
effort:
|
|
537
|
+
effort: z4.string().optional(),
|
|
396
538
|
/** The runner preferred for the agent's turns where nothing else decides; any runner with its CLI otherwise. */
|
|
397
539
|
homeRunner: NameSchema.optional(),
|
|
398
|
-
memberships:
|
|
399
|
-
subscriptions:
|
|
540
|
+
memberships: z4.array(NameSchema),
|
|
541
|
+
subscriptions: z4.array(ChannelRefSchema),
|
|
400
542
|
status: AgentStatusSchema,
|
|
401
543
|
createdAt: IsoDateTimeSchema,
|
|
402
|
-
tokenHash:
|
|
544
|
+
tokenHash: z4.string().min(1),
|
|
403
545
|
retiredAt: IsoDateTimeSchema.optional(),
|
|
404
|
-
retiredReason:
|
|
546
|
+
retiredReason: z4.string().optional()
|
|
405
547
|
});
|
|
406
|
-
var MemberSchema =
|
|
548
|
+
var MemberSchema = z4.object({
|
|
407
549
|
name: NameSchema,
|
|
408
550
|
role: NameSchema,
|
|
409
551
|
cli: CliKindSchema.nullable(),
|
|
410
552
|
homeRunner: NameSchema.optional(),
|
|
411
553
|
status: AgentStatusSchema,
|
|
412
|
-
resident:
|
|
554
|
+
resident: z4.boolean().default(false),
|
|
413
555
|
/** The model configured on the record, if any; the CLI's own default applies otherwise. */
|
|
414
|
-
model:
|
|
556
|
+
model: z4.string().optional(),
|
|
415
557
|
/** The model the CLI reported on the citizen's last turn. */
|
|
416
|
-
lastModel:
|
|
558
|
+
lastModel: z4.string().optional(),
|
|
417
559
|
/** The reasoning effort configured on the record, if any; the model's own default applies otherwise. */
|
|
418
|
-
effort:
|
|
560
|
+
effort: z4.string().optional(),
|
|
419
561
|
/** The citizen's own skills, by name. */
|
|
420
|
-
skills:
|
|
421
|
-
memberships:
|
|
422
|
-
subscriptions:
|
|
423
|
-
claimsHeld:
|
|
424
|
-
tasksDone:
|
|
562
|
+
skills: z4.array(NameSchema).default([]),
|
|
563
|
+
memberships: z4.array(NameSchema),
|
|
564
|
+
subscriptions: z4.array(ChannelRefSchema),
|
|
565
|
+
claimsHeld: z4.number().int().nonnegative().default(0),
|
|
566
|
+
tasksDone: z4.number().int().nonnegative().default(0),
|
|
425
567
|
lastTurnAt: IsoDateTimeSchema.optional(),
|
|
426
|
-
lastTurnOutcome:
|
|
568
|
+
lastTurnOutcome: z4.string().optional(),
|
|
427
569
|
createdAt: IsoDateTimeSchema,
|
|
428
570
|
retiredAt: IsoDateTimeSchema.optional()
|
|
429
571
|
});
|
|
430
|
-
var RunnerOsSchema =
|
|
431
|
-
var RunnerSchema =
|
|
572
|
+
var RunnerOsSchema = z4.enum(["linux", "windows", "darwin"]);
|
|
573
|
+
var RunnerSchema = z4.object({
|
|
432
574
|
name: NameSchema,
|
|
433
575
|
os: RunnerOsSchema,
|
|
434
|
-
clis:
|
|
435
|
-
capabilities:
|
|
576
|
+
clis: z4.array(CliKindSchema),
|
|
577
|
+
capabilities: z4.array(z4.string()),
|
|
436
578
|
/** The release the runner reported when it last registered; absent until it has. */
|
|
437
|
-
version:
|
|
438
|
-
status:
|
|
579
|
+
version: z4.string().optional(),
|
|
580
|
+
status: z4.enum(["connected", "disconnected"]),
|
|
439
581
|
lastSeen: IsoDateTimeSchema.optional()
|
|
440
582
|
});
|
|
441
|
-
var ProposalKindSchema =
|
|
583
|
+
var ProposalKindSchema = z4.enum([
|
|
442
584
|
"role",
|
|
443
585
|
"member",
|
|
444
586
|
"channel",
|
|
@@ -447,43 +589,43 @@ var ProposalKindSchema = z3.enum([
|
|
|
447
589
|
"skill",
|
|
448
590
|
"archive"
|
|
449
591
|
]);
|
|
450
|
-
var ProposalStatusSchema =
|
|
592
|
+
var ProposalStatusSchema = z4.enum([
|
|
451
593
|
"proposed",
|
|
452
594
|
"approved",
|
|
453
595
|
"rejected",
|
|
454
596
|
"provisioned",
|
|
455
597
|
"retired"
|
|
456
598
|
]);
|
|
457
|
-
var MemberProposalSchema =
|
|
599
|
+
var MemberProposalSchema = z4.object({
|
|
458
600
|
name: NameSchema,
|
|
459
601
|
role: NameSchema,
|
|
460
602
|
cli: CliKindSchema,
|
|
461
|
-
model:
|
|
603
|
+
model: z4.string().optional(),
|
|
462
604
|
homeRunner: NameSchema.optional(),
|
|
463
|
-
memberships:
|
|
464
|
-
subscriptions:
|
|
465
|
-
seedInstructions:
|
|
605
|
+
memberships: z4.array(NameSchema).default([]),
|
|
606
|
+
subscriptions: z4.array(ChannelRefSchema).default([]),
|
|
607
|
+
seedInstructions: z4.string().optional()
|
|
466
608
|
});
|
|
467
|
-
var ChannelProposalSchema =
|
|
609
|
+
var ChannelProposalSchema = z4.object({
|
|
468
610
|
project: NameSchema.nullable().default(null),
|
|
469
611
|
name: NameSchema,
|
|
470
|
-
purpose:
|
|
612
|
+
purpose: z4.string().min(1)
|
|
471
613
|
});
|
|
472
|
-
var ReallocationProposalSchema =
|
|
473
|
-
description:
|
|
614
|
+
var ReallocationProposalSchema = z4.object({
|
|
615
|
+
description: z4.string().min(1)
|
|
474
616
|
});
|
|
475
|
-
var RetirementProposalSchema =
|
|
617
|
+
var RetirementProposalSchema = z4.object({
|
|
476
618
|
agent: NameSchema,
|
|
477
|
-
reason:
|
|
619
|
+
reason: z4.string().min(1)
|
|
478
620
|
});
|
|
479
|
-
var ArchiveProposalSchema =
|
|
621
|
+
var ArchiveProposalSchema = z4.object({
|
|
480
622
|
project: NameSchema,
|
|
481
|
-
reason:
|
|
623
|
+
reason: z4.string().min(1)
|
|
482
624
|
});
|
|
483
|
-
var SkillProposalSchema =
|
|
625
|
+
var SkillProposalSchema = z4.object({
|
|
484
626
|
name: NameSchema,
|
|
485
|
-
summary:
|
|
486
|
-
body:
|
|
627
|
+
summary: z4.string().min(1).max(200),
|
|
628
|
+
body: z4.string().min(1)
|
|
487
629
|
});
|
|
488
630
|
var ProposalCharterSchemas = {
|
|
489
631
|
role: RoleCharterSchema,
|
|
@@ -555,17 +697,17 @@ function describeCharter(kind, charter) {
|
|
|
555
697
|
}
|
|
556
698
|
return `${kind} ${JSON.stringify(charter)}`;
|
|
557
699
|
}
|
|
558
|
-
var SkillSchema =
|
|
700
|
+
var SkillSchema = z4.object({
|
|
559
701
|
name: NameSchema,
|
|
560
|
-
summary:
|
|
561
|
-
scope:
|
|
562
|
-
path:
|
|
702
|
+
summary: z4.string().default(""),
|
|
703
|
+
scope: z4.enum(["own", "society"]),
|
|
704
|
+
path: z4.string().min(1)
|
|
563
705
|
});
|
|
564
|
-
var HomeConflictSchema =
|
|
706
|
+
var HomeConflictSchema = z4.object({
|
|
565
707
|
/** The copy, relative to the home. */
|
|
566
|
-
path:
|
|
708
|
+
path: z4.string().min(1),
|
|
567
709
|
/** The file beside it, whose version the board kept. */
|
|
568
|
-
file:
|
|
710
|
+
file: z4.string().min(1),
|
|
569
711
|
/** When the copy reached the board. */
|
|
570
712
|
since: IsoDateTimeSchema
|
|
571
713
|
});
|
|
@@ -573,60 +715,60 @@ var CONFLICT_COPY = /^(.+)\.conflict-[^/.]+$/;
|
|
|
573
715
|
function conflictCopyOf(copy) {
|
|
574
716
|
return CONFLICT_COPY.exec(copy)?.[1] ?? null;
|
|
575
717
|
}
|
|
576
|
-
var CommitIdSchema =
|
|
577
|
-
var HomeFileChangeSchema =
|
|
578
|
-
path:
|
|
579
|
-
status:
|
|
718
|
+
var CommitIdSchema = z4.string().regex(/^[0-9a-f]{40}([0-9a-f]{24})?$/);
|
|
719
|
+
var HomeFileChangeSchema = z4.object({
|
|
720
|
+
path: z4.string().min(1),
|
|
721
|
+
status: z4.enum(["added", "modified", "deleted"]),
|
|
580
722
|
/** Lines added and removed, or null for a binary file. */
|
|
581
|
-
added:
|
|
582
|
-
removed:
|
|
723
|
+
added: z4.number().int().nonnegative().nullable(),
|
|
724
|
+
removed: z4.number().int().nonnegative().nullable()
|
|
583
725
|
});
|
|
584
|
-
var HomeChangeSchema =
|
|
726
|
+
var HomeChangeSchema = z4.object({
|
|
585
727
|
commit: CommitIdSchema,
|
|
586
728
|
at: IsoDateTimeSchema,
|
|
587
|
-
kind:
|
|
588
|
-
author:
|
|
729
|
+
kind: z4.enum(["turn", "board", "merge"]),
|
|
730
|
+
author: z4.string(),
|
|
589
731
|
/** The turn that made it, from the runner's commit message. */
|
|
590
732
|
turnId: UlidSchema.optional(),
|
|
591
|
-
subject:
|
|
592
|
-
files:
|
|
733
|
+
subject: z4.string(),
|
|
734
|
+
files: z4.array(HomeFileChangeSchema)
|
|
593
735
|
});
|
|
594
|
-
var HomeHistorySchema =
|
|
595
|
-
changes:
|
|
736
|
+
var HomeHistorySchema = z4.object({
|
|
737
|
+
changes: z4.array(HomeChangeSchema),
|
|
596
738
|
/** Whether older commits remain past the ones read. */
|
|
597
|
-
more:
|
|
739
|
+
more: z4.boolean()
|
|
598
740
|
});
|
|
599
741
|
var HomeFileDiffSchema = HomeFileChangeSchema.pick({ path: true, status: true }).extend({
|
|
600
|
-
patch:
|
|
742
|
+
patch: z4.string().nullable(),
|
|
601
743
|
/** The patch was cut at its size limit. */
|
|
602
|
-
truncated:
|
|
744
|
+
truncated: z4.boolean()
|
|
603
745
|
});
|
|
604
|
-
var KnowledgeSchema =
|
|
746
|
+
var KnowledgeSchema = z4.object({
|
|
605
747
|
topic: NameSchema,
|
|
606
748
|
project: NameSchema.nullable(),
|
|
607
749
|
updatedBy: NameSchema,
|
|
608
750
|
updatedAt: IsoDateTimeSchema
|
|
609
751
|
});
|
|
610
|
-
var RemovedKnowledgeSchema =
|
|
752
|
+
var RemovedKnowledgeSchema = z4.object({
|
|
611
753
|
topic: NameSchema,
|
|
612
754
|
project: NameSchema.nullable(),
|
|
613
755
|
removedBy: NameSchema,
|
|
614
756
|
removedAt: IsoDateTimeSchema
|
|
615
757
|
});
|
|
616
|
-
var DashboardSchema =
|
|
758
|
+
var DashboardSchema = z4.object({
|
|
617
759
|
project: NameSchema,
|
|
618
|
-
revision:
|
|
760
|
+
revision: z4.number().int().min(0),
|
|
619
761
|
/** Absent until someone updates the dashboard the project began with. */
|
|
620
762
|
updatedBy: NameSchema.optional(),
|
|
621
763
|
updatedAt: IsoDateTimeSchema.optional(),
|
|
622
|
-
body:
|
|
764
|
+
body: z4.string()
|
|
623
765
|
});
|
|
624
|
-
var ProposalOriginSchema =
|
|
766
|
+
var ProposalOriginSchema = z4.object({
|
|
625
767
|
scope: NameSchema,
|
|
626
768
|
thread: UlidSchema.optional(),
|
|
627
769
|
channel: NameSchema.optional()
|
|
628
770
|
});
|
|
629
|
-
var ProposalFrontmatterSchema =
|
|
771
|
+
var ProposalFrontmatterSchema = z4.object({
|
|
630
772
|
id: UlidSchema,
|
|
631
773
|
kind: ProposalKindSchema,
|
|
632
774
|
proposedBy: NameSchema,
|
|
@@ -636,140 +778,175 @@ var ProposalFrontmatterSchema = z3.object({
|
|
|
636
778
|
filedFrom: ProposalOriginSchema.optional(),
|
|
637
779
|
decidedBy: NameSchema.optional(),
|
|
638
780
|
decidedAt: IsoDateTimeSchema.optional(),
|
|
639
|
-
reason:
|
|
640
|
-
charter:
|
|
781
|
+
reason: z4.string().optional(),
|
|
782
|
+
charter: z4.record(z4.string(), z4.unknown()),
|
|
641
783
|
/** What approval created, for kinds the board provisions: the agent, channel, role, or retirement. */
|
|
642
784
|
provisionedAt: IsoDateTimeSchema.optional(),
|
|
643
|
-
provision:
|
|
785
|
+
provision: z4.record(z4.string(), z4.unknown()).optional()
|
|
644
786
|
});
|
|
645
|
-
var DecisionSchema =
|
|
787
|
+
var DecisionSchema = z4.object({
|
|
646
788
|
id: UlidSchema,
|
|
647
789
|
proposalId: UlidSchema,
|
|
648
790
|
decidedBy: NameSchema,
|
|
649
|
-
outcome:
|
|
650
|
-
reason:
|
|
791
|
+
outcome: z4.enum(["approved", "rejected"]),
|
|
792
|
+
reason: z4.string().optional(),
|
|
651
793
|
ts: IsoDateTimeSchema
|
|
652
794
|
});
|
|
653
795
|
|
|
654
796
|
// packages/shared/dist/verbs.js
|
|
655
|
-
import { z as
|
|
797
|
+
import { z as z5 } from "zod";
|
|
798
|
+
var MomentSchema = z5.iso.datetime({ offset: true });
|
|
799
|
+
var CronTitleSchema = z5.string().trim().min(1).max(120);
|
|
800
|
+
var CronNoteSchema = z5.string().trim().min(1).max(4e3);
|
|
656
801
|
var VerbInputs = {
|
|
657
|
-
post_message:
|
|
802
|
+
post_message: z5.object({
|
|
658
803
|
/** Required outside a thread; in one it may be left out, and must otherwise be the thread's. */
|
|
659
804
|
channel: ChannelRefSchema.optional(),
|
|
660
|
-
body:
|
|
805
|
+
body: z5.string().min(1),
|
|
661
806
|
thread_id: UlidSchema.optional()
|
|
662
807
|
}),
|
|
663
|
-
read_inbox:
|
|
808
|
+
read_inbox: z5.object({
|
|
664
809
|
since_cursor: UlidSchema.nullable().optional(),
|
|
665
|
-
limit:
|
|
666
|
-
advance:
|
|
810
|
+
limit: z5.number().int().positive().max(200).default(50),
|
|
811
|
+
advance: z5.boolean().default(true)
|
|
667
812
|
}),
|
|
668
|
-
search:
|
|
669
|
-
query:
|
|
813
|
+
search: z5.object({
|
|
814
|
+
query: z5.string().min(1),
|
|
670
815
|
project: NameSchema.optional(),
|
|
671
816
|
channel: ChannelRefSchema.optional(),
|
|
672
|
-
limit:
|
|
817
|
+
limit: z5.number().int().positive().max(100).default(20)
|
|
673
818
|
}),
|
|
674
819
|
/** On a task or a proposal, whose id the thread takes, or on a channel with a title. */
|
|
675
|
-
open_thread:
|
|
820
|
+
open_thread: z5.object({
|
|
676
821
|
task_id: UlidSchema.optional(),
|
|
677
822
|
proposal_id: UlidSchema.optional(),
|
|
678
823
|
channel: ChannelRefSchema.optional(),
|
|
679
|
-
title:
|
|
824
|
+
title: z5.string().min(1).max(200).optional()
|
|
680
825
|
}),
|
|
681
|
-
close_thread:
|
|
682
|
-
create_task:
|
|
826
|
+
close_thread: z5.object({ thread_id: UlidSchema, summary: z5.string().min(1) }),
|
|
827
|
+
create_task: z5.object({
|
|
683
828
|
project: NameSchema,
|
|
684
|
-
title:
|
|
685
|
-
body:
|
|
829
|
+
title: z5.string().min(1).max(200),
|
|
830
|
+
body: z5.string().default(""),
|
|
686
831
|
parent_id: UlidSchema.optional(),
|
|
687
|
-
required_capabilities:
|
|
832
|
+
required_capabilities: z5.array(z5.string()).default([]),
|
|
688
833
|
/** The plan; without it the task gets one stage, work, that anyone in the project may take. */
|
|
689
|
-
stages:
|
|
834
|
+
stages: z5.array(PlanStageSchema).min(1).optional(),
|
|
690
835
|
/** A channel of the project the task belongs to, such as a release's; its thread opens there. */
|
|
691
836
|
channel: NameSchema.default("general")
|
|
692
837
|
}),
|
|
693
|
-
claim_task:
|
|
694
|
-
release_task:
|
|
695
|
-
update_task:
|
|
838
|
+
claim_task: z5.object({ task_id: UlidSchema }),
|
|
839
|
+
release_task: z5.object({ task_id: UlidSchema }),
|
|
840
|
+
update_task: z5.object({
|
|
696
841
|
task_id: UlidSchema,
|
|
697
842
|
/** Move the task back to this earlier stage. */
|
|
698
843
|
stage: StageIdSchema.optional(),
|
|
699
|
-
status:
|
|
844
|
+
status: z5.enum(["abandoned"]).optional(),
|
|
700
845
|
/** Posted to the task's thread as the caller, marked with the step when the task moved. */
|
|
701
|
-
note:
|
|
702
|
-
blocked_by:
|
|
846
|
+
note: z5.string().min(1).optional(),
|
|
847
|
+
blocked_by: z5.array(UlidSchema).optional(),
|
|
703
848
|
/**
|
|
704
849
|
* The pull request a `ghpr` task lands through, on its project's repository, with the merge
|
|
705
850
|
* commit's subject and body; null unlinks it.
|
|
706
851
|
*/
|
|
707
|
-
pull_request:
|
|
852
|
+
pull_request: z5.object({
|
|
708
853
|
url: PullRequestUrlSchema,
|
|
709
854
|
merge_subject: MergeSubjectSchema.optional(),
|
|
710
|
-
merge_body:
|
|
855
|
+
merge_body: z5.string().max(2e4).optional()
|
|
711
856
|
}).nullable().optional()
|
|
712
857
|
}),
|
|
713
|
-
get_task:
|
|
714
|
-
subscribe:
|
|
715
|
-
unsubscribe:
|
|
716
|
-
propose:
|
|
858
|
+
get_task: z5.object({ task_id: UlidSchema }),
|
|
859
|
+
subscribe: z5.object({ channel: ChannelRefSchema }),
|
|
860
|
+
unsubscribe: z5.object({ channel: ChannelRefSchema }),
|
|
861
|
+
propose: z5.object({
|
|
717
862
|
kind: ProposalKindSchema,
|
|
718
|
-
charter:
|
|
719
|
-
rationale:
|
|
863
|
+
charter: z5.record(z5.string(), z5.unknown()),
|
|
864
|
+
rationale: z5.string().default("")
|
|
720
865
|
}),
|
|
721
|
-
approve:
|
|
722
|
-
reject:
|
|
723
|
-
create_project:
|
|
866
|
+
approve: z5.object({ proposal_id: UlidSchema, reason: z5.string().optional() }),
|
|
867
|
+
reject: z5.object({ proposal_id: UlidSchema, reason: z5.string().min(1) }),
|
|
868
|
+
create_project: z5.object({
|
|
724
869
|
slug: NameSchema,
|
|
725
|
-
name:
|
|
726
|
-
repo:
|
|
870
|
+
name: z5.string().min(1).optional(),
|
|
871
|
+
repo: z5.string().min(1).nullable().default(null),
|
|
727
872
|
default_branch: DefaultBranchSchema.default("main"),
|
|
728
873
|
on_done: CompletionEffectSchema.optional()
|
|
729
874
|
}),
|
|
730
|
-
join_project:
|
|
731
|
-
leave_project:
|
|
732
|
-
write_knowledge:
|
|
875
|
+
join_project: z5.object({ project: NameSchema, agent: NameSchema.optional() }),
|
|
876
|
+
leave_project: z5.object({ project: NameSchema, agent: NameSchema.optional() }),
|
|
877
|
+
write_knowledge: z5.object({
|
|
733
878
|
/** A project you belong to, or null for society knowledge, which the steward and the user curate. */
|
|
734
879
|
project: NameSchema.nullable().default(null),
|
|
735
880
|
topic: NameSchema,
|
|
736
|
-
body:
|
|
881
|
+
body: z5.string().min(1)
|
|
737
882
|
}),
|
|
738
|
-
remove_knowledge:
|
|
883
|
+
remove_knowledge: z5.object({
|
|
739
884
|
/** As for write_knowledge: a project you belong to, or null for society knowledge. */
|
|
740
885
|
project: NameSchema.nullable().default(null),
|
|
741
886
|
topic: NameSchema
|
|
742
887
|
}),
|
|
743
|
-
plan_task:
|
|
888
|
+
plan_task: z5.object({
|
|
744
889
|
task_id: UlidSchema,
|
|
745
|
-
stages:
|
|
890
|
+
stages: z5.array(PlanEditStageSchema),
|
|
746
891
|
on_done: CompletionEffectSchema.optional()
|
|
747
892
|
}),
|
|
748
893
|
/** The note is posted to the task's thread as the caller, marked with the step. */
|
|
749
|
-
advance_task:
|
|
750
|
-
configure_project:
|
|
894
|
+
advance_task: z5.object({ task_id: UlidSchema, note: z5.string().min(1).optional() }),
|
|
895
|
+
configure_project: z5.object({
|
|
751
896
|
project: NameSchema,
|
|
752
897
|
on_done: CompletionEffectSchema.optional(),
|
|
753
898
|
/** A new display name; the slug, which everything refers to the project by, never changes. */
|
|
754
|
-
name:
|
|
899
|
+
name: z5.string().trim().min(1).max(120).optional(),
|
|
755
900
|
/** The branch tasks start from and land on from now; tasks in play keep their branches. */
|
|
756
901
|
default_branch: DefaultBranchSchema.optional()
|
|
757
902
|
}),
|
|
758
|
-
archive_project:
|
|
759
|
-
create_channel:
|
|
903
|
+
archive_project: z5.object({ project: NameSchema, reason: z5.string().min(1) }),
|
|
904
|
+
create_channel: z5.object({
|
|
760
905
|
/** A project, or null for a channel of the society. */
|
|
761
906
|
project: NameSchema.nullable().default(null),
|
|
762
907
|
name: NameSchema,
|
|
763
|
-
purpose:
|
|
908
|
+
purpose: z5.string().min(1)
|
|
764
909
|
}),
|
|
765
|
-
archive_channel:
|
|
766
|
-
update_dashboard:
|
|
910
|
+
archive_channel: z5.object({ channel: ChannelRefSchema, reason: z5.string().min(1) }),
|
|
911
|
+
update_dashboard: z5.object({
|
|
767
912
|
project: NameSchema,
|
|
768
913
|
/** The whole dashboard as markdown; the board keeps its frontmatter. */
|
|
769
|
-
body:
|
|
914
|
+
body: z5.string().min(1),
|
|
770
915
|
/** The `revision` in the frontmatter of the dashboard you read, 0 when it names none. */
|
|
771
|
-
revision:
|
|
772
|
-
})
|
|
916
|
+
revision: z5.number().int().min(0)
|
|
917
|
+
}),
|
|
918
|
+
create_cron: z5.object({
|
|
919
|
+
title: CronTitleSchema,
|
|
920
|
+
/** What to do when it fires; quoted in the prompt of every turn it starts. */
|
|
921
|
+
note: CronNoteSchema,
|
|
922
|
+
/** Minute, hour, day of month, month, day of week, such as `0 9 * * 1-5`; or `at` instead. */
|
|
923
|
+
cron: CronExpressionSchema.optional(),
|
|
924
|
+
/** The IANA time zone `cron` is read in; the society's when left out. */
|
|
925
|
+
timezone: TimeZoneSchema.optional(),
|
|
926
|
+
/** One time to fire, instead of `cron`; the cron ends once it has fired. */
|
|
927
|
+
at: MomentSchema.optional(),
|
|
928
|
+
/** The citizen it wakes: yourself when left out. */
|
|
929
|
+
agent: NameSchema.optional(),
|
|
930
|
+
/** Where it fires: a project the citizen belongs to, or `society`; this turn's scope when left out. */
|
|
931
|
+
project: NameSchema.optional(),
|
|
932
|
+
/** An open thread there, whose conversation it fires in. */
|
|
933
|
+
thread_id: UlidSchema.optional(),
|
|
934
|
+
/** An open channel of that place other than general, whose conversation it fires in. */
|
|
935
|
+
channel: NameSchema.optional()
|
|
936
|
+
}),
|
|
937
|
+
update_cron: z5.object({
|
|
938
|
+
cron_id: UlidSchema,
|
|
939
|
+
/** Pause it, or resume it, which starts its schedule afresh from now. */
|
|
940
|
+
paused: z5.boolean().optional(),
|
|
941
|
+
title: CronTitleSchema.optional(),
|
|
942
|
+
note: CronNoteSchema.optional(),
|
|
943
|
+
/** A new expression, or `at` for one time; either starts the schedule afresh. */
|
|
944
|
+
cron: CronExpressionSchema.optional(),
|
|
945
|
+
/** The zone `cron` is read in; null for the society's. */
|
|
946
|
+
timezone: TimeZoneSchema.nullable().optional(),
|
|
947
|
+
at: MomentSchema.optional()
|
|
948
|
+
}),
|
|
949
|
+
remove_cron: z5.object({ cron_id: UlidSchema, reason: z5.string().trim().min(1) })
|
|
773
950
|
};
|
|
774
951
|
var VERB_DESCRIPTIONS = {
|
|
775
952
|
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.",
|
|
@@ -798,11 +975,14 @@ var VERB_DESCRIPTIONS = {
|
|
|
798
975
|
archive_project: "Archive a project whose work is finished or has moved: its members leave, its open threads close, and nothing more is posted, filed, or joined there, while its files and history stay. Refused while a task there is in play.",
|
|
799
976
|
create_channel: "Open a channel in a project, or with project null in the society, for a workstream such as a release, whose tasks are filed there. You follow it; a notice in the place's general announces it, and members subscribe when it concerns them. User, steward, concierge.",
|
|
800
977
|
archive_channel: "Archive a channel whose workstream is done: its open threads close, its followers stop following it, and nothing more is posted or filed there, while its history stays readable. Refused while a task filed there is in play, and for the channels the board itself uses. User, steward, concierge.",
|
|
801
|
-
update_dashboard: "Replace the dashboard of a project you are a member of, the status page the user reads on its view, with a whole markdown body. Pass the revision from the frontmatter of the dashboard you read; if it was updated since, the call is refused with the current text to merge yours into. dashboard.md in the board projection is a copy, so editing it changes nothing."
|
|
978
|
+
update_dashboard: "Replace the dashboard of a project you are a member of, the status page the user reads on its view, with a whole markdown body. Pass the revision from the frontmatter of the dashboard you read; if it was updated since, the call is refused with the current text to merge yours into. dashboard.md in the board projection is a copy, so editing it changes nothing.",
|
|
979
|
+
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).",
|
|
980
|
+
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.",
|
|
981
|
+
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."
|
|
802
982
|
};
|
|
803
983
|
|
|
804
984
|
// packages/shared/dist/events.js
|
|
805
|
-
import { z as
|
|
985
|
+
import { z as z6 } from "zod";
|
|
806
986
|
var BOARD_EVENT_TYPES = [
|
|
807
987
|
"society.initialized",
|
|
808
988
|
"project.added",
|
|
@@ -857,30 +1037,36 @@ var BOARD_EVENT_TYPES = [
|
|
|
857
1037
|
"merge.failed",
|
|
858
1038
|
"pull_request.closed",
|
|
859
1039
|
"pull_request.close_failed",
|
|
860
|
-
"workspace.leftovers"
|
|
1040
|
+
"workspace.leftovers",
|
|
1041
|
+
"society.configured",
|
|
1042
|
+
"cron.created",
|
|
1043
|
+
"cron.updated",
|
|
1044
|
+
"cron.fired",
|
|
1045
|
+
"cron.skipped",
|
|
1046
|
+
"cron.ended"
|
|
861
1047
|
];
|
|
862
|
-
var BoardEventTypeSchema =
|
|
863
|
-
var BoardEventSchema =
|
|
1048
|
+
var BoardEventTypeSchema = z6.enum(BOARD_EVENT_TYPES);
|
|
1049
|
+
var BoardEventSchema = z6.object({
|
|
864
1050
|
id: UlidSchema,
|
|
865
1051
|
ts: IsoDateTimeSchema,
|
|
866
1052
|
type: BoardEventTypeSchema,
|
|
867
1053
|
actor: NameSchema,
|
|
868
|
-
payload:
|
|
1054
|
+
payload: z6.record(z6.string(), z6.unknown())
|
|
869
1055
|
});
|
|
870
|
-
var UsageSchema =
|
|
871
|
-
inputTokens:
|
|
872
|
-
outputTokens:
|
|
873
|
-
cacheReadTokens:
|
|
874
|
-
cacheWriteTokens:
|
|
1056
|
+
var UsageSchema = z6.object({
|
|
1057
|
+
inputTokens: z6.number().int().nonnegative(),
|
|
1058
|
+
outputTokens: z6.number().int().nonnegative(),
|
|
1059
|
+
cacheReadTokens: z6.number().int().nonnegative().default(0),
|
|
1060
|
+
cacheWriteTokens: z6.number().int().nonnegative().default(0)
|
|
875
1061
|
});
|
|
876
|
-
var TurnStatusSchema =
|
|
877
|
-
summary:
|
|
878
|
-
claimsHeld:
|
|
879
|
-
blockedOn:
|
|
880
|
-
needsUserDecision:
|
|
881
|
-
memoryUpdated:
|
|
1062
|
+
var TurnStatusSchema = z6.object({
|
|
1063
|
+
summary: z6.string().min(1),
|
|
1064
|
+
claimsHeld: z6.array(UlidSchema).default([]),
|
|
1065
|
+
blockedOn: z6.array(z6.string()).default([]),
|
|
1066
|
+
needsUserDecision: z6.boolean().default(false),
|
|
1067
|
+
memoryUpdated: z6.boolean().default(false)
|
|
882
1068
|
});
|
|
883
|
-
var TurnExitReasonSchema =
|
|
1069
|
+
var TurnExitReasonSchema = z6.enum([
|
|
884
1070
|
"completed",
|
|
885
1071
|
"timeout",
|
|
886
1072
|
"error",
|
|
@@ -888,50 +1074,50 @@ var TurnExitReasonSchema = z5.enum([
|
|
|
888
1074
|
"blocked",
|
|
889
1075
|
"stopped"
|
|
890
1076
|
]);
|
|
891
|
-
var AgentEventSchema =
|
|
892
|
-
|
|
893
|
-
type:
|
|
1077
|
+
var AgentEventSchema = z6.discriminatedUnion("type", [
|
|
1078
|
+
z6.object({
|
|
1079
|
+
type: z6.literal("turn_started"),
|
|
894
1080
|
agent: NameSchema,
|
|
895
|
-
session:
|
|
1081
|
+
session: z6.string(),
|
|
896
1082
|
runner: NameSchema,
|
|
897
1083
|
/** The model the CLI reports running, when it reports one. */
|
|
898
|
-
model:
|
|
1084
|
+
model: z6.string().optional()
|
|
899
1085
|
}),
|
|
900
1086
|
/**
|
|
901
1087
|
* The CLI took a steer into the running turn. Adapters name the steer; the server adds the
|
|
902
1088
|
* messages it delivered and the text the CLI was given.
|
|
903
1089
|
*/
|
|
904
|
-
|
|
905
|
-
type:
|
|
906
|
-
steer:
|
|
907
|
-
messages:
|
|
908
|
-
text:
|
|
1090
|
+
z6.object({
|
|
1091
|
+
type: z6.literal("steered"),
|
|
1092
|
+
steer: z6.string().min(1),
|
|
1093
|
+
messages: z6.array(UlidSchema).optional(),
|
|
1094
|
+
text: z6.string().optional()
|
|
909
1095
|
}),
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
type:
|
|
914
|
-
name:
|
|
915
|
-
ok:
|
|
1096
|
+
z6.object({ type: z6.literal("text"), delta: z6.string() }),
|
|
1097
|
+
z6.object({ type: z6.literal("tool_call"), name: z6.string(), input: z6.unknown() }),
|
|
1098
|
+
z6.object({
|
|
1099
|
+
type: z6.literal("tool_result"),
|
|
1100
|
+
name: z6.string(),
|
|
1101
|
+
ok: z6.boolean(),
|
|
916
1102
|
/** What the tool returned, cut to `OUTPUT_LIMIT` characters by `capOutput`. */
|
|
917
|
-
output:
|
|
1103
|
+
output: z6.string().optional()
|
|
918
1104
|
}),
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
type:
|
|
1105
|
+
z6.object({ type: z6.literal("approval_requested"), kind: z6.string(), detail: z6.unknown() }),
|
|
1106
|
+
z6.object({
|
|
1107
|
+
type: z6.literal("turn_completed"),
|
|
922
1108
|
usage: UsageSchema,
|
|
923
|
-
costUsd:
|
|
1109
|
+
costUsd: z6.number().nonnegative(),
|
|
924
1110
|
status: TurnStatusSchema.nullable(),
|
|
925
1111
|
exitReason: TurnExitReasonSchema
|
|
926
1112
|
}),
|
|
927
|
-
|
|
1113
|
+
z6.object({ type: z6.literal("error"), message: z6.string() })
|
|
928
1114
|
]);
|
|
929
|
-
var TranscriptEntrySchema =
|
|
1115
|
+
var TranscriptEntrySchema = z6.object({
|
|
930
1116
|
ts: IsoDateTimeSchema,
|
|
931
1117
|
event: AgentEventSchema
|
|
932
1118
|
});
|
|
933
|
-
var LiveTurnEventSchema =
|
|
934
|
-
seq:
|
|
1119
|
+
var LiveTurnEventSchema = z6.object({
|
|
1120
|
+
seq: z6.number().int().positive(),
|
|
935
1121
|
ts: IsoDateTimeSchema,
|
|
936
1122
|
agent: NameSchema,
|
|
937
1123
|
project: NameSchema,
|
|
@@ -943,10 +1129,10 @@ var LiveTurnEventSchema = z5.object({
|
|
|
943
1129
|
});
|
|
944
1130
|
|
|
945
1131
|
// packages/shared/dist/turns.js
|
|
946
|
-
import { z as
|
|
1132
|
+
import { z as z8 } from "zod";
|
|
947
1133
|
|
|
948
1134
|
// packages/shared/dist/runner.js
|
|
949
|
-
import { z as
|
|
1135
|
+
import { z as z7 } from "zod";
|
|
950
1136
|
var RUNNER_PROTOCOL = 1;
|
|
951
1137
|
var PATH_TOKENS = {
|
|
952
1138
|
home: "{{stellaris:home}}",
|
|
@@ -957,67 +1143,67 @@ var SERVER_TOKEN = "{{stellaris:server}}";
|
|
|
957
1143
|
var WORKSPACE_TOKEN = "{{stellaris:workspace}}";
|
|
958
1144
|
var HOME_SCRATCH = "scratch";
|
|
959
1145
|
var HOME_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
960
|
-
var RunnerHelloSchema =
|
|
961
|
-
protocol:
|
|
962
|
-
version:
|
|
1146
|
+
var RunnerHelloSchema = z7.object({
|
|
1147
|
+
protocol: z7.number().int().positive(),
|
|
1148
|
+
version: z7.string().min(1),
|
|
963
1149
|
os: RunnerOsSchema,
|
|
964
|
-
clis:
|
|
1150
|
+
clis: z7.array(CliKindSchema),
|
|
965
1151
|
/** The CLIs whose adapter can keep a session warm between turns. */
|
|
966
|
-
residentClis:
|
|
1152
|
+
residentClis: z7.array(CliKindSchema).default([]),
|
|
967
1153
|
/** The CLIs whose adapter can take input into a running turn; the server steers only these. */
|
|
968
|
-
steerableClis:
|
|
1154
|
+
steerableClis: z7.array(CliKindSchema).default([]),
|
|
969
1155
|
/** The CLIs whose adapter can stop a running turn; the server stops only these. */
|
|
970
|
-
stoppableClis:
|
|
1156
|
+
stoppableClis: z7.array(CliKindSchema).default([]),
|
|
971
1157
|
/** Whether the runner answers file reads on its projects' branches; one from before never does. */
|
|
972
|
-
branchReads:
|
|
1158
|
+
branchReads: z7.boolean().default(false),
|
|
973
1159
|
/** Whether the runner lists what a branch changed; one from before never does. */
|
|
974
|
-
branchChanges:
|
|
975
|
-
capabilities:
|
|
1160
|
+
branchChanges: z7.boolean().default(false),
|
|
1161
|
+
capabilities: z7.array(z7.string()).default([]),
|
|
976
1162
|
/** Turns the runner runs at once, or null for no limit. */
|
|
977
|
-
slots:
|
|
1163
|
+
slots: z7.number().int().positive().nullable(),
|
|
978
1164
|
/** Turns the runner is still running, when it registers again; the server fails the rest of its turns. */
|
|
979
|
-
turns:
|
|
1165
|
+
turns: z7.array(UlidSchema).default([])
|
|
980
1166
|
});
|
|
981
|
-
var RunnerWelcomeSchema =
|
|
982
|
-
var RunnerEnrollRequestSchema =
|
|
983
|
-
protocol:
|
|
984
|
-
version:
|
|
985
|
-
hostname:
|
|
1167
|
+
var RunnerWelcomeSchema = z7.object({ name: NameSchema, version: z7.string() });
|
|
1168
|
+
var RunnerEnrollRequestSchema = z7.object({
|
|
1169
|
+
protocol: z7.number().int().positive(),
|
|
1170
|
+
version: z7.string().min(1).max(64),
|
|
1171
|
+
hostname: z7.string().min(1).max(253),
|
|
986
1172
|
os: RunnerOsSchema,
|
|
987
|
-
clis:
|
|
988
|
-
capabilities:
|
|
1173
|
+
clis: z7.array(CliKindSchema).max(8),
|
|
1174
|
+
capabilities: z7.array(z7.string().min(1).max(64)).max(32).default([])
|
|
989
1175
|
});
|
|
990
|
-
var RunnerEnrollmentSchema =
|
|
991
|
-
deviceCode:
|
|
992
|
-
userCode:
|
|
1176
|
+
var RunnerEnrollmentSchema = z7.object({
|
|
1177
|
+
deviceCode: z7.string().min(1),
|
|
1178
|
+
userCode: z7.string().min(1),
|
|
993
1179
|
expiresAt: IsoDateTimeSchema,
|
|
994
|
-
intervalMs:
|
|
1180
|
+
intervalMs: z7.number().int().positive()
|
|
995
1181
|
});
|
|
996
|
-
var RunnerEnrollPollSchema =
|
|
997
|
-
var RunnerEnrollStatusSchema =
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1182
|
+
var RunnerEnrollPollSchema = z7.object({ deviceCode: z7.string().min(1) });
|
|
1183
|
+
var RunnerEnrollStatusSchema = z7.discriminatedUnion("status", [
|
|
1184
|
+
z7.object({ status: z7.literal("pending") }),
|
|
1185
|
+
z7.object({ status: z7.literal("approved"), name: NameSchema, token: z7.string().min(1) }),
|
|
1186
|
+
z7.object({ status: z7.literal("denied") })
|
|
1001
1187
|
]);
|
|
1002
|
-
var PendingEnrollmentSchema =
|
|
1003
|
-
userCode:
|
|
1004
|
-
hostname:
|
|
1188
|
+
var PendingEnrollmentSchema = z7.object({
|
|
1189
|
+
userCode: z7.string().min(1),
|
|
1190
|
+
hostname: z7.string(),
|
|
1005
1191
|
os: RunnerOsSchema,
|
|
1006
|
-
clis:
|
|
1007
|
-
capabilities:
|
|
1008
|
-
version:
|
|
1192
|
+
clis: z7.array(CliKindSchema),
|
|
1193
|
+
capabilities: z7.array(z7.string()),
|
|
1194
|
+
version: z7.string(),
|
|
1009
1195
|
requestedAt: IsoDateTimeSchema,
|
|
1010
1196
|
expiresAt: IsoDateTimeSchema
|
|
1011
1197
|
});
|
|
1012
|
-
var ProjectRepoSchema =
|
|
1198
|
+
var ProjectRepoSchema = z7.object({
|
|
1013
1199
|
slug: NameSchema,
|
|
1014
|
-
origin:
|
|
1015
|
-
defaultBranch:
|
|
1200
|
+
origin: z7.string().nullable(),
|
|
1201
|
+
defaultBranch: z7.string().min(1),
|
|
1016
1202
|
/**
|
|
1017
1203
|
* The board lands finished tasks on the default branch, itself or through their pull requests,
|
|
1018
1204
|
* so the runner lets nothing else move it.
|
|
1019
1205
|
*/
|
|
1020
|
-
boardLands:
|
|
1206
|
+
boardLands: z7.boolean()
|
|
1021
1207
|
});
|
|
1022
1208
|
function projectRepo(project) {
|
|
1023
1209
|
return {
|
|
@@ -1027,29 +1213,29 @@ function projectRepo(project) {
|
|
|
1027
1213
|
boardLands: project.onDone !== "none"
|
|
1028
1214
|
};
|
|
1029
1215
|
}
|
|
1030
|
-
var TurnWorkspaceSchema =
|
|
1031
|
-
|
|
1032
|
-
kind:
|
|
1216
|
+
var TurnWorkspaceSchema = z7.discriminatedUnion("kind", [
|
|
1217
|
+
z7.object({
|
|
1218
|
+
kind: z7.literal("home"),
|
|
1033
1219
|
thread: UlidSchema.optional(),
|
|
1034
1220
|
channel: NameSchema.optional()
|
|
1035
1221
|
}),
|
|
1036
|
-
|
|
1037
|
-
kind:
|
|
1222
|
+
z7.object({
|
|
1223
|
+
kind: z7.literal("project"),
|
|
1038
1224
|
repo: ProjectRepoSchema,
|
|
1039
|
-
branches:
|
|
1225
|
+
branches: z7.array(z7.string().min(1)),
|
|
1040
1226
|
thread: UlidSchema.optional(),
|
|
1041
1227
|
channel: NameSchema.optional()
|
|
1042
1228
|
}),
|
|
1043
|
-
|
|
1229
|
+
z7.object({ kind: z7.literal("task"), repo: ProjectRepoSchema, taskId: UlidSchema })
|
|
1044
1230
|
]);
|
|
1045
|
-
var TurnJobSchema =
|
|
1231
|
+
var TurnJobSchema = z7.object({
|
|
1046
1232
|
turnId: UlidSchema,
|
|
1047
1233
|
agent: NameSchema,
|
|
1048
1234
|
role: NameSchema,
|
|
1049
1235
|
cli: CliKindSchema,
|
|
1050
|
-
model:
|
|
1236
|
+
model: z7.string().optional(),
|
|
1051
1237
|
/** The reasoning effort set on the agent; absent for the model's own default. */
|
|
1052
|
-
effort:
|
|
1238
|
+
effort: z7.string().optional(),
|
|
1053
1239
|
/** A project slug, or the society scope. */
|
|
1054
1240
|
scope: NameSchema,
|
|
1055
1241
|
/** The thread whose conversation the turn is in; absent for the home conversation. */
|
|
@@ -1060,117 +1246,117 @@ var TurnJobSchema = z6.object({
|
|
|
1060
1246
|
* The session to resume, or null to start one: the CLI's adapter picks a fresh session's id, and
|
|
1061
1247
|
* the outcome reports it.
|
|
1062
1248
|
*/
|
|
1063
|
-
session:
|
|
1249
|
+
session: z7.string().min(1).nullable(),
|
|
1064
1250
|
/** The running total the session reported after its last turn, which a resumed session starts from. */
|
|
1065
|
-
costSoFarUsd:
|
|
1251
|
+
costSoFarUsd: z7.number().nonnegative(),
|
|
1066
1252
|
/** The rendered charter, norms, memory core, and skills index, with `PATH_TOKENS` for places. */
|
|
1067
|
-
instructions:
|
|
1068
|
-
prompt:
|
|
1069
|
-
mcp:
|
|
1070
|
-
limits:
|
|
1071
|
-
timeoutMs:
|
|
1072
|
-
maxTurns:
|
|
1253
|
+
instructions: z7.string(),
|
|
1254
|
+
prompt: z7.string(),
|
|
1255
|
+
mcp: z7.object({ url: z7.string().min(1), token: z7.string().min(1) }),
|
|
1256
|
+
limits: z7.object({
|
|
1257
|
+
timeoutMs: z7.number().int().positive().nullable(),
|
|
1258
|
+
maxTurns: z7.number().int().positive().nullable()
|
|
1073
1259
|
}),
|
|
1074
1260
|
/** Present when the runner keeps the conversation's session warm between turns, under `key`. */
|
|
1075
|
-
resident:
|
|
1261
|
+
resident: z7.object({ key: z7.string().min(1), idleMs: z7.number().int().positive() }).optional(),
|
|
1076
1262
|
workspace: TurnWorkspaceSchema
|
|
1077
1263
|
});
|
|
1078
|
-
var TurnWorkSchema =
|
|
1079
|
-
var TurnEventsSchema =
|
|
1080
|
-
var TurnOutcomeSchema =
|
|
1264
|
+
var TurnWorkSchema = z7.object({ branch: z7.string().min(1), head: z7.string().min(1) });
|
|
1265
|
+
var TurnEventsSchema = z7.object({ entries: z7.array(TranscriptEntrySchema) });
|
|
1266
|
+
var TurnOutcomeSchema = z7.object({
|
|
1081
1267
|
exitReason: TurnExitReasonSchema,
|
|
1082
1268
|
status: TurnStatusSchema.nullable(),
|
|
1083
|
-
error:
|
|
1269
|
+
error: z7.string().nullable(),
|
|
1084
1270
|
usage: UsageSchema,
|
|
1085
|
-
costUsd:
|
|
1271
|
+
costUsd: z7.number().nonnegative(),
|
|
1086
1272
|
/** The session's running total after this turn, for CLIs that report one. */
|
|
1087
|
-
sessionCostUsd:
|
|
1273
|
+
sessionCostUsd: z7.number().nonnegative().optional(),
|
|
1088
1274
|
/** The session the CLI used, which differs from the job's for CLIs that assign their own ids. */
|
|
1089
|
-
session:
|
|
1275
|
+
session: z7.string().min(1),
|
|
1090
1276
|
/** The model the CLI reported, when it reported one. */
|
|
1091
|
-
model:
|
|
1277
|
+
model: z7.string().nullable(),
|
|
1092
1278
|
work: TurnWorkSchema.nullable(),
|
|
1093
1279
|
/**
|
|
1094
1280
|
* Whether the turn's own workspace, a thread's, a channel's, or a task's, holds work on no
|
|
1095
1281
|
* branch after the hand-back: uncommitted changes, or commits no branch contains.
|
|
1096
1282
|
*/
|
|
1097
|
-
leftovers:
|
|
1283
|
+
leftovers: z7.boolean().default(false)
|
|
1098
1284
|
});
|
|
1099
|
-
var TurnAckSchema =
|
|
1100
|
-
var LandRequestSchema =
|
|
1285
|
+
var TurnAckSchema = z7.object({ dropWorkspace: z7.boolean() });
|
|
1286
|
+
var LandRequestSchema = z7.object({
|
|
1101
1287
|
repo: ProjectRepoSchema,
|
|
1102
|
-
branch:
|
|
1288
|
+
branch: z7.string().min(1),
|
|
1103
1289
|
/**
|
|
1104
1290
|
* For a `ghpr` task, the pull request to merge in place of the branch, which must hold the
|
|
1105
1291
|
* branch's files, and the merge commit's subject and body, the pull request's title and an
|
|
1106
1292
|
* empty body when left out.
|
|
1107
1293
|
*/
|
|
1108
|
-
pullRequest:
|
|
1109
|
-
url:
|
|
1110
|
-
subject:
|
|
1111
|
-
body:
|
|
1294
|
+
pullRequest: z7.object({
|
|
1295
|
+
url: z7.string().min(1),
|
|
1296
|
+
subject: z7.string().min(1).optional(),
|
|
1297
|
+
body: z7.string().optional()
|
|
1112
1298
|
}).optional()
|
|
1113
1299
|
});
|
|
1114
|
-
var CloseRequestSchema =
|
|
1300
|
+
var CloseRequestSchema = z7.object({
|
|
1115
1301
|
repo: ProjectRepoSchema,
|
|
1116
|
-
url:
|
|
1117
|
-
comment:
|
|
1302
|
+
url: z7.string().min(1),
|
|
1303
|
+
comment: z7.string().min(1)
|
|
1118
1304
|
});
|
|
1119
|
-
var MergeOutcomeSchema =
|
|
1120
|
-
var ModelListSchema =
|
|
1305
|
+
var MergeOutcomeSchema = z7.object({ ok: z7.boolean(), detail: z7.string() });
|
|
1306
|
+
var ModelListSchema = z7.array(ModelOptionSchema);
|
|
1121
1307
|
var BRANCH_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
1122
|
-
var BranchPathSchema =
|
|
1123
|
-
var BranchNameSchema =
|
|
1124
|
-
var BranchReadSchema =
|
|
1308
|
+
var BranchPathSchema = z7.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" });
|
|
1309
|
+
var BranchNameSchema = z7.string().regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/);
|
|
1310
|
+
var BranchReadSchema = z7.object({
|
|
1125
1311
|
repo: ProjectRepoSchema,
|
|
1126
1312
|
branch: BranchNameSchema,
|
|
1127
1313
|
path: BranchPathSchema
|
|
1128
1314
|
});
|
|
1129
|
-
var BranchCommitSchema =
|
|
1130
|
-
id:
|
|
1315
|
+
var BranchCommitSchema = z7.object({
|
|
1316
|
+
id: z7.string().min(1),
|
|
1131
1317
|
at: IsoDateTimeSchema,
|
|
1132
|
-
author:
|
|
1318
|
+
author: z7.string()
|
|
1133
1319
|
});
|
|
1134
|
-
var BranchFileSchema =
|
|
1135
|
-
|
|
1136
|
-
kind:
|
|
1320
|
+
var BranchFileSchema = z7.discriminatedUnion("kind", [
|
|
1321
|
+
z7.object({
|
|
1322
|
+
kind: z7.literal("file"),
|
|
1137
1323
|
path: BranchPathSchema,
|
|
1138
1324
|
commit: BranchCommitSchema,
|
|
1139
|
-
size:
|
|
1140
|
-
content:
|
|
1325
|
+
size: z7.number().int().nonnegative(),
|
|
1326
|
+
content: z7.string().nullable()
|
|
1141
1327
|
}),
|
|
1142
|
-
|
|
1143
|
-
kind:
|
|
1328
|
+
z7.object({
|
|
1329
|
+
kind: z7.literal("dir"),
|
|
1144
1330
|
path: BranchPathSchema,
|
|
1145
1331
|
commit: BranchCommitSchema,
|
|
1146
|
-
entries:
|
|
1147
|
-
name:
|
|
1148
|
-
kind:
|
|
1149
|
-
size:
|
|
1332
|
+
entries: z7.array(z7.object({
|
|
1333
|
+
name: z7.string().min(1),
|
|
1334
|
+
kind: z7.enum(["file", "dir"]),
|
|
1335
|
+
size: z7.number().int().nonnegative().nullable()
|
|
1150
1336
|
}))
|
|
1151
1337
|
})
|
|
1152
1338
|
]);
|
|
1153
|
-
var BranchChangesReadSchema =
|
|
1339
|
+
var BranchChangesReadSchema = z7.object({
|
|
1154
1340
|
repo: ProjectRepoSchema,
|
|
1155
1341
|
branch: BranchNameSchema
|
|
1156
1342
|
});
|
|
1157
|
-
var BranchFileChangeSchema =
|
|
1158
|
-
path:
|
|
1159
|
-
status:
|
|
1343
|
+
var BranchFileChangeSchema = z7.object({
|
|
1344
|
+
path: z7.string().min(1),
|
|
1345
|
+
status: z7.enum(["added", "modified", "deleted"]),
|
|
1160
1346
|
/** Lines added and removed, or null for a binary file. */
|
|
1161
|
-
added:
|
|
1162
|
-
removed:
|
|
1347
|
+
added: z7.number().int().nonnegative().nullable(),
|
|
1348
|
+
removed: z7.number().int().nonnegative().nullable(),
|
|
1163
1349
|
/** Absent when the file changed only through a merge of another branch. */
|
|
1164
|
-
lastChange:
|
|
1350
|
+
lastChange: z7.object({ author: z7.string(), at: IsoDateTimeSchema }).optional()
|
|
1165
1351
|
});
|
|
1166
|
-
var BranchChangesSchema =
|
|
1352
|
+
var BranchChangesSchema = z7.object({
|
|
1167
1353
|
head: BranchCommitSchema,
|
|
1168
|
-
files:
|
|
1169
|
-
total:
|
|
1354
|
+
files: z7.array(BranchFileChangeSchema),
|
|
1355
|
+
total: z7.number().int().nonnegative()
|
|
1170
1356
|
});
|
|
1171
|
-
var WorkspaceConversationSchema =
|
|
1172
|
-
|
|
1173
|
-
|
|
1357
|
+
var WorkspaceConversationSchema = z7.discriminatedUnion("kind", [
|
|
1358
|
+
z7.object({ kind: z7.literal("thread"), id: UlidSchema }),
|
|
1359
|
+
z7.object({ kind: z7.literal("channel"), scope: NameSchema, name: NameSchema })
|
|
1174
1360
|
]);
|
|
1175
1361
|
function workspaceConversationOf(turn) {
|
|
1176
1362
|
if (turn.thread !== void 0) {
|
|
@@ -1178,55 +1364,55 @@ function workspaceConversationOf(turn) {
|
|
|
1178
1364
|
}
|
|
1179
1365
|
return turn.channel === void 0 ? null : { kind: "channel", scope: turn.scope, name: turn.channel };
|
|
1180
1366
|
}
|
|
1181
|
-
var HeldWorkspaceSchema =
|
|
1367
|
+
var HeldWorkspaceSchema = z7.object({
|
|
1182
1368
|
agent: NameSchema,
|
|
1183
1369
|
scope: NameSchema,
|
|
1184
1370
|
conversation: WorkspaceConversationSchema
|
|
1185
1371
|
});
|
|
1186
|
-
var HeldWorkspacesSchema =
|
|
1187
|
-
var SweepResultSchema =
|
|
1188
|
-
var TurnSteerSchema =
|
|
1189
|
-
var RunnerMessageSchema =
|
|
1190
|
-
|
|
1191
|
-
|
|
1372
|
+
var HeldWorkspacesSchema = z7.object({ workspaces: z7.array(HeldWorkspaceSchema) });
|
|
1373
|
+
var SweepResultSchema = z7.object({ kept: z7.array(HeldWorkspaceSchema) });
|
|
1374
|
+
var TurnSteerSchema = z7.object({ id: z7.uuid(), text: z7.string().min(1) });
|
|
1375
|
+
var RunnerMessageSchema = z7.discriminatedUnion("type", [
|
|
1376
|
+
z7.object({ type: z7.literal("turn"), job: TurnJobSchema }),
|
|
1377
|
+
z7.object({ type: z7.literal("land"), request: z7.string().min(1), land: LandRequestSchema }),
|
|
1192
1378
|
/** Answered with a `MergeOutcome`. */
|
|
1193
|
-
|
|
1194
|
-
|
|
1379
|
+
z7.object({ type: z7.literal("close"), request: z7.string().min(1), close: CloseRequestSchema }),
|
|
1380
|
+
z7.object({ type: z7.literal("models"), request: z7.string().min(1), cli: CliKindSchema }),
|
|
1195
1381
|
/** Answered with a `BranchFile`, or null when the branch or the path does not exist. */
|
|
1196
|
-
|
|
1382
|
+
z7.object({ type: z7.literal("file"), request: z7.string().min(1), read: BranchReadSchema }),
|
|
1197
1383
|
/** Answered with `BranchChanges`, or null when the repository or the branch does not exist. */
|
|
1198
|
-
|
|
1199
|
-
type:
|
|
1200
|
-
request:
|
|
1384
|
+
z7.object({
|
|
1385
|
+
type: z7.literal("changes"),
|
|
1386
|
+
request: z7.string().min(1),
|
|
1201
1387
|
read: BranchChangesReadSchema
|
|
1202
1388
|
}),
|
|
1203
1389
|
/** Answered with whether the CLI accepted it; that it took it comes as a `steered` event. */
|
|
1204
|
-
|
|
1205
|
-
type:
|
|
1206
|
-
request:
|
|
1390
|
+
z7.object({
|
|
1391
|
+
type: z7.literal("steer"),
|
|
1392
|
+
request: z7.string().min(1),
|
|
1207
1393
|
turnId: UlidSchema,
|
|
1208
1394
|
steer: TurnSteerSchema
|
|
1209
1395
|
}),
|
|
1210
1396
|
/** Answered with whether the turn was there to stop; it ends `stopped`. */
|
|
1211
|
-
|
|
1397
|
+
z7.object({ type: z7.literal("stop"), request: z7.string().min(1), turnId: UlidSchema }),
|
|
1212
1398
|
/**
|
|
1213
1399
|
* A conversation ended: the runner removes every citizen's workspace for it but those a turn
|
|
1214
1400
|
* still uses, and answers with a `SweepResult` naming those it kept for holding work on no branch.
|
|
1215
1401
|
*/
|
|
1216
|
-
|
|
1217
|
-
type:
|
|
1218
|
-
request:
|
|
1402
|
+
z7.object({
|
|
1403
|
+
type: z7.literal("sweep"),
|
|
1404
|
+
request: z7.string().min(1),
|
|
1219
1405
|
conversation: WorkspaceConversationSchema
|
|
1220
1406
|
})
|
|
1221
1407
|
]);
|
|
1222
|
-
var RunnerAnswerSchema =
|
|
1223
|
-
|
|
1224
|
-
|
|
1408
|
+
var RunnerAnswerSchema = z7.discriminatedUnion("ok", [
|
|
1409
|
+
z7.object({ ok: z7.literal(true), value: z7.unknown() }),
|
|
1410
|
+
z7.object({ ok: z7.literal(false), error: z7.string() })
|
|
1225
1411
|
]);
|
|
1226
|
-
var WarmSessionsSchema =
|
|
1227
|
-
var FileManifestSchema =
|
|
1228
|
-
var FileReadSchema =
|
|
1229
|
-
var FileContentsSchema =
|
|
1412
|
+
var WarmSessionsSchema = z7.object({ warm: z7.array(z7.string()) });
|
|
1413
|
+
var FileManifestSchema = z7.object({ files: z7.record(z7.string(), z7.string()) });
|
|
1414
|
+
var FileReadSchema = z7.object({ paths: z7.array(z7.string()) });
|
|
1415
|
+
var FileContentsSchema = z7.object({ files: z7.record(z7.string(), z7.string()) });
|
|
1230
1416
|
function isSafeRelativePath(file) {
|
|
1231
1417
|
if (file.length === 0 || file.startsWith("/") || file.includes("\\") || file.includes("\0")) {
|
|
1232
1418
|
return false;
|
|
@@ -1235,7 +1421,7 @@ function isSafeRelativePath(file) {
|
|
|
1235
1421
|
}
|
|
1236
1422
|
|
|
1237
1423
|
// packages/shared/dist/turns.js
|
|
1238
|
-
var TriggerKindSchema =
|
|
1424
|
+
var TriggerKindSchema = z8.enum([
|
|
1239
1425
|
"mention",
|
|
1240
1426
|
"stage",
|
|
1241
1427
|
"task_done",
|
|
@@ -1246,27 +1432,32 @@ var TriggerKindSchema = z7.enum([
|
|
|
1246
1432
|
"manual",
|
|
1247
1433
|
"ops_event",
|
|
1248
1434
|
"user_post",
|
|
1249
|
-
"closing"
|
|
1435
|
+
"closing",
|
|
1436
|
+
"cron"
|
|
1250
1437
|
]);
|
|
1251
|
-
var TriggerSchema =
|
|
1438
|
+
var TriggerSchema = z8.object({
|
|
1252
1439
|
kind: TriggerKindSchema,
|
|
1253
1440
|
from: NameSchema.optional(),
|
|
1254
|
-
fromUser:
|
|
1255
|
-
reason:
|
|
1441
|
+
fromUser: z8.boolean().default(false),
|
|
1442
|
+
reason: z8.string().default(""),
|
|
1256
1443
|
taskId: UlidSchema.optional(),
|
|
1257
1444
|
messageId: UlidSchema.optional(),
|
|
1258
1445
|
/** The channel the waking message was posted in. */
|
|
1259
|
-
channel: ChannelRefSchema.optional()
|
|
1446
|
+
channel: ChannelRefSchema.optional(),
|
|
1447
|
+
/** The cron that came due, for a `cron` wake. */
|
|
1448
|
+
cronId: UlidSchema.optional()
|
|
1260
1449
|
});
|
|
1261
|
-
var TurnThreadSchema =
|
|
1262
|
-
var TurnDispatchSchema =
|
|
1450
|
+
var TurnThreadSchema = z8.object({ id: UlidSchema, task: z8.boolean() });
|
|
1451
|
+
var TurnDispatchSchema = z8.object({
|
|
1263
1452
|
agent: NameSchema,
|
|
1264
1453
|
project: NameSchema,
|
|
1265
1454
|
thread: TurnThreadSchema.optional(),
|
|
1266
1455
|
channel: NameSchema.optional(),
|
|
1267
1456
|
trigger: TriggerSchema,
|
|
1268
|
-
priority:
|
|
1269
|
-
onboarding:
|
|
1457
|
+
priority: z8.number().int().min(0).max(2),
|
|
1458
|
+
onboarding: z8.boolean().default(false),
|
|
1459
|
+
/** Every cron that came due for this turn, whatever wake it merged into; its prompt quotes each. */
|
|
1460
|
+
crons: z8.array(UlidSchema).optional()
|
|
1270
1461
|
});
|
|
1271
1462
|
function conversationPart(conversation) {
|
|
1272
1463
|
return conversation.thread ?? (conversation.channel === void 0 ? void 0 : `#${conversation.channel}`);
|
|
@@ -1290,7 +1481,7 @@ function channelConversation(scope, channel) {
|
|
|
1290
1481
|
const name = at < 0 ? channel : channel.slice(at + 1);
|
|
1291
1482
|
return place === scope && name !== "general" ? name : void 0;
|
|
1292
1483
|
}
|
|
1293
|
-
var TurnRecordSchema =
|
|
1484
|
+
var TurnRecordSchema = z8.object({
|
|
1294
1485
|
/** Assigned by the board when the turn begins; the turn's transcript is filed under it. */
|
|
1295
1486
|
id: UlidSchema.optional(),
|
|
1296
1487
|
agent: NameSchema,
|
|
@@ -1301,29 +1492,31 @@ var TurnRecordSchema = z7.object({
|
|
|
1301
1492
|
channel: NameSchema.optional(),
|
|
1302
1493
|
runner: NameSchema,
|
|
1303
1494
|
cli: CliKindSchema.nullable(),
|
|
1304
|
-
session:
|
|
1495
|
+
session: z8.string().nullable(),
|
|
1305
1496
|
trigger: TriggerSchema,
|
|
1306
1497
|
startedAt: IsoDateTimeSchema,
|
|
1307
1498
|
endedAt: IsoDateTimeSchema.nullable(),
|
|
1308
1499
|
exitReason: TurnExitReasonSchema.nullable(),
|
|
1309
1500
|
status: TurnStatusSchema.nullable(),
|
|
1310
|
-
error:
|
|
1501
|
+
error: z8.string().nullable(),
|
|
1311
1502
|
usage: UsageSchema.nullable(),
|
|
1312
|
-
costUsd:
|
|
1503
|
+
costUsd: z8.number().nonnegative().default(0),
|
|
1313
1504
|
/**
|
|
1314
1505
|
* The running total the CLI reported for the session after this turn, when it reports one. A
|
|
1315
1506
|
* resumed session starts from it, so the next turn's own cost is measured from here.
|
|
1316
1507
|
*/
|
|
1317
|
-
sessionCostUsd:
|
|
1318
|
-
toolCalls:
|
|
1508
|
+
sessionCostUsd: z8.number().nonnegative().optional(),
|
|
1509
|
+
toolCalls: z8.number().int().nonnegative().default(0),
|
|
1319
1510
|
/** The model the CLI reported for this turn, else the model configured for the agent, else null. */
|
|
1320
|
-
model:
|
|
1511
|
+
model: z8.string().nullable().default(null),
|
|
1321
1512
|
/** The task branch the turn left work on, with its commit, as the runner reported it. */
|
|
1322
1513
|
work: TurnWorkSchema.optional(),
|
|
1323
1514
|
/** Who stopped the turn, for one that ended `stopped`. */
|
|
1324
|
-
stoppedBy: NameSchema.optional()
|
|
1515
|
+
stoppedBy: NameSchema.optional(),
|
|
1516
|
+
/** The crons whose fire started the turn, which record how it ended. */
|
|
1517
|
+
crons: z8.array(UlidSchema).optional()
|
|
1325
1518
|
});
|
|
1326
|
-
var RunningTurnSchema =
|
|
1519
|
+
var RunningTurnSchema = z8.object({
|
|
1327
1520
|
turnId: UlidSchema,
|
|
1328
1521
|
agent: NameSchema,
|
|
1329
1522
|
scope: NameSchema,
|
|
@@ -1331,49 +1524,49 @@ var RunningTurnSchema = z7.object({
|
|
|
1331
1524
|
channel: NameSchema.optional(),
|
|
1332
1525
|
cli: CliKindSchema,
|
|
1333
1526
|
askedIn: ChannelRefSchema.optional(),
|
|
1334
|
-
steerable:
|
|
1335
|
-
stoppable:
|
|
1527
|
+
steerable: z8.boolean(),
|
|
1528
|
+
stoppable: z8.boolean()
|
|
1336
1529
|
});
|
|
1337
|
-
var SessionsFileSchema =
|
|
1338
|
-
claude:
|
|
1339
|
-
codex:
|
|
1530
|
+
var SessionsFileSchema = z8.object({
|
|
1531
|
+
claude: z8.string().optional(),
|
|
1532
|
+
codex: z8.string().optional(),
|
|
1340
1533
|
runner: NameSchema.optional()
|
|
1341
1534
|
});
|
|
1342
|
-
var WakeRequestSchema =
|
|
1535
|
+
var WakeRequestSchema = z8.object({
|
|
1343
1536
|
agent: NameSchema,
|
|
1344
1537
|
project: NameSchema,
|
|
1345
|
-
reason:
|
|
1538
|
+
reason: z8.string().default("manual wake"),
|
|
1346
1539
|
/** A manual wake, or a reflection turn requested ahead of the cadence. */
|
|
1347
|
-
kind:
|
|
1540
|
+
kind: z8.enum(["manual", "reflection"]).default("manual")
|
|
1348
1541
|
});
|
|
1349
|
-
var TurnHistoryEntrySchema =
|
|
1542
|
+
var TurnHistoryEntrySchema = z8.object({
|
|
1350
1543
|
/** The id of the event that ended the turn. */
|
|
1351
1544
|
id: UlidSchema,
|
|
1352
1545
|
/** The turn's own id, when it has a transcript to open; turns before transcripts have none. */
|
|
1353
1546
|
turnId: UlidSchema.optional(),
|
|
1354
1547
|
ts: IsoDateTimeSchema,
|
|
1355
|
-
outcome:
|
|
1548
|
+
outcome: z8.enum(["completed", "failed"]),
|
|
1356
1549
|
/** A project slug, or the society scope. */
|
|
1357
1550
|
project: NameSchema,
|
|
1358
1551
|
/** The thread whose conversation the turn was in; absent for the home conversation. */
|
|
1359
1552
|
thread: UlidSchema.optional(),
|
|
1360
1553
|
/** The channel, never general, whose conversation the turn was in. */
|
|
1361
1554
|
channel: NameSchema.optional(),
|
|
1362
|
-
trigger:
|
|
1363
|
-
exitReason:
|
|
1364
|
-
costUsd:
|
|
1555
|
+
trigger: z8.string(),
|
|
1556
|
+
exitReason: z8.string().nullable(),
|
|
1557
|
+
costUsd: z8.number(),
|
|
1365
1558
|
/** The tokens the turn used; absent for turns logged before turns recorded them. */
|
|
1366
1559
|
usage: UsageSchema.optional(),
|
|
1367
|
-
model:
|
|
1368
|
-
summary:
|
|
1369
|
-
error:
|
|
1560
|
+
model: z8.string().nullable(),
|
|
1561
|
+
summary: z8.string().nullable(),
|
|
1562
|
+
error: z8.string().nullable(),
|
|
1370
1563
|
/** When the log recorded the turn's start; absent when the start is missing from the log. */
|
|
1371
1564
|
startedAt: IsoDateTimeSchema.optional(),
|
|
1372
|
-
toolCalls:
|
|
1565
|
+
toolCalls: z8.number().int().nonnegative().optional()
|
|
1373
1566
|
});
|
|
1374
1567
|
|
|
1375
1568
|
// packages/shared/dist/ops.js
|
|
1376
|
-
import { z as
|
|
1569
|
+
import { z as z9 } from "zod";
|
|
1377
1570
|
var OPS_SIGNAL_KINDS = [
|
|
1378
1571
|
"waiting_stage",
|
|
1379
1572
|
"backlog",
|
|
@@ -1386,9 +1579,10 @@ var OPS_SIGNAL_KINDS = [
|
|
|
1386
1579
|
"scaled",
|
|
1387
1580
|
"runner",
|
|
1388
1581
|
"home_conflict",
|
|
1389
|
-
"stuck_workspace"
|
|
1582
|
+
"stuck_workspace",
|
|
1583
|
+
"cron_failing"
|
|
1390
1584
|
];
|
|
1391
|
-
var OpsSignalKindSchema =
|
|
1585
|
+
var OpsSignalKindSchema = z9.enum(OPS_SIGNAL_KINDS);
|
|
1392
1586
|
var WAKING_SIGNAL_KINDS = [
|
|
1393
1587
|
"backlog",
|
|
1394
1588
|
"role_gap",
|
|
@@ -1398,15 +1592,15 @@ var WAKING_SIGNAL_KINDS = [
|
|
|
1398
1592
|
"blocked_capability",
|
|
1399
1593
|
"scaled"
|
|
1400
1594
|
];
|
|
1401
|
-
var OpsSignalSchema =
|
|
1595
|
+
var OpsSignalSchema = z9.object({
|
|
1402
1596
|
kind: OpsSignalKindSchema,
|
|
1403
1597
|
/** Identifies the condition, for example `backlog:demo:engineer`, so a persisting one is not logged again every pass. */
|
|
1404
|
-
key:
|
|
1598
|
+
key: z9.string().min(1),
|
|
1405
1599
|
/** One line a reader can act on. */
|
|
1406
|
-
summary:
|
|
1600
|
+
summary: z9.string().min(1),
|
|
1407
1601
|
/** The counter or timer value behind the signal. */
|
|
1408
|
-
value:
|
|
1409
|
-
threshold:
|
|
1602
|
+
value: z9.number(),
|
|
1603
|
+
threshold: z9.number().optional(),
|
|
1410
1604
|
project: NameSchema.optional(),
|
|
1411
1605
|
agent: NameSchema.optional(),
|
|
1412
1606
|
role: NameSchema.optional(),
|
|
@@ -1414,8 +1608,8 @@ var OpsSignalSchema = z8.object({
|
|
|
1414
1608
|
});
|
|
1415
1609
|
|
|
1416
1610
|
// packages/shared/dist/metrics.js
|
|
1417
|
-
import { z as
|
|
1418
|
-
var MetricsWindowSchema =
|
|
1611
|
+
import { z as z10 } from "zod";
|
|
1612
|
+
var MetricsWindowSchema = z10.enum(["24h", "7d", "all"]);
|
|
1419
1613
|
var METRICS_WINDOW_MS = {
|
|
1420
1614
|
"24h": 24 * 36e5,
|
|
1421
1615
|
"7d": 7 * 24 * 36e5,
|
|
@@ -1426,9 +1620,9 @@ function isBoardAction(tool) {
|
|
|
1426
1620
|
const verb = /^mcp__board__(.+)$/.exec(tool)?.[1];
|
|
1427
1621
|
return verb !== void 0 && !READ_VERBS.some((read2) => read2 === verb);
|
|
1428
1622
|
}
|
|
1429
|
-
var CountSchema =
|
|
1430
|
-
var MsSchema =
|
|
1431
|
-
var MetricsSchema =
|
|
1623
|
+
var CountSchema = z10.number().int().nonnegative();
|
|
1624
|
+
var MsSchema = z10.number().nonnegative();
|
|
1625
|
+
var MetricsSchema = z10.object({
|
|
1432
1626
|
window: MetricsWindowSchema,
|
|
1433
1627
|
since: IsoDateTimeSchema.nullable(),
|
|
1434
1628
|
until: IsoDateTimeSchema,
|
|
@@ -1436,12 +1630,12 @@ var MetricsSchema = z9.object({
|
|
|
1436
1630
|
* Finished turns that changed nothing on the board. Reflections are left out, since they write
|
|
1437
1631
|
* memory, not the board; turns without a transcript are counted apart, their actions unknown.
|
|
1438
1632
|
*/
|
|
1439
|
-
idle:
|
|
1633
|
+
idle: z10.object({
|
|
1440
1634
|
turns: CountSchema,
|
|
1441
1635
|
idle: CountSchema,
|
|
1442
1636
|
unknown: CountSchema,
|
|
1443
|
-
byTrigger:
|
|
1444
|
-
byAgent:
|
|
1637
|
+
byTrigger: z10.array(z10.object({ trigger: z10.string(), turns: CountSchema, idle: CountSchema })),
|
|
1638
|
+
byAgent: z10.array(z10.object({
|
|
1445
1639
|
agent: NameSchema,
|
|
1446
1640
|
role: NameSchema.nullable(),
|
|
1447
1641
|
turns: CountSchema,
|
|
@@ -1449,33 +1643,33 @@ var MetricsSchema = z9.object({
|
|
|
1449
1643
|
}))
|
|
1450
1644
|
}),
|
|
1451
1645
|
/** Work sent back to an earlier stage: over the tasks finished in the window, and by where. */
|
|
1452
|
-
sentBack:
|
|
1646
|
+
sentBack: z10.object({
|
|
1453
1647
|
finished: CountSchema,
|
|
1454
1648
|
sendBacks: CountSchema,
|
|
1455
1649
|
tasksSentBack: CountSchema,
|
|
1456
|
-
byStage:
|
|
1457
|
-
bySender:
|
|
1650
|
+
byStage: z10.array(z10.object({ project: NameSchema, stage: z10.string(), count: CountSchema })),
|
|
1651
|
+
bySender: z10.array(z10.object({ agent: NameSchema, count: CountSchema }))
|
|
1458
1652
|
}),
|
|
1459
1653
|
/** Posts in the threads of the tasks finished in the window, the board's own notices left out. */
|
|
1460
|
-
messages:
|
|
1654
|
+
messages: z10.object({
|
|
1461
1655
|
finished: CountSchema,
|
|
1462
1656
|
messages: CountSchema,
|
|
1463
|
-
byProject:
|
|
1464
|
-
busiest:
|
|
1657
|
+
byProject: z10.array(z10.object({ project: NameSchema, finished: CountSchema, messages: CountSchema })),
|
|
1658
|
+
busiest: z10.array(z10.object({
|
|
1465
1659
|
taskId: UlidSchema,
|
|
1466
|
-
title:
|
|
1660
|
+
title: z10.string(),
|
|
1467
1661
|
project: NameSchema,
|
|
1468
1662
|
messages: CountSchema
|
|
1469
1663
|
}))
|
|
1470
1664
|
}),
|
|
1471
1665
|
/** Time from a mention to the start of the turn it woke, in the conversation it was made in. */
|
|
1472
|
-
latency:
|
|
1666
|
+
latency: z10.object({
|
|
1473
1667
|
mentions: CountSchema,
|
|
1474
1668
|
/** Mentions no turn has followed yet. */
|
|
1475
1669
|
unanswered: CountSchema,
|
|
1476
1670
|
medianMs: MsSchema.nullable(),
|
|
1477
1671
|
slowestMs: MsSchema.nullable(),
|
|
1478
|
-
byAgent:
|
|
1672
|
+
byAgent: z10.array(z10.object({
|
|
1479
1673
|
agent: NameSchema,
|
|
1480
1674
|
mentions: CountSchema,
|
|
1481
1675
|
medianMs: MsSchema,
|
|
@@ -1483,61 +1677,61 @@ var MetricsSchema = z9.object({
|
|
|
1483
1677
|
}))
|
|
1484
1678
|
}),
|
|
1485
1679
|
/** The user's decisions: proposals decided, questions answered, and stages passed or sent back. */
|
|
1486
|
-
decisions:
|
|
1680
|
+
decisions: z10.object({
|
|
1487
1681
|
total: CountSchema,
|
|
1488
|
-
byDay:
|
|
1489
|
-
day:
|
|
1682
|
+
byDay: z10.array(z10.object({
|
|
1683
|
+
day: z10.string(),
|
|
1490
1684
|
proposals: CountSchema,
|
|
1491
1685
|
answers: CountSchema,
|
|
1492
1686
|
stages: CountSchema
|
|
1493
1687
|
}))
|
|
1494
1688
|
}),
|
|
1495
1689
|
/** Tasks that needed a capability no connected runner offered, and whether they still do. */
|
|
1496
|
-
blocked:
|
|
1690
|
+
blocked: z10.array(z10.object({
|
|
1497
1691
|
taskId: UlidSchema,
|
|
1498
|
-
title:
|
|
1692
|
+
title: z10.string().nullable(),
|
|
1499
1693
|
project: NameSchema.nullable(),
|
|
1500
|
-
summary:
|
|
1694
|
+
summary: z10.string(),
|
|
1501
1695
|
firstAt: IsoDateTimeSchema,
|
|
1502
1696
|
lastAt: IsoDateTimeSchema,
|
|
1503
|
-
holds:
|
|
1697
|
+
holds: z10.boolean()
|
|
1504
1698
|
}))
|
|
1505
1699
|
});
|
|
1506
1700
|
|
|
1507
1701
|
// packages/shared/dist/config.js
|
|
1508
|
-
import { z as
|
|
1509
|
-
var LogLevelSchema =
|
|
1510
|
-
var GithubSignInSchema =
|
|
1511
|
-
clientId:
|
|
1512
|
-
clientSecret:
|
|
1702
|
+
import { z as z11 } from "zod";
|
|
1703
|
+
var LogLevelSchema = z11.enum(["trace", "debug", "info", "warn", "error", "fatal"]);
|
|
1704
|
+
var GithubSignInSchema = z11.object({
|
|
1705
|
+
clientId: z11.string().min(1),
|
|
1706
|
+
clientSecret: z11.string().min(1),
|
|
1513
1707
|
/** Logins, lowercased; GitHub logins are case-insensitive. */
|
|
1514
|
-
users:
|
|
1708
|
+
users: z11.array(z11.string().min(1)).min(1)
|
|
1515
1709
|
});
|
|
1516
|
-
var ServerConfigSchema =
|
|
1517
|
-
dataDir:
|
|
1518
|
-
host:
|
|
1519
|
-
port:
|
|
1710
|
+
var ServerConfigSchema = z11.object({
|
|
1711
|
+
dataDir: z11.string().min(1),
|
|
1712
|
+
host: z11.string().min(1),
|
|
1713
|
+
port: z11.number().int().positive().max(65535),
|
|
1520
1714
|
/**
|
|
1521
1715
|
* An address every runner's agents reach this server at, for example https://board.example, or
|
|
1522
1716
|
* null for each runner to use the address it reaches the server at itself.
|
|
1523
1717
|
*/
|
|
1524
|
-
publicUrl:
|
|
1718
|
+
publicUrl: z11.string().min(1).nullable(),
|
|
1525
1719
|
logLevel: LogLevelSchema,
|
|
1526
1720
|
/** Turns running at once across every runner, or null for no limit beyond each runner's own. */
|
|
1527
|
-
concurrency:
|
|
1721
|
+
concurrency: z11.number().int().positive().nullable(),
|
|
1528
1722
|
/** How long one turn may run before it is stopped, or null for no limit. */
|
|
1529
|
-
turnTimeoutMs:
|
|
1723
|
+
turnTimeoutMs: z11.number().int().positive().nullable(),
|
|
1530
1724
|
/** Rounds of tool calls one Claude turn may take, or null for no limit. */
|
|
1531
|
-
toolRounds:
|
|
1725
|
+
toolRounds: z11.number().int().positive().nullable(),
|
|
1532
1726
|
/** How long a resident session stays warm after its last turn. */
|
|
1533
|
-
residentIdleMs:
|
|
1727
|
+
residentIdleMs: z11.number().int().positive(),
|
|
1534
1728
|
/** Signing in to the board with GitHub, or null when only a token lets anyone in. */
|
|
1535
1729
|
github: GithubSignInSchema.nullable(),
|
|
1536
1730
|
/**
|
|
1537
1731
|
* Reverse proxies in front of the server whose X-Forwarded-For entries name the client, for the
|
|
1538
1732
|
* rate limits on what anyone may ask without a token: 0 when clients connect directly.
|
|
1539
1733
|
*/
|
|
1540
|
-
trustedProxies:
|
|
1734
|
+
trustedProxies: z11.number().int().min(0).max(16)
|
|
1541
1735
|
});
|
|
1542
1736
|
function limit(value, fallback) {
|
|
1543
1737
|
if (value === void 0 || value.trim() === "") {
|
|
@@ -1575,25 +1769,25 @@ function loadServerConfig(env) {
|
|
|
1575
1769
|
trustedProxies: Number(env["STELLARIS_TRUSTED_PROXIES"] ?? "0")
|
|
1576
1770
|
});
|
|
1577
1771
|
}
|
|
1578
|
-
var RunnerConfigSchema =
|
|
1772
|
+
var RunnerConfigSchema = z11.object({
|
|
1579
1773
|
/** The board server, for example http://127.0.0.1:4700. */
|
|
1580
|
-
serverUrl:
|
|
1774
|
+
serverUrl: z11.string().url(),
|
|
1581
1775
|
/**
|
|
1582
1776
|
* The runner's token, from `runner add`, or null for the one it saved when it was enrolled, or
|
|
1583
1777
|
* to enroll it.
|
|
1584
1778
|
*/
|
|
1585
|
-
token:
|
|
1779
|
+
token: z11.string().min(1).nullable(),
|
|
1586
1780
|
/** The runner's own data directory: the board mirror, agent homes, repositories, worktrees. */
|
|
1587
|
-
dataDir:
|
|
1781
|
+
dataDir: z11.string().min(1),
|
|
1588
1782
|
logLevel: LogLevelSchema,
|
|
1589
1783
|
/** Turns this machine runs at once, or null for no limit. */
|
|
1590
|
-
slots:
|
|
1591
|
-
clis:
|
|
1592
|
-
capabilities:
|
|
1784
|
+
slots: z11.number().int().positive().nullable(),
|
|
1785
|
+
clis: z11.array(CliKindSchema).min(1),
|
|
1786
|
+
capabilities: z11.array(z11.string())
|
|
1593
1787
|
});
|
|
1594
1788
|
|
|
1595
1789
|
// packages/shared/dist/version.js
|
|
1596
|
-
var STELLARIS_VERSION = "0.3.
|
|
1790
|
+
var STELLARIS_VERSION = "0.3.15";
|
|
1597
1791
|
|
|
1598
1792
|
// packages/board-core/dist/errors.js
|
|
1599
1793
|
var BoardError = class extends Error {
|
|
@@ -2141,32 +2335,32 @@ function filesOf(lines, status) {
|
|
|
2141
2335
|
}
|
|
2142
2336
|
|
|
2143
2337
|
// packages/board-core/dist/metrics.js
|
|
2144
|
-
import { z as
|
|
2145
|
-
var TurnPayload =
|
|
2146
|
-
turnId:
|
|
2147
|
-
project:
|
|
2148
|
-
thread:
|
|
2149
|
-
channel:
|
|
2150
|
-
trigger:
|
|
2338
|
+
import { z as z12 } from "zod";
|
|
2339
|
+
var TurnPayload = z12.object({
|
|
2340
|
+
turnId: z12.string().optional(),
|
|
2341
|
+
project: z12.string(),
|
|
2342
|
+
thread: z12.string().optional(),
|
|
2343
|
+
channel: z12.string().optional(),
|
|
2344
|
+
trigger: z12.string()
|
|
2151
2345
|
});
|
|
2152
|
-
var TaskPayload =
|
|
2153
|
-
taskId:
|
|
2154
|
-
project:
|
|
2155
|
-
from:
|
|
2346
|
+
var TaskPayload = z12.object({
|
|
2347
|
+
taskId: z12.string(),
|
|
2348
|
+
project: z12.string(),
|
|
2349
|
+
from: z12.string().optional()
|
|
2156
2350
|
});
|
|
2157
|
-
var MessagePayload =
|
|
2158
|
-
id:
|
|
2159
|
-
channel:
|
|
2160
|
-
thread:
|
|
2161
|
-
mentions:
|
|
2351
|
+
var MessagePayload = z12.object({
|
|
2352
|
+
id: z12.string().optional(),
|
|
2353
|
+
channel: z12.string(),
|
|
2354
|
+
thread: z12.string().nullable().optional(),
|
|
2355
|
+
mentions: z12.array(z12.string()).default([])
|
|
2162
2356
|
});
|
|
2163
|
-
var SteeredPayload =
|
|
2164
|
-
var SignalPayload =
|
|
2165
|
-
kind:
|
|
2166
|
-
key:
|
|
2167
|
-
summary:
|
|
2168
|
-
taskId:
|
|
2169
|
-
project:
|
|
2357
|
+
var SteeredPayload = z12.object({ messages: z12.array(z12.string()) });
|
|
2358
|
+
var SignalPayload = z12.object({
|
|
2359
|
+
kind: z12.string(),
|
|
2360
|
+
key: z12.string(),
|
|
2361
|
+
summary: z12.string(),
|
|
2362
|
+
taskId: z12.string().optional(),
|
|
2363
|
+
project: z12.string().optional()
|
|
2170
2364
|
});
|
|
2171
2365
|
function median(values) {
|
|
2172
2366
|
const sorted = values.toSorted((a, b) => a - b);
|
|
@@ -2535,6 +2729,13 @@ var BoardPaths = class {
|
|
|
2535
2729
|
members() {
|
|
2536
2730
|
return path5.join(this.society, "members");
|
|
2537
2731
|
}
|
|
2732
|
+
/** Crons, one file each with its note as the body; ended ones stay. */
|
|
2733
|
+
crons() {
|
|
2734
|
+
return path5.join(this.society, "crons");
|
|
2735
|
+
}
|
|
2736
|
+
cron(id) {
|
|
2737
|
+
return path5.join(this.crons(), `${id}.md`);
|
|
2738
|
+
}
|
|
2538
2739
|
member(name) {
|
|
2539
2740
|
return path5.join(this.members(), `${name}.md`);
|
|
2540
2741
|
}
|
|
@@ -2681,41 +2882,41 @@ function namesList(names) {
|
|
|
2681
2882
|
return names.length <= 1 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} and ${names.at(-1) ?? ""}`;
|
|
2682
2883
|
}
|
|
2683
2884
|
var SKIPPED_IN_HOME = /* @__PURE__ */ new Set([".git", "turns", ".claude", ".codex"]);
|
|
2684
|
-
var RunnerTokensSchema =
|
|
2685
|
-
runners:
|
|
2885
|
+
var RunnerTokensSchema = z13.object({
|
|
2886
|
+
runners: z13.record(z13.string(), z13.object({ tokenHash: z13.string().min(1) }))
|
|
2686
2887
|
});
|
|
2687
|
-
var SignInsSchema =
|
|
2688
|
-
signIns:
|
|
2689
|
-
tokenHash:
|
|
2690
|
-
login:
|
|
2888
|
+
var SignInsSchema = z13.object({
|
|
2889
|
+
signIns: z13.array(z13.object({
|
|
2890
|
+
tokenHash: z13.string().min(1),
|
|
2891
|
+
login: z13.string().min(1),
|
|
2691
2892
|
/** The account's picture at the identity provider; sign-ins from before it have none. */
|
|
2692
|
-
avatarUrl:
|
|
2893
|
+
avatarUrl: z13.url().optional(),
|
|
2693
2894
|
createdAt: IsoDateTimeSchema,
|
|
2694
2895
|
expiresAt: IsoDateTimeSchema
|
|
2695
2896
|
})).default([])
|
|
2696
2897
|
});
|
|
2697
|
-
var TurnHistoryPayloadSchema =
|
|
2898
|
+
var TurnHistoryPayloadSchema = z13.object({
|
|
2698
2899
|
project: NameSchema,
|
|
2699
2900
|
thread: UlidSchema.optional(),
|
|
2700
2901
|
channel: NameSchema.optional(),
|
|
2701
|
-
trigger:
|
|
2702
|
-
exitReason:
|
|
2703
|
-
costUsd:
|
|
2902
|
+
trigger: z13.string().default("manual"),
|
|
2903
|
+
exitReason: z13.string().nullable().default(null),
|
|
2904
|
+
costUsd: z13.number().default(0),
|
|
2704
2905
|
usage: UsageSchema.optional(),
|
|
2705
|
-
model:
|
|
2706
|
-
summary:
|
|
2707
|
-
error:
|
|
2708
|
-
toolCalls:
|
|
2906
|
+
model: z13.string().nullable().default(null),
|
|
2907
|
+
summary: z13.string().nullable().default(null),
|
|
2908
|
+
error: z13.string().nullable().default(null),
|
|
2909
|
+
toolCalls: z13.number().int().nonnegative().optional(),
|
|
2709
2910
|
turnId: UlidSchema.optional()
|
|
2710
2911
|
});
|
|
2711
|
-
var TurnStartPayloadSchema =
|
|
2712
|
-
var CursorsSchema =
|
|
2713
|
-
digest:
|
|
2714
|
-
scopes:
|
|
2715
|
-
threadsFrom:
|
|
2716
|
-
channelsFrom:
|
|
2912
|
+
var TurnStartPayloadSchema = z13.object({ project: NameSchema, turnId: UlidSchema.optional() });
|
|
2913
|
+
var CursorsSchema = z13.object({
|
|
2914
|
+
digest: z13.string().nullable(),
|
|
2915
|
+
scopes: z13.record(z13.string(), z13.string()).default({}),
|
|
2916
|
+
threadsFrom: z13.record(z13.string(), z13.string().nullable()).default({}),
|
|
2917
|
+
channelsFrom: z13.record(z13.string(), z13.string().nullable()).default({}),
|
|
2717
2918
|
/** How far a work role has read news, which goes to whichever of its general conversations reads first. */
|
|
2718
|
-
news:
|
|
2919
|
+
news: z13.string().optional()
|
|
2719
2920
|
});
|
|
2720
2921
|
var NO_CURSORS = { digest: null, scopes: {}, threadsFrom: {}, channelsFrom: {} };
|
|
2721
2922
|
function conversationKey(scope, part) {
|
|
@@ -2754,7 +2955,7 @@ function byId(a, b) {
|
|
|
2754
2955
|
function turnTime(record) {
|
|
2755
2956
|
return record.endedAt ?? record.startedAt;
|
|
2756
2957
|
}
|
|
2757
|
-
var PausedSchema =
|
|
2958
|
+
var PausedSchema = z13.object({ paused: z13.boolean() });
|
|
2758
2959
|
var DEFAULT_LEASE_MS = 30 * 60 * 1e3;
|
|
2759
2960
|
var MENTION_PATTERN = /(^|[^\w@])@([a-z0-9][a-z0-9-]{0,31})(?![\w-])/g;
|
|
2760
2961
|
function withoutMentions(text) {
|
|
@@ -2766,7 +2967,7 @@ var OPS_WAKE_TRIGGER = "ops_event";
|
|
|
2766
2967
|
var FRONT_DESK_TRIGGER = "user_post";
|
|
2767
2968
|
var OPS_CHANNELS = ["governance"];
|
|
2768
2969
|
var FRONT_DESK_CHANNELS = [ASK_CHANNEL];
|
|
2769
|
-
var AlignmentsSchema =
|
|
2970
|
+
var AlignmentsSchema = z13.object({ applied: z13.array(z13.string()).default([]) });
|
|
2770
2971
|
var FOLLOW_DECISIONS = "ops-readers-follow-decisions";
|
|
2771
2972
|
var THREAD_RECORDS = "threads-as-records";
|
|
2772
2973
|
var DIGEST_CURSORS = "digest-replaces-inbox";
|
|
@@ -2778,6 +2979,8 @@ var ASKS_CHANNEL_OPENED = "asks-channel-opened";
|
|
|
2778
2979
|
var KNOWLEDGE_REMOVAL = "knowledge-removal-granted";
|
|
2779
2980
|
var CHANNEL_CONVERSATIONS = "channel-conversations";
|
|
2780
2981
|
var DASHBOARD_UPDATES = "dashboard-updates-granted";
|
|
2982
|
+
var CRON_VERBS = "cron-verbs-granted";
|
|
2983
|
+
var CRON_VERB_NAMES = ["create_cron", "update_cron", "remove_cron"];
|
|
2781
2984
|
var OLD_DASHBOARD_LINE = "Agents may edit this file. It is rendered by the board UI.";
|
|
2782
2985
|
var DASHBOARD_LINE = "The project's members keep this page current with update_dashboard.";
|
|
2783
2986
|
var PLANNING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
@@ -2979,7 +3182,8 @@ var Board = class _Board {
|
|
|
2979
3182
|
ASKS_CHANNEL_OPENED,
|
|
2980
3183
|
KNOWLEDGE_REMOVAL,
|
|
2981
3184
|
CHANNEL_CONVERSATIONS,
|
|
2982
|
-
DASHBOARD_UPDATES
|
|
3185
|
+
DASHBOARD_UPDATES,
|
|
3186
|
+
CRON_VERBS
|
|
2983
3187
|
].filter((name) => !applied.includes(name));
|
|
2984
3188
|
if (pending.length === 0) {
|
|
2985
3189
|
return;
|
|
@@ -3018,6 +3222,9 @@ var Board = class _Board {
|
|
|
3018
3222
|
await this.grantBesideKnowledge("update_dashboard");
|
|
3019
3223
|
await this.rewordDashboards();
|
|
3020
3224
|
}
|
|
3225
|
+
if (pending.includes(CRON_VERBS)) {
|
|
3226
|
+
await this.grantToEveryRole(CRON_VERB_NAMES);
|
|
3227
|
+
}
|
|
3021
3228
|
await writeJson(file, { applied: [...applied, ...pending] });
|
|
3022
3229
|
}
|
|
3023
3230
|
/**
|
|
@@ -3031,6 +3238,15 @@ var Board = class _Board {
|
|
|
3031
3238
|
}
|
|
3032
3239
|
}
|
|
3033
3240
|
}
|
|
3241
|
+
/** Grants verbs once to every charter but the user's, which holds every verb already. */
|
|
3242
|
+
async grantToEveryRole(verbs) {
|
|
3243
|
+
for (const charter of await this.listRoles()) {
|
|
3244
|
+
const missing = verbs.filter((verb) => !charter.verbs.includes(verb));
|
|
3245
|
+
if (charter.name !== USER_ROLE && missing.length > 0) {
|
|
3246
|
+
await this.writeRoleUnlocked(USER_NAME, { ...charter, verbs: [...charter.verbs, ...missing] }, { verbsAdded: missing });
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3034
3250
|
/** A dashboard still saying agents may edit its file says how it changes now. */
|
|
3035
3251
|
async rewordDashboards() {
|
|
3036
3252
|
for (const slug of await listDirs(this.paths.projects())) {
|
|
@@ -3175,7 +3391,7 @@ var Board = class _Board {
|
|
|
3175
3391
|
}
|
|
3176
3392
|
}
|
|
3177
3393
|
for (const file of await listFiles(this.paths.tasks(slug))) {
|
|
3178
|
-
const raw = await readMarkdown(path6.join(this.paths.tasks(slug), file),
|
|
3394
|
+
const raw = await readMarkdown(path6.join(this.paths.tasks(slug), file), z13.record(z13.string(), z13.unknown()));
|
|
3179
3395
|
const legacy = raw.data["thread"];
|
|
3180
3396
|
if (legacy === void 0) {
|
|
3181
3397
|
continue;
|
|
@@ -3253,7 +3469,7 @@ var Board = class _Board {
|
|
|
3253
3469
|
* turns, so it follows nothing now.
|
|
3254
3470
|
*/
|
|
3255
3471
|
async renameInboxCursors() {
|
|
3256
|
-
const Legacy =
|
|
3472
|
+
const Legacy = z13.object({ inbox: z13.string().nullable() });
|
|
3257
3473
|
for (const agent of await this.listAgents()) {
|
|
3258
3474
|
const file = this.paths.agentCursors(agent.name);
|
|
3259
3475
|
if (await exists(file)) {
|
|
@@ -3299,7 +3515,7 @@ ${charter.purpose}
|
|
|
3299
3515
|
*/
|
|
3300
3516
|
async alignSeedRole(seed) {
|
|
3301
3517
|
const file = this.paths.role(seed.name);
|
|
3302
|
-
const raw = await readMarkdown(file,
|
|
3518
|
+
const raw = await readMarkdown(file, z13.record(z13.string(), z13.unknown()));
|
|
3303
3519
|
const current = RoleCharterSchema.parse(raw.data);
|
|
3304
3520
|
const verbsAdded = seed.verbs.filter((verb) => !current.verbs.includes(verb));
|
|
3305
3521
|
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));
|
|
@@ -3612,14 +3828,14 @@ ${charter.purpose}
|
|
|
3612
3828
|
/** The society's norms, the one knowledge topic every turn loads. Empty until the steward writes it. */
|
|
3613
3829
|
async readSocietyNorms() {
|
|
3614
3830
|
const file = this.paths.societyKnowledgeFile("norms");
|
|
3615
|
-
return await exists(file) ? (await readMarkdown(file,
|
|
3831
|
+
return await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
|
|
3616
3832
|
}
|
|
3617
3833
|
/** Knowledge topics of a project, or of the society when the project is null. */
|
|
3618
3834
|
async listKnowledge(project) {
|
|
3619
3835
|
const dir = project === null ? this.paths.societyKnowledge() : this.paths.projectKnowledge(project);
|
|
3620
3836
|
const topics = [];
|
|
3621
3837
|
for (const file of await listFiles(dir)) {
|
|
3622
|
-
const doc = await readMarkdown(path6.join(dir, file),
|
|
3838
|
+
const doc = await readMarkdown(path6.join(dir, file), z13.record(z13.string(), z13.unknown()));
|
|
3623
3839
|
const parsed = KnowledgeSchema.safeParse({
|
|
3624
3840
|
...doc.data,
|
|
3625
3841
|
project,
|
|
@@ -4019,6 +4235,21 @@ ${DASHBOARD_LINE}
|
|
|
4019
4235
|
await this.events.append("paused.changed", actor.name, { paused });
|
|
4020
4236
|
});
|
|
4021
4237
|
}
|
|
4238
|
+
/** Sets the society's time zone, the user's alone: crons that name none are read in it. */
|
|
4239
|
+
async setTimezone(actor, zone) {
|
|
4240
|
+
this.assertUser(actor, "only the user sets the society's time zone");
|
|
4241
|
+
const timezone = TimeZoneSchema.parse(zone);
|
|
4242
|
+
return this.mutex.run(async () => {
|
|
4243
|
+
const doc = await readMarkdown(this.paths.societyFile(), SocietySchema);
|
|
4244
|
+
const society = { ...doc.data, timezone };
|
|
4245
|
+
await writeMarkdown(this.paths.societyFile(), society, doc.body);
|
|
4246
|
+
await this.events.append("society.configured", actor.name, {
|
|
4247
|
+
timezone,
|
|
4248
|
+
previousTimezone: doc.data.timezone
|
|
4249
|
+
});
|
|
4250
|
+
return society;
|
|
4251
|
+
});
|
|
4252
|
+
}
|
|
4022
4253
|
/** Releases every claim whose lease has expired. Called by the scheduler on its tick. */
|
|
4023
4254
|
async expireLeases() {
|
|
4024
4255
|
return this.mutex.run(async () => {
|
|
@@ -4441,6 +4672,241 @@ ${current.body}`);
|
|
|
4441
4672
|
return updated;
|
|
4442
4673
|
});
|
|
4443
4674
|
}
|
|
4675
|
+
/** Every cron, ended ones included, oldest first. */
|
|
4676
|
+
async listCrons() {
|
|
4677
|
+
const crons = [];
|
|
4678
|
+
for (const file of await listFiles(this.paths.crons())) {
|
|
4679
|
+
crons.push(await this.readCronFile(path6.join(this.paths.crons(), file)));
|
|
4680
|
+
}
|
|
4681
|
+
return crons;
|
|
4682
|
+
}
|
|
4683
|
+
async readCron(id) {
|
|
4684
|
+
const file = this.paths.cron(id);
|
|
4685
|
+
if (!await exists(file)) {
|
|
4686
|
+
throw new BoardError("NOT_FOUND", `cron ${id} not found`);
|
|
4687
|
+
}
|
|
4688
|
+
return this.readCronFile(file);
|
|
4689
|
+
}
|
|
4690
|
+
/**
|
|
4691
|
+
* Sets a cron: one that wakes its setter, or, set by a planning role, any citizen. Without a
|
|
4692
|
+
* place named it fires in the conversation of the turn that set it.
|
|
4693
|
+
*/
|
|
4694
|
+
async createCron(actor, input) {
|
|
4695
|
+
const args = VerbInputs.create_cron.parse(input);
|
|
4696
|
+
await this.authorize(actor, "create_cron");
|
|
4697
|
+
const target = args.agent ?? actor.name;
|
|
4698
|
+
this.assertMayCron(actor, target, "set");
|
|
4699
|
+
if (args.cron === void 0 === (args.at === void 0)) {
|
|
4700
|
+
throw new BoardError("VALIDATION", "a cron needs either cron, a five-field expression, or at, one time");
|
|
4701
|
+
}
|
|
4702
|
+
return this.mutex.run(async () => {
|
|
4703
|
+
const agent = await this.readAgent(target);
|
|
4704
|
+
if (agent.status !== "active" || agent.cli === null) {
|
|
4705
|
+
throw new BoardError("INVALID_STATE", `${target} takes no turns, so no cron can wake it`);
|
|
4706
|
+
}
|
|
4707
|
+
const where = await this.cronConversation(actor, agent, args);
|
|
4708
|
+
const now = this.now();
|
|
4709
|
+
const schedule = await this.cronSchedule(args, void 0, now);
|
|
4710
|
+
if (schedule === void 0) {
|
|
4711
|
+
throw new BoardError("VALIDATION", "a cron needs a schedule");
|
|
4712
|
+
}
|
|
4713
|
+
const cron = await this.writeCron({
|
|
4714
|
+
id: this.newId(),
|
|
4715
|
+
title: args.title,
|
|
4716
|
+
agent: agent.name,
|
|
4717
|
+
...where,
|
|
4718
|
+
schedule,
|
|
4719
|
+
createdBy: actor.name,
|
|
4720
|
+
createdAt: now.toISOString(),
|
|
4721
|
+
since: now.toISOString(),
|
|
4722
|
+
failures: 0,
|
|
4723
|
+
note: args.note
|
|
4724
|
+
});
|
|
4725
|
+
await this.events.append("cron.created", actor.name, {
|
|
4726
|
+
...this.cronPayload(cron),
|
|
4727
|
+
title: cron.title,
|
|
4728
|
+
schedule: cron.schedule
|
|
4729
|
+
});
|
|
4730
|
+
return cron;
|
|
4731
|
+
});
|
|
4732
|
+
}
|
|
4733
|
+
/** Pauses, resumes, retitles, rewrites, or reschedules a cron; resuming or rescheduling starts it afresh. */
|
|
4734
|
+
async updateCron(actor, input) {
|
|
4735
|
+
const args = VerbInputs.update_cron.parse(input);
|
|
4736
|
+
await this.authorize(actor, "update_cron");
|
|
4737
|
+
if (args.cron !== void 0 && args.at !== void 0) {
|
|
4738
|
+
throw new BoardError("VALIDATION", "a cron fires on an expression or at one time, not both");
|
|
4739
|
+
}
|
|
4740
|
+
return this.mutex.run(async () => {
|
|
4741
|
+
const current = await this.readCron(args.cron_id);
|
|
4742
|
+
this.assertMayCron(actor, current.agent, "change");
|
|
4743
|
+
if (current.ended !== void 0) {
|
|
4744
|
+
throw new BoardError("INVALID_STATE", `cron ${current.id} has ended (${current.ended.reason})`);
|
|
4745
|
+
}
|
|
4746
|
+
const now = this.now();
|
|
4747
|
+
const schedule = await this.cronSchedule(args, current.schedule, now);
|
|
4748
|
+
const resumed = args.paused === false && current.paused !== void 0;
|
|
4749
|
+
const changed = [
|
|
4750
|
+
...args.title === void 0 ? [] : ["title"],
|
|
4751
|
+
...args.note === void 0 ? [] : ["note"],
|
|
4752
|
+
...schedule === void 0 ? [] : ["schedule"],
|
|
4753
|
+
...args.paused === true && current.paused === void 0 ? ["paused"] : [],
|
|
4754
|
+
...resumed ? ["resumed"] : []
|
|
4755
|
+
];
|
|
4756
|
+
if (changed.length === 0 && args.paused === void 0) {
|
|
4757
|
+
throw new BoardError("VALIDATION", "update_cron needs paused, title, note, cron, timezone, or at");
|
|
4758
|
+
}
|
|
4759
|
+
const ts = now.toISOString();
|
|
4760
|
+
const { paused: wasPaused, ...rest } = current;
|
|
4761
|
+
const paused = args.paused === void 0 ? wasPaused : args.paused ? wasPaused ?? { at: ts, by: actor.name } : void 0;
|
|
4762
|
+
const cron = await this.writeCron({
|
|
4763
|
+
...rest,
|
|
4764
|
+
...args.title === void 0 ? {} : { title: args.title },
|
|
4765
|
+
...args.note === void 0 ? {} : { note: args.note },
|
|
4766
|
+
...schedule === void 0 ? {} : { schedule },
|
|
4767
|
+
...schedule !== void 0 || resumed ? { since: ts } : {},
|
|
4768
|
+
...paused === void 0 ? {} : { paused }
|
|
4769
|
+
});
|
|
4770
|
+
if (changed.length > 0) {
|
|
4771
|
+
await this.events.append("cron.updated", actor.name, {
|
|
4772
|
+
...this.cronPayload(cron),
|
|
4773
|
+
changed,
|
|
4774
|
+
...schedule === void 0 ? {} : { schedule }
|
|
4775
|
+
});
|
|
4776
|
+
}
|
|
4777
|
+
return cron;
|
|
4778
|
+
});
|
|
4779
|
+
}
|
|
4780
|
+
/** Ends a cron for good: its setter's, or anyone's for a planning role. */
|
|
4781
|
+
async removeCron(actor, input) {
|
|
4782
|
+
const args = VerbInputs.remove_cron.parse(input);
|
|
4783
|
+
await this.authorize(actor, "remove_cron");
|
|
4784
|
+
return this.mutex.run(async () => {
|
|
4785
|
+
const current = await this.readCron(args.cron_id);
|
|
4786
|
+
this.assertMayCron(actor, current.agent, "end");
|
|
4787
|
+
if (current.ended !== void 0) {
|
|
4788
|
+
throw new BoardError("INVALID_STATE", `cron ${current.id} has ended already (${current.ended.reason})`);
|
|
4789
|
+
}
|
|
4790
|
+
return this.endCronUnlocked(current, actor.name, args.reason);
|
|
4791
|
+
});
|
|
4792
|
+
}
|
|
4793
|
+
/** A cron wakes its setter; one that wakes another citizen is the planning roles' to set, change, and end. */
|
|
4794
|
+
assertMayCron(actor, agent, what) {
|
|
4795
|
+
if (agent !== actor.name && !PLANNING_ROLES.includes(actor.role)) {
|
|
4796
|
+
throw new BoardError("FORBIDDEN", `only the user, the steward, and the concierge ${what} a cron that wakes another citizen`);
|
|
4797
|
+
}
|
|
4798
|
+
}
|
|
4799
|
+
/**
|
|
4800
|
+
* Where a cron fires for its citizen: the thread, the channel, or the scope's home it names, else
|
|
4801
|
+
* the conversation of the turn that set it when it wakes its setter, else its home outside projects.
|
|
4802
|
+
*/
|
|
4803
|
+
async cronConversation(actor, agent, named) {
|
|
4804
|
+
const own = actor.name === agent.name;
|
|
4805
|
+
if (named.project === void 0 && named.thread_id === void 0 && named.channel === void 0) {
|
|
4806
|
+
return own && actor.scope !== void 0 ? this.cronConversation(actor, agent, {
|
|
4807
|
+
project: actor.scope,
|
|
4808
|
+
thread_id: actor.thread,
|
|
4809
|
+
channel: actor.channel
|
|
4810
|
+
}) : { scope: SOCIETY_SCOPE };
|
|
4811
|
+
}
|
|
4812
|
+
if (named.thread_id !== void 0) {
|
|
4813
|
+
const { thread } = await this.findThread(named.thread_id);
|
|
4814
|
+
if (thread.state !== "open") {
|
|
4815
|
+
throw new BoardError("INVALID_STATE", `thread ${thread.id} is closed`);
|
|
4816
|
+
}
|
|
4817
|
+
const scope2 = wakeScope(agent, parseChannelRef(thread.channel).project);
|
|
4818
|
+
if (named.project !== void 0 && named.project !== scope2) {
|
|
4819
|
+
throw new BoardError("VALIDATION", `thread ${thread.id} is on ${thread.channel}, so ${agent.name}'s conversation there is in ${scope2}, not ${named.project}`);
|
|
4820
|
+
}
|
|
4821
|
+
return { scope: scope2, thread: thread.id };
|
|
4822
|
+
}
|
|
4823
|
+
const scope = named.project ?? (own ? actor.scope : void 0) ?? SOCIETY_SCOPE;
|
|
4824
|
+
if (scope !== SOCIETY_SCOPE) {
|
|
4825
|
+
await this.readActiveProject(scope);
|
|
4826
|
+
if (!agent.memberships.includes(scope)) {
|
|
4827
|
+
throw new BoardError("VALIDATION", `${agent.name} is not a member of ${scope}`);
|
|
4828
|
+
}
|
|
4829
|
+
}
|
|
4830
|
+
if (named.channel === void 0 || named.channel === "general") {
|
|
4831
|
+
return { scope };
|
|
4832
|
+
}
|
|
4833
|
+
await this.assertChannelOpen(channelRef(scope === SOCIETY_SCOPE ? null : scope, named.channel));
|
|
4834
|
+
return { scope, channel: named.channel };
|
|
4835
|
+
}
|
|
4836
|
+
/**
|
|
4837
|
+
* The schedule a cron verb asks for, or undefined when it asks for none: an expression, read in
|
|
4838
|
+
* its zone or the society's, which must name a time to come and no two fires closer than the
|
|
4839
|
+
* floor, or one time to come. A zone alone keeps the expression of `existing`.
|
|
4840
|
+
*/
|
|
4841
|
+
async cronSchedule(args, existing, now) {
|
|
4842
|
+
if (args.at !== void 0) {
|
|
4843
|
+
if (args.timezone !== void 0) {
|
|
4844
|
+
throw new BoardError("VALIDATION", "at carries its own offset; timezone is for a cron expression");
|
|
4845
|
+
}
|
|
4846
|
+
const at = new Date(args.at);
|
|
4847
|
+
if (at <= now) {
|
|
4848
|
+
throw new BoardError("VALIDATION", `${args.at} has passed; a cron fires at a time to come`);
|
|
4849
|
+
}
|
|
4850
|
+
return { at: at.toISOString() };
|
|
4851
|
+
}
|
|
4852
|
+
if (args.cron === void 0 && args.timezone === void 0) {
|
|
4853
|
+
return void 0;
|
|
4854
|
+
}
|
|
4855
|
+
const expression = args.cron ?? (existing === void 0 || isOneTime(existing) ? void 0 : existing.cron);
|
|
4856
|
+
if (expression === void 0) {
|
|
4857
|
+
throw new BoardError("VALIDATION", "timezone is for a cron expression, which this cron has not");
|
|
4858
|
+
}
|
|
4859
|
+
const kept = args.timezone === void 0 && existing !== void 0 && !isOneTime(existing) ? existing.timezone : args.timezone ?? void 0;
|
|
4860
|
+
const schedule = {
|
|
4861
|
+
cron: expression,
|
|
4862
|
+
...kept === void 0 ? {} : { timezone: kept }
|
|
4863
|
+
};
|
|
4864
|
+
const { timezone: societyZone } = await this.society();
|
|
4865
|
+
if (fireTimes(schedule, societyZone, now, 1).length === 0) {
|
|
4866
|
+
throw new BoardError("VALIDATION", `${expression} names no time to come`);
|
|
4867
|
+
}
|
|
4868
|
+
const gap = shortestGapMs(schedule, societyZone, now);
|
|
4869
|
+
if (gap !== null && gap < CRON_FLOOR_MS) {
|
|
4870
|
+
throw new BoardError("VALIDATION", `${expression} fires ${Math.round(gap / 6e4)} minute(s) apart at its closest; a cron fires at most every ${CRON_FLOOR_MS / 6e4} minutes, and every fire is a turn`);
|
|
4871
|
+
}
|
|
4872
|
+
return schedule;
|
|
4873
|
+
}
|
|
4874
|
+
/** A cron's events name its citizen and the conversation it fires in, as turn events do. */
|
|
4875
|
+
cronPayload(cron) {
|
|
4876
|
+
return {
|
|
4877
|
+
cronId: cron.id,
|
|
4878
|
+
agent: cron.agent,
|
|
4879
|
+
project: cron.scope,
|
|
4880
|
+
...cron.thread === void 0 ? {} : { thread: cron.thread },
|
|
4881
|
+
...cron.channel === void 0 ? {} : { channel: cron.channel }
|
|
4882
|
+
};
|
|
4883
|
+
}
|
|
4884
|
+
async readCronFile(file) {
|
|
4885
|
+
const doc = await readMarkdown(file, CronFrontmatterSchema);
|
|
4886
|
+
return { ...doc.data, note: doc.body.trimEnd() };
|
|
4887
|
+
}
|
|
4888
|
+
async writeCron(cron) {
|
|
4889
|
+
const { note, ...frontmatter } = cron;
|
|
4890
|
+
const data = CronFrontmatterSchema.parse(Object.fromEntries(Object.entries(frontmatter).filter(([, value]) => value !== void 0)));
|
|
4891
|
+
await writeMarkdown(this.paths.cron(cron.id), data, note);
|
|
4892
|
+
return { ...data, note };
|
|
4893
|
+
}
|
|
4894
|
+
async endCronUnlocked(cron, by, reason) {
|
|
4895
|
+
const ended = await this.writeCron({
|
|
4896
|
+
...cron,
|
|
4897
|
+
ended: { at: this.now().toISOString(), by, reason }
|
|
4898
|
+
});
|
|
4899
|
+
await this.events.append("cron.ended", by, { ...this.cronPayload(ended), reason });
|
|
4900
|
+
return ended;
|
|
4901
|
+
}
|
|
4902
|
+
/** Ends every running cron `ends` picks, in the transaction that ends the conversation it fires in. */
|
|
4903
|
+
async endCronsUnlocked(by, reason, ends) {
|
|
4904
|
+
for (const cron of await this.listCrons()) {
|
|
4905
|
+
if (cron.ended === void 0 && ends(cron)) {
|
|
4906
|
+
await this.endCronUnlocked(cron, by, reason);
|
|
4907
|
+
}
|
|
4908
|
+
}
|
|
4909
|
+
}
|
|
4444
4910
|
async openThread(actor, input) {
|
|
4445
4911
|
const args = VerbInputs.open_thread.parse(input);
|
|
4446
4912
|
await this.authorize(actor, "open_thread");
|
|
@@ -4657,6 +5123,7 @@ ${args.summary}`);
|
|
|
4657
5123
|
threadId: thread.id,
|
|
4658
5124
|
channel: thread.channel
|
|
4659
5125
|
});
|
|
5126
|
+
await this.endCronsUnlocked(actor.name, "its thread closed", (cron) => cron.thread === thread.id);
|
|
4660
5127
|
return summary;
|
|
4661
5128
|
});
|
|
4662
5129
|
}
|
|
@@ -5336,6 +5803,7 @@ ${rationale}`}`);
|
|
|
5336
5803
|
members: project.members.filter((member) => member !== target)
|
|
5337
5804
|
}));
|
|
5338
5805
|
await this.refreshMember(target);
|
|
5806
|
+
await this.endCronsUnlocked(actor.name, `${target} left ${args.project}`, (cron) => cron.agent === target && cron.scope === args.project);
|
|
5339
5807
|
await this.events.append("agent.left", actor.name, {
|
|
5340
5808
|
name: target,
|
|
5341
5809
|
project: args.project,
|
|
@@ -5404,6 +5872,12 @@ ${rationale}`}`);
|
|
|
5404
5872
|
return this.archiveChannel(actor, VerbInputs.archive_channel.parse(input));
|
|
5405
5873
|
case "update_dashboard":
|
|
5406
5874
|
return this.updateDashboard(actor, VerbInputs.update_dashboard.parse(input));
|
|
5875
|
+
case "create_cron":
|
|
5876
|
+
return this.createCron(actor, VerbInputs.create_cron.parse(input));
|
|
5877
|
+
case "update_cron":
|
|
5878
|
+
return this.updateCron(actor, VerbInputs.update_cron.parse(input));
|
|
5879
|
+
case "remove_cron":
|
|
5880
|
+
return this.removeCron(actor, VerbInputs.remove_cron.parse(input));
|
|
5407
5881
|
default:
|
|
5408
5882
|
throw new BoardError("VALIDATION", `unknown verb ${String(verb)}`);
|
|
5409
5883
|
}
|
|
@@ -5450,7 +5924,7 @@ ${rationale}`}`);
|
|
|
5450
5924
|
}
|
|
5451
5925
|
async readAgentRoleBody(agent) {
|
|
5452
5926
|
const file = this.paths.agentRole(agent);
|
|
5453
|
-
return await exists(file) ? (await readMarkdown(file,
|
|
5927
|
+
return await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
|
|
5454
5928
|
}
|
|
5455
5929
|
/** The steps of one finished turn, as the runner handed them over when it ended. */
|
|
5456
5930
|
/**
|
|
@@ -5628,6 +6102,9 @@ ${rationale}`}`);
|
|
|
5628
6102
|
}
|
|
5629
6103
|
await this.writeTurnRecord(parsed);
|
|
5630
6104
|
await this.refreshMember(parsed.agent);
|
|
6105
|
+
for (const id of parsed.crons ?? []) {
|
|
6106
|
+
await this.recordCronRun(id, parsed, failed);
|
|
6107
|
+
}
|
|
5631
6108
|
if (parsed.exitReason === "stopped") {
|
|
5632
6109
|
await this.events.append("turn.stopped", parsed.stoppedBy ?? USER_NAME, {
|
|
5633
6110
|
...parsed.id === void 0 ? {} : { turnId: parsed.id },
|
|
@@ -5709,6 +6186,45 @@ ${rationale}`}`);
|
|
|
5709
6186
|
return true;
|
|
5710
6187
|
});
|
|
5711
6188
|
}
|
|
6189
|
+
/**
|
|
6190
|
+
* Records a cron's fire, which the scheduler made; a one-time cron ends with it. Null, recording
|
|
6191
|
+
* nothing, for a cron ended or paused since the scheduler read it, which does not fire.
|
|
6192
|
+
*/
|
|
6193
|
+
async cronFired(id, at) {
|
|
6194
|
+
return this.mutex.run(async () => {
|
|
6195
|
+
const current = await this.readCron(id);
|
|
6196
|
+
if (current.ended !== void 0 || current.paused !== void 0) {
|
|
6197
|
+
return null;
|
|
6198
|
+
}
|
|
6199
|
+
const cron = await this.writeCron({ ...current, lastFiredAt: at });
|
|
6200
|
+
await this.events.append("cron.fired", SYSTEM_ACTOR.name, this.cronPayload(cron));
|
|
6201
|
+
return isOneTime(cron.schedule) ? this.endCronUnlocked(cron, SYSTEM_ACTOR.name, "fired once, as set") : cron;
|
|
6202
|
+
});
|
|
6203
|
+
}
|
|
6204
|
+
/** Records a time a cron passed because the turn of its previous fire was still queued or running. */
|
|
6205
|
+
async cronSkipped(id, at) {
|
|
6206
|
+
return this.mutex.run(async () => {
|
|
6207
|
+
const cron = await this.writeCron({ ...await this.readCron(id), lastSkippedAt: at });
|
|
6208
|
+
await this.events.append("cron.skipped", SYSTEM_ACTOR.name, this.cronPayload(cron));
|
|
6209
|
+
return cron;
|
|
6210
|
+
});
|
|
6211
|
+
}
|
|
6212
|
+
/**
|
|
6213
|
+
* How the turn a cron's fire started ended. A failure counts toward `cron_failing`, a completed
|
|
6214
|
+
* turn clears the count, and a stop, the user's decision, leaves it.
|
|
6215
|
+
*/
|
|
6216
|
+
async recordCronRun(id, record, failed) {
|
|
6217
|
+
if (record.id === void 0 || !await exists(this.paths.cron(id))) {
|
|
6218
|
+
return;
|
|
6219
|
+
}
|
|
6220
|
+
const cron = await this.readCron(id);
|
|
6221
|
+
const outcome = record.exitReason === "stopped" ? "stopped" : failed ? "failed" : "completed";
|
|
6222
|
+
await this.writeCron({
|
|
6223
|
+
...cron,
|
|
6224
|
+
lastRun: { turnId: record.id, at: record.endedAt ?? this.now().toISOString(), outcome },
|
|
6225
|
+
failures: outcome === "failed" ? cron.failures + 1 : outcome === "completed" ? 0 : cron.failures
|
|
6226
|
+
});
|
|
6227
|
+
}
|
|
5712
6228
|
/**
|
|
5713
6229
|
* Whether a conversation with a workspace of its own has ended: a thread closed, a task ended
|
|
5714
6230
|
* (for one from before tasks had threads), or a channel archived.
|
|
@@ -5808,7 +6324,7 @@ ${rationale}`}`);
|
|
|
5808
6324
|
return { project: slug, revision: 0, body: "" };
|
|
5809
6325
|
}
|
|
5810
6326
|
const { data, body } = await readLooseMarkdown(file);
|
|
5811
|
-
const revision =
|
|
6327
|
+
const revision = z13.number().int().min(0).safeParse(data["revision"]);
|
|
5812
6328
|
const updatedBy = NameSchema.safeParse(data["updatedBy"]);
|
|
5813
6329
|
const updatedAt = IsoDateTimeSchema.safeParse(data["updatedAt"]);
|
|
5814
6330
|
return {
|
|
@@ -6099,7 +6615,7 @@ ${seed}
|
|
|
6099
6615
|
continue;
|
|
6100
6616
|
}
|
|
6101
6617
|
const file = this.paths.agentRole(agent.name);
|
|
6102
|
-
const body = await exists(file) ? (await readMarkdown(file,
|
|
6618
|
+
const body = await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
|
|
6103
6619
|
const at = body.indexOf(`
|
|
6104
6620
|
${SEED_INSTRUCTIONS_HEADING}
|
|
6105
6621
|
`);
|
|
@@ -6543,6 +7059,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
|
|
|
6543
7059
|
archivedChannels: [...current.archivedChannels, archived]
|
|
6544
7060
|
}));
|
|
6545
7061
|
}
|
|
7062
|
+
await this.endCronsUnlocked(by, `${ref} was archived`, (cron) => cron.scope === scope && cron.channel === channel || cron.thread !== void 0 && threadsClosed.includes(cron.thread));
|
|
6546
7063
|
await this.events.append("channel.archived", by, {
|
|
6547
7064
|
channel: ref,
|
|
6548
7065
|
reason,
|
|
@@ -6647,6 +7164,7 @@ ${charter.purpose}
|
|
|
6647
7164
|
});
|
|
6648
7165
|
threadsClosed.push(doc.data.id);
|
|
6649
7166
|
}
|
|
7167
|
+
await this.endCronsUnlocked(by, `project ${slug} was archived`, (cron) => cron.scope === slug || cron.thread !== void 0 && threadsClosed.includes(cron.thread));
|
|
6650
7168
|
const project = await this.updateProject(slug, (p) => ({
|
|
6651
7169
|
...p,
|
|
6652
7170
|
members: [],
|
|
@@ -6711,6 +7229,7 @@ ${charter.purpose}
|
|
|
6711
7229
|
}
|
|
6712
7230
|
this.tokenIndex.delete(current.tokenHash);
|
|
6713
7231
|
await this.refreshMember(name);
|
|
7232
|
+
await this.endCronsUnlocked(by, `${name} was retired`, (cron) => cron.agent === name);
|
|
6714
7233
|
await this.events.append("agent.retired", by, {
|
|
6715
7234
|
name,
|
|
6716
7235
|
role: agent.role,
|
|
@@ -7022,6 +7541,7 @@ ${charter.purpose}
|
|
|
7022
7541
|
channel: found.thread.channel,
|
|
7023
7542
|
ended
|
|
7024
7543
|
});
|
|
7544
|
+
await this.endCronsUnlocked(by, `its thread closed: its ${found.thread.subject?.kind ?? "subject"} was ${ended}`, (cron) => cron.thread === subjectId);
|
|
7025
7545
|
}
|
|
7026
7546
|
async readMessagesIn(dir, since) {
|
|
7027
7547
|
const messages = [];
|
|
@@ -7085,6 +7605,8 @@ function decideWake(input) {
|
|
|
7085
7605
|
return { wake: true, reason: "user post", priority: 2 };
|
|
7086
7606
|
case "closing":
|
|
7087
7607
|
return { wake: true, reason: "an ended conversation left work on no branch", priority: 1 };
|
|
7608
|
+
case "cron":
|
|
7609
|
+
return { wake: true, reason: "a cron came due", priority: 1 };
|
|
7088
7610
|
case "heartbeat":
|
|
7089
7611
|
break;
|
|
7090
7612
|
}
|
|
@@ -7099,7 +7621,7 @@ function decideWake(input) {
|
|
|
7099
7621
|
}
|
|
7100
7622
|
|
|
7101
7623
|
// packages/scheduler/dist/scheduler.js
|
|
7102
|
-
import { z as
|
|
7624
|
+
import { z as z14 } from "zod";
|
|
7103
7625
|
function besideFields(beside) {
|
|
7104
7626
|
if (beside === void 0) {
|
|
7105
7627
|
return {};
|
|
@@ -7126,21 +7648,21 @@ var DEFAULT_TIMINGS = Object.freeze({
|
|
|
7126
7648
|
scaleCooldownMs: 36e5,
|
|
7127
7649
|
reflectionMs: 24 * 36e5
|
|
7128
7650
|
});
|
|
7129
|
-
var SchedulerTimingsSchema =
|
|
7130
|
-
pollMs:
|
|
7131
|
-
debounceMs:
|
|
7132
|
-
userDebounceMs:
|
|
7133
|
-
heartbeatMs:
|
|
7134
|
-
waitingStageMs:
|
|
7135
|
-
leaseSweepMs:
|
|
7136
|
-
opsIntervalMs:
|
|
7137
|
-
signalRepeatMs:
|
|
7138
|
-
costReportMs:
|
|
7139
|
-
staleThreadMs:
|
|
7140
|
-
idleMemberMs:
|
|
7141
|
-
homeConflictMs:
|
|
7142
|
-
scaleCooldownMs:
|
|
7143
|
-
reflectionMs:
|
|
7651
|
+
var SchedulerTimingsSchema = z14.object({
|
|
7652
|
+
pollMs: z14.number().positive(),
|
|
7653
|
+
debounceMs: z14.number().nonnegative(),
|
|
7654
|
+
userDebounceMs: z14.number().nonnegative(),
|
|
7655
|
+
heartbeatMs: z14.number().positive(),
|
|
7656
|
+
waitingStageMs: z14.number().positive(),
|
|
7657
|
+
leaseSweepMs: z14.number().positive(),
|
|
7658
|
+
opsIntervalMs: z14.number().positive(),
|
|
7659
|
+
signalRepeatMs: z14.number().positive(),
|
|
7660
|
+
costReportMs: z14.number().positive(),
|
|
7661
|
+
staleThreadMs: z14.number().positive(),
|
|
7662
|
+
idleMemberMs: z14.number().positive(),
|
|
7663
|
+
homeConflictMs: z14.number().positive(),
|
|
7664
|
+
scaleCooldownMs: z14.number().positive(),
|
|
7665
|
+
reflectionMs: z14.number().positive()
|
|
7144
7666
|
}).partial();
|
|
7145
7667
|
function parseTimings(input) {
|
|
7146
7668
|
const parsed = SchedulerTimingsSchema.parse(input);
|
|
@@ -7152,31 +7674,32 @@ function parseTimings(input) {
|
|
|
7152
7674
|
}
|
|
7153
7675
|
return overrides;
|
|
7154
7676
|
}
|
|
7155
|
-
var PendingSchema =
|
|
7677
|
+
var PendingSchema = z14.object({
|
|
7156
7678
|
dispatch: TurnDispatchSchema,
|
|
7157
|
-
readyAt:
|
|
7158
|
-
stageWakes:
|
|
7159
|
-
holder:
|
|
7679
|
+
readyAt: z14.number(),
|
|
7680
|
+
stageWakes: z14.array(TriggerSchema).optional(),
|
|
7681
|
+
holder: z14.boolean().optional(),
|
|
7160
7682
|
newestMessage: UlidSchema.optional(),
|
|
7161
|
-
onlyMessages:
|
|
7162
|
-
steeredThrough: UlidSchema.optional()
|
|
7683
|
+
onlyMessages: z14.boolean().optional(),
|
|
7684
|
+
steeredThrough: UlidSchema.optional(),
|
|
7685
|
+
onlyCrons: z14.boolean().optional()
|
|
7163
7686
|
});
|
|
7164
|
-
var StateSchema =
|
|
7165
|
-
cursor:
|
|
7166
|
-
lastHeartbeat:
|
|
7687
|
+
var StateSchema = z14.object({
|
|
7688
|
+
cursor: z14.string().nullable(),
|
|
7689
|
+
lastHeartbeat: z14.record(z14.string(), z14.string()),
|
|
7167
7690
|
/** Waiting stages already signalled, as `task:stage`, so each is logged once while it waits. */
|
|
7168
|
-
waitingSeen:
|
|
7691
|
+
waitingSeen: z14.record(z14.string(), z14.string()).default({}),
|
|
7169
7692
|
/** Queued dispatches survive a restart; the cursor has already moved past the events that made them. */
|
|
7170
|
-
pending:
|
|
7171
|
-
opsReported:
|
|
7172
|
-
releaseCounts:
|
|
7173
|
-
lastOps:
|
|
7174
|
-
lastScaled:
|
|
7175
|
-
lastCostReport:
|
|
7176
|
-
costSinceReport:
|
|
7177
|
-
turnsSinceReport:
|
|
7693
|
+
pending: z14.record(z14.string(), PendingSchema).default({}),
|
|
7694
|
+
opsReported: z14.record(z14.string(), z14.string()).default({}),
|
|
7695
|
+
releaseCounts: z14.record(z14.string(), z14.number().int().nonnegative()).default({}),
|
|
7696
|
+
lastOps: z14.string().nullable().default(null),
|
|
7697
|
+
lastScaled: z14.record(z14.string(), z14.string()).default({}),
|
|
7698
|
+
lastCostReport: z14.string().nullable().default(null),
|
|
7699
|
+
costSinceReport: z14.number().nonnegative().default(0),
|
|
7700
|
+
turnsSinceReport: z14.number().int().nonnegative().default(0),
|
|
7178
7701
|
/** When each member last reflected, or was first seen; the cadence counts from here. */
|
|
7179
|
-
lastReflection:
|
|
7702
|
+
lastReflection: z14.record(z14.string(), z14.string()).default({})
|
|
7180
7703
|
});
|
|
7181
7704
|
function messageOf(trigger) {
|
|
7182
7705
|
return trigger.kind === "mention" || trigger.kind === "user_post" ? trigger.messageId : void 0;
|
|
@@ -7247,6 +7770,8 @@ var Scheduler = class {
|
|
|
7247
7770
|
landing = /* @__PURE__ */ new Set();
|
|
7248
7771
|
/** Sweeps of ended conversations' workspaces under way, which `drain` and `stop` wait for. */
|
|
7249
7772
|
sweeping = /* @__PURE__ */ new Set();
|
|
7773
|
+
/** Crons whose fire's turn is running; a cron's next time passes while one is. */
|
|
7774
|
+
cronsInFlight = /* @__PURE__ */ new Set();
|
|
7250
7775
|
state = StateSchema.parse({
|
|
7251
7776
|
cursor: null,
|
|
7252
7777
|
lastHeartbeat: {}
|
|
@@ -7326,6 +7851,7 @@ var Scheduler = class {
|
|
|
7326
7851
|
this.state.cursor = event.id;
|
|
7327
7852
|
}
|
|
7328
7853
|
if (!paused) {
|
|
7854
|
+
await this.checkCrons(now);
|
|
7329
7855
|
await this.checkHeartbeats(now);
|
|
7330
7856
|
await this.checkWaitingStages(now);
|
|
7331
7857
|
await this.checkReflections(now);
|
|
@@ -7583,6 +8109,13 @@ var Scheduler = class {
|
|
|
7583
8109
|
}
|
|
7584
8110
|
return;
|
|
7585
8111
|
}
|
|
8112
|
+
case "cron.ended": {
|
|
8113
|
+
const cronId = stringOf(payload["cronId"]);
|
|
8114
|
+
if (cronId !== null && event.actor !== SYSTEM_ACTOR.name) {
|
|
8115
|
+
this.dropCronWakes(cronId);
|
|
8116
|
+
}
|
|
8117
|
+
return;
|
|
8118
|
+
}
|
|
7586
8119
|
case "proposal.decided": {
|
|
7587
8120
|
const proposalId = stringOf(payload["proposalId"]);
|
|
7588
8121
|
const proposer = stringOf(payload["proposedBy"]);
|
|
@@ -7914,6 +8447,7 @@ var Scheduler = class {
|
|
|
7914
8447
|
const readyAt = now + debounce;
|
|
7915
8448
|
const existing = this.pending.get(key);
|
|
7916
8449
|
const message = messageOf(trigger);
|
|
8450
|
+
const cron = trigger.kind === "cron" ? trigger.cronId : void 0;
|
|
7917
8451
|
if (existing === void 0) {
|
|
7918
8452
|
this.pending.set(key, {
|
|
7919
8453
|
dispatch: {
|
|
@@ -7922,24 +8456,30 @@ var Scheduler = class {
|
|
|
7922
8456
|
...besideFields(beside),
|
|
7923
8457
|
trigger,
|
|
7924
8458
|
priority: decision.priority,
|
|
7925
|
-
onboarding: trigger.kind === "onboarding"
|
|
8459
|
+
onboarding: trigger.kind === "onboarding",
|
|
8460
|
+
...cron === void 0 ? {} : { crons: [cron] }
|
|
7926
8461
|
},
|
|
7927
8462
|
readyAt,
|
|
7928
8463
|
...trigger.kind === "stage" ? { stageWakes: [trigger] } : {},
|
|
7929
8464
|
...message === void 0 ? {} : { newestMessage: message },
|
|
7930
|
-
onlyMessages: message !== void 0
|
|
8465
|
+
onlyMessages: message !== void 0,
|
|
8466
|
+
onlyCrons: cron !== void 0
|
|
7931
8467
|
});
|
|
7932
8468
|
return;
|
|
7933
8469
|
}
|
|
7934
8470
|
const newestMessage = later(existing.newestMessage, message);
|
|
7935
8471
|
const keepNew = decision.priority > existing.dispatch.priority;
|
|
7936
8472
|
const priority = keepNew ? decision.priority : existing.dispatch.priority;
|
|
8473
|
+
const crons = [
|
|
8474
|
+
.../* @__PURE__ */ new Set([...existing.dispatch.crons ?? [], ...cron === void 0 ? [] : [cron]])
|
|
8475
|
+
];
|
|
7937
8476
|
this.pending.set(key, {
|
|
7938
8477
|
dispatch: {
|
|
7939
8478
|
...existing.dispatch,
|
|
7940
8479
|
trigger: keepNew ? trigger : existing.dispatch.trigger,
|
|
7941
8480
|
priority,
|
|
7942
|
-
onboarding: existing.dispatch.onboarding || trigger.kind === "onboarding"
|
|
8481
|
+
onboarding: existing.dispatch.onboarding || trigger.kind === "onboarding",
|
|
8482
|
+
...crons.length === 0 ? {} : { crons }
|
|
7943
8483
|
},
|
|
7944
8484
|
readyAt: Math.min(existing.readyAt, readyAt),
|
|
7945
8485
|
...existing.stageWakes !== void 0 && trigger.kind === "stage" ? {
|
|
@@ -7948,7 +8488,8 @@ var Scheduler = class {
|
|
|
7948
8488
|
} : {},
|
|
7949
8489
|
...newestMessage === void 0 ? {} : { newestMessage },
|
|
7950
8490
|
onlyMessages: existing.onlyMessages === true && message !== void 0,
|
|
7951
|
-
...existing.steeredThrough === void 0 ? {} : { steeredThrough: existing.steeredThrough }
|
|
8491
|
+
...existing.steeredThrough === void 0 ? {} : { steeredThrough: existing.steeredThrough },
|
|
8492
|
+
onlyCrons: existing.onlyCrons === true && cron !== void 0
|
|
7952
8493
|
});
|
|
7953
8494
|
}
|
|
7954
8495
|
/** Debounce coalesces chatter: mentions, stage handoffs, and bursts of signals. Everything else runs at once. */
|
|
@@ -8058,6 +8599,71 @@ var Scheduler = class {
|
|
|
8058
8599
|
}
|
|
8059
8600
|
}
|
|
8060
8601
|
}
|
|
8602
|
+
/**
|
|
8603
|
+
* Each active cron whose next time has come wakes its citizen in its conversation, unless the turn
|
|
8604
|
+
* of its previous fire is still queued or running, in which case the time passes. The fire is
|
|
8605
|
+
* recorded on the board, so its next time counts from it and a restart does not fire it again.
|
|
8606
|
+
*/
|
|
8607
|
+
async checkCrons(now) {
|
|
8608
|
+
const crons = (await this.board.listCrons()).filter((cron) => cron.ended === void 0 && cron.paused === void 0);
|
|
8609
|
+
if (crons.length === 0) {
|
|
8610
|
+
return;
|
|
8611
|
+
}
|
|
8612
|
+
const { timezone } = await this.board.society();
|
|
8613
|
+
for (const cron of crons) {
|
|
8614
|
+
const due = nextFire(cron, timezone);
|
|
8615
|
+
if (due === null || due.getTime() > now) {
|
|
8616
|
+
continue;
|
|
8617
|
+
}
|
|
8618
|
+
if (this.cronQueuedOrRunning(cron.id)) {
|
|
8619
|
+
await this.board.cronSkipped(cron.id, iso(now));
|
|
8620
|
+
this.log.info({ cron: cron.id, agent: cron.agent }, "cron time passed, its previous turn has not ended");
|
|
8621
|
+
continue;
|
|
8622
|
+
}
|
|
8623
|
+
const agent = await this.tryReadAgent(cron.agent);
|
|
8624
|
+
if (agent === null) {
|
|
8625
|
+
continue;
|
|
8626
|
+
}
|
|
8627
|
+
if (await this.board.cronFired(cron.id, iso(now)) === null) {
|
|
8628
|
+
continue;
|
|
8629
|
+
}
|
|
8630
|
+
this.enqueue(agent.name, cron.scope, {
|
|
8631
|
+
kind: "cron",
|
|
8632
|
+
from: cron.createdBy,
|
|
8633
|
+
fromUser: cron.createdBy === USER_NAME,
|
|
8634
|
+
reason: `cron "${cron.title}" came due`,
|
|
8635
|
+
cronId: cron.id
|
|
8636
|
+
}, now, await this.cronConversation(cron));
|
|
8637
|
+
}
|
|
8638
|
+
}
|
|
8639
|
+
/** The conversation a cron fires in, beside its scope's home or none for the home. */
|
|
8640
|
+
async cronConversation(cron) {
|
|
8641
|
+
if (cron.thread !== void 0) {
|
|
8642
|
+
return { thread: await this.conversationOf(cron.thread, cron.scope) };
|
|
8643
|
+
}
|
|
8644
|
+
return cron.channel === void 0 ? void 0 : { channel: cron.channel };
|
|
8645
|
+
}
|
|
8646
|
+
/** Whether a cron's wake waits in the queue or its turn is running. */
|
|
8647
|
+
cronQueuedOrRunning(id) {
|
|
8648
|
+
return this.cronsInFlight.has(id) || [...this.pending.values()].some((item) => item.dispatch.crons?.includes(id) === true);
|
|
8649
|
+
}
|
|
8650
|
+
/** Takes an ended cron out of the queued wakes, and drops a wake left with nothing else to run. */
|
|
8651
|
+
dropCronWakes(id) {
|
|
8652
|
+
for (const [key, item] of this.pending) {
|
|
8653
|
+
const crons = item.dispatch.crons;
|
|
8654
|
+
if (crons?.includes(id) !== true) {
|
|
8655
|
+
continue;
|
|
8656
|
+
}
|
|
8657
|
+
const rest = crons.filter((each) => each !== id);
|
|
8658
|
+
if (rest.length === 0 && item.onlyCrons === true) {
|
|
8659
|
+
this.pending.delete(key);
|
|
8660
|
+
this.log.info({ agent: item.dispatch.agent, cron: id }, "dropping cron wake, the cron ended");
|
|
8661
|
+
continue;
|
|
8662
|
+
}
|
|
8663
|
+
const { crons: _crons, ...dispatch } = item.dispatch;
|
|
8664
|
+
item.dispatch = rest.length === 0 ? dispatch : { ...dispatch, crons: rest };
|
|
8665
|
+
}
|
|
8666
|
+
}
|
|
8061
8667
|
/**
|
|
8062
8668
|
* Every `reflectionMs`, a member whose charter reflects takes a reflection turn in the scope of
|
|
8063
8669
|
* its latest working turn, unless it has not worked since its last reflection.
|
|
@@ -8272,6 +8878,7 @@ var Scheduler = class {
|
|
|
8272
8878
|
});
|
|
8273
8879
|
}
|
|
8274
8880
|
}
|
|
8881
|
+
const crons = (await this.board.listCrons()).filter((cron) => cron.ended === void 0);
|
|
8275
8882
|
for (const agent of await this.board.listAgents()) {
|
|
8276
8883
|
if (agent.status !== "active" || agent.cli === null) {
|
|
8277
8884
|
continue;
|
|
@@ -8296,6 +8903,20 @@ var Scheduler = class {
|
|
|
8296
8903
|
role: agent.role
|
|
8297
8904
|
});
|
|
8298
8905
|
}
|
|
8906
|
+
for (const cron of crons.filter((each) => each.agent === agent.name)) {
|
|
8907
|
+
if (cron.failures >= CRON_FAILING_RUNS) {
|
|
8908
|
+
signals.push({
|
|
8909
|
+
kind: "cron_failing",
|
|
8910
|
+
key: `cron_failing:${cron.id}`,
|
|
8911
|
+
summary: `${agent.name}'s cron ${cron.id} "${cron.title}" has had ${cron.failures} failed turns in a row`,
|
|
8912
|
+
value: cron.failures,
|
|
8913
|
+
threshold: CRON_FAILING_RUNS,
|
|
8914
|
+
agent: agent.name,
|
|
8915
|
+
role: agent.role,
|
|
8916
|
+
...cron.scope === SOCIETY_SCOPE ? {} : { project: cron.scope }
|
|
8917
|
+
});
|
|
8918
|
+
}
|
|
8919
|
+
}
|
|
8299
8920
|
const left = (await this.board.listHomeConflicts(agent.name)).filter((conflict) => now - Date.parse(conflict.since) >= this.timings.homeConflictMs);
|
|
8300
8921
|
if (left.length > 0) {
|
|
8301
8922
|
const oldest = Math.max(...left.map((conflict) => now - Date.parse(conflict.since)));
|
|
@@ -8463,6 +9084,10 @@ var Scheduler = class {
|
|
|
8463
9084
|
channel: dispatch.channel,
|
|
8464
9085
|
trigger: dispatch.trigger.kind
|
|
8465
9086
|
}, "dispatching turn");
|
|
9087
|
+
const crons = dispatch.crons ?? [];
|
|
9088
|
+
for (const id of crons) {
|
|
9089
|
+
this.cronsInFlight.add(id);
|
|
9090
|
+
}
|
|
8466
9091
|
const promise = (async () => {
|
|
8467
9092
|
try {
|
|
8468
9093
|
const record = await this.runner.runTurn(dispatch, assignment);
|
|
@@ -8477,6 +9102,9 @@ var Scheduler = class {
|
|
|
8477
9102
|
} finally {
|
|
8478
9103
|
this.running.delete(key);
|
|
8479
9104
|
this.exclusive.delete(key);
|
|
9105
|
+
for (const id of crons) {
|
|
9106
|
+
this.cronsInFlight.delete(id);
|
|
9107
|
+
}
|
|
8480
9108
|
}
|
|
8481
9109
|
})();
|
|
8482
9110
|
this.running.set(key, promise);
|
|
@@ -8575,6 +9203,7 @@ var TURN_CONTRACT = [
|
|
|
8575
9203
|
"- Work only inside your worktree. In a task's conversation it is a worktree of its own on the task's branch `task/<id>`: commit your work there. After each turn the runner commits what you left on the branch and lets it go for the next holder. Your home worktree is on your own branch, `agent/<name>`, for work tied to no task; nothing lands from it by itself. In a proposal's, a topic's, or a channel's conversation it is a worktree of its own, detached at the project's default branch, which goes when the thread closes or the channel is archived, so work that should last goes on a task's branch. Before each turn the runner fetches the project's remote and brings what holds no work of yours up to the default branch: your own branch when it has no commits of its own, a task's branch nobody has worked on, and a detached worktree with nothing uncommitted; what it leaves where it is, and why, is under Your workspace in the prompt. It never merges or commits for you.",
|
|
8576
9204
|
"- 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.",
|
|
8577
9205
|
"- Silence is allowed. If the digest needs no reply, post nothing.",
|
|
9206
|
+
"- 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.",
|
|
8578
9207
|
"- 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.",
|
|
8579
9208
|
"- 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.",
|
|
8580
9209
|
"- 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's codebase or process 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.",
|
|
@@ -8737,6 +9366,17 @@ function messageLines(messages, threads) {
|
|
|
8737
9366
|
}
|
|
8738
9367
|
return lines;
|
|
8739
9368
|
}
|
|
9369
|
+
function cronLines(cron, timeZone) {
|
|
9370
|
+
const previous = cron.lastRun === void 0 ? "" : ` The turn of its previous fire ${cron.lastRun.outcome === "stopped" ? "was stopped by the user" : cron.lastRun.outcome} at ${cron.lastRun.at}.`;
|
|
9371
|
+
const next = nextFire(cron, timeZone);
|
|
9372
|
+
const state = isOneTime(cron.schedule) ? " It fired once, as set, and has ended." : cron.ended !== void 0 ? ` ${cron.ended.by} has ended it since it fired (${cron.ended.reason}), so do what it asks only if it still matters.` : next === null ? "" : ` Next it fires at ${next.toISOString()}.`;
|
|
9373
|
+
return [
|
|
9374
|
+
`- "${cron.title}" (cron ${cron.id}), set by ${cron.createdBy} at ${cron.createdAt}, fires ${describeSchedule(cron.schedule, timeZone)}.${previous}${state}`,
|
|
9375
|
+
"",
|
|
9376
|
+
clip(cron.note).split("\n").map((line) => ` ${line}`).join("\n"),
|
|
9377
|
+
""
|
|
9378
|
+
];
|
|
9379
|
+
}
|
|
8740
9380
|
function buildSteerText(messages, threads) {
|
|
8741
9381
|
return `${[
|
|
8742
9382
|
`## New in this conversation while you work (${messages.length})`,
|
|
@@ -8757,6 +9397,10 @@ function buildTurnPrompt(input) {
|
|
|
8757
9397
|
if (dispatch.trigger.taskId !== void 0) {
|
|
8758
9398
|
lines.push(`Task in question: ${dispatch.trigger.taskId}`);
|
|
8759
9399
|
}
|
|
9400
|
+
if (input.clock !== void 0) {
|
|
9401
|
+
const { now, timeZone } = input.clock;
|
|
9402
|
+
lines.push(`It is ${now.toISOString()} now, ${localTime(now, timeZone)} in the society's time zone, ${timeZone}.`);
|
|
9403
|
+
}
|
|
8760
9404
|
const askedIn = dispatch.trigger.channel === void 0 ? null : parseChannelRef(dispatch.trigger.channel).project;
|
|
8761
9405
|
if (dispatch.project === SOCIETY_SCOPE && askedIn !== null) {
|
|
8762
9406
|
lines.push(`You were asked in ${dispatch.trigger.channel}, in project ${askedIn}, which you are not a member of, so this turn is outside any project. Answer there with post_message. If the work needs you in ${askedIn}, ask the concierge or @user to add you, or join it with join_project if your charter grants that verb.`);
|
|
@@ -8773,6 +9417,9 @@ function buildTurnPrompt(input) {
|
|
|
8773
9417
|
if (dispatch.trigger.kind === "proposal_decided") {
|
|
8774
9418
|
lines.push("A proposal you made was decided; the decision is the last post in the proposal's thread. A decision wakes you in the conversation you filed the proposal from, or at home once that has ended. Carry on with what an approval enables or a rejection asks, and stay silent if nothing follows.");
|
|
8775
9419
|
}
|
|
9420
|
+
if (dispatch.trigger.kind === "cron") {
|
|
9421
|
+
lines.push("A cron came due: what it asks is under Crons below.");
|
|
9422
|
+
}
|
|
8776
9423
|
if (dispatch.trigger.kind === "user_post") {
|
|
8777
9424
|
lines.push("The user posted. Route it: answer where it was posted, in its thread when it came in one, or create what it needs: a task with a plan whose stages name who does them, a thread, or a project, adding citizens to a project first when they are not members. Stay silent when the user already addressed a citizen and nothing else is needed.");
|
|
8778
9425
|
}
|
|
@@ -8799,6 +9446,11 @@ function buildTurnPrompt(input) {
|
|
|
8799
9446
|
if (ending !== null) {
|
|
8800
9447
|
lines.push("", "## This conversation has ended", "", `${capitalized(ending.how)}. You are woken once more because your workspace here still holds work on no branch, listed under Your workspace, and it is removed when this turn ends, whatever it still holds. You may not be the one who ended the conversation, and the work may not be yours to finish, so decide for each piece:`, "- It is yours and it matters: land it through a task. File one, or use one you hold, switch this worktree to its branch task/<id> (git switch -c task/<id> for a task filed in this turn), and commit there.", `- It matters but is someone else's to finish: put it on a task's branch the same way, then hand that task to whoever should own it and ask them in ${ending.askIn} with a mention, since this conversation takes no more posts.`, "- It is disposable: leave it, and it goes with the workspace.");
|
|
8801
9448
|
}
|
|
9449
|
+
const crons = input.crons ?? [];
|
|
9450
|
+
if (crons.length > 0) {
|
|
9451
|
+
const timeZone = input.clock?.timeZone ?? DEFAULT_TIME_ZONE;
|
|
9452
|
+
lines.push("", "## Crons", "", "Each of these came due for this turn; its note says what to do. Change one with update_cron, or end it with remove_cron, by its id.", "", ...crons.flatMap((cron) => cronLines(cron, timeZone)));
|
|
9453
|
+
}
|
|
8802
9454
|
lines.push("", WORKSPACE_TOKEN);
|
|
8803
9455
|
const conflicts = input.conflicts ?? [];
|
|
8804
9456
|
if (dispatch.trigger.kind === "reflection") {
|
|
@@ -8981,7 +9633,8 @@ var TurnHost = class {
|
|
|
8981
9633
|
usage: null,
|
|
8982
9634
|
costUsd: 0,
|
|
8983
9635
|
toolCalls: 0,
|
|
8984
|
-
model: agent.model ?? null
|
|
9636
|
+
model: agent.model ?? null,
|
|
9637
|
+
...dispatch.crons === void 0 ? {} : { crons: dispatch.crons }
|
|
8985
9638
|
};
|
|
8986
9639
|
if (agent.status === "retired") {
|
|
8987
9640
|
return { failed: await this.fail(base, `${agent.name} is retired`) };
|
|
@@ -9068,8 +9721,18 @@ var TurnHost = class {
|
|
|
9068
9721
|
...threadRecord.state === "closed" ? { replyIn: await this.replyIn(threadRecord) } : {}
|
|
9069
9722
|
};
|
|
9070
9723
|
const ending = dispatch.trigger.kind === "closing" ? await this.endingOf(dispatch.project, threadRecord, task, channel) : null;
|
|
9724
|
+
const society = await this.board.society();
|
|
9725
|
+
const crons = [];
|
|
9726
|
+
for (const id of dispatch.crons ?? []) {
|
|
9727
|
+
const cron = await this.board.readCron(id).catch(() => null);
|
|
9728
|
+
if (cron !== null) {
|
|
9729
|
+
crons.push(cron);
|
|
9730
|
+
}
|
|
9731
|
+
}
|
|
9071
9732
|
const prompt = buildTurnPrompt({
|
|
9072
9733
|
dispatch,
|
|
9734
|
+
clock: { now: this.now(), timeZone: society.timezone },
|
|
9735
|
+
crons,
|
|
9073
9736
|
messages,
|
|
9074
9737
|
threads,
|
|
9075
9738
|
conversation,
|
|
@@ -9428,7 +10091,8 @@ var TurnHost = class {
|
|
|
9428
10091
|
usage: null,
|
|
9429
10092
|
costUsd: 0,
|
|
9430
10093
|
toolCalls: 0,
|
|
9431
|
-
model: agent?.model ?? null
|
|
10094
|
+
model: agent?.model ?? null,
|
|
10095
|
+
...dispatch.crons === void 0 ? {} : { crons: dispatch.crons }
|
|
9432
10096
|
}, reason);
|
|
9433
10097
|
}
|
|
9434
10098
|
/**
|
|
@@ -10334,20 +10998,20 @@ async function handleMcpRequest(board2, actor, request, version) {
|
|
|
10334
10998
|
import { Hono as Hono3 } from "hono";
|
|
10335
10999
|
import { compress } from "hono/compress";
|
|
10336
11000
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
10337
|
-
import { z as
|
|
11001
|
+
import { z as z16, ZodError } from "zod";
|
|
10338
11002
|
|
|
10339
11003
|
// apps/server/dist/github-auth.js
|
|
10340
11004
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
10341
11005
|
import { Hono } from "hono";
|
|
10342
|
-
import { z as
|
|
11006
|
+
import { z as z15 } from "zod";
|
|
10343
11007
|
var SIGN_IN_TTL_MS = 30 * 24 * 36e5;
|
|
10344
11008
|
var STATE_TTL_MS = 10 * 6e4;
|
|
10345
11009
|
var MAX_STATES = 1e3;
|
|
10346
|
-
var TokenAnswerSchema =
|
|
10347
|
-
var GithubUserSchema =
|
|
10348
|
-
login:
|
|
10349
|
-
id:
|
|
10350
|
-
avatar_url:
|
|
11010
|
+
var TokenAnswerSchema = z15.object({ access_token: z15.string().min(1) });
|
|
11011
|
+
var GithubUserSchema = z15.object({
|
|
11012
|
+
login: z15.string().min(1),
|
|
11013
|
+
id: z15.number().int(),
|
|
11014
|
+
avatar_url: z15.string().optional()
|
|
10351
11015
|
});
|
|
10352
11016
|
function githubAvatar(url) {
|
|
10353
11017
|
try {
|
|
@@ -10821,17 +11485,18 @@ var ERROR_STATUS = {
|
|
|
10821
11485
|
INVALID_TRANSITION: 409,
|
|
10822
11486
|
INVALID_STATE: 409
|
|
10823
11487
|
};
|
|
10824
|
-
var RetireBodySchema =
|
|
10825
|
-
var RunnerBodySchema =
|
|
10826
|
-
var EnrollmentApprovalSchema =
|
|
10827
|
-
var HomeRunnerBodySchema =
|
|
10828
|
-
var ModelBodySchema =
|
|
10829
|
-
var EffortBodySchema =
|
|
10830
|
-
var HistoryLimitSchema =
|
|
10831
|
-
var
|
|
11488
|
+
var RetireBodySchema = z16.object({ reason: z16.string().min(1) });
|
|
11489
|
+
var RunnerBodySchema = z16.object({ name: NameSchema });
|
|
11490
|
+
var EnrollmentApprovalSchema = z16.object({ name: NameSchema });
|
|
11491
|
+
var HomeRunnerBodySchema = z16.object({ runner: NameSchema.nullable() });
|
|
11492
|
+
var ModelBodySchema = z16.object({ model: ModelNameSchema.nullable() });
|
|
11493
|
+
var EffortBodySchema = z16.object({ effort: EffortSchema.nullable() });
|
|
11494
|
+
var HistoryLimitSchema = z16.coerce.number().int().min(1).max(500);
|
|
11495
|
+
var TimezoneBodySchema = z16.object({ timezone: TimeZoneSchema });
|
|
11496
|
+
var ChannelBodySchema = z16.object({
|
|
10832
11497
|
project: NameSchema.nullable().default(null),
|
|
10833
11498
|
name: NameSchema,
|
|
10834
|
-
purpose:
|
|
11499
|
+
purpose: z16.string().min(1)
|
|
10835
11500
|
});
|
|
10836
11501
|
function bearer2(c) {
|
|
10837
11502
|
const header = c.req.header("authorization") ?? "";
|
|
@@ -10880,6 +11545,10 @@ function createApp(deps) {
|
|
|
10880
11545
|
});
|
|
10881
11546
|
api.delete("/sign-in", async (c) => c.json({ signedOut: await board2.signOut(bearer2(c) ?? "") }));
|
|
10882
11547
|
api.get("/society", async (c) => c.json(await board2.society()));
|
|
11548
|
+
api.put("/society/timezone", async (c) => {
|
|
11549
|
+
const body = TimezoneBodySchema.parse(await c.req.json());
|
|
11550
|
+
return c.json(await board2.setTimezone(c.get("actor"), body.timezone));
|
|
11551
|
+
});
|
|
10883
11552
|
api.get("/version", (c) => c.json({ version }));
|
|
10884
11553
|
api.get("/agents", async (c) => c.json((await board2.listAgents()).map(({ tokenHash: _hash, ...agent }) => agent)));
|
|
10885
11554
|
api.get("/members", async (c) => c.json(await board2.listMembers()));
|
|
@@ -10931,6 +11600,7 @@ function createApp(deps) {
|
|
|
10931
11600
|
});
|
|
10932
11601
|
}
|
|
10933
11602
|
api.get("/proposals", async (c) => c.json(await board2.listProposals()));
|
|
11603
|
+
api.get("/crons", async (c) => c.json(await board2.listCrons()));
|
|
10934
11604
|
api.get("/proposals/:id", async (c) => c.json(await board2.readProposal(c.req.param("id"))));
|
|
10935
11605
|
api.get("/signals", async (c) => c.json(await board2.listSignals(Number(c.req.query("limit") ?? "100"))));
|
|
10936
11606
|
api.get("/metrics", async (c) => {
|
|
@@ -10980,7 +11650,7 @@ function createApp(deps) {
|
|
|
10980
11650
|
});
|
|
10981
11651
|
api.put("/roles/:name", async (c) => {
|
|
10982
11652
|
const charter = RoleCharterSchema.parse({
|
|
10983
|
-
...
|
|
11653
|
+
...z16.record(z16.string(), z16.unknown()).parse(await c.req.json()),
|
|
10984
11654
|
name: c.req.param("name")
|
|
10985
11655
|
});
|
|
10986
11656
|
return c.json(await board2.setRoleCharter(c.get("actor"), charter));
|