@kubitnodes/stellaris 0.3.12 → 0.3.14
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 +1149 -535
- package/dist/server/index.js +1426 -593
- package/package.json +2 -1
- package/web/dist/assets/{BackLink-BlJvN2RA.js → BackLink-giAdtos8.js} +1 -1
- package/web/dist/assets/{ChannelForms-CmFxBXNK.js → ChannelForms-b6eKogvP.js} +1 -1
- package/web/dist/assets/ChannelView-DXoJ5w3z.js +1 -0
- package/web/dist/assets/CitizenView-yOnQtNz4.js +2 -0
- package/web/dist/assets/{CitizensView-B8lVGSBD.js → CitizensView-Dbo47F82.js} +1 -1
- package/web/dist/assets/{CliIcon-CT5ljQDC.js → CliIcon-DxDvRRcX.js} +1 -1
- package/web/dist/assets/CronsView-DYx7dQcr.js +1 -0
- package/web/dist/assets/KnowledgeView-BXLj85OH.js +1 -0
- package/web/dist/assets/{Markdown-B5zEdYuO.js → Markdown-Dw04_ImA.js} +4 -4
- package/web/dist/assets/{MessageItem-DuxSZwn0.js → MessageItem-DnfMDxDB.js} +1 -1
- package/web/dist/assets/{MetricsView-D8D-y-sz.js → MetricsView-_h2TWeo8.js} +1 -1
- package/web/dist/assets/{NeedsYouView-Buwq8-_l.js → NeedsYouView-BI1-_bYI.js} +1 -1
- package/web/dist/assets/{Overview-CrQ8ybpq.js → Overview-BdZiHglw.js} +1 -1
- package/web/dist/assets/{Pane-CFoSKOo5.js → Pane-CODhA5zE.js} +1 -1
- package/web/dist/assets/ProjectView-CScK4F7F.js +1 -0
- package/web/dist/assets/ProposalView-BQ1rflqo.js +1 -0
- package/web/dist/assets/{ProposalsView-DjyZpZe0.js → ProposalsView-DSi2u6qO.js} +1 -1
- package/web/dist/assets/{RunnersView-2h5NWUuD.js → RunnersView-9E2qUSO5.js} +1 -1
- package/web/dist/assets/{SocietyView-BuaSb_3d.js → SocietyView-DEeUo4wb.js} +1 -1
- package/web/dist/assets/SubjectThread-DhGB5RPc.js +1 -0
- package/web/dist/assets/{TaskFileView-CeEzbSCL.js → TaskFileView-DQC_ZUdY.js} +2 -2
- package/web/dist/assets/TaskView-yVpUG83f.js +3 -0
- package/web/dist/assets/{TasksView-DCVU5oZn.js → TasksView-CJbJt5dM.js} +1 -1
- package/web/dist/assets/ThreadCard-DVAx3gGc.js +1 -0
- package/web/dist/assets/{ThreadForms-CTBaAJ57.js → ThreadForms-RnrV7V5X.js} +1 -1
- package/web/dist/assets/{ThreadView-zg8KqF3t.js → ThreadView-CMwiVZFq.js} +1 -1
- package/web/dist/assets/{citizen-CImM6WB6.js → citizen-aLkZ4owL.js} +1 -1
- package/web/dist/assets/{compose-BG_-Tp87.js → compose-D7DOz6kC.js} +1 -1
- package/web/dist/assets/format-BORdp4w-.js +55 -0
- package/web/dist/assets/{governance-cDTg2_iq.js → governance-WrDQawLN.js} +1 -1
- package/web/dist/assets/index-7uInG5D9.js +23 -0
- package/web/dist/assets/index-D02Wgo_C.css +2 -0
- package/web/dist/assets/{link-CFdt17Sp.js → link-1v5lugnf.js} +1 -1
- package/web/dist/assets/{tasks-C10zwkED.js → tasks-C-9dAYQV.js} +1 -1
- package/web/dist/assets/useMatch-CyeFfbcR.js +1 -0
- package/web/dist/assets/{useStickyScroll-BjqdEt_f.js → useStickyScroll-CE7dyz0Q.js} +1 -1
- package/web/dist/index.html +9 -9
- package/web/dist/assets/ChannelView-4s--IwKC.js +0 -1
- package/web/dist/assets/CitizenView-B5-LVVCG.js +0 -2
- package/web/dist/assets/KnowledgeView-DbI8DIZb.js +0 -1
- package/web/dist/assets/ProjectView-BBQg-ZxC.js +0 -1
- package/web/dist/assets/ProposalView-BjPHVxj4.js +0 -1
- package/web/dist/assets/SubjectThread-C1mHvHMI.js +0 -1
- package/web/dist/assets/TaskView-yL9hCM7G.js +0 -3
- package/web/dist/assets/ThreadCard-CXXf9qfA.js +0 -1
- package/web/dist/assets/format-BWr3zKWg.js +0 -55
- package/web/dist/assets/index-Cxqhrg2v.js +0 -23
- package/web/dist/assets/index-VVnGdAXc.css +0 -2
- package/web/dist/assets/useMatch-BpnmzT6s.js +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Command } from "commander";
|
|
|
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,126 @@ 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 DEFAULT_TIME_ZONE = "UTC";
|
|
178
|
+
function canonicalTimeZone(zone) {
|
|
179
|
+
try {
|
|
180
|
+
return new Intl.DateTimeFormat("en-US", { timeZone: zone }).resolvedOptions().timeZone;
|
|
181
|
+
} catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
var TimeZoneSchema = z3.string().trim().min(1).max(64).transform((zone, ctx) => {
|
|
186
|
+
const canonical = canonicalTimeZone(zone);
|
|
187
|
+
if (canonical === null) {
|
|
188
|
+
ctx.addIssue({
|
|
189
|
+
code: "custom",
|
|
190
|
+
message: `${zone} is not a time zone; name one as IANA does, such as Europe/Berlin, America/New_York, or UTC`
|
|
191
|
+
});
|
|
192
|
+
return z3.NEVER;
|
|
193
|
+
}
|
|
194
|
+
return canonical;
|
|
195
|
+
});
|
|
196
|
+
function pattern(expression, timeZone) {
|
|
197
|
+
return new Pattern(expression, { mode: "5-part", timezone: timeZone, paused: true });
|
|
198
|
+
}
|
|
199
|
+
function cronExpressionError(expression) {
|
|
200
|
+
try {
|
|
201
|
+
pattern(expression, DEFAULT_TIME_ZONE);
|
|
202
|
+
return null;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
return error instanceof Error ? error.message : String(error);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
var CronExpressionSchema = z3.string().trim().min(1).max(120).superRefine((expression, ctx) => {
|
|
208
|
+
const error = cronExpressionError(expression);
|
|
209
|
+
if (error !== null) {
|
|
210
|
+
ctx.addIssue({
|
|
211
|
+
code: "custom",
|
|
212
|
+
message: `not a five-field cron expression (minute, hour, day of month, month, day of week): ${error}`
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
var CronScheduleSchema = z3.union([
|
|
217
|
+
z3.object({ cron: CronExpressionSchema, timezone: TimeZoneSchema.optional() }),
|
|
218
|
+
z3.object({ at: IsoDateTimeSchema })
|
|
219
|
+
]);
|
|
220
|
+
var CronRunSchema = z3.object({
|
|
221
|
+
turnId: UlidSchema,
|
|
222
|
+
at: IsoDateTimeSchema,
|
|
223
|
+
outcome: z3.enum(["completed", "failed", "stopped"])
|
|
224
|
+
});
|
|
225
|
+
var CronFrontmatterSchema = z3.object({
|
|
226
|
+
id: UlidSchema,
|
|
227
|
+
title: z3.string().trim().min(1).max(120),
|
|
228
|
+
agent: NameSchema,
|
|
229
|
+
scope: NameSchema,
|
|
230
|
+
thread: UlidSchema.optional(),
|
|
231
|
+
channel: NameSchema.optional(),
|
|
232
|
+
schedule: CronScheduleSchema,
|
|
233
|
+
createdBy: NameSchema,
|
|
234
|
+
createdAt: IsoDateTimeSchema,
|
|
235
|
+
/** When the current schedule took effect: set, changed, or resumed. It fires only after this. */
|
|
236
|
+
since: IsoDateTimeSchema,
|
|
237
|
+
paused: z3.object({ at: IsoDateTimeSchema, by: NameSchema }).optional(),
|
|
238
|
+
lastFiredAt: IsoDateTimeSchema.optional(),
|
|
239
|
+
/** When a time came while its previous fire's turn was still queued or running, so it passed. */
|
|
240
|
+
lastSkippedAt: IsoDateTimeSchema.optional(),
|
|
241
|
+
/** How the turn of its latest fire ended. */
|
|
242
|
+
lastRun: CronRunSchema.optional(),
|
|
243
|
+
/** Its turns that failed in a row, up to the latest. */
|
|
244
|
+
failures: z3.number().int().nonnegative().default(0),
|
|
245
|
+
ended: z3.object({ at: IsoDateTimeSchema, by: NameSchema, reason: z3.string().min(1) }).optional()
|
|
246
|
+
});
|
|
247
|
+
function isOneTime(schedule) {
|
|
248
|
+
return "at" in schedule;
|
|
249
|
+
}
|
|
250
|
+
function scheduleZone(schedule, societyZone) {
|
|
251
|
+
return isOneTime(schedule) ? societyZone : schedule.timezone ?? societyZone;
|
|
252
|
+
}
|
|
253
|
+
function fireTimes(schedule, societyZone, after, count) {
|
|
254
|
+
if (isOneTime(schedule)) {
|
|
255
|
+
const at = new Date(schedule.at);
|
|
256
|
+
return at > after && count > 0 ? [at] : [];
|
|
257
|
+
}
|
|
258
|
+
return pattern(schedule.cron, scheduleZone(schedule, societyZone)).nextRuns(count, after);
|
|
259
|
+
}
|
|
260
|
+
function shortestGapMs(schedule, societyZone, after) {
|
|
261
|
+
const times = fireTimes(schedule, societyZone, after, FLOOR_LOOKAHEAD);
|
|
262
|
+
let shortest = null;
|
|
263
|
+
for (let index = 1; index < times.length; index += 1) {
|
|
264
|
+
const gap = (times[index]?.getTime() ?? 0) - (times[index - 1]?.getTime() ?? 0);
|
|
265
|
+
shortest = shortest === null ? gap : Math.min(shortest, gap);
|
|
266
|
+
}
|
|
267
|
+
return shortest;
|
|
268
|
+
}
|
|
269
|
+
function nextFire(cron2, societyZone, floorMs = CRON_FLOOR_MS) {
|
|
270
|
+
if (cron2.ended !== void 0 || cron2.paused !== void 0) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const fired = cron2.lastFiredAt === void 0 ? null : Date.parse(cron2.lastFiredAt);
|
|
274
|
+
const since = Date.parse(cron2.since);
|
|
275
|
+
if (isOneTime(cron2.schedule)) {
|
|
276
|
+
return fired !== null && fired >= since ? null : new Date(cron2.schedule.at);
|
|
277
|
+
}
|
|
278
|
+
const from = Math.max(since, fired === null ? since : fired + floorMs - FIRE_SLACK_MS, cron2.lastSkippedAt === void 0 ? since : Date.parse(cron2.lastSkippedAt));
|
|
279
|
+
return fireTimes(cron2.schedule, societyZone, new Date(from), 1)[0] ?? null;
|
|
280
|
+
}
|
|
281
|
+
function describeSchedule(schedule, societyZone) {
|
|
282
|
+
if (isOneTime(schedule)) {
|
|
283
|
+
return `once at ${schedule.at}`;
|
|
284
|
+
}
|
|
285
|
+
return schedule.timezone === void 0 ? `\`${schedule.cron}\` in the society's time zone, ${societyZone}` : `\`${schedule.cron}\` in ${schedule.timezone}`;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// packages/shared/dist/board.js
|
|
164
289
|
var ASK_CHANNEL = "asks";
|
|
165
290
|
var SOCIETY_CHANNELS = ["general", "governance", ASK_CHANNEL];
|
|
166
291
|
var PROJECT_DEFAULT_CHANNELS = ["general"];
|
|
@@ -172,30 +297,32 @@ var SOCIETY_SCOPE = "society";
|
|
|
172
297
|
function wakeScope(member, project2) {
|
|
173
298
|
return project2 !== null && member.memberships.includes(project2) ? project2 : SOCIETY_SCOPE;
|
|
174
299
|
}
|
|
175
|
-
var CliKindSchema =
|
|
176
|
-
var ArchivedChannelSchema =
|
|
300
|
+
var CliKindSchema = z4.enum(["claude", "codex"]);
|
|
301
|
+
var ArchivedChannelSchema = z4.object({
|
|
177
302
|
name: NameSchema,
|
|
178
303
|
at: IsoDateTimeSchema,
|
|
179
304
|
by: NameSchema,
|
|
180
|
-
reason:
|
|
305
|
+
reason: z4.string().min(1)
|
|
181
306
|
});
|
|
182
|
-
var SocietySchema =
|
|
183
|
-
name:
|
|
184
|
-
version:
|
|
307
|
+
var SocietySchema = z4.object({
|
|
308
|
+
name: z4.string().min(1),
|
|
309
|
+
version: z4.literal(1),
|
|
185
310
|
createdAt: IsoDateTimeSchema,
|
|
186
311
|
/** The channels that take posts; an archived one moves to `archivedChannels`. */
|
|
187
|
-
channels:
|
|
188
|
-
archivedChannels:
|
|
312
|
+
channels: z4.array(NameSchema),
|
|
313
|
+
archivedChannels: z4.array(ArchivedChannelSchema).default([]),
|
|
314
|
+
/** The time zone crons are read in when they name none, and every prompt tells the time in; the user's to set. */
|
|
315
|
+
timezone: TimeZoneSchema.default(DEFAULT_TIME_ZONE)
|
|
189
316
|
});
|
|
190
|
-
var CompletionEffectSchema =
|
|
317
|
+
var CompletionEffectSchema = z4.enum(["none", "merge", "ghpr"]);
|
|
191
318
|
var GITHUB_REMOTE = [
|
|
192
319
|
/^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?\/?$/,
|
|
193
320
|
/^git@github\.com:([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?$/,
|
|
194
321
|
/^ssh:\/\/git@github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+?)(?:\.git)?\/?$/
|
|
195
322
|
];
|
|
196
323
|
function githubRepository(remote) {
|
|
197
|
-
for (const
|
|
198
|
-
const match = remote === null ? null :
|
|
324
|
+
for (const pattern2 of GITHUB_REMOTE) {
|
|
325
|
+
const match = remote === null ? null : pattern2.exec(remote);
|
|
199
326
|
if (match?.[1] !== void 0 && match[2] !== void 0) {
|
|
200
327
|
return { owner: match[1], name: match[2] };
|
|
201
328
|
}
|
|
@@ -203,7 +330,7 @@ function githubRepository(remote) {
|
|
|
203
330
|
return null;
|
|
204
331
|
}
|
|
205
332
|
var PULL_REQUEST_URL = /^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)\/pull\/([1-9]\d*)\/?$/;
|
|
206
|
-
var PullRequestUrlSchema =
|
|
333
|
+
var PullRequestUrlSchema = z4.string().trim().regex(PULL_REQUEST_URL, "a pull request's address, https://github.com/<owner>/<repo>/pull/<number>");
|
|
207
334
|
function parsePullRequestUrl(url) {
|
|
208
335
|
const match = PULL_REQUEST_URL.exec(url.trim());
|
|
209
336
|
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,60 +338,65 @@ function parsePullRequestUrl(url) {
|
|
|
211
338
|
function sameRepository(a, b) {
|
|
212
339
|
return a.owner.toLowerCase() === b.owner.toLowerCase() && a.name.toLowerCase() === b.name.toLowerCase();
|
|
213
340
|
}
|
|
214
|
-
var MergeSubjectSchema =
|
|
215
|
-
var PullRequestLinkSchema =
|
|
341
|
+
var MergeSubjectSchema = z4.string().trim().min(1).max(300).regex(/^[^\n]+$/, "a merge commit's subject is one line");
|
|
342
|
+
var PullRequestLinkSchema = z4.object({
|
|
216
343
|
url: PullRequestUrlSchema,
|
|
217
|
-
number:
|
|
344
|
+
number: z4.number().int().positive(),
|
|
218
345
|
subject: MergeSubjectSchema.optional(),
|
|
219
|
-
body:
|
|
346
|
+
body: z4.string().max(2e4).optional(),
|
|
220
347
|
linkedBy: NameSchema,
|
|
221
|
-
linkedAt: IsoDateTimeSchema
|
|
348
|
+
linkedAt: IsoDateTimeSchema,
|
|
349
|
+
/**
|
|
350
|
+
* Once its `ghpr` task was abandoned, how the board's closing of it on GitHub went: `ok` false
|
|
351
|
+
* leaves it open, for the reason `detail` gives.
|
|
352
|
+
*/
|
|
353
|
+
closed: z4.object({ at: IsoDateTimeSchema, ok: z4.boolean(), detail: z4.string() }).optional()
|
|
222
354
|
});
|
|
223
|
-
var DefaultBranchSchema =
|
|
224
|
-
var StageIdSchema =
|
|
355
|
+
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");
|
|
356
|
+
var StageIdSchema = z4.string().regex(/^s[1-9][0-9]*$/, "expected a stage id such as s1");
|
|
225
357
|
var stageFields = {
|
|
226
|
-
name:
|
|
358
|
+
name: z4.string().trim().min(1).max(120),
|
|
227
359
|
role: NameSchema.optional(),
|
|
228
360
|
agent: NameSchema.optional(),
|
|
229
|
-
gate:
|
|
361
|
+
gate: z4.boolean().default(false)
|
|
230
362
|
};
|
|
231
363
|
function oneAssignee(stage) {
|
|
232
364
|
return stage.role === void 0 || stage.agent === void 0;
|
|
233
365
|
}
|
|
234
366
|
var ONE_ASSIGNEE = { message: "a stage names a role or an agent, not both" };
|
|
235
|
-
var PlanStageSchema =
|
|
236
|
-
var PlanEditStageSchema =
|
|
237
|
-
var PlanSchema =
|
|
238
|
-
var PlanEditSchema =
|
|
239
|
-
var ProjectSchema =
|
|
367
|
+
var PlanStageSchema = z4.object(stageFields).refine(oneAssignee, ONE_ASSIGNEE);
|
|
368
|
+
var PlanEditStageSchema = z4.object({ id: StageIdSchema.optional(), ...stageFields }).refine(oneAssignee, ONE_ASSIGNEE);
|
|
369
|
+
var PlanSchema = z4.array(PlanStageSchema);
|
|
370
|
+
var PlanEditSchema = z4.array(PlanEditStageSchema);
|
|
371
|
+
var ProjectSchema = z4.object({
|
|
240
372
|
slug: NameSchema,
|
|
241
|
-
name:
|
|
242
|
-
repo:
|
|
243
|
-
defaultBranch:
|
|
373
|
+
name: z4.string().min(1),
|
|
374
|
+
repo: z4.string().nullable(),
|
|
375
|
+
defaultBranch: z4.string().min(1),
|
|
244
376
|
/** The channels that take posts; an archived one moves to `archivedChannels`. */
|
|
245
|
-
channels:
|
|
246
|
-
archivedChannels:
|
|
247
|
-
members:
|
|
248
|
-
approvers:
|
|
249
|
-
requiredCapabilities:
|
|
377
|
+
channels: z4.array(NameSchema),
|
|
378
|
+
archivedChannels: z4.array(ArchivedChannelSchema).default([]),
|
|
379
|
+
members: z4.array(NameSchema),
|
|
380
|
+
approvers: z4.array(NameSchema),
|
|
381
|
+
requiredCapabilities: z4.array(z4.string()),
|
|
250
382
|
createdAt: IsoDateTimeSchema,
|
|
251
383
|
onDone: CompletionEffectSchema.default("none"),
|
|
252
384
|
/** The runner the project lives on, which holds its repository; set on the project's first turn. */
|
|
253
385
|
runner: NameSchema.optional(),
|
|
254
386
|
/** Set once the project is archived: nobody belongs to it, and nothing more is posted, filed, or joined there. */
|
|
255
|
-
archived:
|
|
387
|
+
archived: z4.object({ at: IsoDateTimeSchema, by: NameSchema, reason: z4.string().min(1) }).optional()
|
|
256
388
|
});
|
|
257
|
-
var TaskStepSchema =
|
|
258
|
-
action:
|
|
389
|
+
var TaskStepSchema = z4.object({
|
|
390
|
+
action: z4.enum(["advanced", "returned", "abandoned", "landed", "reopened"]),
|
|
259
391
|
stage: StageIdSchema,
|
|
260
392
|
to: StageIdSchema.nullable()
|
|
261
393
|
});
|
|
262
|
-
var ProposalStepSchema =
|
|
263
|
-
var MessageStepSchema =
|
|
394
|
+
var ProposalStepSchema = z4.object({ action: z4.enum(["approved", "rejected"]) });
|
|
395
|
+
var MessageStepSchema = z4.union([TaskStepSchema, ProposalStepSchema]);
|
|
264
396
|
function isTaskStep(step) {
|
|
265
397
|
return "stage" in step;
|
|
266
398
|
}
|
|
267
|
-
var MessageFrontmatterSchema =
|
|
399
|
+
var MessageFrontmatterSchema = z4.object({
|
|
268
400
|
id: UlidSchema,
|
|
269
401
|
author: NameSchema,
|
|
270
402
|
channel: ChannelRefSchema,
|
|
@@ -276,18 +408,18 @@ var MessageFrontmatterSchema = z3.object({
|
|
|
276
408
|
/** On a post a task verb made in the task's thread, or a decision in a proposal's: the step it recorded. */
|
|
277
409
|
step: MessageStepSchema.optional(),
|
|
278
410
|
ts: IsoDateTimeSchema,
|
|
279
|
-
mentions:
|
|
411
|
+
mentions: z4.array(NameSchema)
|
|
280
412
|
});
|
|
281
|
-
var TaskStatusSchema =
|
|
282
|
-
var ThreadStateSchema =
|
|
283
|
-
var ThreadSubjectSchema =
|
|
284
|
-
kind:
|
|
413
|
+
var TaskStatusSchema = z4.enum(["open", "claimed", "done", "abandoned"]);
|
|
414
|
+
var ThreadStateSchema = z4.enum(["open", "closed"]);
|
|
415
|
+
var ThreadSubjectSchema = z4.object({
|
|
416
|
+
kind: z4.enum(["task", "proposal"]),
|
|
285
417
|
id: UlidSchema
|
|
286
418
|
});
|
|
287
|
-
var ThreadFrontmatterSchema =
|
|
419
|
+
var ThreadFrontmatterSchema = z4.object({
|
|
288
420
|
id: UlidSchema,
|
|
289
421
|
channel: ChannelRefSchema,
|
|
290
|
-
title:
|
|
422
|
+
title: z4.string().min(1).max(200),
|
|
291
423
|
subject: ThreadSubjectSchema.optional(),
|
|
292
424
|
state: ThreadStateSchema,
|
|
293
425
|
openedBy: NameSchema,
|
|
@@ -295,28 +427,28 @@ var ThreadFrontmatterSchema = z3.object({
|
|
|
295
427
|
closedBy: NameSchema.optional(),
|
|
296
428
|
closedAt: IsoDateTimeSchema.optional()
|
|
297
429
|
});
|
|
298
|
-
var StageSchema =
|
|
430
|
+
var StageSchema = z4.object({
|
|
299
431
|
id: StageIdSchema,
|
|
300
|
-
name:
|
|
432
|
+
name: z4.string().min(1),
|
|
301
433
|
role: NameSchema.optional(),
|
|
302
434
|
agent: NameSchema.optional(),
|
|
303
|
-
gate:
|
|
435
|
+
gate: z4.boolean().default(false),
|
|
304
436
|
/** Everyone who has held the stage, oldest first. */
|
|
305
|
-
holders:
|
|
437
|
+
holders: z4.array(NameSchema).default([]),
|
|
306
438
|
completedBy: NameSchema.optional(),
|
|
307
439
|
completedAt: IsoDateTimeSchema.optional()
|
|
308
440
|
});
|
|
309
|
-
var TaskReturnSchema =
|
|
441
|
+
var TaskReturnSchema = z4.object({
|
|
310
442
|
from: StageIdSchema,
|
|
311
443
|
by: NameSchema,
|
|
312
444
|
at: IsoDateTimeSchema
|
|
313
445
|
});
|
|
314
|
-
var TaskFrontmatterSchema =
|
|
446
|
+
var TaskFrontmatterSchema = z4.object({
|
|
315
447
|
id: UlidSchema,
|
|
316
448
|
project: NameSchema,
|
|
317
449
|
/** The channel of the project the task is filed in, where its thread hangs. */
|
|
318
450
|
channel: NameSchema.default("general"),
|
|
319
|
-
title:
|
|
451
|
+
title: z4.string().min(1),
|
|
320
452
|
status: TaskStatusSchema,
|
|
321
453
|
createdBy: NameSchema,
|
|
322
454
|
createdAt: IsoDateTimeSchema,
|
|
@@ -325,19 +457,19 @@ var TaskFrontmatterSchema = z3.object({
|
|
|
325
457
|
claimedBy: NameSchema.optional(),
|
|
326
458
|
leaseExpiresAt: IsoDateTimeSchema.optional(),
|
|
327
459
|
parentId: UlidSchema.optional(),
|
|
328
|
-
blockedBy:
|
|
329
|
-
requiredCapabilities:
|
|
330
|
-
stages:
|
|
460
|
+
blockedBy: z4.array(UlidSchema),
|
|
461
|
+
requiredCapabilities: z4.array(z4.string()),
|
|
462
|
+
stages: z4.array(StageSchema).min(1),
|
|
331
463
|
stage: StageIdSchema,
|
|
332
464
|
/** When the current stage last became current or lost its holder: the waiting clock. */
|
|
333
465
|
stageSince: IsoDateTimeSchema,
|
|
334
466
|
/** The highest stage number issued, so a removed stage's id is never reused. */
|
|
335
|
-
stageSeq:
|
|
467
|
+
stageSeq: z4.number().int().positive(),
|
|
336
468
|
onDone: CompletionEffectSchema.default("none"),
|
|
337
469
|
/** The pull request a `ghpr` task lands through. */
|
|
338
470
|
pullRequest: PullRequestLinkSchema.optional(),
|
|
339
471
|
/** The last stage is complete and the completion effect is running. */
|
|
340
|
-
completing:
|
|
472
|
+
completing: z4.boolean().default(false),
|
|
341
473
|
/** The latest send-back while its rework lasts: cleared once the task reaches that stage again. */
|
|
342
474
|
returned: TaskReturnSchema.optional()
|
|
343
475
|
});
|
|
@@ -361,79 +493,79 @@ function mayHoldStage(member, task2) {
|
|
|
361
493
|
}
|
|
362
494
|
return !(stage.gate && task2.stages.slice(0, index).some((s) => s.holders.includes(member.name)));
|
|
363
495
|
}
|
|
364
|
-
var ModelNameSchema =
|
|
365
|
-
var EffortSchema =
|
|
366
|
-
var EffortOptionSchema =
|
|
496
|
+
var ModelNameSchema = z4.string().trim().min(1).max(120).regex(/^[\w.:/[\]-]+$/, "a model name is one word of letters, digits, and . _ : / - [ ]");
|
|
497
|
+
var EffortSchema = z4.string().trim().min(1).max(32).regex(/^[a-z][a-z0-9_-]*$/, "an effort is one lowercase word, such as high");
|
|
498
|
+
var EffortOptionSchema = z4.object({
|
|
367
499
|
id: EffortSchema,
|
|
368
|
-
description:
|
|
500
|
+
description: z4.string().default("")
|
|
369
501
|
});
|
|
370
|
-
var ModelOptionSchema =
|
|
502
|
+
var ModelOptionSchema = z4.object({
|
|
371
503
|
id: ModelNameSchema,
|
|
372
|
-
name:
|
|
373
|
-
description:
|
|
374
|
-
isDefault:
|
|
375
|
-
efforts:
|
|
504
|
+
name: z4.string(),
|
|
505
|
+
description: z4.string().default(""),
|
|
506
|
+
isDefault: z4.boolean().default(false),
|
|
507
|
+
efforts: z4.array(EffortOptionSchema).default([]),
|
|
376
508
|
defaultEffort: EffortSchema.optional()
|
|
377
509
|
});
|
|
378
|
-
var RunnerModelsSchema =
|
|
510
|
+
var RunnerModelsSchema = z4.object({
|
|
379
511
|
runner: NameSchema,
|
|
380
512
|
cli: CliKindSchema,
|
|
381
|
-
models:
|
|
513
|
+
models: z4.array(ModelOptionSchema)
|
|
382
514
|
});
|
|
383
|
-
var AgentStatusSchema =
|
|
384
|
-
var AgentSchema =
|
|
515
|
+
var AgentStatusSchema = z4.enum(["active", "retired"]);
|
|
516
|
+
var AgentSchema = z4.object({
|
|
385
517
|
name: NameSchema,
|
|
386
518
|
role: NameSchema,
|
|
387
519
|
cli: CliKindSchema.nullable(),
|
|
388
|
-
model:
|
|
520
|
+
model: z4.string().optional(),
|
|
389
521
|
/** The reasoning effort set for the agent's turns; the model's own default applies otherwise. */
|
|
390
|
-
effort:
|
|
522
|
+
effort: z4.string().optional(),
|
|
391
523
|
/** The runner preferred for the agent's turns where nothing else decides; any runner with its CLI otherwise. */
|
|
392
524
|
homeRunner: NameSchema.optional(),
|
|
393
|
-
memberships:
|
|
394
|
-
subscriptions:
|
|
525
|
+
memberships: z4.array(NameSchema),
|
|
526
|
+
subscriptions: z4.array(ChannelRefSchema),
|
|
395
527
|
status: AgentStatusSchema,
|
|
396
528
|
createdAt: IsoDateTimeSchema,
|
|
397
|
-
tokenHash:
|
|
529
|
+
tokenHash: z4.string().min(1),
|
|
398
530
|
retiredAt: IsoDateTimeSchema.optional(),
|
|
399
|
-
retiredReason:
|
|
531
|
+
retiredReason: z4.string().optional()
|
|
400
532
|
});
|
|
401
|
-
var MemberSchema =
|
|
533
|
+
var MemberSchema = z4.object({
|
|
402
534
|
name: NameSchema,
|
|
403
535
|
role: NameSchema,
|
|
404
536
|
cli: CliKindSchema.nullable(),
|
|
405
537
|
homeRunner: NameSchema.optional(),
|
|
406
538
|
status: AgentStatusSchema,
|
|
407
|
-
resident:
|
|
539
|
+
resident: z4.boolean().default(false),
|
|
408
540
|
/** The model configured on the record, if any; the CLI's own default applies otherwise. */
|
|
409
|
-
model:
|
|
541
|
+
model: z4.string().optional(),
|
|
410
542
|
/** The model the CLI reported on the citizen's last turn. */
|
|
411
|
-
lastModel:
|
|
543
|
+
lastModel: z4.string().optional(),
|
|
412
544
|
/** The reasoning effort configured on the record, if any; the model's own default applies otherwise. */
|
|
413
|
-
effort:
|
|
545
|
+
effort: z4.string().optional(),
|
|
414
546
|
/** The citizen's own skills, by name. */
|
|
415
|
-
skills:
|
|
416
|
-
memberships:
|
|
417
|
-
subscriptions:
|
|
418
|
-
claimsHeld:
|
|
419
|
-
tasksDone:
|
|
547
|
+
skills: z4.array(NameSchema).default([]),
|
|
548
|
+
memberships: z4.array(NameSchema),
|
|
549
|
+
subscriptions: z4.array(ChannelRefSchema),
|
|
550
|
+
claimsHeld: z4.number().int().nonnegative().default(0),
|
|
551
|
+
tasksDone: z4.number().int().nonnegative().default(0),
|
|
420
552
|
lastTurnAt: IsoDateTimeSchema.optional(),
|
|
421
|
-
lastTurnOutcome:
|
|
553
|
+
lastTurnOutcome: z4.string().optional(),
|
|
422
554
|
createdAt: IsoDateTimeSchema,
|
|
423
555
|
retiredAt: IsoDateTimeSchema.optional()
|
|
424
556
|
});
|
|
425
|
-
var RunnerOsSchema =
|
|
426
|
-
var RunnerSchema =
|
|
557
|
+
var RunnerOsSchema = z4.enum(["linux", "windows", "darwin"]);
|
|
558
|
+
var RunnerSchema = z4.object({
|
|
427
559
|
name: NameSchema,
|
|
428
560
|
os: RunnerOsSchema,
|
|
429
|
-
clis:
|
|
430
|
-
capabilities:
|
|
561
|
+
clis: z4.array(CliKindSchema),
|
|
562
|
+
capabilities: z4.array(z4.string()),
|
|
431
563
|
/** The release the runner reported when it last registered; absent until it has. */
|
|
432
|
-
version:
|
|
433
|
-
status:
|
|
564
|
+
version: z4.string().optional(),
|
|
565
|
+
status: z4.enum(["connected", "disconnected"]),
|
|
434
566
|
lastSeen: IsoDateTimeSchema.optional()
|
|
435
567
|
});
|
|
436
|
-
var ProposalKindSchema =
|
|
568
|
+
var ProposalKindSchema = z4.enum([
|
|
437
569
|
"role",
|
|
438
570
|
"member",
|
|
439
571
|
"channel",
|
|
@@ -442,43 +574,43 @@ var ProposalKindSchema = z3.enum([
|
|
|
442
574
|
"skill",
|
|
443
575
|
"archive"
|
|
444
576
|
]);
|
|
445
|
-
var ProposalStatusSchema =
|
|
577
|
+
var ProposalStatusSchema = z4.enum([
|
|
446
578
|
"proposed",
|
|
447
579
|
"approved",
|
|
448
580
|
"rejected",
|
|
449
581
|
"provisioned",
|
|
450
582
|
"retired"
|
|
451
583
|
]);
|
|
452
|
-
var MemberProposalSchema =
|
|
584
|
+
var MemberProposalSchema = z4.object({
|
|
453
585
|
name: NameSchema,
|
|
454
586
|
role: NameSchema,
|
|
455
587
|
cli: CliKindSchema,
|
|
456
|
-
model:
|
|
588
|
+
model: z4.string().optional(),
|
|
457
589
|
homeRunner: NameSchema.optional(),
|
|
458
|
-
memberships:
|
|
459
|
-
subscriptions:
|
|
460
|
-
seedInstructions:
|
|
590
|
+
memberships: z4.array(NameSchema).default([]),
|
|
591
|
+
subscriptions: z4.array(ChannelRefSchema).default([]),
|
|
592
|
+
seedInstructions: z4.string().optional()
|
|
461
593
|
});
|
|
462
|
-
var ChannelProposalSchema =
|
|
594
|
+
var ChannelProposalSchema = z4.object({
|
|
463
595
|
project: NameSchema.nullable().default(null),
|
|
464
596
|
name: NameSchema,
|
|
465
|
-
purpose:
|
|
597
|
+
purpose: z4.string().min(1)
|
|
466
598
|
});
|
|
467
|
-
var ReallocationProposalSchema =
|
|
468
|
-
description:
|
|
599
|
+
var ReallocationProposalSchema = z4.object({
|
|
600
|
+
description: z4.string().min(1)
|
|
469
601
|
});
|
|
470
|
-
var RetirementProposalSchema =
|
|
602
|
+
var RetirementProposalSchema = z4.object({
|
|
471
603
|
agent: NameSchema,
|
|
472
|
-
reason:
|
|
604
|
+
reason: z4.string().min(1)
|
|
473
605
|
});
|
|
474
|
-
var ArchiveProposalSchema =
|
|
606
|
+
var ArchiveProposalSchema = z4.object({
|
|
475
607
|
project: NameSchema,
|
|
476
|
-
reason:
|
|
608
|
+
reason: z4.string().min(1)
|
|
477
609
|
});
|
|
478
|
-
var SkillProposalSchema =
|
|
610
|
+
var SkillProposalSchema = z4.object({
|
|
479
611
|
name: NameSchema,
|
|
480
|
-
summary:
|
|
481
|
-
body:
|
|
612
|
+
summary: z4.string().min(1).max(200),
|
|
613
|
+
body: z4.string().min(1)
|
|
482
614
|
});
|
|
483
615
|
var ProposalCharterSchemas = {
|
|
484
616
|
role: RoleCharterSchema,
|
|
@@ -550,17 +682,17 @@ function describeCharter(kind, charter) {
|
|
|
550
682
|
}
|
|
551
683
|
return `${kind} ${JSON.stringify(charter)}`;
|
|
552
684
|
}
|
|
553
|
-
var SkillSchema =
|
|
685
|
+
var SkillSchema = z4.object({
|
|
554
686
|
name: NameSchema,
|
|
555
|
-
summary:
|
|
556
|
-
scope:
|
|
557
|
-
path:
|
|
687
|
+
summary: z4.string().default(""),
|
|
688
|
+
scope: z4.enum(["own", "society"]),
|
|
689
|
+
path: z4.string().min(1)
|
|
558
690
|
});
|
|
559
|
-
var HomeConflictSchema =
|
|
691
|
+
var HomeConflictSchema = z4.object({
|
|
560
692
|
/** The copy, relative to the home. */
|
|
561
|
-
path:
|
|
693
|
+
path: z4.string().min(1),
|
|
562
694
|
/** The file beside it, whose version the board kept. */
|
|
563
|
-
file:
|
|
695
|
+
file: z4.string().min(1),
|
|
564
696
|
/** When the copy reached the board. */
|
|
565
697
|
since: IsoDateTimeSchema
|
|
566
698
|
});
|
|
@@ -568,200 +700,242 @@ var CONFLICT_COPY = /^(.+)\.conflict-[^/.]+$/;
|
|
|
568
700
|
function conflictCopyOf(copy) {
|
|
569
701
|
return CONFLICT_COPY.exec(copy)?.[1] ?? null;
|
|
570
702
|
}
|
|
571
|
-
var CommitIdSchema =
|
|
572
|
-
var HomeFileChangeSchema =
|
|
573
|
-
path:
|
|
574
|
-
status:
|
|
703
|
+
var CommitIdSchema = z4.string().regex(/^[0-9a-f]{40}([0-9a-f]{24})?$/);
|
|
704
|
+
var HomeFileChangeSchema = z4.object({
|
|
705
|
+
path: z4.string().min(1),
|
|
706
|
+
status: z4.enum(["added", "modified", "deleted"]),
|
|
575
707
|
/** Lines added and removed, or null for a binary file. */
|
|
576
|
-
added:
|
|
577
|
-
removed:
|
|
708
|
+
added: z4.number().int().nonnegative().nullable(),
|
|
709
|
+
removed: z4.number().int().nonnegative().nullable()
|
|
578
710
|
});
|
|
579
|
-
var HomeChangeSchema =
|
|
711
|
+
var HomeChangeSchema = z4.object({
|
|
580
712
|
commit: CommitIdSchema,
|
|
581
713
|
at: IsoDateTimeSchema,
|
|
582
|
-
kind:
|
|
583
|
-
author:
|
|
714
|
+
kind: z4.enum(["turn", "board", "merge"]),
|
|
715
|
+
author: z4.string(),
|
|
584
716
|
/** The turn that made it, from the runner's commit message. */
|
|
585
717
|
turnId: UlidSchema.optional(),
|
|
586
|
-
subject:
|
|
587
|
-
files:
|
|
718
|
+
subject: z4.string(),
|
|
719
|
+
files: z4.array(HomeFileChangeSchema)
|
|
588
720
|
});
|
|
589
|
-
var HomeHistorySchema =
|
|
590
|
-
changes:
|
|
721
|
+
var HomeHistorySchema = z4.object({
|
|
722
|
+
changes: z4.array(HomeChangeSchema),
|
|
591
723
|
/** Whether older commits remain past the ones read. */
|
|
592
|
-
more:
|
|
724
|
+
more: z4.boolean()
|
|
593
725
|
});
|
|
594
726
|
var HomeFileDiffSchema = HomeFileChangeSchema.pick({ path: true, status: true }).extend({
|
|
595
|
-
patch:
|
|
727
|
+
patch: z4.string().nullable(),
|
|
596
728
|
/** The patch was cut at its size limit. */
|
|
597
|
-
truncated:
|
|
729
|
+
truncated: z4.boolean()
|
|
598
730
|
});
|
|
599
|
-
var KnowledgeSchema =
|
|
731
|
+
var KnowledgeSchema = z4.object({
|
|
600
732
|
topic: NameSchema,
|
|
601
733
|
project: NameSchema.nullable(),
|
|
602
734
|
updatedBy: NameSchema,
|
|
603
735
|
updatedAt: IsoDateTimeSchema
|
|
604
736
|
});
|
|
605
|
-
var RemovedKnowledgeSchema =
|
|
737
|
+
var RemovedKnowledgeSchema = z4.object({
|
|
606
738
|
topic: NameSchema,
|
|
607
739
|
project: NameSchema.nullable(),
|
|
608
740
|
removedBy: NameSchema,
|
|
609
741
|
removedAt: IsoDateTimeSchema
|
|
610
742
|
});
|
|
611
|
-
var DashboardSchema =
|
|
743
|
+
var DashboardSchema = z4.object({
|
|
612
744
|
project: NameSchema,
|
|
613
|
-
revision:
|
|
745
|
+
revision: z4.number().int().min(0),
|
|
614
746
|
/** Absent until someone updates the dashboard the project began with. */
|
|
615
747
|
updatedBy: NameSchema.optional(),
|
|
616
748
|
updatedAt: IsoDateTimeSchema.optional(),
|
|
617
|
-
body:
|
|
749
|
+
body: z4.string()
|
|
618
750
|
});
|
|
619
|
-
var
|
|
751
|
+
var ProposalOriginSchema = z4.object({
|
|
752
|
+
scope: NameSchema,
|
|
753
|
+
thread: UlidSchema.optional(),
|
|
754
|
+
channel: NameSchema.optional()
|
|
755
|
+
});
|
|
756
|
+
var ProposalFrontmatterSchema = z4.object({
|
|
620
757
|
id: UlidSchema,
|
|
621
758
|
kind: ProposalKindSchema,
|
|
622
759
|
proposedBy: NameSchema,
|
|
623
760
|
status: ProposalStatusSchema,
|
|
624
761
|
createdAt: IsoDateTimeSchema,
|
|
762
|
+
/** Absent for a proposal filed outside a turn, or before proposals recorded it. */
|
|
763
|
+
filedFrom: ProposalOriginSchema.optional(),
|
|
625
764
|
decidedBy: NameSchema.optional(),
|
|
626
765
|
decidedAt: IsoDateTimeSchema.optional(),
|
|
627
|
-
reason:
|
|
628
|
-
charter:
|
|
766
|
+
reason: z4.string().optional(),
|
|
767
|
+
charter: z4.record(z4.string(), z4.unknown()),
|
|
629
768
|
/** What approval created, for kinds the board provisions: the agent, channel, role, or retirement. */
|
|
630
769
|
provisionedAt: IsoDateTimeSchema.optional(),
|
|
631
|
-
provision:
|
|
770
|
+
provision: z4.record(z4.string(), z4.unknown()).optional()
|
|
632
771
|
});
|
|
633
|
-
var DecisionSchema =
|
|
772
|
+
var DecisionSchema = z4.object({
|
|
634
773
|
id: UlidSchema,
|
|
635
774
|
proposalId: UlidSchema,
|
|
636
775
|
decidedBy: NameSchema,
|
|
637
|
-
outcome:
|
|
638
|
-
reason:
|
|
776
|
+
outcome: z4.enum(["approved", "rejected"]),
|
|
777
|
+
reason: z4.string().optional(),
|
|
639
778
|
ts: IsoDateTimeSchema
|
|
640
779
|
});
|
|
641
780
|
|
|
642
781
|
// packages/shared/dist/verbs.js
|
|
643
|
-
import { z as
|
|
782
|
+
import { z as z5 } from "zod";
|
|
783
|
+
var MomentSchema = z5.iso.datetime({ offset: true });
|
|
784
|
+
var CronTitleSchema = z5.string().trim().min(1).max(120);
|
|
785
|
+
var CronNoteSchema = z5.string().trim().min(1).max(4e3);
|
|
644
786
|
var VerbInputs = {
|
|
645
|
-
post_message:
|
|
787
|
+
post_message: z5.object({
|
|
646
788
|
/** Required outside a thread; in one it may be left out, and must otherwise be the thread's. */
|
|
647
789
|
channel: ChannelRefSchema.optional(),
|
|
648
|
-
body:
|
|
790
|
+
body: z5.string().min(1),
|
|
649
791
|
thread_id: UlidSchema.optional()
|
|
650
792
|
}),
|
|
651
|
-
read_inbox:
|
|
793
|
+
read_inbox: z5.object({
|
|
652
794
|
since_cursor: UlidSchema.nullable().optional(),
|
|
653
|
-
limit:
|
|
654
|
-
advance:
|
|
795
|
+
limit: z5.number().int().positive().max(200).default(50),
|
|
796
|
+
advance: z5.boolean().default(true)
|
|
655
797
|
}),
|
|
656
|
-
search:
|
|
657
|
-
query:
|
|
798
|
+
search: z5.object({
|
|
799
|
+
query: z5.string().min(1),
|
|
658
800
|
project: NameSchema.optional(),
|
|
659
801
|
channel: ChannelRefSchema.optional(),
|
|
660
|
-
limit:
|
|
802
|
+
limit: z5.number().int().positive().max(100).default(20)
|
|
661
803
|
}),
|
|
662
804
|
/** On a task or a proposal, whose id the thread takes, or on a channel with a title. */
|
|
663
|
-
open_thread:
|
|
805
|
+
open_thread: z5.object({
|
|
664
806
|
task_id: UlidSchema.optional(),
|
|
665
807
|
proposal_id: UlidSchema.optional(),
|
|
666
808
|
channel: ChannelRefSchema.optional(),
|
|
667
|
-
title:
|
|
809
|
+
title: z5.string().min(1).max(200).optional()
|
|
668
810
|
}),
|
|
669
|
-
close_thread:
|
|
670
|
-
create_task:
|
|
811
|
+
close_thread: z5.object({ thread_id: UlidSchema, summary: z5.string().min(1) }),
|
|
812
|
+
create_task: z5.object({
|
|
671
813
|
project: NameSchema,
|
|
672
|
-
title:
|
|
673
|
-
body:
|
|
814
|
+
title: z5.string().min(1).max(200),
|
|
815
|
+
body: z5.string().default(""),
|
|
674
816
|
parent_id: UlidSchema.optional(),
|
|
675
|
-
required_capabilities:
|
|
817
|
+
required_capabilities: z5.array(z5.string()).default([]),
|
|
676
818
|
/** The plan; without it the task gets one stage, work, that anyone in the project may take. */
|
|
677
|
-
stages:
|
|
819
|
+
stages: z5.array(PlanStageSchema).min(1).optional(),
|
|
678
820
|
/** A channel of the project the task belongs to, such as a release's; its thread opens there. */
|
|
679
821
|
channel: NameSchema.default("general")
|
|
680
822
|
}),
|
|
681
|
-
claim_task:
|
|
682
|
-
release_task:
|
|
683
|
-
update_task:
|
|
823
|
+
claim_task: z5.object({ task_id: UlidSchema }),
|
|
824
|
+
release_task: z5.object({ task_id: UlidSchema }),
|
|
825
|
+
update_task: z5.object({
|
|
684
826
|
task_id: UlidSchema,
|
|
685
827
|
/** Move the task back to this earlier stage. */
|
|
686
828
|
stage: StageIdSchema.optional(),
|
|
687
|
-
status:
|
|
829
|
+
status: z5.enum(["abandoned"]).optional(),
|
|
688
830
|
/** Posted to the task's thread as the caller, marked with the step when the task moved. */
|
|
689
|
-
note:
|
|
690
|
-
blocked_by:
|
|
831
|
+
note: z5.string().min(1).optional(),
|
|
832
|
+
blocked_by: z5.array(UlidSchema).optional(),
|
|
691
833
|
/**
|
|
692
834
|
* The pull request a `ghpr` task lands through, on its project's repository, with the merge
|
|
693
835
|
* commit's subject and body; null unlinks it.
|
|
694
836
|
*/
|
|
695
|
-
pull_request:
|
|
837
|
+
pull_request: z5.object({
|
|
696
838
|
url: PullRequestUrlSchema,
|
|
697
839
|
merge_subject: MergeSubjectSchema.optional(),
|
|
698
|
-
merge_body:
|
|
840
|
+
merge_body: z5.string().max(2e4).optional()
|
|
699
841
|
}).nullable().optional()
|
|
700
842
|
}),
|
|
701
|
-
get_task:
|
|
702
|
-
subscribe:
|
|
703
|
-
unsubscribe:
|
|
704
|
-
propose:
|
|
843
|
+
get_task: z5.object({ task_id: UlidSchema }),
|
|
844
|
+
subscribe: z5.object({ channel: ChannelRefSchema }),
|
|
845
|
+
unsubscribe: z5.object({ channel: ChannelRefSchema }),
|
|
846
|
+
propose: z5.object({
|
|
705
847
|
kind: ProposalKindSchema,
|
|
706
|
-
charter:
|
|
707
|
-
rationale:
|
|
848
|
+
charter: z5.record(z5.string(), z5.unknown()),
|
|
849
|
+
rationale: z5.string().default("")
|
|
708
850
|
}),
|
|
709
|
-
approve:
|
|
710
|
-
reject:
|
|
711
|
-
create_project:
|
|
851
|
+
approve: z5.object({ proposal_id: UlidSchema, reason: z5.string().optional() }),
|
|
852
|
+
reject: z5.object({ proposal_id: UlidSchema, reason: z5.string().min(1) }),
|
|
853
|
+
create_project: z5.object({
|
|
712
854
|
slug: NameSchema,
|
|
713
|
-
name:
|
|
714
|
-
repo:
|
|
855
|
+
name: z5.string().min(1).optional(),
|
|
856
|
+
repo: z5.string().min(1).nullable().default(null),
|
|
715
857
|
default_branch: DefaultBranchSchema.default("main"),
|
|
716
858
|
on_done: CompletionEffectSchema.optional()
|
|
717
859
|
}),
|
|
718
|
-
join_project:
|
|
719
|
-
leave_project:
|
|
720
|
-
write_knowledge:
|
|
860
|
+
join_project: z5.object({ project: NameSchema, agent: NameSchema.optional() }),
|
|
861
|
+
leave_project: z5.object({ project: NameSchema, agent: NameSchema.optional() }),
|
|
862
|
+
write_knowledge: z5.object({
|
|
721
863
|
/** A project you belong to, or null for society knowledge, which the steward and the user curate. */
|
|
722
864
|
project: NameSchema.nullable().default(null),
|
|
723
865
|
topic: NameSchema,
|
|
724
|
-
body:
|
|
866
|
+
body: z5.string().min(1)
|
|
725
867
|
}),
|
|
726
|
-
remove_knowledge:
|
|
868
|
+
remove_knowledge: z5.object({
|
|
727
869
|
/** As for write_knowledge: a project you belong to, or null for society knowledge. */
|
|
728
870
|
project: NameSchema.nullable().default(null),
|
|
729
871
|
topic: NameSchema
|
|
730
872
|
}),
|
|
731
|
-
plan_task:
|
|
873
|
+
plan_task: z5.object({
|
|
732
874
|
task_id: UlidSchema,
|
|
733
|
-
stages:
|
|
875
|
+
stages: z5.array(PlanEditStageSchema),
|
|
734
876
|
on_done: CompletionEffectSchema.optional()
|
|
735
877
|
}),
|
|
736
878
|
/** The note is posted to the task's thread as the caller, marked with the step. */
|
|
737
|
-
advance_task:
|
|
738
|
-
configure_project:
|
|
879
|
+
advance_task: z5.object({ task_id: UlidSchema, note: z5.string().min(1).optional() }),
|
|
880
|
+
configure_project: z5.object({
|
|
739
881
|
project: NameSchema,
|
|
740
882
|
on_done: CompletionEffectSchema.optional(),
|
|
741
883
|
/** A new display name; the slug, which everything refers to the project by, never changes. */
|
|
742
|
-
name:
|
|
884
|
+
name: z5.string().trim().min(1).max(120).optional(),
|
|
743
885
|
/** The branch tasks start from and land on from now; tasks in play keep their branches. */
|
|
744
886
|
default_branch: DefaultBranchSchema.optional()
|
|
745
887
|
}),
|
|
746
|
-
archive_project:
|
|
747
|
-
create_channel:
|
|
888
|
+
archive_project: z5.object({ project: NameSchema, reason: z5.string().min(1) }),
|
|
889
|
+
create_channel: z5.object({
|
|
748
890
|
/** A project, or null for a channel of the society. */
|
|
749
891
|
project: NameSchema.nullable().default(null),
|
|
750
892
|
name: NameSchema,
|
|
751
|
-
purpose:
|
|
893
|
+
purpose: z5.string().min(1)
|
|
752
894
|
}),
|
|
753
|
-
archive_channel:
|
|
754
|
-
update_dashboard:
|
|
895
|
+
archive_channel: z5.object({ channel: ChannelRefSchema, reason: z5.string().min(1) }),
|
|
896
|
+
update_dashboard: z5.object({
|
|
755
897
|
project: NameSchema,
|
|
756
898
|
/** The whole dashboard as markdown; the board keeps its frontmatter. */
|
|
757
|
-
body:
|
|
899
|
+
body: z5.string().min(1),
|
|
758
900
|
/** The `revision` in the frontmatter of the dashboard you read, 0 when it names none. */
|
|
759
|
-
revision:
|
|
760
|
-
})
|
|
901
|
+
revision: z5.number().int().min(0)
|
|
902
|
+
}),
|
|
903
|
+
create_cron: z5.object({
|
|
904
|
+
title: CronTitleSchema,
|
|
905
|
+
/** What to do when it fires; quoted in the prompt of every turn it starts. */
|
|
906
|
+
note: CronNoteSchema,
|
|
907
|
+
/** Minute, hour, day of month, month, day of week, such as `0 9 * * 1-5`; or `at` instead. */
|
|
908
|
+
cron: CronExpressionSchema.optional(),
|
|
909
|
+
/** The IANA time zone `cron` is read in; the society's when left out. */
|
|
910
|
+
timezone: TimeZoneSchema.optional(),
|
|
911
|
+
/** One time to fire, instead of `cron`; the cron ends once it has fired. */
|
|
912
|
+
at: MomentSchema.optional(),
|
|
913
|
+
/** The citizen it wakes: yourself when left out. */
|
|
914
|
+
agent: NameSchema.optional(),
|
|
915
|
+
/** Where it fires: a project the citizen belongs to, or `society`; this turn's scope when left out. */
|
|
916
|
+
project: NameSchema.optional(),
|
|
917
|
+
/** An open thread there, whose conversation it fires in. */
|
|
918
|
+
thread_id: UlidSchema.optional(),
|
|
919
|
+
/** An open channel of that place other than general, whose conversation it fires in. */
|
|
920
|
+
channel: NameSchema.optional()
|
|
921
|
+
}),
|
|
922
|
+
update_cron: z5.object({
|
|
923
|
+
cron_id: UlidSchema,
|
|
924
|
+
/** Pause it, or resume it, which starts its schedule afresh from now. */
|
|
925
|
+
paused: z5.boolean().optional(),
|
|
926
|
+
title: CronTitleSchema.optional(),
|
|
927
|
+
note: CronNoteSchema.optional(),
|
|
928
|
+
/** A new expression, or `at` for one time; either starts the schedule afresh. */
|
|
929
|
+
cron: CronExpressionSchema.optional(),
|
|
930
|
+
/** The zone `cron` is read in; null for the society's. */
|
|
931
|
+
timezone: TimeZoneSchema.nullable().optional(),
|
|
932
|
+
at: MomentSchema.optional()
|
|
933
|
+
}),
|
|
934
|
+
remove_cron: z5.object({ cron_id: UlidSchema, reason: z5.string().trim().min(1) })
|
|
761
935
|
};
|
|
762
936
|
|
|
763
937
|
// packages/shared/dist/events.js
|
|
764
|
-
import { z as
|
|
938
|
+
import { z as z6 } from "zod";
|
|
765
939
|
var BOARD_EVENT_TYPES = [
|
|
766
940
|
"society.initialized",
|
|
767
941
|
"project.added",
|
|
@@ -814,30 +988,38 @@ var BOARD_EVENT_TYPES = [
|
|
|
814
988
|
"turn.failed",
|
|
815
989
|
"merge.completed",
|
|
816
990
|
"merge.failed",
|
|
817
|
-
"
|
|
991
|
+
"pull_request.closed",
|
|
992
|
+
"pull_request.close_failed",
|
|
993
|
+
"workspace.leftovers",
|
|
994
|
+
"society.configured",
|
|
995
|
+
"cron.created",
|
|
996
|
+
"cron.updated",
|
|
997
|
+
"cron.fired",
|
|
998
|
+
"cron.skipped",
|
|
999
|
+
"cron.ended"
|
|
818
1000
|
];
|
|
819
|
-
var BoardEventTypeSchema =
|
|
820
|
-
var BoardEventSchema =
|
|
1001
|
+
var BoardEventTypeSchema = z6.enum(BOARD_EVENT_TYPES);
|
|
1002
|
+
var BoardEventSchema = z6.object({
|
|
821
1003
|
id: UlidSchema,
|
|
822
1004
|
ts: IsoDateTimeSchema,
|
|
823
1005
|
type: BoardEventTypeSchema,
|
|
824
1006
|
actor: NameSchema,
|
|
825
|
-
payload:
|
|
826
|
-
});
|
|
827
|
-
var UsageSchema =
|
|
828
|
-
inputTokens:
|
|
829
|
-
outputTokens:
|
|
830
|
-
cacheReadTokens:
|
|
831
|
-
cacheWriteTokens:
|
|
832
|
-
});
|
|
833
|
-
var TurnStatusSchema =
|
|
834
|
-
summary:
|
|
835
|
-
claimsHeld:
|
|
836
|
-
blockedOn:
|
|
837
|
-
needsUserDecision:
|
|
838
|
-
memoryUpdated:
|
|
839
|
-
});
|
|
840
|
-
var TurnExitReasonSchema =
|
|
1007
|
+
payload: z6.record(z6.string(), z6.unknown())
|
|
1008
|
+
});
|
|
1009
|
+
var UsageSchema = z6.object({
|
|
1010
|
+
inputTokens: z6.number().int().nonnegative(),
|
|
1011
|
+
outputTokens: z6.number().int().nonnegative(),
|
|
1012
|
+
cacheReadTokens: z6.number().int().nonnegative().default(0),
|
|
1013
|
+
cacheWriteTokens: z6.number().int().nonnegative().default(0)
|
|
1014
|
+
});
|
|
1015
|
+
var TurnStatusSchema = z6.object({
|
|
1016
|
+
summary: z6.string().min(1),
|
|
1017
|
+
claimsHeld: z6.array(UlidSchema).default([]),
|
|
1018
|
+
blockedOn: z6.array(z6.string()).default([]),
|
|
1019
|
+
needsUserDecision: z6.boolean().default(false),
|
|
1020
|
+
memoryUpdated: z6.boolean().default(false)
|
|
1021
|
+
});
|
|
1022
|
+
var TurnExitReasonSchema = z6.enum([
|
|
841
1023
|
"completed",
|
|
842
1024
|
"timeout",
|
|
843
1025
|
"error",
|
|
@@ -845,50 +1027,50 @@ var TurnExitReasonSchema = z5.enum([
|
|
|
845
1027
|
"blocked",
|
|
846
1028
|
"stopped"
|
|
847
1029
|
]);
|
|
848
|
-
var AgentEventSchema =
|
|
849
|
-
|
|
850
|
-
type:
|
|
1030
|
+
var AgentEventSchema = z6.discriminatedUnion("type", [
|
|
1031
|
+
z6.object({
|
|
1032
|
+
type: z6.literal("turn_started"),
|
|
851
1033
|
agent: NameSchema,
|
|
852
|
-
session:
|
|
1034
|
+
session: z6.string(),
|
|
853
1035
|
runner: NameSchema,
|
|
854
1036
|
/** The model the CLI reports running, when it reports one. */
|
|
855
|
-
model:
|
|
1037
|
+
model: z6.string().optional()
|
|
856
1038
|
}),
|
|
857
1039
|
/**
|
|
858
1040
|
* The CLI took a steer into the running turn. Adapters name the steer; the server adds the
|
|
859
1041
|
* messages it delivered and the text the CLI was given.
|
|
860
1042
|
*/
|
|
861
|
-
|
|
862
|
-
type:
|
|
863
|
-
steer:
|
|
864
|
-
messages:
|
|
865
|
-
text:
|
|
1043
|
+
z6.object({
|
|
1044
|
+
type: z6.literal("steered"),
|
|
1045
|
+
steer: z6.string().min(1),
|
|
1046
|
+
messages: z6.array(UlidSchema).optional(),
|
|
1047
|
+
text: z6.string().optional()
|
|
866
1048
|
}),
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
type:
|
|
871
|
-
name:
|
|
872
|
-
ok:
|
|
1049
|
+
z6.object({ type: z6.literal("text"), delta: z6.string() }),
|
|
1050
|
+
z6.object({ type: z6.literal("tool_call"), name: z6.string(), input: z6.unknown() }),
|
|
1051
|
+
z6.object({
|
|
1052
|
+
type: z6.literal("tool_result"),
|
|
1053
|
+
name: z6.string(),
|
|
1054
|
+
ok: z6.boolean(),
|
|
873
1055
|
/** What the tool returned, cut to `OUTPUT_LIMIT` characters by `capOutput`. */
|
|
874
|
-
output:
|
|
1056
|
+
output: z6.string().optional()
|
|
875
1057
|
}),
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
type:
|
|
1058
|
+
z6.object({ type: z6.literal("approval_requested"), kind: z6.string(), detail: z6.unknown() }),
|
|
1059
|
+
z6.object({
|
|
1060
|
+
type: z6.literal("turn_completed"),
|
|
879
1061
|
usage: UsageSchema,
|
|
880
|
-
costUsd:
|
|
1062
|
+
costUsd: z6.number().nonnegative(),
|
|
881
1063
|
status: TurnStatusSchema.nullable(),
|
|
882
1064
|
exitReason: TurnExitReasonSchema
|
|
883
1065
|
}),
|
|
884
|
-
|
|
1066
|
+
z6.object({ type: z6.literal("error"), message: z6.string() })
|
|
885
1067
|
]);
|
|
886
|
-
var TranscriptEntrySchema =
|
|
1068
|
+
var TranscriptEntrySchema = z6.object({
|
|
887
1069
|
ts: IsoDateTimeSchema,
|
|
888
1070
|
event: AgentEventSchema
|
|
889
1071
|
});
|
|
890
|
-
var LiveTurnEventSchema =
|
|
891
|
-
seq:
|
|
1072
|
+
var LiveTurnEventSchema = z6.object({
|
|
1073
|
+
seq: z6.number().int().positive(),
|
|
892
1074
|
ts: IsoDateTimeSchema,
|
|
893
1075
|
agent: NameSchema,
|
|
894
1076
|
project: NameSchema,
|
|
@@ -900,97 +1082,97 @@ var LiveTurnEventSchema = z5.object({
|
|
|
900
1082
|
});
|
|
901
1083
|
|
|
902
1084
|
// packages/shared/dist/turns.js
|
|
903
|
-
import { z as
|
|
1085
|
+
import { z as z8 } from "zod";
|
|
904
1086
|
|
|
905
1087
|
// packages/shared/dist/runner.js
|
|
906
|
-
import { z as
|
|
1088
|
+
import { z as z7 } from "zod";
|
|
907
1089
|
var HOME_SCRATCH = "scratch";
|
|
908
1090
|
var HOME_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
909
|
-
var RunnerHelloSchema =
|
|
910
|
-
protocol:
|
|
911
|
-
version:
|
|
1091
|
+
var RunnerHelloSchema = z7.object({
|
|
1092
|
+
protocol: z7.number().int().positive(),
|
|
1093
|
+
version: z7.string().min(1),
|
|
912
1094
|
os: RunnerOsSchema,
|
|
913
|
-
clis:
|
|
1095
|
+
clis: z7.array(CliKindSchema),
|
|
914
1096
|
/** The CLIs whose adapter can keep a session warm between turns. */
|
|
915
|
-
residentClis:
|
|
1097
|
+
residentClis: z7.array(CliKindSchema).default([]),
|
|
916
1098
|
/** The CLIs whose adapter can take input into a running turn; the server steers only these. */
|
|
917
|
-
steerableClis:
|
|
1099
|
+
steerableClis: z7.array(CliKindSchema).default([]),
|
|
918
1100
|
/** The CLIs whose adapter can stop a running turn; the server stops only these. */
|
|
919
|
-
stoppableClis:
|
|
1101
|
+
stoppableClis: z7.array(CliKindSchema).default([]),
|
|
920
1102
|
/** Whether the runner answers file reads on its projects' branches; one from before never does. */
|
|
921
|
-
branchReads:
|
|
1103
|
+
branchReads: z7.boolean().default(false),
|
|
922
1104
|
/** Whether the runner lists what a branch changed; one from before never does. */
|
|
923
|
-
branchChanges:
|
|
924
|
-
capabilities:
|
|
1105
|
+
branchChanges: z7.boolean().default(false),
|
|
1106
|
+
capabilities: z7.array(z7.string()).default([]),
|
|
925
1107
|
/** Turns the runner runs at once, or null for no limit. */
|
|
926
|
-
slots:
|
|
1108
|
+
slots: z7.number().int().positive().nullable(),
|
|
927
1109
|
/** Turns the runner is still running, when it registers again; the server fails the rest of its turns. */
|
|
928
|
-
turns:
|
|
1110
|
+
turns: z7.array(UlidSchema).default([])
|
|
929
1111
|
});
|
|
930
|
-
var RunnerWelcomeSchema =
|
|
931
|
-
var RunnerEnrollRequestSchema =
|
|
932
|
-
protocol:
|
|
933
|
-
version:
|
|
934
|
-
hostname:
|
|
1112
|
+
var RunnerWelcomeSchema = z7.object({ name: NameSchema, version: z7.string() });
|
|
1113
|
+
var RunnerEnrollRequestSchema = z7.object({
|
|
1114
|
+
protocol: z7.number().int().positive(),
|
|
1115
|
+
version: z7.string().min(1).max(64),
|
|
1116
|
+
hostname: z7.string().min(1).max(253),
|
|
935
1117
|
os: RunnerOsSchema,
|
|
936
|
-
clis:
|
|
937
|
-
capabilities:
|
|
1118
|
+
clis: z7.array(CliKindSchema).max(8),
|
|
1119
|
+
capabilities: z7.array(z7.string().min(1).max(64)).max(32).default([])
|
|
938
1120
|
});
|
|
939
|
-
var RunnerEnrollmentSchema =
|
|
940
|
-
deviceCode:
|
|
941
|
-
userCode:
|
|
1121
|
+
var RunnerEnrollmentSchema = z7.object({
|
|
1122
|
+
deviceCode: z7.string().min(1),
|
|
1123
|
+
userCode: z7.string().min(1),
|
|
942
1124
|
expiresAt: IsoDateTimeSchema,
|
|
943
|
-
intervalMs:
|
|
1125
|
+
intervalMs: z7.number().int().positive()
|
|
944
1126
|
});
|
|
945
|
-
var RunnerEnrollPollSchema =
|
|
946
|
-
var RunnerEnrollStatusSchema =
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1127
|
+
var RunnerEnrollPollSchema = z7.object({ deviceCode: z7.string().min(1) });
|
|
1128
|
+
var RunnerEnrollStatusSchema = z7.discriminatedUnion("status", [
|
|
1129
|
+
z7.object({ status: z7.literal("pending") }),
|
|
1130
|
+
z7.object({ status: z7.literal("approved"), name: NameSchema, token: z7.string().min(1) }),
|
|
1131
|
+
z7.object({ status: z7.literal("denied") })
|
|
950
1132
|
]);
|
|
951
|
-
var PendingEnrollmentSchema =
|
|
952
|
-
userCode:
|
|
953
|
-
hostname:
|
|
1133
|
+
var PendingEnrollmentSchema = z7.object({
|
|
1134
|
+
userCode: z7.string().min(1),
|
|
1135
|
+
hostname: z7.string(),
|
|
954
1136
|
os: RunnerOsSchema,
|
|
955
|
-
clis:
|
|
956
|
-
capabilities:
|
|
957
|
-
version:
|
|
1137
|
+
clis: z7.array(CliKindSchema),
|
|
1138
|
+
capabilities: z7.array(z7.string()),
|
|
1139
|
+
version: z7.string(),
|
|
958
1140
|
requestedAt: IsoDateTimeSchema,
|
|
959
1141
|
expiresAt: IsoDateTimeSchema
|
|
960
1142
|
});
|
|
961
|
-
var ProjectRepoSchema =
|
|
1143
|
+
var ProjectRepoSchema = z7.object({
|
|
962
1144
|
slug: NameSchema,
|
|
963
|
-
origin:
|
|
964
|
-
defaultBranch:
|
|
1145
|
+
origin: z7.string().nullable(),
|
|
1146
|
+
defaultBranch: z7.string().min(1),
|
|
965
1147
|
/**
|
|
966
1148
|
* The board lands finished tasks on the default branch, itself or through their pull requests,
|
|
967
1149
|
* so the runner lets nothing else move it.
|
|
968
1150
|
*/
|
|
969
|
-
boardLands:
|
|
1151
|
+
boardLands: z7.boolean()
|
|
970
1152
|
});
|
|
971
|
-
var TurnWorkspaceSchema =
|
|
972
|
-
|
|
973
|
-
kind:
|
|
1153
|
+
var TurnWorkspaceSchema = z7.discriminatedUnion("kind", [
|
|
1154
|
+
z7.object({
|
|
1155
|
+
kind: z7.literal("home"),
|
|
974
1156
|
thread: UlidSchema.optional(),
|
|
975
1157
|
channel: NameSchema.optional()
|
|
976
1158
|
}),
|
|
977
|
-
|
|
978
|
-
kind:
|
|
1159
|
+
z7.object({
|
|
1160
|
+
kind: z7.literal("project"),
|
|
979
1161
|
repo: ProjectRepoSchema,
|
|
980
|
-
branches:
|
|
1162
|
+
branches: z7.array(z7.string().min(1)),
|
|
981
1163
|
thread: UlidSchema.optional(),
|
|
982
1164
|
channel: NameSchema.optional()
|
|
983
1165
|
}),
|
|
984
|
-
|
|
1166
|
+
z7.object({ kind: z7.literal("task"), repo: ProjectRepoSchema, taskId: UlidSchema })
|
|
985
1167
|
]);
|
|
986
|
-
var TurnJobSchema =
|
|
1168
|
+
var TurnJobSchema = z7.object({
|
|
987
1169
|
turnId: UlidSchema,
|
|
988
1170
|
agent: NameSchema,
|
|
989
1171
|
role: NameSchema,
|
|
990
1172
|
cli: CliKindSchema,
|
|
991
|
-
model:
|
|
1173
|
+
model: z7.string().optional(),
|
|
992
1174
|
/** The reasoning effort set on the agent; absent for the model's own default. */
|
|
993
|
-
effort:
|
|
1175
|
+
effort: z7.string().optional(),
|
|
994
1176
|
/** A project slug, or the society scope. */
|
|
995
1177
|
scope: NameSchema,
|
|
996
1178
|
/** The thread whose conversation the turn is in; absent for the home conversation. */
|
|
@@ -1001,160 +1183,167 @@ var TurnJobSchema = z6.object({
|
|
|
1001
1183
|
* The session to resume, or null to start one: the CLI's adapter picks a fresh session's id, and
|
|
1002
1184
|
* the outcome reports it.
|
|
1003
1185
|
*/
|
|
1004
|
-
session:
|
|
1186
|
+
session: z7.string().min(1).nullable(),
|
|
1005
1187
|
/** The running total the session reported after its last turn, which a resumed session starts from. */
|
|
1006
|
-
costSoFarUsd:
|
|
1188
|
+
costSoFarUsd: z7.number().nonnegative(),
|
|
1007
1189
|
/** The rendered charter, norms, memory core, and skills index, with `PATH_TOKENS` for places. */
|
|
1008
|
-
instructions:
|
|
1009
|
-
prompt:
|
|
1010
|
-
mcp:
|
|
1011
|
-
limits:
|
|
1012
|
-
timeoutMs:
|
|
1013
|
-
maxTurns:
|
|
1190
|
+
instructions: z7.string(),
|
|
1191
|
+
prompt: z7.string(),
|
|
1192
|
+
mcp: z7.object({ url: z7.string().min(1), token: z7.string().min(1) }),
|
|
1193
|
+
limits: z7.object({
|
|
1194
|
+
timeoutMs: z7.number().int().positive().nullable(),
|
|
1195
|
+
maxTurns: z7.number().int().positive().nullable()
|
|
1014
1196
|
}),
|
|
1015
1197
|
/** Present when the runner keeps the conversation's session warm between turns, under `key`. */
|
|
1016
|
-
resident:
|
|
1198
|
+
resident: z7.object({ key: z7.string().min(1), idleMs: z7.number().int().positive() }).optional(),
|
|
1017
1199
|
workspace: TurnWorkspaceSchema
|
|
1018
1200
|
});
|
|
1019
|
-
var TurnWorkSchema =
|
|
1020
|
-
var TurnEventsSchema =
|
|
1021
|
-
var TurnOutcomeSchema =
|
|
1201
|
+
var TurnWorkSchema = z7.object({ branch: z7.string().min(1), head: z7.string().min(1) });
|
|
1202
|
+
var TurnEventsSchema = z7.object({ entries: z7.array(TranscriptEntrySchema) });
|
|
1203
|
+
var TurnOutcomeSchema = z7.object({
|
|
1022
1204
|
exitReason: TurnExitReasonSchema,
|
|
1023
1205
|
status: TurnStatusSchema.nullable(),
|
|
1024
|
-
error:
|
|
1206
|
+
error: z7.string().nullable(),
|
|
1025
1207
|
usage: UsageSchema,
|
|
1026
|
-
costUsd:
|
|
1208
|
+
costUsd: z7.number().nonnegative(),
|
|
1027
1209
|
/** The session's running total after this turn, for CLIs that report one. */
|
|
1028
|
-
sessionCostUsd:
|
|
1210
|
+
sessionCostUsd: z7.number().nonnegative().optional(),
|
|
1029
1211
|
/** The session the CLI used, which differs from the job's for CLIs that assign their own ids. */
|
|
1030
|
-
session:
|
|
1212
|
+
session: z7.string().min(1),
|
|
1031
1213
|
/** The model the CLI reported, when it reported one. */
|
|
1032
|
-
model:
|
|
1214
|
+
model: z7.string().nullable(),
|
|
1033
1215
|
work: TurnWorkSchema.nullable(),
|
|
1034
1216
|
/**
|
|
1035
1217
|
* Whether the turn's own workspace, a thread's, a channel's, or a task's, holds work on no
|
|
1036
1218
|
* branch after the hand-back: uncommitted changes, or commits no branch contains.
|
|
1037
1219
|
*/
|
|
1038
|
-
leftovers:
|
|
1220
|
+
leftovers: z7.boolean().default(false)
|
|
1039
1221
|
});
|
|
1040
|
-
var TurnAckSchema =
|
|
1041
|
-
var LandRequestSchema =
|
|
1222
|
+
var TurnAckSchema = z7.object({ dropWorkspace: z7.boolean() });
|
|
1223
|
+
var LandRequestSchema = z7.object({
|
|
1042
1224
|
repo: ProjectRepoSchema,
|
|
1043
|
-
branch:
|
|
1225
|
+
branch: z7.string().min(1),
|
|
1044
1226
|
/**
|
|
1045
1227
|
* For a `ghpr` task, the pull request to merge in place of the branch, which must hold the
|
|
1046
1228
|
* branch's files, and the merge commit's subject and body, the pull request's title and an
|
|
1047
1229
|
* empty body when left out.
|
|
1048
1230
|
*/
|
|
1049
|
-
pullRequest:
|
|
1050
|
-
url:
|
|
1051
|
-
subject:
|
|
1052
|
-
body:
|
|
1231
|
+
pullRequest: z7.object({
|
|
1232
|
+
url: z7.string().min(1),
|
|
1233
|
+
subject: z7.string().min(1).optional(),
|
|
1234
|
+
body: z7.string().optional()
|
|
1053
1235
|
}).optional()
|
|
1054
1236
|
});
|
|
1055
|
-
var
|
|
1056
|
-
|
|
1237
|
+
var CloseRequestSchema = z7.object({
|
|
1238
|
+
repo: ProjectRepoSchema,
|
|
1239
|
+
url: z7.string().min(1),
|
|
1240
|
+
comment: z7.string().min(1)
|
|
1241
|
+
});
|
|
1242
|
+
var MergeOutcomeSchema = z7.object({ ok: z7.boolean(), detail: z7.string() });
|
|
1243
|
+
var ModelListSchema = z7.array(ModelOptionSchema);
|
|
1057
1244
|
var BRANCH_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
1058
|
-
var BranchPathSchema =
|
|
1059
|
-
var BranchNameSchema =
|
|
1060
|
-
var BranchReadSchema =
|
|
1245
|
+
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" });
|
|
1246
|
+
var BranchNameSchema = z7.string().regex(/^[A-Za-z0-9][A-Za-z0-9._/-]*$/);
|
|
1247
|
+
var BranchReadSchema = z7.object({
|
|
1061
1248
|
repo: ProjectRepoSchema,
|
|
1062
1249
|
branch: BranchNameSchema,
|
|
1063
1250
|
path: BranchPathSchema
|
|
1064
1251
|
});
|
|
1065
|
-
var BranchCommitSchema =
|
|
1066
|
-
id:
|
|
1252
|
+
var BranchCommitSchema = z7.object({
|
|
1253
|
+
id: z7.string().min(1),
|
|
1067
1254
|
at: IsoDateTimeSchema,
|
|
1068
|
-
author:
|
|
1255
|
+
author: z7.string()
|
|
1069
1256
|
});
|
|
1070
|
-
var BranchFileSchema =
|
|
1071
|
-
|
|
1072
|
-
kind:
|
|
1257
|
+
var BranchFileSchema = z7.discriminatedUnion("kind", [
|
|
1258
|
+
z7.object({
|
|
1259
|
+
kind: z7.literal("file"),
|
|
1073
1260
|
path: BranchPathSchema,
|
|
1074
1261
|
commit: BranchCommitSchema,
|
|
1075
|
-
size:
|
|
1076
|
-
content:
|
|
1262
|
+
size: z7.number().int().nonnegative(),
|
|
1263
|
+
content: z7.string().nullable()
|
|
1077
1264
|
}),
|
|
1078
|
-
|
|
1079
|
-
kind:
|
|
1265
|
+
z7.object({
|
|
1266
|
+
kind: z7.literal("dir"),
|
|
1080
1267
|
path: BranchPathSchema,
|
|
1081
1268
|
commit: BranchCommitSchema,
|
|
1082
|
-
entries:
|
|
1083
|
-
name:
|
|
1084
|
-
kind:
|
|
1085
|
-
size:
|
|
1269
|
+
entries: z7.array(z7.object({
|
|
1270
|
+
name: z7.string().min(1),
|
|
1271
|
+
kind: z7.enum(["file", "dir"]),
|
|
1272
|
+
size: z7.number().int().nonnegative().nullable()
|
|
1086
1273
|
}))
|
|
1087
1274
|
})
|
|
1088
1275
|
]);
|
|
1089
|
-
var BranchChangesReadSchema =
|
|
1276
|
+
var BranchChangesReadSchema = z7.object({
|
|
1090
1277
|
repo: ProjectRepoSchema,
|
|
1091
1278
|
branch: BranchNameSchema
|
|
1092
1279
|
});
|
|
1093
|
-
var BranchFileChangeSchema =
|
|
1094
|
-
path:
|
|
1095
|
-
status:
|
|
1280
|
+
var BranchFileChangeSchema = z7.object({
|
|
1281
|
+
path: z7.string().min(1),
|
|
1282
|
+
status: z7.enum(["added", "modified", "deleted"]),
|
|
1096
1283
|
/** Lines added and removed, or null for a binary file. */
|
|
1097
|
-
added:
|
|
1098
|
-
removed:
|
|
1284
|
+
added: z7.number().int().nonnegative().nullable(),
|
|
1285
|
+
removed: z7.number().int().nonnegative().nullable(),
|
|
1099
1286
|
/** Absent when the file changed only through a merge of another branch. */
|
|
1100
|
-
lastChange:
|
|
1287
|
+
lastChange: z7.object({ author: z7.string(), at: IsoDateTimeSchema }).optional()
|
|
1101
1288
|
});
|
|
1102
|
-
var BranchChangesSchema =
|
|
1289
|
+
var BranchChangesSchema = z7.object({
|
|
1103
1290
|
head: BranchCommitSchema,
|
|
1104
|
-
files:
|
|
1105
|
-
total:
|
|
1291
|
+
files: z7.array(BranchFileChangeSchema),
|
|
1292
|
+
total: z7.number().int().nonnegative()
|
|
1106
1293
|
});
|
|
1107
|
-
var WorkspaceConversationSchema =
|
|
1108
|
-
|
|
1109
|
-
|
|
1294
|
+
var WorkspaceConversationSchema = z7.discriminatedUnion("kind", [
|
|
1295
|
+
z7.object({ kind: z7.literal("thread"), id: UlidSchema }),
|
|
1296
|
+
z7.object({ kind: z7.literal("channel"), scope: NameSchema, name: NameSchema })
|
|
1110
1297
|
]);
|
|
1111
|
-
var HeldWorkspaceSchema =
|
|
1298
|
+
var HeldWorkspaceSchema = z7.object({
|
|
1112
1299
|
agent: NameSchema,
|
|
1113
1300
|
scope: NameSchema,
|
|
1114
1301
|
conversation: WorkspaceConversationSchema
|
|
1115
1302
|
});
|
|
1116
|
-
var HeldWorkspacesSchema =
|
|
1117
|
-
var SweepResultSchema =
|
|
1118
|
-
var TurnSteerSchema =
|
|
1119
|
-
var RunnerMessageSchema =
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1303
|
+
var HeldWorkspacesSchema = z7.object({ workspaces: z7.array(HeldWorkspaceSchema) });
|
|
1304
|
+
var SweepResultSchema = z7.object({ kept: z7.array(HeldWorkspaceSchema) });
|
|
1305
|
+
var TurnSteerSchema = z7.object({ id: z7.uuid(), text: z7.string().min(1) });
|
|
1306
|
+
var RunnerMessageSchema = z7.discriminatedUnion("type", [
|
|
1307
|
+
z7.object({ type: z7.literal("turn"), job: TurnJobSchema }),
|
|
1308
|
+
z7.object({ type: z7.literal("land"), request: z7.string().min(1), land: LandRequestSchema }),
|
|
1309
|
+
/** Answered with a `MergeOutcome`. */
|
|
1310
|
+
z7.object({ type: z7.literal("close"), request: z7.string().min(1), close: CloseRequestSchema }),
|
|
1311
|
+
z7.object({ type: z7.literal("models"), request: z7.string().min(1), cli: CliKindSchema }),
|
|
1123
1312
|
/** Answered with a `BranchFile`, or null when the branch or the path does not exist. */
|
|
1124
|
-
|
|
1313
|
+
z7.object({ type: z7.literal("file"), request: z7.string().min(1), read: BranchReadSchema }),
|
|
1125
1314
|
/** Answered with `BranchChanges`, or null when the repository or the branch does not exist. */
|
|
1126
|
-
|
|
1127
|
-
type:
|
|
1128
|
-
request:
|
|
1315
|
+
z7.object({
|
|
1316
|
+
type: z7.literal("changes"),
|
|
1317
|
+
request: z7.string().min(1),
|
|
1129
1318
|
read: BranchChangesReadSchema
|
|
1130
1319
|
}),
|
|
1131
1320
|
/** Answered with whether the CLI accepted it; that it took it comes as a `steered` event. */
|
|
1132
|
-
|
|
1133
|
-
type:
|
|
1134
|
-
request:
|
|
1321
|
+
z7.object({
|
|
1322
|
+
type: z7.literal("steer"),
|
|
1323
|
+
request: z7.string().min(1),
|
|
1135
1324
|
turnId: UlidSchema,
|
|
1136
1325
|
steer: TurnSteerSchema
|
|
1137
1326
|
}),
|
|
1138
1327
|
/** Answered with whether the turn was there to stop; it ends `stopped`. */
|
|
1139
|
-
|
|
1328
|
+
z7.object({ type: z7.literal("stop"), request: z7.string().min(1), turnId: UlidSchema }),
|
|
1140
1329
|
/**
|
|
1141
1330
|
* A conversation ended: the runner removes every citizen's workspace for it but those a turn
|
|
1142
1331
|
* still uses, and answers with a `SweepResult` naming those it kept for holding work on no branch.
|
|
1143
1332
|
*/
|
|
1144
|
-
|
|
1145
|
-
type:
|
|
1146
|
-
request:
|
|
1333
|
+
z7.object({
|
|
1334
|
+
type: z7.literal("sweep"),
|
|
1335
|
+
request: z7.string().min(1),
|
|
1147
1336
|
conversation: WorkspaceConversationSchema
|
|
1148
1337
|
})
|
|
1149
1338
|
]);
|
|
1150
|
-
var RunnerAnswerSchema =
|
|
1151
|
-
|
|
1152
|
-
|
|
1339
|
+
var RunnerAnswerSchema = z7.discriminatedUnion("ok", [
|
|
1340
|
+
z7.object({ ok: z7.literal(true), value: z7.unknown() }),
|
|
1341
|
+
z7.object({ ok: z7.literal(false), error: z7.string() })
|
|
1153
1342
|
]);
|
|
1154
|
-
var WarmSessionsSchema =
|
|
1155
|
-
var FileManifestSchema =
|
|
1156
|
-
var FileReadSchema =
|
|
1157
|
-
var FileContentsSchema =
|
|
1343
|
+
var WarmSessionsSchema = z7.object({ warm: z7.array(z7.string()) });
|
|
1344
|
+
var FileManifestSchema = z7.object({ files: z7.record(z7.string(), z7.string()) });
|
|
1345
|
+
var FileReadSchema = z7.object({ paths: z7.array(z7.string()) });
|
|
1346
|
+
var FileContentsSchema = z7.object({ files: z7.record(z7.string(), z7.string()) });
|
|
1158
1347
|
function isSafeRelativePath(file) {
|
|
1159
1348
|
if (file.length === 0 || file.startsWith("/") || file.includes("\\") || file.includes("\0")) {
|
|
1160
1349
|
return false;
|
|
@@ -1163,7 +1352,7 @@ function isSafeRelativePath(file) {
|
|
|
1163
1352
|
}
|
|
1164
1353
|
|
|
1165
1354
|
// packages/shared/dist/turns.js
|
|
1166
|
-
var TriggerKindSchema =
|
|
1355
|
+
var TriggerKindSchema = z8.enum([
|
|
1167
1356
|
"mention",
|
|
1168
1357
|
"stage",
|
|
1169
1358
|
"task_done",
|
|
@@ -1174,27 +1363,32 @@ var TriggerKindSchema = z7.enum([
|
|
|
1174
1363
|
"manual",
|
|
1175
1364
|
"ops_event",
|
|
1176
1365
|
"user_post",
|
|
1177
|
-
"closing"
|
|
1366
|
+
"closing",
|
|
1367
|
+
"cron"
|
|
1178
1368
|
]);
|
|
1179
|
-
var TriggerSchema =
|
|
1369
|
+
var TriggerSchema = z8.object({
|
|
1180
1370
|
kind: TriggerKindSchema,
|
|
1181
1371
|
from: NameSchema.optional(),
|
|
1182
|
-
fromUser:
|
|
1183
|
-
reason:
|
|
1372
|
+
fromUser: z8.boolean().default(false),
|
|
1373
|
+
reason: z8.string().default(""),
|
|
1184
1374
|
taskId: UlidSchema.optional(),
|
|
1185
1375
|
messageId: UlidSchema.optional(),
|
|
1186
1376
|
/** The channel the waking message was posted in. */
|
|
1187
|
-
channel: ChannelRefSchema.optional()
|
|
1377
|
+
channel: ChannelRefSchema.optional(),
|
|
1378
|
+
/** The cron that came due, for a `cron` wake. */
|
|
1379
|
+
cronId: UlidSchema.optional()
|
|
1188
1380
|
});
|
|
1189
|
-
var TurnThreadSchema =
|
|
1190
|
-
var TurnDispatchSchema =
|
|
1381
|
+
var TurnThreadSchema = z8.object({ id: UlidSchema, task: z8.boolean() });
|
|
1382
|
+
var TurnDispatchSchema = z8.object({
|
|
1191
1383
|
agent: NameSchema,
|
|
1192
1384
|
project: NameSchema,
|
|
1193
1385
|
thread: TurnThreadSchema.optional(),
|
|
1194
1386
|
channel: NameSchema.optional(),
|
|
1195
1387
|
trigger: TriggerSchema,
|
|
1196
|
-
priority:
|
|
1197
|
-
onboarding:
|
|
1388
|
+
priority: z8.number().int().min(0).max(2),
|
|
1389
|
+
onboarding: z8.boolean().default(false),
|
|
1390
|
+
/** Every cron that came due for this turn, whatever wake it merged into; its prompt quotes each. */
|
|
1391
|
+
crons: z8.array(UlidSchema).optional()
|
|
1198
1392
|
});
|
|
1199
1393
|
function conversationPart(conversation) {
|
|
1200
1394
|
return conversation.thread ?? (conversation.channel === void 0 ? void 0 : `#${conversation.channel}`);
|
|
@@ -1205,7 +1399,7 @@ function channelConversation(scope, channel) {
|
|
|
1205
1399
|
const name = at < 0 ? channel : channel.slice(at + 1);
|
|
1206
1400
|
return place === scope && name !== "general" ? name : void 0;
|
|
1207
1401
|
}
|
|
1208
|
-
var TurnRecordSchema =
|
|
1402
|
+
var TurnRecordSchema = z8.object({
|
|
1209
1403
|
/** Assigned by the board when the turn begins; the turn's transcript is filed under it. */
|
|
1210
1404
|
id: UlidSchema.optional(),
|
|
1211
1405
|
agent: NameSchema,
|
|
@@ -1216,29 +1410,31 @@ var TurnRecordSchema = z7.object({
|
|
|
1216
1410
|
channel: NameSchema.optional(),
|
|
1217
1411
|
runner: NameSchema,
|
|
1218
1412
|
cli: CliKindSchema.nullable(),
|
|
1219
|
-
session:
|
|
1413
|
+
session: z8.string().nullable(),
|
|
1220
1414
|
trigger: TriggerSchema,
|
|
1221
1415
|
startedAt: IsoDateTimeSchema,
|
|
1222
1416
|
endedAt: IsoDateTimeSchema.nullable(),
|
|
1223
1417
|
exitReason: TurnExitReasonSchema.nullable(),
|
|
1224
1418
|
status: TurnStatusSchema.nullable(),
|
|
1225
|
-
error:
|
|
1419
|
+
error: z8.string().nullable(),
|
|
1226
1420
|
usage: UsageSchema.nullable(),
|
|
1227
|
-
costUsd:
|
|
1421
|
+
costUsd: z8.number().nonnegative().default(0),
|
|
1228
1422
|
/**
|
|
1229
1423
|
* The running total the CLI reported for the session after this turn, when it reports one. A
|
|
1230
1424
|
* resumed session starts from it, so the next turn's own cost is measured from here.
|
|
1231
1425
|
*/
|
|
1232
|
-
sessionCostUsd:
|
|
1233
|
-
toolCalls:
|
|
1426
|
+
sessionCostUsd: z8.number().nonnegative().optional(),
|
|
1427
|
+
toolCalls: z8.number().int().nonnegative().default(0),
|
|
1234
1428
|
/** The model the CLI reported for this turn, else the model configured for the agent, else null. */
|
|
1235
|
-
model:
|
|
1429
|
+
model: z8.string().nullable().default(null),
|
|
1236
1430
|
/** The task branch the turn left work on, with its commit, as the runner reported it. */
|
|
1237
1431
|
work: TurnWorkSchema.optional(),
|
|
1238
1432
|
/** Who stopped the turn, for one that ended `stopped`. */
|
|
1239
|
-
stoppedBy: NameSchema.optional()
|
|
1433
|
+
stoppedBy: NameSchema.optional(),
|
|
1434
|
+
/** The crons whose fire started the turn, which record how it ended. */
|
|
1435
|
+
crons: z8.array(UlidSchema).optional()
|
|
1240
1436
|
});
|
|
1241
|
-
var RunningTurnSchema =
|
|
1437
|
+
var RunningTurnSchema = z8.object({
|
|
1242
1438
|
turnId: UlidSchema,
|
|
1243
1439
|
agent: NameSchema,
|
|
1244
1440
|
scope: NameSchema,
|
|
@@ -1246,49 +1442,49 @@ var RunningTurnSchema = z7.object({
|
|
|
1246
1442
|
channel: NameSchema.optional(),
|
|
1247
1443
|
cli: CliKindSchema,
|
|
1248
1444
|
askedIn: ChannelRefSchema.optional(),
|
|
1249
|
-
steerable:
|
|
1250
|
-
stoppable:
|
|
1445
|
+
steerable: z8.boolean(),
|
|
1446
|
+
stoppable: z8.boolean()
|
|
1251
1447
|
});
|
|
1252
|
-
var SessionsFileSchema =
|
|
1253
|
-
claude:
|
|
1254
|
-
codex:
|
|
1448
|
+
var SessionsFileSchema = z8.object({
|
|
1449
|
+
claude: z8.string().optional(),
|
|
1450
|
+
codex: z8.string().optional(),
|
|
1255
1451
|
runner: NameSchema.optional()
|
|
1256
1452
|
});
|
|
1257
|
-
var WakeRequestSchema =
|
|
1453
|
+
var WakeRequestSchema = z8.object({
|
|
1258
1454
|
agent: NameSchema,
|
|
1259
1455
|
project: NameSchema,
|
|
1260
|
-
reason:
|
|
1456
|
+
reason: z8.string().default("manual wake"),
|
|
1261
1457
|
/** A manual wake, or a reflection turn requested ahead of the cadence. */
|
|
1262
|
-
kind:
|
|
1458
|
+
kind: z8.enum(["manual", "reflection"]).default("manual")
|
|
1263
1459
|
});
|
|
1264
|
-
var TurnHistoryEntrySchema =
|
|
1460
|
+
var TurnHistoryEntrySchema = z8.object({
|
|
1265
1461
|
/** The id of the event that ended the turn. */
|
|
1266
1462
|
id: UlidSchema,
|
|
1267
1463
|
/** The turn's own id, when it has a transcript to open; turns before transcripts have none. */
|
|
1268
1464
|
turnId: UlidSchema.optional(),
|
|
1269
1465
|
ts: IsoDateTimeSchema,
|
|
1270
|
-
outcome:
|
|
1466
|
+
outcome: z8.enum(["completed", "failed"]),
|
|
1271
1467
|
/** A project slug, or the society scope. */
|
|
1272
1468
|
project: NameSchema,
|
|
1273
1469
|
/** The thread whose conversation the turn was in; absent for the home conversation. */
|
|
1274
1470
|
thread: UlidSchema.optional(),
|
|
1275
1471
|
/** The channel, never general, whose conversation the turn was in. */
|
|
1276
1472
|
channel: NameSchema.optional(),
|
|
1277
|
-
trigger:
|
|
1278
|
-
exitReason:
|
|
1279
|
-
costUsd:
|
|
1473
|
+
trigger: z8.string(),
|
|
1474
|
+
exitReason: z8.string().nullable(),
|
|
1475
|
+
costUsd: z8.number(),
|
|
1280
1476
|
/** The tokens the turn used; absent for turns logged before turns recorded them. */
|
|
1281
1477
|
usage: UsageSchema.optional(),
|
|
1282
|
-
model:
|
|
1283
|
-
summary:
|
|
1284
|
-
error:
|
|
1478
|
+
model: z8.string().nullable(),
|
|
1479
|
+
summary: z8.string().nullable(),
|
|
1480
|
+
error: z8.string().nullable(),
|
|
1285
1481
|
/** When the log recorded the turn's start; absent when the start is missing from the log. */
|
|
1286
1482
|
startedAt: IsoDateTimeSchema.optional(),
|
|
1287
|
-
toolCalls:
|
|
1483
|
+
toolCalls: z8.number().int().nonnegative().optional()
|
|
1288
1484
|
});
|
|
1289
1485
|
|
|
1290
1486
|
// packages/shared/dist/ops.js
|
|
1291
|
-
import { z as
|
|
1487
|
+
import { z as z9 } from "zod";
|
|
1292
1488
|
var OPS_SIGNAL_KINDS = [
|
|
1293
1489
|
"waiting_stage",
|
|
1294
1490
|
"backlog",
|
|
@@ -1301,18 +1497,19 @@ var OPS_SIGNAL_KINDS = [
|
|
|
1301
1497
|
"scaled",
|
|
1302
1498
|
"runner",
|
|
1303
1499
|
"home_conflict",
|
|
1304
|
-
"stuck_workspace"
|
|
1500
|
+
"stuck_workspace",
|
|
1501
|
+
"cron_failing"
|
|
1305
1502
|
];
|
|
1306
|
-
var OpsSignalKindSchema =
|
|
1307
|
-
var OpsSignalSchema =
|
|
1503
|
+
var OpsSignalKindSchema = z9.enum(OPS_SIGNAL_KINDS);
|
|
1504
|
+
var OpsSignalSchema = z9.object({
|
|
1308
1505
|
kind: OpsSignalKindSchema,
|
|
1309
1506
|
/** Identifies the condition, for example `backlog:demo:engineer`, so a persisting one is not logged again every pass. */
|
|
1310
|
-
key:
|
|
1507
|
+
key: z9.string().min(1),
|
|
1311
1508
|
/** One line a reader can act on. */
|
|
1312
|
-
summary:
|
|
1509
|
+
summary: z9.string().min(1),
|
|
1313
1510
|
/** The counter or timer value behind the signal. */
|
|
1314
|
-
value:
|
|
1315
|
-
threshold:
|
|
1511
|
+
value: z9.number(),
|
|
1512
|
+
threshold: z9.number().optional(),
|
|
1316
1513
|
project: NameSchema.optional(),
|
|
1317
1514
|
agent: NameSchema.optional(),
|
|
1318
1515
|
role: NameSchema.optional(),
|
|
@@ -1320,8 +1517,8 @@ var OpsSignalSchema = z8.object({
|
|
|
1320
1517
|
});
|
|
1321
1518
|
|
|
1322
1519
|
// packages/shared/dist/metrics.js
|
|
1323
|
-
import { z as
|
|
1324
|
-
var MetricsWindowSchema =
|
|
1520
|
+
import { z as z10 } from "zod";
|
|
1521
|
+
var MetricsWindowSchema = z10.enum(["24h", "7d", "all"]);
|
|
1325
1522
|
var METRICS_WINDOW_MS = {
|
|
1326
1523
|
"24h": 24 * 36e5,
|
|
1327
1524
|
"7d": 7 * 24 * 36e5,
|
|
@@ -1332,9 +1529,9 @@ function isBoardAction(tool) {
|
|
|
1332
1529
|
const verb = /^mcp__board__(.+)$/.exec(tool)?.[1];
|
|
1333
1530
|
return verb !== void 0 && !READ_VERBS.some((read2) => read2 === verb);
|
|
1334
1531
|
}
|
|
1335
|
-
var CountSchema =
|
|
1336
|
-
var MsSchema =
|
|
1337
|
-
var MetricsSchema =
|
|
1532
|
+
var CountSchema = z10.number().int().nonnegative();
|
|
1533
|
+
var MsSchema = z10.number().nonnegative();
|
|
1534
|
+
var MetricsSchema = z10.object({
|
|
1338
1535
|
window: MetricsWindowSchema,
|
|
1339
1536
|
since: IsoDateTimeSchema.nullable(),
|
|
1340
1537
|
until: IsoDateTimeSchema,
|
|
@@ -1342,12 +1539,12 @@ var MetricsSchema = z9.object({
|
|
|
1342
1539
|
* Finished turns that changed nothing on the board. Reflections are left out, since they write
|
|
1343
1540
|
* memory, not the board; turns without a transcript are counted apart, their actions unknown.
|
|
1344
1541
|
*/
|
|
1345
|
-
idle:
|
|
1542
|
+
idle: z10.object({
|
|
1346
1543
|
turns: CountSchema,
|
|
1347
1544
|
idle: CountSchema,
|
|
1348
1545
|
unknown: CountSchema,
|
|
1349
|
-
byTrigger:
|
|
1350
|
-
byAgent:
|
|
1546
|
+
byTrigger: z10.array(z10.object({ trigger: z10.string(), turns: CountSchema, idle: CountSchema })),
|
|
1547
|
+
byAgent: z10.array(z10.object({
|
|
1351
1548
|
agent: NameSchema,
|
|
1352
1549
|
role: NameSchema.nullable(),
|
|
1353
1550
|
turns: CountSchema,
|
|
@@ -1355,33 +1552,33 @@ var MetricsSchema = z9.object({
|
|
|
1355
1552
|
}))
|
|
1356
1553
|
}),
|
|
1357
1554
|
/** Work sent back to an earlier stage: over the tasks finished in the window, and by where. */
|
|
1358
|
-
sentBack:
|
|
1555
|
+
sentBack: z10.object({
|
|
1359
1556
|
finished: CountSchema,
|
|
1360
1557
|
sendBacks: CountSchema,
|
|
1361
1558
|
tasksSentBack: CountSchema,
|
|
1362
|
-
byStage:
|
|
1363
|
-
bySender:
|
|
1559
|
+
byStage: z10.array(z10.object({ project: NameSchema, stage: z10.string(), count: CountSchema })),
|
|
1560
|
+
bySender: z10.array(z10.object({ agent: NameSchema, count: CountSchema }))
|
|
1364
1561
|
}),
|
|
1365
1562
|
/** Posts in the threads of the tasks finished in the window, the board's own notices left out. */
|
|
1366
|
-
messages:
|
|
1563
|
+
messages: z10.object({
|
|
1367
1564
|
finished: CountSchema,
|
|
1368
1565
|
messages: CountSchema,
|
|
1369
|
-
byProject:
|
|
1370
|
-
busiest:
|
|
1566
|
+
byProject: z10.array(z10.object({ project: NameSchema, finished: CountSchema, messages: CountSchema })),
|
|
1567
|
+
busiest: z10.array(z10.object({
|
|
1371
1568
|
taskId: UlidSchema,
|
|
1372
|
-
title:
|
|
1569
|
+
title: z10.string(),
|
|
1373
1570
|
project: NameSchema,
|
|
1374
1571
|
messages: CountSchema
|
|
1375
1572
|
}))
|
|
1376
1573
|
}),
|
|
1377
1574
|
/** Time from a mention to the start of the turn it woke, in the conversation it was made in. */
|
|
1378
|
-
latency:
|
|
1575
|
+
latency: z10.object({
|
|
1379
1576
|
mentions: CountSchema,
|
|
1380
1577
|
/** Mentions no turn has followed yet. */
|
|
1381
1578
|
unanswered: CountSchema,
|
|
1382
1579
|
medianMs: MsSchema.nullable(),
|
|
1383
1580
|
slowestMs: MsSchema.nullable(),
|
|
1384
|
-
byAgent:
|
|
1581
|
+
byAgent: z10.array(z10.object({
|
|
1385
1582
|
agent: NameSchema,
|
|
1386
1583
|
mentions: CountSchema,
|
|
1387
1584
|
medianMs: MsSchema,
|
|
@@ -1389,81 +1586,81 @@ var MetricsSchema = z9.object({
|
|
|
1389
1586
|
}))
|
|
1390
1587
|
}),
|
|
1391
1588
|
/** The user's decisions: proposals decided, questions answered, and stages passed or sent back. */
|
|
1392
|
-
decisions:
|
|
1589
|
+
decisions: z10.object({
|
|
1393
1590
|
total: CountSchema,
|
|
1394
|
-
byDay:
|
|
1395
|
-
day:
|
|
1591
|
+
byDay: z10.array(z10.object({
|
|
1592
|
+
day: z10.string(),
|
|
1396
1593
|
proposals: CountSchema,
|
|
1397
1594
|
answers: CountSchema,
|
|
1398
1595
|
stages: CountSchema
|
|
1399
1596
|
}))
|
|
1400
1597
|
}),
|
|
1401
1598
|
/** Tasks that needed a capability no connected runner offered, and whether they still do. */
|
|
1402
|
-
blocked:
|
|
1599
|
+
blocked: z10.array(z10.object({
|
|
1403
1600
|
taskId: UlidSchema,
|
|
1404
|
-
title:
|
|
1601
|
+
title: z10.string().nullable(),
|
|
1405
1602
|
project: NameSchema.nullable(),
|
|
1406
|
-
summary:
|
|
1603
|
+
summary: z10.string(),
|
|
1407
1604
|
firstAt: IsoDateTimeSchema,
|
|
1408
1605
|
lastAt: IsoDateTimeSchema,
|
|
1409
|
-
holds:
|
|
1606
|
+
holds: z10.boolean()
|
|
1410
1607
|
}))
|
|
1411
1608
|
});
|
|
1412
1609
|
|
|
1413
1610
|
// packages/shared/dist/config.js
|
|
1414
|
-
import { z as
|
|
1415
|
-
var LogLevelSchema =
|
|
1416
|
-
var GithubSignInSchema =
|
|
1417
|
-
clientId:
|
|
1418
|
-
clientSecret:
|
|
1611
|
+
import { z as z11 } from "zod";
|
|
1612
|
+
var LogLevelSchema = z11.enum(["trace", "debug", "info", "warn", "error", "fatal"]);
|
|
1613
|
+
var GithubSignInSchema = z11.object({
|
|
1614
|
+
clientId: z11.string().min(1),
|
|
1615
|
+
clientSecret: z11.string().min(1),
|
|
1419
1616
|
/** Logins, lowercased; GitHub logins are case-insensitive. */
|
|
1420
|
-
users:
|
|
1617
|
+
users: z11.array(z11.string().min(1)).min(1)
|
|
1421
1618
|
});
|
|
1422
|
-
var ServerConfigSchema =
|
|
1423
|
-
dataDir:
|
|
1424
|
-
host:
|
|
1425
|
-
port:
|
|
1619
|
+
var ServerConfigSchema = z11.object({
|
|
1620
|
+
dataDir: z11.string().min(1),
|
|
1621
|
+
host: z11.string().min(1),
|
|
1622
|
+
port: z11.number().int().positive().max(65535),
|
|
1426
1623
|
/**
|
|
1427
1624
|
* An address every runner's agents reach this server at, for example https://board.example, or
|
|
1428
1625
|
* null for each runner to use the address it reaches the server at itself.
|
|
1429
1626
|
*/
|
|
1430
|
-
publicUrl:
|
|
1627
|
+
publicUrl: z11.string().min(1).nullable(),
|
|
1431
1628
|
logLevel: LogLevelSchema,
|
|
1432
1629
|
/** Turns running at once across every runner, or null for no limit beyond each runner's own. */
|
|
1433
|
-
concurrency:
|
|
1630
|
+
concurrency: z11.number().int().positive().nullable(),
|
|
1434
1631
|
/** How long one turn may run before it is stopped, or null for no limit. */
|
|
1435
|
-
turnTimeoutMs:
|
|
1632
|
+
turnTimeoutMs: z11.number().int().positive().nullable(),
|
|
1436
1633
|
/** Rounds of tool calls one Claude turn may take, or null for no limit. */
|
|
1437
|
-
toolRounds:
|
|
1634
|
+
toolRounds: z11.number().int().positive().nullable(),
|
|
1438
1635
|
/** How long a resident session stays warm after its last turn. */
|
|
1439
|
-
residentIdleMs:
|
|
1636
|
+
residentIdleMs: z11.number().int().positive(),
|
|
1440
1637
|
/** Signing in to the board with GitHub, or null when only a token lets anyone in. */
|
|
1441
1638
|
github: GithubSignInSchema.nullable(),
|
|
1442
1639
|
/**
|
|
1443
1640
|
* Reverse proxies in front of the server whose X-Forwarded-For entries name the client, for the
|
|
1444
1641
|
* rate limits on what anyone may ask without a token: 0 when clients connect directly.
|
|
1445
1642
|
*/
|
|
1446
|
-
trustedProxies:
|
|
1643
|
+
trustedProxies: z11.number().int().min(0).max(16)
|
|
1447
1644
|
});
|
|
1448
|
-
var RunnerConfigSchema =
|
|
1645
|
+
var RunnerConfigSchema = z11.object({
|
|
1449
1646
|
/** The board server, for example http://127.0.0.1:4700. */
|
|
1450
|
-
serverUrl:
|
|
1647
|
+
serverUrl: z11.string().url(),
|
|
1451
1648
|
/**
|
|
1452
1649
|
* The runner's token, from `runner add`, or null for the one it saved when it was enrolled, or
|
|
1453
1650
|
* to enroll it.
|
|
1454
1651
|
*/
|
|
1455
|
-
token:
|
|
1652
|
+
token: z11.string().min(1).nullable(),
|
|
1456
1653
|
/** The runner's own data directory: the board mirror, agent homes, repositories, worktrees. */
|
|
1457
|
-
dataDir:
|
|
1654
|
+
dataDir: z11.string().min(1),
|
|
1458
1655
|
logLevel: LogLevelSchema,
|
|
1459
1656
|
/** Turns this machine runs at once, or null for no limit. */
|
|
1460
|
-
slots:
|
|
1461
|
-
clis:
|
|
1462
|
-
capabilities:
|
|
1657
|
+
slots: z11.number().int().positive().nullable(),
|
|
1658
|
+
clis: z11.array(CliKindSchema).min(1),
|
|
1659
|
+
capabilities: z11.array(z11.string())
|
|
1463
1660
|
});
|
|
1464
1661
|
|
|
1465
1662
|
// packages/shared/dist/version.js
|
|
1466
|
-
var STELLARIS_VERSION = "0.3.
|
|
1663
|
+
var STELLARIS_VERSION = "0.3.14";
|
|
1467
1664
|
|
|
1468
1665
|
// packages/board-core/dist/errors.js
|
|
1469
1666
|
var BoardError = class extends Error {
|
|
@@ -2008,32 +2205,32 @@ function filesOf(lines, status) {
|
|
|
2008
2205
|
}
|
|
2009
2206
|
|
|
2010
2207
|
// packages/board-core/dist/metrics.js
|
|
2011
|
-
import { z as
|
|
2012
|
-
var TurnPayload =
|
|
2013
|
-
turnId:
|
|
2014
|
-
project:
|
|
2015
|
-
thread:
|
|
2016
|
-
channel:
|
|
2017
|
-
trigger:
|
|
2018
|
-
});
|
|
2019
|
-
var TaskPayload =
|
|
2020
|
-
taskId:
|
|
2021
|
-
project:
|
|
2022
|
-
from:
|
|
2023
|
-
});
|
|
2024
|
-
var MessagePayload =
|
|
2025
|
-
id:
|
|
2026
|
-
channel:
|
|
2027
|
-
thread:
|
|
2028
|
-
mentions:
|
|
2029
|
-
});
|
|
2030
|
-
var SteeredPayload =
|
|
2031
|
-
var SignalPayload =
|
|
2032
|
-
kind:
|
|
2033
|
-
key:
|
|
2034
|
-
summary:
|
|
2035
|
-
taskId:
|
|
2036
|
-
project:
|
|
2208
|
+
import { z as z12 } from "zod";
|
|
2209
|
+
var TurnPayload = z12.object({
|
|
2210
|
+
turnId: z12.string().optional(),
|
|
2211
|
+
project: z12.string(),
|
|
2212
|
+
thread: z12.string().optional(),
|
|
2213
|
+
channel: z12.string().optional(),
|
|
2214
|
+
trigger: z12.string()
|
|
2215
|
+
});
|
|
2216
|
+
var TaskPayload = z12.object({
|
|
2217
|
+
taskId: z12.string(),
|
|
2218
|
+
project: z12.string(),
|
|
2219
|
+
from: z12.string().optional()
|
|
2220
|
+
});
|
|
2221
|
+
var MessagePayload = z12.object({
|
|
2222
|
+
id: z12.string().optional(),
|
|
2223
|
+
channel: z12.string(),
|
|
2224
|
+
thread: z12.string().nullable().optional(),
|
|
2225
|
+
mentions: z12.array(z12.string()).default([])
|
|
2226
|
+
});
|
|
2227
|
+
var SteeredPayload = z12.object({ messages: z12.array(z12.string()) });
|
|
2228
|
+
var SignalPayload = z12.object({
|
|
2229
|
+
kind: z12.string(),
|
|
2230
|
+
key: z12.string(),
|
|
2231
|
+
summary: z12.string(),
|
|
2232
|
+
taskId: z12.string().optional(),
|
|
2233
|
+
project: z12.string().optional()
|
|
2037
2234
|
});
|
|
2038
2235
|
function median(values) {
|
|
2039
2236
|
const sorted = values.toSorted((a, b) => a - b);
|
|
@@ -2402,6 +2599,13 @@ var BoardPaths = class {
|
|
|
2402
2599
|
members() {
|
|
2403
2600
|
return path5.join(this.society, "members");
|
|
2404
2601
|
}
|
|
2602
|
+
/** Crons, one file each with its note as the body; ended ones stay. */
|
|
2603
|
+
crons() {
|
|
2604
|
+
return path5.join(this.society, "crons");
|
|
2605
|
+
}
|
|
2606
|
+
cron(id) {
|
|
2607
|
+
return path5.join(this.crons(), `${id}.md`);
|
|
2608
|
+
}
|
|
2405
2609
|
member(name) {
|
|
2406
2610
|
return path5.join(this.members(), `${name}.md`);
|
|
2407
2611
|
}
|
|
@@ -2548,41 +2752,41 @@ function namesList(names) {
|
|
|
2548
2752
|
return names.length <= 1 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} and ${names.at(-1) ?? ""}`;
|
|
2549
2753
|
}
|
|
2550
2754
|
var SKIPPED_IN_HOME = /* @__PURE__ */ new Set([".git", "turns", ".claude", ".codex"]);
|
|
2551
|
-
var RunnerTokensSchema =
|
|
2552
|
-
runners:
|
|
2755
|
+
var RunnerTokensSchema = z13.object({
|
|
2756
|
+
runners: z13.record(z13.string(), z13.object({ tokenHash: z13.string().min(1) }))
|
|
2553
2757
|
});
|
|
2554
|
-
var SignInsSchema =
|
|
2555
|
-
signIns:
|
|
2556
|
-
tokenHash:
|
|
2557
|
-
login:
|
|
2758
|
+
var SignInsSchema = z13.object({
|
|
2759
|
+
signIns: z13.array(z13.object({
|
|
2760
|
+
tokenHash: z13.string().min(1),
|
|
2761
|
+
login: z13.string().min(1),
|
|
2558
2762
|
/** The account's picture at the identity provider; sign-ins from before it have none. */
|
|
2559
|
-
avatarUrl:
|
|
2763
|
+
avatarUrl: z13.url().optional(),
|
|
2560
2764
|
createdAt: IsoDateTimeSchema,
|
|
2561
2765
|
expiresAt: IsoDateTimeSchema
|
|
2562
2766
|
})).default([])
|
|
2563
2767
|
});
|
|
2564
|
-
var TurnHistoryPayloadSchema =
|
|
2768
|
+
var TurnHistoryPayloadSchema = z13.object({
|
|
2565
2769
|
project: NameSchema,
|
|
2566
2770
|
thread: UlidSchema.optional(),
|
|
2567
2771
|
channel: NameSchema.optional(),
|
|
2568
|
-
trigger:
|
|
2569
|
-
exitReason:
|
|
2570
|
-
costUsd:
|
|
2772
|
+
trigger: z13.string().default("manual"),
|
|
2773
|
+
exitReason: z13.string().nullable().default(null),
|
|
2774
|
+
costUsd: z13.number().default(0),
|
|
2571
2775
|
usage: UsageSchema.optional(),
|
|
2572
|
-
model:
|
|
2573
|
-
summary:
|
|
2574
|
-
error:
|
|
2575
|
-
toolCalls:
|
|
2776
|
+
model: z13.string().nullable().default(null),
|
|
2777
|
+
summary: z13.string().nullable().default(null),
|
|
2778
|
+
error: z13.string().nullable().default(null),
|
|
2779
|
+
toolCalls: z13.number().int().nonnegative().optional(),
|
|
2576
2780
|
turnId: UlidSchema.optional()
|
|
2577
2781
|
});
|
|
2578
|
-
var TurnStartPayloadSchema =
|
|
2579
|
-
var CursorsSchema =
|
|
2580
|
-
digest:
|
|
2581
|
-
scopes:
|
|
2582
|
-
threadsFrom:
|
|
2583
|
-
channelsFrom:
|
|
2782
|
+
var TurnStartPayloadSchema = z13.object({ project: NameSchema, turnId: UlidSchema.optional() });
|
|
2783
|
+
var CursorsSchema = z13.object({
|
|
2784
|
+
digest: z13.string().nullable(),
|
|
2785
|
+
scopes: z13.record(z13.string(), z13.string()).default({}),
|
|
2786
|
+
threadsFrom: z13.record(z13.string(), z13.string().nullable()).default({}),
|
|
2787
|
+
channelsFrom: z13.record(z13.string(), z13.string().nullable()).default({}),
|
|
2584
2788
|
/** How far a work role has read news, which goes to whichever of its general conversations reads first. */
|
|
2585
|
-
news:
|
|
2789
|
+
news: z13.string().optional()
|
|
2586
2790
|
});
|
|
2587
2791
|
var NO_CURSORS = { digest: null, scopes: {}, threadsFrom: {}, channelsFrom: {} };
|
|
2588
2792
|
function conversationKey(scope, part) {
|
|
@@ -2621,7 +2825,7 @@ function byId(a, b) {
|
|
|
2621
2825
|
function turnTime(record) {
|
|
2622
2826
|
return record.endedAt ?? record.startedAt;
|
|
2623
2827
|
}
|
|
2624
|
-
var PausedSchema =
|
|
2828
|
+
var PausedSchema = z13.object({ paused: z13.boolean() });
|
|
2625
2829
|
var DEFAULT_LEASE_MS = 30 * 60 * 1e3;
|
|
2626
2830
|
var MENTION_PATTERN = /(^|[^\w@])@([a-z0-9][a-z0-9-]{0,31})(?![\w-])/g;
|
|
2627
2831
|
function withoutMentions(text) {
|
|
@@ -2633,7 +2837,7 @@ var OPS_WAKE_TRIGGER = "ops_event";
|
|
|
2633
2837
|
var FRONT_DESK_TRIGGER = "user_post";
|
|
2634
2838
|
var OPS_CHANNELS = ["governance"];
|
|
2635
2839
|
var FRONT_DESK_CHANNELS = [ASK_CHANNEL];
|
|
2636
|
-
var AlignmentsSchema =
|
|
2840
|
+
var AlignmentsSchema = z13.object({ applied: z13.array(z13.string()).default([]) });
|
|
2637
2841
|
var FOLLOW_DECISIONS = "ops-readers-follow-decisions";
|
|
2638
2842
|
var THREAD_RECORDS = "threads-as-records";
|
|
2639
2843
|
var DIGEST_CURSORS = "digest-replaces-inbox";
|
|
@@ -2645,6 +2849,8 @@ var ASKS_CHANNEL_OPENED = "asks-channel-opened";
|
|
|
2645
2849
|
var KNOWLEDGE_REMOVAL = "knowledge-removal-granted";
|
|
2646
2850
|
var CHANNEL_CONVERSATIONS = "channel-conversations";
|
|
2647
2851
|
var DASHBOARD_UPDATES = "dashboard-updates-granted";
|
|
2852
|
+
var CRON_VERBS = "cron-verbs-granted";
|
|
2853
|
+
var CRON_VERB_NAMES = ["create_cron", "update_cron", "remove_cron"];
|
|
2648
2854
|
var OLD_DASHBOARD_LINE = "Agents may edit this file. It is rendered by the board UI.";
|
|
2649
2855
|
var DASHBOARD_LINE = "The project's members keep this page current with update_dashboard.";
|
|
2650
2856
|
var PLANNING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
@@ -2846,7 +3052,8 @@ var Board = class _Board {
|
|
|
2846
3052
|
ASKS_CHANNEL_OPENED,
|
|
2847
3053
|
KNOWLEDGE_REMOVAL,
|
|
2848
3054
|
CHANNEL_CONVERSATIONS,
|
|
2849
|
-
DASHBOARD_UPDATES
|
|
3055
|
+
DASHBOARD_UPDATES,
|
|
3056
|
+
CRON_VERBS
|
|
2850
3057
|
].filter((name) => !applied.includes(name));
|
|
2851
3058
|
if (pending.length === 0) {
|
|
2852
3059
|
return;
|
|
@@ -2885,6 +3092,9 @@ var Board = class _Board {
|
|
|
2885
3092
|
await this.grantBesideKnowledge("update_dashboard");
|
|
2886
3093
|
await this.rewordDashboards();
|
|
2887
3094
|
}
|
|
3095
|
+
if (pending.includes(CRON_VERBS)) {
|
|
3096
|
+
await this.grantToEveryRole(CRON_VERB_NAMES);
|
|
3097
|
+
}
|
|
2888
3098
|
await writeJson(file, { applied: [...applied, ...pending] });
|
|
2889
3099
|
}
|
|
2890
3100
|
/**
|
|
@@ -2898,6 +3108,15 @@ var Board = class _Board {
|
|
|
2898
3108
|
}
|
|
2899
3109
|
}
|
|
2900
3110
|
}
|
|
3111
|
+
/** Grants verbs once to every charter but the user's, which holds every verb already. */
|
|
3112
|
+
async grantToEveryRole(verbs) {
|
|
3113
|
+
for (const charter of await this.listRoles()) {
|
|
3114
|
+
const missing = verbs.filter((verb) => !charter.verbs.includes(verb));
|
|
3115
|
+
if (charter.name !== USER_ROLE && missing.length > 0) {
|
|
3116
|
+
await this.writeRoleUnlocked(USER_NAME, { ...charter, verbs: [...charter.verbs, ...missing] }, { verbsAdded: missing });
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
2901
3120
|
/** A dashboard still saying agents may edit its file says how it changes now. */
|
|
2902
3121
|
async rewordDashboards() {
|
|
2903
3122
|
for (const slug of await listDirs(this.paths.projects())) {
|
|
@@ -3042,7 +3261,7 @@ var Board = class _Board {
|
|
|
3042
3261
|
}
|
|
3043
3262
|
}
|
|
3044
3263
|
for (const file of await listFiles(this.paths.tasks(slug))) {
|
|
3045
|
-
const raw = await readMarkdown(path6.join(this.paths.tasks(slug), file),
|
|
3264
|
+
const raw = await readMarkdown(path6.join(this.paths.tasks(slug), file), z13.record(z13.string(), z13.unknown()));
|
|
3046
3265
|
const legacy = raw.data["thread"];
|
|
3047
3266
|
if (legacy === void 0) {
|
|
3048
3267
|
continue;
|
|
@@ -3120,7 +3339,7 @@ var Board = class _Board {
|
|
|
3120
3339
|
* turns, so it follows nothing now.
|
|
3121
3340
|
*/
|
|
3122
3341
|
async renameInboxCursors() {
|
|
3123
|
-
const Legacy =
|
|
3342
|
+
const Legacy = z13.object({ inbox: z13.string().nullable() });
|
|
3124
3343
|
for (const agent2 of await this.listAgents()) {
|
|
3125
3344
|
const file = this.paths.agentCursors(agent2.name);
|
|
3126
3345
|
if (await exists(file)) {
|
|
@@ -3166,7 +3385,7 @@ ${charter.purpose}
|
|
|
3166
3385
|
*/
|
|
3167
3386
|
async alignSeedRole(seed) {
|
|
3168
3387
|
const file = this.paths.role(seed.name);
|
|
3169
|
-
const raw = await readMarkdown(file,
|
|
3388
|
+
const raw = await readMarkdown(file, z13.record(z13.string(), z13.unknown()));
|
|
3170
3389
|
const current = RoleCharterSchema.parse(raw.data);
|
|
3171
3390
|
const verbsAdded = seed.verbs.filter((verb) => !current.verbs.includes(verb));
|
|
3172
3391
|
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));
|
|
@@ -3479,14 +3698,14 @@ ${charter.purpose}
|
|
|
3479
3698
|
/** The society's norms, the one knowledge topic every turn loads. Empty until the steward writes it. */
|
|
3480
3699
|
async readSocietyNorms() {
|
|
3481
3700
|
const file = this.paths.societyKnowledgeFile("norms");
|
|
3482
|
-
return await exists(file) ? (await readMarkdown(file,
|
|
3701
|
+
return await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
|
|
3483
3702
|
}
|
|
3484
3703
|
/** Knowledge topics of a project, or of the society when the project is null. */
|
|
3485
3704
|
async listKnowledge(project2) {
|
|
3486
3705
|
const dir = project2 === null ? this.paths.societyKnowledge() : this.paths.projectKnowledge(project2);
|
|
3487
3706
|
const topics = [];
|
|
3488
3707
|
for (const file of await listFiles(dir)) {
|
|
3489
|
-
const doc = await readMarkdown(path6.join(dir, file),
|
|
3708
|
+
const doc = await readMarkdown(path6.join(dir, file), z13.record(z13.string(), z13.unknown()));
|
|
3490
3709
|
const parsed = KnowledgeSchema.safeParse({
|
|
3491
3710
|
...doc.data,
|
|
3492
3711
|
project: project2,
|
|
@@ -3886,6 +4105,21 @@ ${DASHBOARD_LINE}
|
|
|
3886
4105
|
await this.events.append("paused.changed", actor.name, { paused });
|
|
3887
4106
|
});
|
|
3888
4107
|
}
|
|
4108
|
+
/** Sets the society's time zone, the user's alone: crons that name none are read in it. */
|
|
4109
|
+
async setTimezone(actor, zone) {
|
|
4110
|
+
this.assertUser(actor, "only the user sets the society's time zone");
|
|
4111
|
+
const timezone = TimeZoneSchema.parse(zone);
|
|
4112
|
+
return this.mutex.run(async () => {
|
|
4113
|
+
const doc = await readMarkdown(this.paths.societyFile(), SocietySchema);
|
|
4114
|
+
const society = { ...doc.data, timezone };
|
|
4115
|
+
await writeMarkdown(this.paths.societyFile(), society, doc.body);
|
|
4116
|
+
await this.events.append("society.configured", actor.name, {
|
|
4117
|
+
timezone,
|
|
4118
|
+
previousTimezone: doc.data.timezone
|
|
4119
|
+
});
|
|
4120
|
+
return society;
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
3889
4123
|
/** Releases every claim whose lease has expired. Called by the scheduler on its tick. */
|
|
3890
4124
|
async expireLeases() {
|
|
3891
4125
|
return this.mutex.run(async () => {
|
|
@@ -4308,6 +4542,241 @@ ${current.body}`);
|
|
|
4308
4542
|
return updated;
|
|
4309
4543
|
});
|
|
4310
4544
|
}
|
|
4545
|
+
/** Every cron, ended ones included, oldest first. */
|
|
4546
|
+
async listCrons() {
|
|
4547
|
+
const crons = [];
|
|
4548
|
+
for (const file of await listFiles(this.paths.crons())) {
|
|
4549
|
+
crons.push(await this.readCronFile(path6.join(this.paths.crons(), file)));
|
|
4550
|
+
}
|
|
4551
|
+
return crons;
|
|
4552
|
+
}
|
|
4553
|
+
async readCron(id) {
|
|
4554
|
+
const file = this.paths.cron(id);
|
|
4555
|
+
if (!await exists(file)) {
|
|
4556
|
+
throw new BoardError("NOT_FOUND", `cron ${id} not found`);
|
|
4557
|
+
}
|
|
4558
|
+
return this.readCronFile(file);
|
|
4559
|
+
}
|
|
4560
|
+
/**
|
|
4561
|
+
* Sets a cron: one that wakes its setter, or, set by a planning role, any citizen. Without a
|
|
4562
|
+
* place named it fires in the conversation of the turn that set it.
|
|
4563
|
+
*/
|
|
4564
|
+
async createCron(actor, input) {
|
|
4565
|
+
const args = VerbInputs.create_cron.parse(input);
|
|
4566
|
+
await this.authorize(actor, "create_cron");
|
|
4567
|
+
const target = args.agent ?? actor.name;
|
|
4568
|
+
this.assertMayCron(actor, target, "set");
|
|
4569
|
+
if (args.cron === void 0 === (args.at === void 0)) {
|
|
4570
|
+
throw new BoardError("VALIDATION", "a cron needs either cron, a five-field expression, or at, one time");
|
|
4571
|
+
}
|
|
4572
|
+
return this.mutex.run(async () => {
|
|
4573
|
+
const agent2 = await this.readAgent(target);
|
|
4574
|
+
if (agent2.status !== "active" || agent2.cli === null) {
|
|
4575
|
+
throw new BoardError("INVALID_STATE", `${target} takes no turns, so no cron can wake it`);
|
|
4576
|
+
}
|
|
4577
|
+
const where = await this.cronConversation(actor, agent2, args);
|
|
4578
|
+
const now = this.now();
|
|
4579
|
+
const schedule = await this.cronSchedule(args, void 0, now);
|
|
4580
|
+
if (schedule === void 0) {
|
|
4581
|
+
throw new BoardError("VALIDATION", "a cron needs a schedule");
|
|
4582
|
+
}
|
|
4583
|
+
const cron2 = await this.writeCron({
|
|
4584
|
+
id: this.newId(),
|
|
4585
|
+
title: args.title,
|
|
4586
|
+
agent: agent2.name,
|
|
4587
|
+
...where,
|
|
4588
|
+
schedule,
|
|
4589
|
+
createdBy: actor.name,
|
|
4590
|
+
createdAt: now.toISOString(),
|
|
4591
|
+
since: now.toISOString(),
|
|
4592
|
+
failures: 0,
|
|
4593
|
+
note: args.note
|
|
4594
|
+
});
|
|
4595
|
+
await this.events.append("cron.created", actor.name, {
|
|
4596
|
+
...this.cronPayload(cron2),
|
|
4597
|
+
title: cron2.title,
|
|
4598
|
+
schedule: cron2.schedule
|
|
4599
|
+
});
|
|
4600
|
+
return cron2;
|
|
4601
|
+
});
|
|
4602
|
+
}
|
|
4603
|
+
/** Pauses, resumes, retitles, rewrites, or reschedules a cron; resuming or rescheduling starts it afresh. */
|
|
4604
|
+
async updateCron(actor, input) {
|
|
4605
|
+
const args = VerbInputs.update_cron.parse(input);
|
|
4606
|
+
await this.authorize(actor, "update_cron");
|
|
4607
|
+
if (args.cron !== void 0 && args.at !== void 0) {
|
|
4608
|
+
throw new BoardError("VALIDATION", "a cron fires on an expression or at one time, not both");
|
|
4609
|
+
}
|
|
4610
|
+
return this.mutex.run(async () => {
|
|
4611
|
+
const current = await this.readCron(args.cron_id);
|
|
4612
|
+
this.assertMayCron(actor, current.agent, "change");
|
|
4613
|
+
if (current.ended !== void 0) {
|
|
4614
|
+
throw new BoardError("INVALID_STATE", `cron ${current.id} has ended (${current.ended.reason})`);
|
|
4615
|
+
}
|
|
4616
|
+
const now = this.now();
|
|
4617
|
+
const schedule = await this.cronSchedule(args, current.schedule, now);
|
|
4618
|
+
const resumed = args.paused === false && current.paused !== void 0;
|
|
4619
|
+
const changed = [
|
|
4620
|
+
...args.title === void 0 ? [] : ["title"],
|
|
4621
|
+
...args.note === void 0 ? [] : ["note"],
|
|
4622
|
+
...schedule === void 0 ? [] : ["schedule"],
|
|
4623
|
+
...args.paused === true && current.paused === void 0 ? ["paused"] : [],
|
|
4624
|
+
...resumed ? ["resumed"] : []
|
|
4625
|
+
];
|
|
4626
|
+
if (changed.length === 0 && args.paused === void 0) {
|
|
4627
|
+
throw new BoardError("VALIDATION", "update_cron needs paused, title, note, cron, timezone, or at");
|
|
4628
|
+
}
|
|
4629
|
+
const ts = now.toISOString();
|
|
4630
|
+
const { paused: wasPaused, ...rest } = current;
|
|
4631
|
+
const paused = args.paused === void 0 ? wasPaused : args.paused ? wasPaused ?? { at: ts, by: actor.name } : void 0;
|
|
4632
|
+
const cron2 = await this.writeCron({
|
|
4633
|
+
...rest,
|
|
4634
|
+
...args.title === void 0 ? {} : { title: args.title },
|
|
4635
|
+
...args.note === void 0 ? {} : { note: args.note },
|
|
4636
|
+
...schedule === void 0 ? {} : { schedule },
|
|
4637
|
+
...schedule !== void 0 || resumed ? { since: ts } : {},
|
|
4638
|
+
...paused === void 0 ? {} : { paused }
|
|
4639
|
+
});
|
|
4640
|
+
if (changed.length > 0) {
|
|
4641
|
+
await this.events.append("cron.updated", actor.name, {
|
|
4642
|
+
...this.cronPayload(cron2),
|
|
4643
|
+
changed,
|
|
4644
|
+
...schedule === void 0 ? {} : { schedule }
|
|
4645
|
+
});
|
|
4646
|
+
}
|
|
4647
|
+
return cron2;
|
|
4648
|
+
});
|
|
4649
|
+
}
|
|
4650
|
+
/** Ends a cron for good: its setter's, or anyone's for a planning role. */
|
|
4651
|
+
async removeCron(actor, input) {
|
|
4652
|
+
const args = VerbInputs.remove_cron.parse(input);
|
|
4653
|
+
await this.authorize(actor, "remove_cron");
|
|
4654
|
+
return this.mutex.run(async () => {
|
|
4655
|
+
const current = await this.readCron(args.cron_id);
|
|
4656
|
+
this.assertMayCron(actor, current.agent, "end");
|
|
4657
|
+
if (current.ended !== void 0) {
|
|
4658
|
+
throw new BoardError("INVALID_STATE", `cron ${current.id} has ended already (${current.ended.reason})`);
|
|
4659
|
+
}
|
|
4660
|
+
return this.endCronUnlocked(current, actor.name, args.reason);
|
|
4661
|
+
});
|
|
4662
|
+
}
|
|
4663
|
+
/** A cron wakes its setter; one that wakes another citizen is the planning roles' to set, change, and end. */
|
|
4664
|
+
assertMayCron(actor, agent2, what) {
|
|
4665
|
+
if (agent2 !== actor.name && !PLANNING_ROLES.includes(actor.role)) {
|
|
4666
|
+
throw new BoardError("FORBIDDEN", `only the user, the steward, and the concierge ${what} a cron that wakes another citizen`);
|
|
4667
|
+
}
|
|
4668
|
+
}
|
|
4669
|
+
/**
|
|
4670
|
+
* Where a cron fires for its citizen: the thread, the channel, or the scope's home it names, else
|
|
4671
|
+
* the conversation of the turn that set it when it wakes its setter, else its home outside projects.
|
|
4672
|
+
*/
|
|
4673
|
+
async cronConversation(actor, agent2, named) {
|
|
4674
|
+
const own = actor.name === agent2.name;
|
|
4675
|
+
if (named.project === void 0 && named.thread_id === void 0 && named.channel === void 0) {
|
|
4676
|
+
return own && actor.scope !== void 0 ? this.cronConversation(actor, agent2, {
|
|
4677
|
+
project: actor.scope,
|
|
4678
|
+
thread_id: actor.thread,
|
|
4679
|
+
channel: actor.channel
|
|
4680
|
+
}) : { scope: SOCIETY_SCOPE };
|
|
4681
|
+
}
|
|
4682
|
+
if (named.thread_id !== void 0) {
|
|
4683
|
+
const { thread: thread2 } = await this.findThread(named.thread_id);
|
|
4684
|
+
if (thread2.state !== "open") {
|
|
4685
|
+
throw new BoardError("INVALID_STATE", `thread ${thread2.id} is closed`);
|
|
4686
|
+
}
|
|
4687
|
+
const scope2 = wakeScope(agent2, parseChannelRef(thread2.channel).project);
|
|
4688
|
+
if (named.project !== void 0 && named.project !== scope2) {
|
|
4689
|
+
throw new BoardError("VALIDATION", `thread ${thread2.id} is on ${thread2.channel}, so ${agent2.name}'s conversation there is in ${scope2}, not ${named.project}`);
|
|
4690
|
+
}
|
|
4691
|
+
return { scope: scope2, thread: thread2.id };
|
|
4692
|
+
}
|
|
4693
|
+
const scope = named.project ?? (own ? actor.scope : void 0) ?? SOCIETY_SCOPE;
|
|
4694
|
+
if (scope !== SOCIETY_SCOPE) {
|
|
4695
|
+
await this.readActiveProject(scope);
|
|
4696
|
+
if (!agent2.memberships.includes(scope)) {
|
|
4697
|
+
throw new BoardError("VALIDATION", `${agent2.name} is not a member of ${scope}`);
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
if (named.channel === void 0 || named.channel === "general") {
|
|
4701
|
+
return { scope };
|
|
4702
|
+
}
|
|
4703
|
+
await this.assertChannelOpen(channelRef(scope === SOCIETY_SCOPE ? null : scope, named.channel));
|
|
4704
|
+
return { scope, channel: named.channel };
|
|
4705
|
+
}
|
|
4706
|
+
/**
|
|
4707
|
+
* The schedule a cron verb asks for, or undefined when it asks for none: an expression, read in
|
|
4708
|
+
* its zone or the society's, which must name a time to come and no two fires closer than the
|
|
4709
|
+
* floor, or one time to come. A zone alone keeps the expression of `existing`.
|
|
4710
|
+
*/
|
|
4711
|
+
async cronSchedule(args, existing, now) {
|
|
4712
|
+
if (args.at !== void 0) {
|
|
4713
|
+
if (args.timezone !== void 0) {
|
|
4714
|
+
throw new BoardError("VALIDATION", "at carries its own offset; timezone is for a cron expression");
|
|
4715
|
+
}
|
|
4716
|
+
const at = new Date(args.at);
|
|
4717
|
+
if (at <= now) {
|
|
4718
|
+
throw new BoardError("VALIDATION", `${args.at} has passed; a cron fires at a time to come`);
|
|
4719
|
+
}
|
|
4720
|
+
return { at: at.toISOString() };
|
|
4721
|
+
}
|
|
4722
|
+
if (args.cron === void 0 && args.timezone === void 0) {
|
|
4723
|
+
return void 0;
|
|
4724
|
+
}
|
|
4725
|
+
const expression = args.cron ?? (existing === void 0 || isOneTime(existing) ? void 0 : existing.cron);
|
|
4726
|
+
if (expression === void 0) {
|
|
4727
|
+
throw new BoardError("VALIDATION", "timezone is for a cron expression, which this cron has not");
|
|
4728
|
+
}
|
|
4729
|
+
const kept = args.timezone === void 0 && existing !== void 0 && !isOneTime(existing) ? existing.timezone : args.timezone ?? void 0;
|
|
4730
|
+
const schedule = {
|
|
4731
|
+
cron: expression,
|
|
4732
|
+
...kept === void 0 ? {} : { timezone: kept }
|
|
4733
|
+
};
|
|
4734
|
+
const { timezone: societyZone } = await this.society();
|
|
4735
|
+
if (fireTimes(schedule, societyZone, now, 1).length === 0) {
|
|
4736
|
+
throw new BoardError("VALIDATION", `${expression} names no time to come`);
|
|
4737
|
+
}
|
|
4738
|
+
const gap = shortestGapMs(schedule, societyZone, now);
|
|
4739
|
+
if (gap !== null && gap < CRON_FLOOR_MS) {
|
|
4740
|
+
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`);
|
|
4741
|
+
}
|
|
4742
|
+
return schedule;
|
|
4743
|
+
}
|
|
4744
|
+
/** A cron's events name its citizen and the conversation it fires in, as turn events do. */
|
|
4745
|
+
cronPayload(cron2) {
|
|
4746
|
+
return {
|
|
4747
|
+
cronId: cron2.id,
|
|
4748
|
+
agent: cron2.agent,
|
|
4749
|
+
project: cron2.scope,
|
|
4750
|
+
...cron2.thread === void 0 ? {} : { thread: cron2.thread },
|
|
4751
|
+
...cron2.channel === void 0 ? {} : { channel: cron2.channel }
|
|
4752
|
+
};
|
|
4753
|
+
}
|
|
4754
|
+
async readCronFile(file) {
|
|
4755
|
+
const doc = await readMarkdown(file, CronFrontmatterSchema);
|
|
4756
|
+
return { ...doc.data, note: doc.body.trimEnd() };
|
|
4757
|
+
}
|
|
4758
|
+
async writeCron(cron2) {
|
|
4759
|
+
const { note, ...frontmatter } = cron2;
|
|
4760
|
+
const data = CronFrontmatterSchema.parse(Object.fromEntries(Object.entries(frontmatter).filter(([, value]) => value !== void 0)));
|
|
4761
|
+
await writeMarkdown(this.paths.cron(cron2.id), data, note);
|
|
4762
|
+
return { ...data, note };
|
|
4763
|
+
}
|
|
4764
|
+
async endCronUnlocked(cron2, by, reason) {
|
|
4765
|
+
const ended = await this.writeCron({
|
|
4766
|
+
...cron2,
|
|
4767
|
+
ended: { at: this.now().toISOString(), by, reason }
|
|
4768
|
+
});
|
|
4769
|
+
await this.events.append("cron.ended", by, { ...this.cronPayload(ended), reason });
|
|
4770
|
+
return ended;
|
|
4771
|
+
}
|
|
4772
|
+
/** Ends every running cron `ends` picks, in the transaction that ends the conversation it fires in. */
|
|
4773
|
+
async endCronsUnlocked(by, reason, ends) {
|
|
4774
|
+
for (const cron2 of await this.listCrons()) {
|
|
4775
|
+
if (cron2.ended === void 0 && ends(cron2)) {
|
|
4776
|
+
await this.endCronUnlocked(cron2, by, reason);
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4311
4780
|
async openThread(actor, input) {
|
|
4312
4781
|
const args = VerbInputs.open_thread.parse(input);
|
|
4313
4782
|
await this.authorize(actor, "open_thread");
|
|
@@ -4524,6 +4993,7 @@ ${args.summary}`);
|
|
|
4524
4993
|
threadId: thread2.id,
|
|
4525
4994
|
channel: thread2.channel
|
|
4526
4995
|
});
|
|
4996
|
+
await this.endCronsUnlocked(actor.name, "its thread closed", (cron2) => cron2.thread === thread2.id);
|
|
4527
4997
|
return summary;
|
|
4528
4998
|
});
|
|
4529
4999
|
}
|
|
@@ -5025,6 +5495,13 @@ ${args.summary}`);
|
|
|
5025
5495
|
proposedBy: actor.name,
|
|
5026
5496
|
status: "proposed",
|
|
5027
5497
|
createdAt: this.now().toISOString(),
|
|
5498
|
+
...actor.scope === void 0 ? {} : {
|
|
5499
|
+
filedFrom: {
|
|
5500
|
+
scope: actor.scope,
|
|
5501
|
+
...actor.thread === void 0 ? {} : { thread: actor.thread },
|
|
5502
|
+
...actor.channel === void 0 ? {} : { channel: actor.channel }
|
|
5503
|
+
}
|
|
5504
|
+
},
|
|
5028
5505
|
charter: parsed.data
|
|
5029
5506
|
}),
|
|
5030
5507
|
body: args.rationale
|
|
@@ -5196,6 +5673,7 @@ ${rationale}`}`);
|
|
|
5196
5673
|
members: project2.members.filter((member) => member !== target)
|
|
5197
5674
|
}));
|
|
5198
5675
|
await this.refreshMember(target);
|
|
5676
|
+
await this.endCronsUnlocked(actor.name, `${target} left ${args.project}`, (cron2) => cron2.agent === target && cron2.scope === args.project);
|
|
5199
5677
|
await this.events.append("agent.left", actor.name, {
|
|
5200
5678
|
name: target,
|
|
5201
5679
|
project: args.project,
|
|
@@ -5264,6 +5742,12 @@ ${rationale}`}`);
|
|
|
5264
5742
|
return this.archiveChannel(actor, VerbInputs.archive_channel.parse(input));
|
|
5265
5743
|
case "update_dashboard":
|
|
5266
5744
|
return this.updateDashboard(actor, VerbInputs.update_dashboard.parse(input));
|
|
5745
|
+
case "create_cron":
|
|
5746
|
+
return this.createCron(actor, VerbInputs.create_cron.parse(input));
|
|
5747
|
+
case "update_cron":
|
|
5748
|
+
return this.updateCron(actor, VerbInputs.update_cron.parse(input));
|
|
5749
|
+
case "remove_cron":
|
|
5750
|
+
return this.removeCron(actor, VerbInputs.remove_cron.parse(input));
|
|
5267
5751
|
default:
|
|
5268
5752
|
throw new BoardError("VALIDATION", `unknown verb ${String(verb)}`);
|
|
5269
5753
|
}
|
|
@@ -5310,7 +5794,7 @@ ${rationale}`}`);
|
|
|
5310
5794
|
}
|
|
5311
5795
|
async readAgentRoleBody(agent2) {
|
|
5312
5796
|
const file = this.paths.agentRole(agent2);
|
|
5313
|
-
return await exists(file) ? (await readMarkdown(file,
|
|
5797
|
+
return await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
|
|
5314
5798
|
}
|
|
5315
5799
|
/** The steps of one finished turn, as the runner handed them over when it ended. */
|
|
5316
5800
|
/**
|
|
@@ -5488,6 +5972,9 @@ ${rationale}`}`);
|
|
|
5488
5972
|
}
|
|
5489
5973
|
await this.writeTurnRecord(parsed);
|
|
5490
5974
|
await this.refreshMember(parsed.agent);
|
|
5975
|
+
for (const id of parsed.crons ?? []) {
|
|
5976
|
+
await this.recordCronRun(id, parsed, failed);
|
|
5977
|
+
}
|
|
5491
5978
|
if (parsed.exitReason === "stopped") {
|
|
5492
5979
|
await this.events.append("turn.stopped", parsed.stoppedBy ?? USER_NAME, {
|
|
5493
5980
|
...parsed.id === void 0 ? {} : { turnId: parsed.id },
|
|
@@ -5525,6 +6012,29 @@ ${rationale}`}`);
|
|
|
5525
6012
|
});
|
|
5526
6013
|
});
|
|
5527
6014
|
}
|
|
6015
|
+
/**
|
|
6016
|
+
* Records how closing an abandoned `ghpr` task's pull request on GitHub went, once: the task's
|
|
6017
|
+
* link keeps it, so a restart does not try again.
|
|
6018
|
+
*/
|
|
6019
|
+
async recordPullRequestClosed(actor, input) {
|
|
6020
|
+
return this.mutex.run(async () => {
|
|
6021
|
+
const location = await this.findTask(input.taskId);
|
|
6022
|
+
const current = location.task;
|
|
6023
|
+
const pull = current.pullRequest;
|
|
6024
|
+
if (pull === void 0 || pull.closed !== void 0) {
|
|
6025
|
+
return current;
|
|
6026
|
+
}
|
|
6027
|
+
const task2 = await this.writeTask(location.project, {
|
|
6028
|
+
...current,
|
|
6029
|
+
pullRequest: {
|
|
6030
|
+
...pull,
|
|
6031
|
+
closed: { at: this.now().toISOString(), ok: input.ok, detail: input.detail }
|
|
6032
|
+
}
|
|
6033
|
+
});
|
|
6034
|
+
await this.events.append(input.ok ? "pull_request.closed" : "pull_request.close_failed", actor.name, { project: location.project, taskId: task2.id, url: pull.url, detail: input.detail });
|
|
6035
|
+
return task2;
|
|
6036
|
+
});
|
|
6037
|
+
}
|
|
5528
6038
|
/**
|
|
5529
6039
|
* Records that a citizen's workspace in a conversation that has ended holds work on no branch, so
|
|
5530
6040
|
* the scheduler gives the citizen a closing turn there to decide about it. False, recording
|
|
@@ -5546,6 +6056,45 @@ ${rationale}`}`);
|
|
|
5546
6056
|
return true;
|
|
5547
6057
|
});
|
|
5548
6058
|
}
|
|
6059
|
+
/**
|
|
6060
|
+
* Records a cron's fire, which the scheduler made; a one-time cron ends with it. Null, recording
|
|
6061
|
+
* nothing, for a cron ended or paused since the scheduler read it, which does not fire.
|
|
6062
|
+
*/
|
|
6063
|
+
async cronFired(id, at) {
|
|
6064
|
+
return this.mutex.run(async () => {
|
|
6065
|
+
const current = await this.readCron(id);
|
|
6066
|
+
if (current.ended !== void 0 || current.paused !== void 0) {
|
|
6067
|
+
return null;
|
|
6068
|
+
}
|
|
6069
|
+
const cron2 = await this.writeCron({ ...current, lastFiredAt: at });
|
|
6070
|
+
await this.events.append("cron.fired", SYSTEM_ACTOR.name, this.cronPayload(cron2));
|
|
6071
|
+
return isOneTime(cron2.schedule) ? this.endCronUnlocked(cron2, SYSTEM_ACTOR.name, "fired once, as set") : cron2;
|
|
6072
|
+
});
|
|
6073
|
+
}
|
|
6074
|
+
/** Records a time a cron passed because the turn of its previous fire was still queued or running. */
|
|
6075
|
+
async cronSkipped(id, at) {
|
|
6076
|
+
return this.mutex.run(async () => {
|
|
6077
|
+
const cron2 = await this.writeCron({ ...await this.readCron(id), lastSkippedAt: at });
|
|
6078
|
+
await this.events.append("cron.skipped", SYSTEM_ACTOR.name, this.cronPayload(cron2));
|
|
6079
|
+
return cron2;
|
|
6080
|
+
});
|
|
6081
|
+
}
|
|
6082
|
+
/**
|
|
6083
|
+
* How the turn a cron's fire started ended. A failure counts toward `cron_failing`, a completed
|
|
6084
|
+
* turn clears the count, and a stop, the user's decision, leaves it.
|
|
6085
|
+
*/
|
|
6086
|
+
async recordCronRun(id, record, failed) {
|
|
6087
|
+
if (record.id === void 0 || !await exists(this.paths.cron(id))) {
|
|
6088
|
+
return;
|
|
6089
|
+
}
|
|
6090
|
+
const cron2 = await this.readCron(id);
|
|
6091
|
+
const outcome = record.exitReason === "stopped" ? "stopped" : failed ? "failed" : "completed";
|
|
6092
|
+
await this.writeCron({
|
|
6093
|
+
...cron2,
|
|
6094
|
+
lastRun: { turnId: record.id, at: record.endedAt ?? this.now().toISOString(), outcome },
|
|
6095
|
+
failures: outcome === "failed" ? cron2.failures + 1 : outcome === "completed" ? 0 : cron2.failures
|
|
6096
|
+
});
|
|
6097
|
+
}
|
|
5549
6098
|
/**
|
|
5550
6099
|
* Whether a conversation with a workspace of its own has ended: a thread closed, a task ended
|
|
5551
6100
|
* (for one from before tasks had threads), or a channel archived.
|
|
@@ -5645,7 +6194,7 @@ ${rationale}`}`);
|
|
|
5645
6194
|
return { project: slug, revision: 0, body: "" };
|
|
5646
6195
|
}
|
|
5647
6196
|
const { data, body } = await readLooseMarkdown(file);
|
|
5648
|
-
const revision =
|
|
6197
|
+
const revision = z13.number().int().min(0).safeParse(data["revision"]);
|
|
5649
6198
|
const updatedBy = NameSchema.safeParse(data["updatedBy"]);
|
|
5650
6199
|
const updatedAt = IsoDateTimeSchema.safeParse(data["updatedAt"]);
|
|
5651
6200
|
return {
|
|
@@ -5936,7 +6485,7 @@ ${seed}
|
|
|
5936
6485
|
continue;
|
|
5937
6486
|
}
|
|
5938
6487
|
const file = this.paths.agentRole(agent2.name);
|
|
5939
|
-
const body = await exists(file) ? (await readMarkdown(file,
|
|
6488
|
+
const body = await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
|
|
5940
6489
|
const at = body.indexOf(`
|
|
5941
6490
|
${SEED_INSTRUCTIONS_HEADING}
|
|
5942
6491
|
`);
|
|
@@ -6380,6 +6929,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
|
|
|
6380
6929
|
archivedChannels: [...current.archivedChannels, archived]
|
|
6381
6930
|
}));
|
|
6382
6931
|
}
|
|
6932
|
+
await this.endCronsUnlocked(by, `${ref} was archived`, (cron2) => cron2.scope === scope && cron2.channel === channel || cron2.thread !== void 0 && threadsClosed.includes(cron2.thread));
|
|
6383
6933
|
await this.events.append("channel.archived", by, {
|
|
6384
6934
|
channel: ref,
|
|
6385
6935
|
reason,
|
|
@@ -6484,6 +7034,7 @@ ${charter.purpose}
|
|
|
6484
7034
|
});
|
|
6485
7035
|
threadsClosed.push(doc.data.id);
|
|
6486
7036
|
}
|
|
7037
|
+
await this.endCronsUnlocked(by, `project ${slug} was archived`, (cron2) => cron2.scope === slug || cron2.thread !== void 0 && threadsClosed.includes(cron2.thread));
|
|
6487
7038
|
const project2 = await this.updateProject(slug, (p) => ({
|
|
6488
7039
|
...p,
|
|
6489
7040
|
members: [],
|
|
@@ -6548,6 +7099,7 @@ ${charter.purpose}
|
|
|
6548
7099
|
}
|
|
6549
7100
|
this.tokenIndex.delete(current.tokenHash);
|
|
6550
7101
|
await this.refreshMember(name);
|
|
7102
|
+
await this.endCronsUnlocked(by, `${name} was retired`, (cron2) => cron2.agent === name);
|
|
6551
7103
|
await this.events.append("agent.retired", by, {
|
|
6552
7104
|
name,
|
|
6553
7105
|
role: agent2.role,
|
|
@@ -6757,7 +7309,8 @@ ${charter.purpose}
|
|
|
6757
7309
|
proposalId,
|
|
6758
7310
|
outcome,
|
|
6759
7311
|
kind: proposal2.kind,
|
|
6760
|
-
proposedBy: proposal2.proposedBy
|
|
7312
|
+
proposedBy: proposal2.proposedBy,
|
|
7313
|
+
...proposal2.filedFrom === void 0 ? {} : { filedFrom: proposal2.filedFrom }
|
|
6761
7314
|
});
|
|
6762
7315
|
if (provision !== void 0) {
|
|
6763
7316
|
await this.events.append("proposal.provisioned", actor.name, {
|
|
@@ -6858,6 +7411,7 @@ ${charter.purpose}
|
|
|
6858
7411
|
channel: found.thread.channel,
|
|
6859
7412
|
ended
|
|
6860
7413
|
});
|
|
7414
|
+
await this.endCronsUnlocked(by, `its thread closed: its ${found.thread.subject?.kind ?? "subject"} was ${ended}`, (cron2) => cron2.thread === subjectId);
|
|
6861
7415
|
}
|
|
6862
7416
|
async readMessagesIn(dir, since) {
|
|
6863
7417
|
const messages = [];
|
|
@@ -7293,6 +7847,66 @@ program.command("resume").description("Allow wakeups again").action(async () =>
|
|
|
7293
7847
|
await board.setPaused(board.userActor(), false);
|
|
7294
7848
|
print({ paused: false }, () => "Society resumed");
|
|
7295
7849
|
});
|
|
7850
|
+
program.command("society").description("The society's settings").command("timezone [zone]").description("Show the society's time zone, or set it: crons that name none are read in it").action(async (zone) => {
|
|
7851
|
+
const board = await open();
|
|
7852
|
+
const society = zone === void 0 ? await board.society() : await board.setTimezone(board.userActor(), zone);
|
|
7853
|
+
print({ timezone: society.timezone }, () => `Time zone: ${society.timezone}`);
|
|
7854
|
+
});
|
|
7855
|
+
function describeCron(cron2, timeZone) {
|
|
7856
|
+
const where = [
|
|
7857
|
+
cron2.scope,
|
|
7858
|
+
cron2.thread,
|
|
7859
|
+
cron2.channel === void 0 ? void 0 : `#${cron2.channel}`
|
|
7860
|
+
].filter((part) => part !== void 0).join("/");
|
|
7861
|
+
const next = nextFire(cron2, timeZone);
|
|
7862
|
+
const state = cron2.ended !== void 0 ? `ended (${cron2.ended.reason})` : cron2.paused !== void 0 ? "paused" : next === null ? "no time to come" : `next ${next.toISOString()}`;
|
|
7863
|
+
return `${cron2.id} ${cron2.agent} ${where} ${describeSchedule(cron2.schedule, timeZone)} ${state} ${cron2.title}`;
|
|
7864
|
+
}
|
|
7865
|
+
var cron = program.command("cron").description("Crons that wake citizens on a clock");
|
|
7866
|
+
cron.command("list").description("List crons, ended ones too with --all").option("--all", "include ended crons", false).action(async (opts) => {
|
|
7867
|
+
const board = await open();
|
|
7868
|
+
const { timezone } = await board.society();
|
|
7869
|
+
const crons = (await board.listCrons()).filter((each) => opts.all || each.ended === void 0);
|
|
7870
|
+
print(crons, () => crons.length === 0 ? "No crons." : crons.map((each) => describeCron(each, timezone)).join("\n"));
|
|
7871
|
+
});
|
|
7872
|
+
cron.command("add <agent> <title>").description("Set a cron that wakes a citizen, with a note on what to do then").requiredOption("--note <text>", "what the citizen does when it fires").option("--cron <expression>", "five fields: minute hour day-of-month month day-of-week").option("--timezone <zone>", "the zone the expression is read in; the society's otherwise").option("--at <time>", "one time to fire, with its offset, instead of --cron").option("--project <slug>", "where it fires: a project the citizen is in, or society").option("--thread <id>", "an open thread there whose conversation it fires in").option("--channel <name>", "an open channel there, other than general, whose conversation it fires in").option("--as <agent>", "act as this agent instead of the user").action(async (agentName, title, opts) => {
|
|
7873
|
+
const board = await open();
|
|
7874
|
+
const created = await board.createCron(await actorFor(board, opts.as), {
|
|
7875
|
+
title,
|
|
7876
|
+
note: opts.note,
|
|
7877
|
+
agent: agentName,
|
|
7878
|
+
...opts.cron === void 0 ? {} : { cron: opts.cron },
|
|
7879
|
+
...opts.timezone === void 0 ? {} : { timezone: opts.timezone },
|
|
7880
|
+
...opts.at === void 0 ? {} : { at: opts.at },
|
|
7881
|
+
...opts.project === void 0 ? {} : { project: opts.project },
|
|
7882
|
+
...opts.thread === void 0 ? {} : { thread_id: opts.thread },
|
|
7883
|
+
...opts.channel === void 0 ? {} : { channel: opts.channel }
|
|
7884
|
+
});
|
|
7885
|
+
const { timezone } = await board.society();
|
|
7886
|
+
print(created, () => `Cron set: ${describeCron(created, timezone)}`);
|
|
7887
|
+
});
|
|
7888
|
+
for (const [name, paused] of [
|
|
7889
|
+
["pause", true],
|
|
7890
|
+
["resume", false]
|
|
7891
|
+
]) {
|
|
7892
|
+
cron.command(`${name} <id>`).description(paused ? "Pause a cron" : "Resume a paused cron from its next time").option("--as <agent>", "act as this agent instead of the user").action(async (id, opts) => {
|
|
7893
|
+
const board = await open();
|
|
7894
|
+
const updated = await board.updateCron(await actorFor(board, opts.as), {
|
|
7895
|
+
cron_id: id,
|
|
7896
|
+
paused
|
|
7897
|
+
});
|
|
7898
|
+
const { timezone } = await board.society();
|
|
7899
|
+
print(updated, () => describeCron(updated, timezone));
|
|
7900
|
+
});
|
|
7901
|
+
}
|
|
7902
|
+
cron.command("remove <id>").description("End a cron for good").requiredOption("--reason <text>", "why it ends").option("--as <agent>", "act as this agent instead of the user").action(async (id, opts) => {
|
|
7903
|
+
const board = await open();
|
|
7904
|
+
const removed = await board.removeCron(await actorFor(board, opts.as), {
|
|
7905
|
+
cron_id: id,
|
|
7906
|
+
reason: opts.reason
|
|
7907
|
+
});
|
|
7908
|
+
print(removed, () => `Cron ${removed.id} ended`);
|
|
7909
|
+
});
|
|
7296
7910
|
var knowledge = program.command("knowledge").description("Shared knowledge, by project or society");
|
|
7297
7911
|
knowledge.command("list [project]").description("List knowledge topics of a project, or of the society when no project is given").action(async (projectSlug) => {
|
|
7298
7912
|
const board = await open();
|