@kubitnodes/stellaris 0.3.13 → 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 +1101 -539
- package/dist/server/index.js +1262 -592
- package/package.json +2 -1
- package/web/dist/assets/{BackLink-d0xz7ZLb.js → BackLink-giAdtos8.js} +1 -1
- package/web/dist/assets/{ChannelForms-BIRwT9CY.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-Bv04iH-y.js → CitizensView-Dbo47F82.js} +1 -1
- package/web/dist/assets/{CliIcon-tYTwI_RQ.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-BLiIulgZ.js → Markdown-Dw04_ImA.js} +4 -4
- package/web/dist/assets/{MessageItem-CHrmjF7P.js → MessageItem-DnfMDxDB.js} +1 -1
- package/web/dist/assets/{MetricsView-DG1alTgs.js → MetricsView-_h2TWeo8.js} +1 -1
- package/web/dist/assets/{NeedsYouView-Doc0fKPi.js → NeedsYouView-BI1-_bYI.js} +1 -1
- package/web/dist/assets/{Overview-ChsOgIEW.js → Overview-BdZiHglw.js} +1 -1
- package/web/dist/assets/{Pane-MJgGXQBb.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-CSA8neXR.js → ProposalsView-DSi2u6qO.js} +1 -1
- package/web/dist/assets/{RunnersView-Dx21LDFu.js → RunnersView-9E2qUSO5.js} +1 -1
- package/web/dist/assets/{SocietyView-CdVW2DgG.js → SocietyView-DEeUo4wb.js} +1 -1
- package/web/dist/assets/SubjectThread-DhGB5RPc.js +1 -0
- package/web/dist/assets/{TaskFileView-BqqzxJKb.js → TaskFileView-DQC_ZUdY.js} +2 -2
- package/web/dist/assets/TaskView-yVpUG83f.js +3 -0
- package/web/dist/assets/{TasksView-Ba5fPAvy.js → TasksView-CJbJt5dM.js} +1 -1
- package/web/dist/assets/ThreadCard-DVAx3gGc.js +1 -0
- package/web/dist/assets/{ThreadForms-ChTFRu-P.js → ThreadForms-RnrV7V5X.js} +1 -1
- package/web/dist/assets/{ThreadView-CnJPNS_8.js → ThreadView-CMwiVZFq.js} +1 -1
- package/web/dist/assets/{citizen-D-dxX00V.js → citizen-aLkZ4owL.js} +1 -1
- package/web/dist/assets/{compose-BgpUWGYE.js → compose-D7DOz6kC.js} +1 -1
- package/web/dist/assets/format-BORdp4w-.js +55 -0
- package/web/dist/assets/{governance-Di4MijNG.js → governance-WrDQawLN.js} +1 -1
- package/web/dist/assets/index-7uInG5D9.js +23 -0
- package/web/dist/assets/index-D02Wgo_C.css +2 -0
- package/web/dist/assets/{link-DAMxBItI.js → link-1v5lugnf.js} +1 -1
- package/web/dist/assets/{tasks-bwj_EdwF.js → tasks-C-9dAYQV.js} +1 -1
- package/web/dist/assets/useMatch-CyeFfbcR.js +1 -0
- package/web/dist/assets/{useStickyScroll-BILPUgGj.js → useStickyScroll-CE7dyz0Q.js} +1 -1
- package/web/dist/index.html +9 -9
- package/web/dist/assets/ChannelView-BvMlzMiN.js +0 -1
- package/web/dist/assets/CitizenView-DNQwjpIp.js +0 -2
- package/web/dist/assets/KnowledgeView-L0pdaM_2.js +0 -1
- package/web/dist/assets/ProjectView-CQTn7yDo.js +0 -1
- package/web/dist/assets/ProposalView-C92OJ_w1.js +0 -1
- package/web/dist/assets/SubjectThread-egXeFhT1.js +0 -1
- package/web/dist/assets/TaskView-rks26vuF.js +0 -3
- package/web/dist/assets/ThreadCard-D6i69_15.js +0 -1
- package/web/dist/assets/format-C3aawMM6.js +0 -55
- package/web/dist/assets/index-VVnGdAXc.css +0 -2
- package/web/dist/assets/index-krHkeIbX.js +0 -23
- package/web/dist/assets/useMatch-B-8IcuU6.js +0 -1
package/dist/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,65 +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
348
|
linkedAt: IsoDateTimeSchema,
|
|
222
349
|
/**
|
|
223
350
|
* Once its `ghpr` task was abandoned, how the board's closing of it on GitHub went: `ok` false
|
|
224
351
|
* leaves it open, for the reason `detail` gives.
|
|
225
352
|
*/
|
|
226
|
-
closed:
|
|
353
|
+
closed: z4.object({ at: IsoDateTimeSchema, ok: z4.boolean(), detail: z4.string() }).optional()
|
|
227
354
|
});
|
|
228
|
-
var DefaultBranchSchema =
|
|
229
|
-
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");
|
|
230
357
|
var stageFields = {
|
|
231
|
-
name:
|
|
358
|
+
name: z4.string().trim().min(1).max(120),
|
|
232
359
|
role: NameSchema.optional(),
|
|
233
360
|
agent: NameSchema.optional(),
|
|
234
|
-
gate:
|
|
361
|
+
gate: z4.boolean().default(false)
|
|
235
362
|
};
|
|
236
363
|
function oneAssignee(stage) {
|
|
237
364
|
return stage.role === void 0 || stage.agent === void 0;
|
|
238
365
|
}
|
|
239
366
|
var ONE_ASSIGNEE = { message: "a stage names a role or an agent, not both" };
|
|
240
|
-
var PlanStageSchema =
|
|
241
|
-
var PlanEditStageSchema =
|
|
242
|
-
var PlanSchema =
|
|
243
|
-
var PlanEditSchema =
|
|
244
|
-
var ProjectSchema =
|
|
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({
|
|
245
372
|
slug: NameSchema,
|
|
246
|
-
name:
|
|
247
|
-
repo:
|
|
248
|
-
defaultBranch:
|
|
373
|
+
name: z4.string().min(1),
|
|
374
|
+
repo: z4.string().nullable(),
|
|
375
|
+
defaultBranch: z4.string().min(1),
|
|
249
376
|
/** The channels that take posts; an archived one moves to `archivedChannels`. */
|
|
250
|
-
channels:
|
|
251
|
-
archivedChannels:
|
|
252
|
-
members:
|
|
253
|
-
approvers:
|
|
254
|
-
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()),
|
|
255
382
|
createdAt: IsoDateTimeSchema,
|
|
256
383
|
onDone: CompletionEffectSchema.default("none"),
|
|
257
384
|
/** The runner the project lives on, which holds its repository; set on the project's first turn. */
|
|
258
385
|
runner: NameSchema.optional(),
|
|
259
386
|
/** Set once the project is archived: nobody belongs to it, and nothing more is posted, filed, or joined there. */
|
|
260
|
-
archived:
|
|
387
|
+
archived: z4.object({ at: IsoDateTimeSchema, by: NameSchema, reason: z4.string().min(1) }).optional()
|
|
261
388
|
});
|
|
262
|
-
var TaskStepSchema =
|
|
263
|
-
action:
|
|
389
|
+
var TaskStepSchema = z4.object({
|
|
390
|
+
action: z4.enum(["advanced", "returned", "abandoned", "landed", "reopened"]),
|
|
264
391
|
stage: StageIdSchema,
|
|
265
392
|
to: StageIdSchema.nullable()
|
|
266
393
|
});
|
|
267
|
-
var ProposalStepSchema =
|
|
268
|
-
var MessageStepSchema =
|
|
394
|
+
var ProposalStepSchema = z4.object({ action: z4.enum(["approved", "rejected"]) });
|
|
395
|
+
var MessageStepSchema = z4.union([TaskStepSchema, ProposalStepSchema]);
|
|
269
396
|
function isTaskStep(step) {
|
|
270
397
|
return "stage" in step;
|
|
271
398
|
}
|
|
272
|
-
var MessageFrontmatterSchema =
|
|
399
|
+
var MessageFrontmatterSchema = z4.object({
|
|
273
400
|
id: UlidSchema,
|
|
274
401
|
author: NameSchema,
|
|
275
402
|
channel: ChannelRefSchema,
|
|
@@ -281,18 +408,18 @@ var MessageFrontmatterSchema = z3.object({
|
|
|
281
408
|
/** On a post a task verb made in the task's thread, or a decision in a proposal's: the step it recorded. */
|
|
282
409
|
step: MessageStepSchema.optional(),
|
|
283
410
|
ts: IsoDateTimeSchema,
|
|
284
|
-
mentions:
|
|
411
|
+
mentions: z4.array(NameSchema)
|
|
285
412
|
});
|
|
286
|
-
var TaskStatusSchema =
|
|
287
|
-
var ThreadStateSchema =
|
|
288
|
-
var ThreadSubjectSchema =
|
|
289
|
-
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"]),
|
|
290
417
|
id: UlidSchema
|
|
291
418
|
});
|
|
292
|
-
var ThreadFrontmatterSchema =
|
|
419
|
+
var ThreadFrontmatterSchema = z4.object({
|
|
293
420
|
id: UlidSchema,
|
|
294
421
|
channel: ChannelRefSchema,
|
|
295
|
-
title:
|
|
422
|
+
title: z4.string().min(1).max(200),
|
|
296
423
|
subject: ThreadSubjectSchema.optional(),
|
|
297
424
|
state: ThreadStateSchema,
|
|
298
425
|
openedBy: NameSchema,
|
|
@@ -300,28 +427,28 @@ var ThreadFrontmatterSchema = z3.object({
|
|
|
300
427
|
closedBy: NameSchema.optional(),
|
|
301
428
|
closedAt: IsoDateTimeSchema.optional()
|
|
302
429
|
});
|
|
303
|
-
var StageSchema =
|
|
430
|
+
var StageSchema = z4.object({
|
|
304
431
|
id: StageIdSchema,
|
|
305
|
-
name:
|
|
432
|
+
name: z4.string().min(1),
|
|
306
433
|
role: NameSchema.optional(),
|
|
307
434
|
agent: NameSchema.optional(),
|
|
308
|
-
gate:
|
|
435
|
+
gate: z4.boolean().default(false),
|
|
309
436
|
/** Everyone who has held the stage, oldest first. */
|
|
310
|
-
holders:
|
|
437
|
+
holders: z4.array(NameSchema).default([]),
|
|
311
438
|
completedBy: NameSchema.optional(),
|
|
312
439
|
completedAt: IsoDateTimeSchema.optional()
|
|
313
440
|
});
|
|
314
|
-
var TaskReturnSchema =
|
|
441
|
+
var TaskReturnSchema = z4.object({
|
|
315
442
|
from: StageIdSchema,
|
|
316
443
|
by: NameSchema,
|
|
317
444
|
at: IsoDateTimeSchema
|
|
318
445
|
});
|
|
319
|
-
var TaskFrontmatterSchema =
|
|
446
|
+
var TaskFrontmatterSchema = z4.object({
|
|
320
447
|
id: UlidSchema,
|
|
321
448
|
project: NameSchema,
|
|
322
449
|
/** The channel of the project the task is filed in, where its thread hangs. */
|
|
323
450
|
channel: NameSchema.default("general"),
|
|
324
|
-
title:
|
|
451
|
+
title: z4.string().min(1),
|
|
325
452
|
status: TaskStatusSchema,
|
|
326
453
|
createdBy: NameSchema,
|
|
327
454
|
createdAt: IsoDateTimeSchema,
|
|
@@ -330,19 +457,19 @@ var TaskFrontmatterSchema = z3.object({
|
|
|
330
457
|
claimedBy: NameSchema.optional(),
|
|
331
458
|
leaseExpiresAt: IsoDateTimeSchema.optional(),
|
|
332
459
|
parentId: UlidSchema.optional(),
|
|
333
|
-
blockedBy:
|
|
334
|
-
requiredCapabilities:
|
|
335
|
-
stages:
|
|
460
|
+
blockedBy: z4.array(UlidSchema),
|
|
461
|
+
requiredCapabilities: z4.array(z4.string()),
|
|
462
|
+
stages: z4.array(StageSchema).min(1),
|
|
336
463
|
stage: StageIdSchema,
|
|
337
464
|
/** When the current stage last became current or lost its holder: the waiting clock. */
|
|
338
465
|
stageSince: IsoDateTimeSchema,
|
|
339
466
|
/** The highest stage number issued, so a removed stage's id is never reused. */
|
|
340
|
-
stageSeq:
|
|
467
|
+
stageSeq: z4.number().int().positive(),
|
|
341
468
|
onDone: CompletionEffectSchema.default("none"),
|
|
342
469
|
/** The pull request a `ghpr` task lands through. */
|
|
343
470
|
pullRequest: PullRequestLinkSchema.optional(),
|
|
344
471
|
/** The last stage is complete and the completion effect is running. */
|
|
345
|
-
completing:
|
|
472
|
+
completing: z4.boolean().default(false),
|
|
346
473
|
/** The latest send-back while its rework lasts: cleared once the task reaches that stage again. */
|
|
347
474
|
returned: TaskReturnSchema.optional()
|
|
348
475
|
});
|
|
@@ -366,79 +493,79 @@ function mayHoldStage(member, task2) {
|
|
|
366
493
|
}
|
|
367
494
|
return !(stage.gate && task2.stages.slice(0, index).some((s) => s.holders.includes(member.name)));
|
|
368
495
|
}
|
|
369
|
-
var ModelNameSchema =
|
|
370
|
-
var EffortSchema =
|
|
371
|
-
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({
|
|
372
499
|
id: EffortSchema,
|
|
373
|
-
description:
|
|
500
|
+
description: z4.string().default("")
|
|
374
501
|
});
|
|
375
|
-
var ModelOptionSchema =
|
|
502
|
+
var ModelOptionSchema = z4.object({
|
|
376
503
|
id: ModelNameSchema,
|
|
377
|
-
name:
|
|
378
|
-
description:
|
|
379
|
-
isDefault:
|
|
380
|
-
efforts:
|
|
504
|
+
name: z4.string(),
|
|
505
|
+
description: z4.string().default(""),
|
|
506
|
+
isDefault: z4.boolean().default(false),
|
|
507
|
+
efforts: z4.array(EffortOptionSchema).default([]),
|
|
381
508
|
defaultEffort: EffortSchema.optional()
|
|
382
509
|
});
|
|
383
|
-
var RunnerModelsSchema =
|
|
510
|
+
var RunnerModelsSchema = z4.object({
|
|
384
511
|
runner: NameSchema,
|
|
385
512
|
cli: CliKindSchema,
|
|
386
|
-
models:
|
|
513
|
+
models: z4.array(ModelOptionSchema)
|
|
387
514
|
});
|
|
388
|
-
var AgentStatusSchema =
|
|
389
|
-
var AgentSchema =
|
|
515
|
+
var AgentStatusSchema = z4.enum(["active", "retired"]);
|
|
516
|
+
var AgentSchema = z4.object({
|
|
390
517
|
name: NameSchema,
|
|
391
518
|
role: NameSchema,
|
|
392
519
|
cli: CliKindSchema.nullable(),
|
|
393
|
-
model:
|
|
520
|
+
model: z4.string().optional(),
|
|
394
521
|
/** The reasoning effort set for the agent's turns; the model's own default applies otherwise. */
|
|
395
|
-
effort:
|
|
522
|
+
effort: z4.string().optional(),
|
|
396
523
|
/** The runner preferred for the agent's turns where nothing else decides; any runner with its CLI otherwise. */
|
|
397
524
|
homeRunner: NameSchema.optional(),
|
|
398
|
-
memberships:
|
|
399
|
-
subscriptions:
|
|
525
|
+
memberships: z4.array(NameSchema),
|
|
526
|
+
subscriptions: z4.array(ChannelRefSchema),
|
|
400
527
|
status: AgentStatusSchema,
|
|
401
528
|
createdAt: IsoDateTimeSchema,
|
|
402
|
-
tokenHash:
|
|
529
|
+
tokenHash: z4.string().min(1),
|
|
403
530
|
retiredAt: IsoDateTimeSchema.optional(),
|
|
404
|
-
retiredReason:
|
|
531
|
+
retiredReason: z4.string().optional()
|
|
405
532
|
});
|
|
406
|
-
var MemberSchema =
|
|
533
|
+
var MemberSchema = z4.object({
|
|
407
534
|
name: NameSchema,
|
|
408
535
|
role: NameSchema,
|
|
409
536
|
cli: CliKindSchema.nullable(),
|
|
410
537
|
homeRunner: NameSchema.optional(),
|
|
411
538
|
status: AgentStatusSchema,
|
|
412
|
-
resident:
|
|
539
|
+
resident: z4.boolean().default(false),
|
|
413
540
|
/** The model configured on the record, if any; the CLI's own default applies otherwise. */
|
|
414
|
-
model:
|
|
541
|
+
model: z4.string().optional(),
|
|
415
542
|
/** The model the CLI reported on the citizen's last turn. */
|
|
416
|
-
lastModel:
|
|
543
|
+
lastModel: z4.string().optional(),
|
|
417
544
|
/** The reasoning effort configured on the record, if any; the model's own default applies otherwise. */
|
|
418
|
-
effort:
|
|
545
|
+
effort: z4.string().optional(),
|
|
419
546
|
/** The citizen's own skills, by name. */
|
|
420
|
-
skills:
|
|
421
|
-
memberships:
|
|
422
|
-
subscriptions:
|
|
423
|
-
claimsHeld:
|
|
424
|
-
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),
|
|
425
552
|
lastTurnAt: IsoDateTimeSchema.optional(),
|
|
426
|
-
lastTurnOutcome:
|
|
553
|
+
lastTurnOutcome: z4.string().optional(),
|
|
427
554
|
createdAt: IsoDateTimeSchema,
|
|
428
555
|
retiredAt: IsoDateTimeSchema.optional()
|
|
429
556
|
});
|
|
430
|
-
var RunnerOsSchema =
|
|
431
|
-
var RunnerSchema =
|
|
557
|
+
var RunnerOsSchema = z4.enum(["linux", "windows", "darwin"]);
|
|
558
|
+
var RunnerSchema = z4.object({
|
|
432
559
|
name: NameSchema,
|
|
433
560
|
os: RunnerOsSchema,
|
|
434
|
-
clis:
|
|
435
|
-
capabilities:
|
|
561
|
+
clis: z4.array(CliKindSchema),
|
|
562
|
+
capabilities: z4.array(z4.string()),
|
|
436
563
|
/** The release the runner reported when it last registered; absent until it has. */
|
|
437
|
-
version:
|
|
438
|
-
status:
|
|
564
|
+
version: z4.string().optional(),
|
|
565
|
+
status: z4.enum(["connected", "disconnected"]),
|
|
439
566
|
lastSeen: IsoDateTimeSchema.optional()
|
|
440
567
|
});
|
|
441
|
-
var ProposalKindSchema =
|
|
568
|
+
var ProposalKindSchema = z4.enum([
|
|
442
569
|
"role",
|
|
443
570
|
"member",
|
|
444
571
|
"channel",
|
|
@@ -447,43 +574,43 @@ var ProposalKindSchema = z3.enum([
|
|
|
447
574
|
"skill",
|
|
448
575
|
"archive"
|
|
449
576
|
]);
|
|
450
|
-
var ProposalStatusSchema =
|
|
577
|
+
var ProposalStatusSchema = z4.enum([
|
|
451
578
|
"proposed",
|
|
452
579
|
"approved",
|
|
453
580
|
"rejected",
|
|
454
581
|
"provisioned",
|
|
455
582
|
"retired"
|
|
456
583
|
]);
|
|
457
|
-
var MemberProposalSchema =
|
|
584
|
+
var MemberProposalSchema = z4.object({
|
|
458
585
|
name: NameSchema,
|
|
459
586
|
role: NameSchema,
|
|
460
587
|
cli: CliKindSchema,
|
|
461
|
-
model:
|
|
588
|
+
model: z4.string().optional(),
|
|
462
589
|
homeRunner: NameSchema.optional(),
|
|
463
|
-
memberships:
|
|
464
|
-
subscriptions:
|
|
465
|
-
seedInstructions:
|
|
590
|
+
memberships: z4.array(NameSchema).default([]),
|
|
591
|
+
subscriptions: z4.array(ChannelRefSchema).default([]),
|
|
592
|
+
seedInstructions: z4.string().optional()
|
|
466
593
|
});
|
|
467
|
-
var ChannelProposalSchema =
|
|
594
|
+
var ChannelProposalSchema = z4.object({
|
|
468
595
|
project: NameSchema.nullable().default(null),
|
|
469
596
|
name: NameSchema,
|
|
470
|
-
purpose:
|
|
597
|
+
purpose: z4.string().min(1)
|
|
471
598
|
});
|
|
472
|
-
var ReallocationProposalSchema =
|
|
473
|
-
description:
|
|
599
|
+
var ReallocationProposalSchema = z4.object({
|
|
600
|
+
description: z4.string().min(1)
|
|
474
601
|
});
|
|
475
|
-
var RetirementProposalSchema =
|
|
602
|
+
var RetirementProposalSchema = z4.object({
|
|
476
603
|
agent: NameSchema,
|
|
477
|
-
reason:
|
|
604
|
+
reason: z4.string().min(1)
|
|
478
605
|
});
|
|
479
|
-
var ArchiveProposalSchema =
|
|
606
|
+
var ArchiveProposalSchema = z4.object({
|
|
480
607
|
project: NameSchema,
|
|
481
|
-
reason:
|
|
608
|
+
reason: z4.string().min(1)
|
|
482
609
|
});
|
|
483
|
-
var SkillProposalSchema =
|
|
610
|
+
var SkillProposalSchema = z4.object({
|
|
484
611
|
name: NameSchema,
|
|
485
|
-
summary:
|
|
486
|
-
body:
|
|
612
|
+
summary: z4.string().min(1).max(200),
|
|
613
|
+
body: z4.string().min(1)
|
|
487
614
|
});
|
|
488
615
|
var ProposalCharterSchemas = {
|
|
489
616
|
role: RoleCharterSchema,
|
|
@@ -555,17 +682,17 @@ function describeCharter(kind, charter) {
|
|
|
555
682
|
}
|
|
556
683
|
return `${kind} ${JSON.stringify(charter)}`;
|
|
557
684
|
}
|
|
558
|
-
var SkillSchema =
|
|
685
|
+
var SkillSchema = z4.object({
|
|
559
686
|
name: NameSchema,
|
|
560
|
-
summary:
|
|
561
|
-
scope:
|
|
562
|
-
path:
|
|
687
|
+
summary: z4.string().default(""),
|
|
688
|
+
scope: z4.enum(["own", "society"]),
|
|
689
|
+
path: z4.string().min(1)
|
|
563
690
|
});
|
|
564
|
-
var HomeConflictSchema =
|
|
691
|
+
var HomeConflictSchema = z4.object({
|
|
565
692
|
/** The copy, relative to the home. */
|
|
566
|
-
path:
|
|
693
|
+
path: z4.string().min(1),
|
|
567
694
|
/** The file beside it, whose version the board kept. */
|
|
568
|
-
file:
|
|
695
|
+
file: z4.string().min(1),
|
|
569
696
|
/** When the copy reached the board. */
|
|
570
697
|
since: IsoDateTimeSchema
|
|
571
698
|
});
|
|
@@ -573,60 +700,60 @@ var CONFLICT_COPY = /^(.+)\.conflict-[^/.]+$/;
|
|
|
573
700
|
function conflictCopyOf(copy) {
|
|
574
701
|
return CONFLICT_COPY.exec(copy)?.[1] ?? null;
|
|
575
702
|
}
|
|
576
|
-
var CommitIdSchema =
|
|
577
|
-
var HomeFileChangeSchema =
|
|
578
|
-
path:
|
|
579
|
-
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"]),
|
|
580
707
|
/** Lines added and removed, or null for a binary file. */
|
|
581
|
-
added:
|
|
582
|
-
removed:
|
|
708
|
+
added: z4.number().int().nonnegative().nullable(),
|
|
709
|
+
removed: z4.number().int().nonnegative().nullable()
|
|
583
710
|
});
|
|
584
|
-
var HomeChangeSchema =
|
|
711
|
+
var HomeChangeSchema = z4.object({
|
|
585
712
|
commit: CommitIdSchema,
|
|
586
713
|
at: IsoDateTimeSchema,
|
|
587
|
-
kind:
|
|
588
|
-
author:
|
|
714
|
+
kind: z4.enum(["turn", "board", "merge"]),
|
|
715
|
+
author: z4.string(),
|
|
589
716
|
/** The turn that made it, from the runner's commit message. */
|
|
590
717
|
turnId: UlidSchema.optional(),
|
|
591
|
-
subject:
|
|
592
|
-
files:
|
|
718
|
+
subject: z4.string(),
|
|
719
|
+
files: z4.array(HomeFileChangeSchema)
|
|
593
720
|
});
|
|
594
|
-
var HomeHistorySchema =
|
|
595
|
-
changes:
|
|
721
|
+
var HomeHistorySchema = z4.object({
|
|
722
|
+
changes: z4.array(HomeChangeSchema),
|
|
596
723
|
/** Whether older commits remain past the ones read. */
|
|
597
|
-
more:
|
|
724
|
+
more: z4.boolean()
|
|
598
725
|
});
|
|
599
726
|
var HomeFileDiffSchema = HomeFileChangeSchema.pick({ path: true, status: true }).extend({
|
|
600
|
-
patch:
|
|
727
|
+
patch: z4.string().nullable(),
|
|
601
728
|
/** The patch was cut at its size limit. */
|
|
602
|
-
truncated:
|
|
729
|
+
truncated: z4.boolean()
|
|
603
730
|
});
|
|
604
|
-
var KnowledgeSchema =
|
|
731
|
+
var KnowledgeSchema = z4.object({
|
|
605
732
|
topic: NameSchema,
|
|
606
733
|
project: NameSchema.nullable(),
|
|
607
734
|
updatedBy: NameSchema,
|
|
608
735
|
updatedAt: IsoDateTimeSchema
|
|
609
736
|
});
|
|
610
|
-
var RemovedKnowledgeSchema =
|
|
737
|
+
var RemovedKnowledgeSchema = z4.object({
|
|
611
738
|
topic: NameSchema,
|
|
612
739
|
project: NameSchema.nullable(),
|
|
613
740
|
removedBy: NameSchema,
|
|
614
741
|
removedAt: IsoDateTimeSchema
|
|
615
742
|
});
|
|
616
|
-
var DashboardSchema =
|
|
743
|
+
var DashboardSchema = z4.object({
|
|
617
744
|
project: NameSchema,
|
|
618
|
-
revision:
|
|
745
|
+
revision: z4.number().int().min(0),
|
|
619
746
|
/** Absent until someone updates the dashboard the project began with. */
|
|
620
747
|
updatedBy: NameSchema.optional(),
|
|
621
748
|
updatedAt: IsoDateTimeSchema.optional(),
|
|
622
|
-
body:
|
|
749
|
+
body: z4.string()
|
|
623
750
|
});
|
|
624
|
-
var ProposalOriginSchema =
|
|
751
|
+
var ProposalOriginSchema = z4.object({
|
|
625
752
|
scope: NameSchema,
|
|
626
753
|
thread: UlidSchema.optional(),
|
|
627
754
|
channel: NameSchema.optional()
|
|
628
755
|
});
|
|
629
|
-
var ProposalFrontmatterSchema =
|
|
756
|
+
var ProposalFrontmatterSchema = z4.object({
|
|
630
757
|
id: UlidSchema,
|
|
631
758
|
kind: ProposalKindSchema,
|
|
632
759
|
proposedBy: NameSchema,
|
|
@@ -636,144 +763,179 @@ var ProposalFrontmatterSchema = z3.object({
|
|
|
636
763
|
filedFrom: ProposalOriginSchema.optional(),
|
|
637
764
|
decidedBy: NameSchema.optional(),
|
|
638
765
|
decidedAt: IsoDateTimeSchema.optional(),
|
|
639
|
-
reason:
|
|
640
|
-
charter:
|
|
766
|
+
reason: z4.string().optional(),
|
|
767
|
+
charter: z4.record(z4.string(), z4.unknown()),
|
|
641
768
|
/** What approval created, for kinds the board provisions: the agent, channel, role, or retirement. */
|
|
642
769
|
provisionedAt: IsoDateTimeSchema.optional(),
|
|
643
|
-
provision:
|
|
770
|
+
provision: z4.record(z4.string(), z4.unknown()).optional()
|
|
644
771
|
});
|
|
645
|
-
var DecisionSchema =
|
|
772
|
+
var DecisionSchema = z4.object({
|
|
646
773
|
id: UlidSchema,
|
|
647
774
|
proposalId: UlidSchema,
|
|
648
775
|
decidedBy: NameSchema,
|
|
649
|
-
outcome:
|
|
650
|
-
reason:
|
|
776
|
+
outcome: z4.enum(["approved", "rejected"]),
|
|
777
|
+
reason: z4.string().optional(),
|
|
651
778
|
ts: IsoDateTimeSchema
|
|
652
779
|
});
|
|
653
780
|
|
|
654
781
|
// packages/shared/dist/verbs.js
|
|
655
|
-
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);
|
|
656
786
|
var VerbInputs = {
|
|
657
|
-
post_message:
|
|
787
|
+
post_message: z5.object({
|
|
658
788
|
/** Required outside a thread; in one it may be left out, and must otherwise be the thread's. */
|
|
659
789
|
channel: ChannelRefSchema.optional(),
|
|
660
|
-
body:
|
|
790
|
+
body: z5.string().min(1),
|
|
661
791
|
thread_id: UlidSchema.optional()
|
|
662
792
|
}),
|
|
663
|
-
read_inbox:
|
|
793
|
+
read_inbox: z5.object({
|
|
664
794
|
since_cursor: UlidSchema.nullable().optional(),
|
|
665
|
-
limit:
|
|
666
|
-
advance:
|
|
795
|
+
limit: z5.number().int().positive().max(200).default(50),
|
|
796
|
+
advance: z5.boolean().default(true)
|
|
667
797
|
}),
|
|
668
|
-
search:
|
|
669
|
-
query:
|
|
798
|
+
search: z5.object({
|
|
799
|
+
query: z5.string().min(1),
|
|
670
800
|
project: NameSchema.optional(),
|
|
671
801
|
channel: ChannelRefSchema.optional(),
|
|
672
|
-
limit:
|
|
802
|
+
limit: z5.number().int().positive().max(100).default(20)
|
|
673
803
|
}),
|
|
674
804
|
/** On a task or a proposal, whose id the thread takes, or on a channel with a title. */
|
|
675
|
-
open_thread:
|
|
805
|
+
open_thread: z5.object({
|
|
676
806
|
task_id: UlidSchema.optional(),
|
|
677
807
|
proposal_id: UlidSchema.optional(),
|
|
678
808
|
channel: ChannelRefSchema.optional(),
|
|
679
|
-
title:
|
|
809
|
+
title: z5.string().min(1).max(200).optional()
|
|
680
810
|
}),
|
|
681
|
-
close_thread:
|
|
682
|
-
create_task:
|
|
811
|
+
close_thread: z5.object({ thread_id: UlidSchema, summary: z5.string().min(1) }),
|
|
812
|
+
create_task: z5.object({
|
|
683
813
|
project: NameSchema,
|
|
684
|
-
title:
|
|
685
|
-
body:
|
|
814
|
+
title: z5.string().min(1).max(200),
|
|
815
|
+
body: z5.string().default(""),
|
|
686
816
|
parent_id: UlidSchema.optional(),
|
|
687
|
-
required_capabilities:
|
|
817
|
+
required_capabilities: z5.array(z5.string()).default([]),
|
|
688
818
|
/** The plan; without it the task gets one stage, work, that anyone in the project may take. */
|
|
689
|
-
stages:
|
|
819
|
+
stages: z5.array(PlanStageSchema).min(1).optional(),
|
|
690
820
|
/** A channel of the project the task belongs to, such as a release's; its thread opens there. */
|
|
691
821
|
channel: NameSchema.default("general")
|
|
692
822
|
}),
|
|
693
|
-
claim_task:
|
|
694
|
-
release_task:
|
|
695
|
-
update_task:
|
|
823
|
+
claim_task: z5.object({ task_id: UlidSchema }),
|
|
824
|
+
release_task: z5.object({ task_id: UlidSchema }),
|
|
825
|
+
update_task: z5.object({
|
|
696
826
|
task_id: UlidSchema,
|
|
697
827
|
/** Move the task back to this earlier stage. */
|
|
698
828
|
stage: StageIdSchema.optional(),
|
|
699
|
-
status:
|
|
829
|
+
status: z5.enum(["abandoned"]).optional(),
|
|
700
830
|
/** Posted to the task's thread as the caller, marked with the step when the task moved. */
|
|
701
|
-
note:
|
|
702
|
-
blocked_by:
|
|
831
|
+
note: z5.string().min(1).optional(),
|
|
832
|
+
blocked_by: z5.array(UlidSchema).optional(),
|
|
703
833
|
/**
|
|
704
834
|
* The pull request a `ghpr` task lands through, on its project's repository, with the merge
|
|
705
835
|
* commit's subject and body; null unlinks it.
|
|
706
836
|
*/
|
|
707
|
-
pull_request:
|
|
837
|
+
pull_request: z5.object({
|
|
708
838
|
url: PullRequestUrlSchema,
|
|
709
839
|
merge_subject: MergeSubjectSchema.optional(),
|
|
710
|
-
merge_body:
|
|
840
|
+
merge_body: z5.string().max(2e4).optional()
|
|
711
841
|
}).nullable().optional()
|
|
712
842
|
}),
|
|
713
|
-
get_task:
|
|
714
|
-
subscribe:
|
|
715
|
-
unsubscribe:
|
|
716
|
-
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({
|
|
717
847
|
kind: ProposalKindSchema,
|
|
718
|
-
charter:
|
|
719
|
-
rationale:
|
|
848
|
+
charter: z5.record(z5.string(), z5.unknown()),
|
|
849
|
+
rationale: z5.string().default("")
|
|
720
850
|
}),
|
|
721
|
-
approve:
|
|
722
|
-
reject:
|
|
723
|
-
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({
|
|
724
854
|
slug: NameSchema,
|
|
725
|
-
name:
|
|
726
|
-
repo:
|
|
855
|
+
name: z5.string().min(1).optional(),
|
|
856
|
+
repo: z5.string().min(1).nullable().default(null),
|
|
727
857
|
default_branch: DefaultBranchSchema.default("main"),
|
|
728
858
|
on_done: CompletionEffectSchema.optional()
|
|
729
859
|
}),
|
|
730
|
-
join_project:
|
|
731
|
-
leave_project:
|
|
732
|
-
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({
|
|
733
863
|
/** A project you belong to, or null for society knowledge, which the steward and the user curate. */
|
|
734
864
|
project: NameSchema.nullable().default(null),
|
|
735
865
|
topic: NameSchema,
|
|
736
|
-
body:
|
|
866
|
+
body: z5.string().min(1)
|
|
737
867
|
}),
|
|
738
|
-
remove_knowledge:
|
|
868
|
+
remove_knowledge: z5.object({
|
|
739
869
|
/** As for write_knowledge: a project you belong to, or null for society knowledge. */
|
|
740
870
|
project: NameSchema.nullable().default(null),
|
|
741
871
|
topic: NameSchema
|
|
742
872
|
}),
|
|
743
|
-
plan_task:
|
|
873
|
+
plan_task: z5.object({
|
|
744
874
|
task_id: UlidSchema,
|
|
745
|
-
stages:
|
|
875
|
+
stages: z5.array(PlanEditStageSchema),
|
|
746
876
|
on_done: CompletionEffectSchema.optional()
|
|
747
877
|
}),
|
|
748
878
|
/** The note is posted to the task's thread as the caller, marked with the step. */
|
|
749
|
-
advance_task:
|
|
750
|
-
configure_project:
|
|
879
|
+
advance_task: z5.object({ task_id: UlidSchema, note: z5.string().min(1).optional() }),
|
|
880
|
+
configure_project: z5.object({
|
|
751
881
|
project: NameSchema,
|
|
752
882
|
on_done: CompletionEffectSchema.optional(),
|
|
753
883
|
/** A new display name; the slug, which everything refers to the project by, never changes. */
|
|
754
|
-
name:
|
|
884
|
+
name: z5.string().trim().min(1).max(120).optional(),
|
|
755
885
|
/** The branch tasks start from and land on from now; tasks in play keep their branches. */
|
|
756
886
|
default_branch: DefaultBranchSchema.optional()
|
|
757
887
|
}),
|
|
758
|
-
archive_project:
|
|
759
|
-
create_channel:
|
|
888
|
+
archive_project: z5.object({ project: NameSchema, reason: z5.string().min(1) }),
|
|
889
|
+
create_channel: z5.object({
|
|
760
890
|
/** A project, or null for a channel of the society. */
|
|
761
891
|
project: NameSchema.nullable().default(null),
|
|
762
892
|
name: NameSchema,
|
|
763
|
-
purpose:
|
|
893
|
+
purpose: z5.string().min(1)
|
|
764
894
|
}),
|
|
765
|
-
archive_channel:
|
|
766
|
-
update_dashboard:
|
|
895
|
+
archive_channel: z5.object({ channel: ChannelRefSchema, reason: z5.string().min(1) }),
|
|
896
|
+
update_dashboard: z5.object({
|
|
767
897
|
project: NameSchema,
|
|
768
898
|
/** The whole dashboard as markdown; the board keeps its frontmatter. */
|
|
769
|
-
body:
|
|
899
|
+
body: z5.string().min(1),
|
|
770
900
|
/** The `revision` in the frontmatter of the dashboard you read, 0 when it names none. */
|
|
771
|
-
revision:
|
|
772
|
-
})
|
|
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) })
|
|
773
935
|
};
|
|
774
936
|
|
|
775
937
|
// packages/shared/dist/events.js
|
|
776
|
-
import { z as
|
|
938
|
+
import { z as z6 } from "zod";
|
|
777
939
|
var BOARD_EVENT_TYPES = [
|
|
778
940
|
"society.initialized",
|
|
779
941
|
"project.added",
|
|
@@ -828,30 +990,36 @@ var BOARD_EVENT_TYPES = [
|
|
|
828
990
|
"merge.failed",
|
|
829
991
|
"pull_request.closed",
|
|
830
992
|
"pull_request.close_failed",
|
|
831
|
-
"workspace.leftovers"
|
|
993
|
+
"workspace.leftovers",
|
|
994
|
+
"society.configured",
|
|
995
|
+
"cron.created",
|
|
996
|
+
"cron.updated",
|
|
997
|
+
"cron.fired",
|
|
998
|
+
"cron.skipped",
|
|
999
|
+
"cron.ended"
|
|
832
1000
|
];
|
|
833
|
-
var BoardEventTypeSchema =
|
|
834
|
-
var BoardEventSchema =
|
|
1001
|
+
var BoardEventTypeSchema = z6.enum(BOARD_EVENT_TYPES);
|
|
1002
|
+
var BoardEventSchema = z6.object({
|
|
835
1003
|
id: UlidSchema,
|
|
836
1004
|
ts: IsoDateTimeSchema,
|
|
837
1005
|
type: BoardEventTypeSchema,
|
|
838
1006
|
actor: NameSchema,
|
|
839
|
-
payload:
|
|
840
|
-
});
|
|
841
|
-
var UsageSchema =
|
|
842
|
-
inputTokens:
|
|
843
|
-
outputTokens:
|
|
844
|
-
cacheReadTokens:
|
|
845
|
-
cacheWriteTokens:
|
|
846
|
-
});
|
|
847
|
-
var TurnStatusSchema =
|
|
848
|
-
summary:
|
|
849
|
-
claimsHeld:
|
|
850
|
-
blockedOn:
|
|
851
|
-
needsUserDecision:
|
|
852
|
-
memoryUpdated:
|
|
853
|
-
});
|
|
854
|
-
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([
|
|
855
1023
|
"completed",
|
|
856
1024
|
"timeout",
|
|
857
1025
|
"error",
|
|
@@ -859,50 +1027,50 @@ var TurnExitReasonSchema = z5.enum([
|
|
|
859
1027
|
"blocked",
|
|
860
1028
|
"stopped"
|
|
861
1029
|
]);
|
|
862
|
-
var AgentEventSchema =
|
|
863
|
-
|
|
864
|
-
type:
|
|
1030
|
+
var AgentEventSchema = z6.discriminatedUnion("type", [
|
|
1031
|
+
z6.object({
|
|
1032
|
+
type: z6.literal("turn_started"),
|
|
865
1033
|
agent: NameSchema,
|
|
866
|
-
session:
|
|
1034
|
+
session: z6.string(),
|
|
867
1035
|
runner: NameSchema,
|
|
868
1036
|
/** The model the CLI reports running, when it reports one. */
|
|
869
|
-
model:
|
|
1037
|
+
model: z6.string().optional()
|
|
870
1038
|
}),
|
|
871
1039
|
/**
|
|
872
1040
|
* The CLI took a steer into the running turn. Adapters name the steer; the server adds the
|
|
873
1041
|
* messages it delivered and the text the CLI was given.
|
|
874
1042
|
*/
|
|
875
|
-
|
|
876
|
-
type:
|
|
877
|
-
steer:
|
|
878
|
-
messages:
|
|
879
|
-
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()
|
|
880
1048
|
}),
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
type:
|
|
885
|
-
name:
|
|
886
|
-
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(),
|
|
887
1055
|
/** What the tool returned, cut to `OUTPUT_LIMIT` characters by `capOutput`. */
|
|
888
|
-
output:
|
|
1056
|
+
output: z6.string().optional()
|
|
889
1057
|
}),
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
type:
|
|
1058
|
+
z6.object({ type: z6.literal("approval_requested"), kind: z6.string(), detail: z6.unknown() }),
|
|
1059
|
+
z6.object({
|
|
1060
|
+
type: z6.literal("turn_completed"),
|
|
893
1061
|
usage: UsageSchema,
|
|
894
|
-
costUsd:
|
|
1062
|
+
costUsd: z6.number().nonnegative(),
|
|
895
1063
|
status: TurnStatusSchema.nullable(),
|
|
896
1064
|
exitReason: TurnExitReasonSchema
|
|
897
1065
|
}),
|
|
898
|
-
|
|
1066
|
+
z6.object({ type: z6.literal("error"), message: z6.string() })
|
|
899
1067
|
]);
|
|
900
|
-
var TranscriptEntrySchema =
|
|
1068
|
+
var TranscriptEntrySchema = z6.object({
|
|
901
1069
|
ts: IsoDateTimeSchema,
|
|
902
1070
|
event: AgentEventSchema
|
|
903
1071
|
});
|
|
904
|
-
var LiveTurnEventSchema =
|
|
905
|
-
seq:
|
|
1072
|
+
var LiveTurnEventSchema = z6.object({
|
|
1073
|
+
seq: z6.number().int().positive(),
|
|
906
1074
|
ts: IsoDateTimeSchema,
|
|
907
1075
|
agent: NameSchema,
|
|
908
1076
|
project: NameSchema,
|
|
@@ -914,97 +1082,97 @@ var LiveTurnEventSchema = z5.object({
|
|
|
914
1082
|
});
|
|
915
1083
|
|
|
916
1084
|
// packages/shared/dist/turns.js
|
|
917
|
-
import { z as
|
|
1085
|
+
import { z as z8 } from "zod";
|
|
918
1086
|
|
|
919
1087
|
// packages/shared/dist/runner.js
|
|
920
|
-
import { z as
|
|
1088
|
+
import { z as z7 } from "zod";
|
|
921
1089
|
var HOME_SCRATCH = "scratch";
|
|
922
1090
|
var HOME_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
923
|
-
var RunnerHelloSchema =
|
|
924
|
-
protocol:
|
|
925
|
-
version:
|
|
1091
|
+
var RunnerHelloSchema = z7.object({
|
|
1092
|
+
protocol: z7.number().int().positive(),
|
|
1093
|
+
version: z7.string().min(1),
|
|
926
1094
|
os: RunnerOsSchema,
|
|
927
|
-
clis:
|
|
1095
|
+
clis: z7.array(CliKindSchema),
|
|
928
1096
|
/** The CLIs whose adapter can keep a session warm between turns. */
|
|
929
|
-
residentClis:
|
|
1097
|
+
residentClis: z7.array(CliKindSchema).default([]),
|
|
930
1098
|
/** The CLIs whose adapter can take input into a running turn; the server steers only these. */
|
|
931
|
-
steerableClis:
|
|
1099
|
+
steerableClis: z7.array(CliKindSchema).default([]),
|
|
932
1100
|
/** The CLIs whose adapter can stop a running turn; the server stops only these. */
|
|
933
|
-
stoppableClis:
|
|
1101
|
+
stoppableClis: z7.array(CliKindSchema).default([]),
|
|
934
1102
|
/** Whether the runner answers file reads on its projects' branches; one from before never does. */
|
|
935
|
-
branchReads:
|
|
1103
|
+
branchReads: z7.boolean().default(false),
|
|
936
1104
|
/** Whether the runner lists what a branch changed; one from before never does. */
|
|
937
|
-
branchChanges:
|
|
938
|
-
capabilities:
|
|
1105
|
+
branchChanges: z7.boolean().default(false),
|
|
1106
|
+
capabilities: z7.array(z7.string()).default([]),
|
|
939
1107
|
/** Turns the runner runs at once, or null for no limit. */
|
|
940
|
-
slots:
|
|
1108
|
+
slots: z7.number().int().positive().nullable(),
|
|
941
1109
|
/** Turns the runner is still running, when it registers again; the server fails the rest of its turns. */
|
|
942
|
-
turns:
|
|
1110
|
+
turns: z7.array(UlidSchema).default([])
|
|
943
1111
|
});
|
|
944
|
-
var RunnerWelcomeSchema =
|
|
945
|
-
var RunnerEnrollRequestSchema =
|
|
946
|
-
protocol:
|
|
947
|
-
version:
|
|
948
|
-
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),
|
|
949
1117
|
os: RunnerOsSchema,
|
|
950
|
-
clis:
|
|
951
|
-
capabilities:
|
|
1118
|
+
clis: z7.array(CliKindSchema).max(8),
|
|
1119
|
+
capabilities: z7.array(z7.string().min(1).max(64)).max(32).default([])
|
|
952
1120
|
});
|
|
953
|
-
var RunnerEnrollmentSchema =
|
|
954
|
-
deviceCode:
|
|
955
|
-
userCode:
|
|
1121
|
+
var RunnerEnrollmentSchema = z7.object({
|
|
1122
|
+
deviceCode: z7.string().min(1),
|
|
1123
|
+
userCode: z7.string().min(1),
|
|
956
1124
|
expiresAt: IsoDateTimeSchema,
|
|
957
|
-
intervalMs:
|
|
1125
|
+
intervalMs: z7.number().int().positive()
|
|
958
1126
|
});
|
|
959
|
-
var RunnerEnrollPollSchema =
|
|
960
|
-
var RunnerEnrollStatusSchema =
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
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") })
|
|
964
1132
|
]);
|
|
965
|
-
var PendingEnrollmentSchema =
|
|
966
|
-
userCode:
|
|
967
|
-
hostname:
|
|
1133
|
+
var PendingEnrollmentSchema = z7.object({
|
|
1134
|
+
userCode: z7.string().min(1),
|
|
1135
|
+
hostname: z7.string(),
|
|
968
1136
|
os: RunnerOsSchema,
|
|
969
|
-
clis:
|
|
970
|
-
capabilities:
|
|
971
|
-
version:
|
|
1137
|
+
clis: z7.array(CliKindSchema),
|
|
1138
|
+
capabilities: z7.array(z7.string()),
|
|
1139
|
+
version: z7.string(),
|
|
972
1140
|
requestedAt: IsoDateTimeSchema,
|
|
973
1141
|
expiresAt: IsoDateTimeSchema
|
|
974
1142
|
});
|
|
975
|
-
var ProjectRepoSchema =
|
|
1143
|
+
var ProjectRepoSchema = z7.object({
|
|
976
1144
|
slug: NameSchema,
|
|
977
|
-
origin:
|
|
978
|
-
defaultBranch:
|
|
1145
|
+
origin: z7.string().nullable(),
|
|
1146
|
+
defaultBranch: z7.string().min(1),
|
|
979
1147
|
/**
|
|
980
1148
|
* The board lands finished tasks on the default branch, itself or through their pull requests,
|
|
981
1149
|
* so the runner lets nothing else move it.
|
|
982
1150
|
*/
|
|
983
|
-
boardLands:
|
|
1151
|
+
boardLands: z7.boolean()
|
|
984
1152
|
});
|
|
985
|
-
var TurnWorkspaceSchema =
|
|
986
|
-
|
|
987
|
-
kind:
|
|
1153
|
+
var TurnWorkspaceSchema = z7.discriminatedUnion("kind", [
|
|
1154
|
+
z7.object({
|
|
1155
|
+
kind: z7.literal("home"),
|
|
988
1156
|
thread: UlidSchema.optional(),
|
|
989
1157
|
channel: NameSchema.optional()
|
|
990
1158
|
}),
|
|
991
|
-
|
|
992
|
-
kind:
|
|
1159
|
+
z7.object({
|
|
1160
|
+
kind: z7.literal("project"),
|
|
993
1161
|
repo: ProjectRepoSchema,
|
|
994
|
-
branches:
|
|
1162
|
+
branches: z7.array(z7.string().min(1)),
|
|
995
1163
|
thread: UlidSchema.optional(),
|
|
996
1164
|
channel: NameSchema.optional()
|
|
997
1165
|
}),
|
|
998
|
-
|
|
1166
|
+
z7.object({ kind: z7.literal("task"), repo: ProjectRepoSchema, taskId: UlidSchema })
|
|
999
1167
|
]);
|
|
1000
|
-
var TurnJobSchema =
|
|
1168
|
+
var TurnJobSchema = z7.object({
|
|
1001
1169
|
turnId: UlidSchema,
|
|
1002
1170
|
agent: NameSchema,
|
|
1003
1171
|
role: NameSchema,
|
|
1004
1172
|
cli: CliKindSchema,
|
|
1005
|
-
model:
|
|
1173
|
+
model: z7.string().optional(),
|
|
1006
1174
|
/** The reasoning effort set on the agent; absent for the model's own default. */
|
|
1007
|
-
effort:
|
|
1175
|
+
effort: z7.string().optional(),
|
|
1008
1176
|
/** A project slug, or the society scope. */
|
|
1009
1177
|
scope: NameSchema,
|
|
1010
1178
|
/** The thread whose conversation the turn is in; absent for the home conversation. */
|
|
@@ -1015,167 +1183,167 @@ var TurnJobSchema = z6.object({
|
|
|
1015
1183
|
* The session to resume, or null to start one: the CLI's adapter picks a fresh session's id, and
|
|
1016
1184
|
* the outcome reports it.
|
|
1017
1185
|
*/
|
|
1018
|
-
session:
|
|
1186
|
+
session: z7.string().min(1).nullable(),
|
|
1019
1187
|
/** The running total the session reported after its last turn, which a resumed session starts from. */
|
|
1020
|
-
costSoFarUsd:
|
|
1188
|
+
costSoFarUsd: z7.number().nonnegative(),
|
|
1021
1189
|
/** The rendered charter, norms, memory core, and skills index, with `PATH_TOKENS` for places. */
|
|
1022
|
-
instructions:
|
|
1023
|
-
prompt:
|
|
1024
|
-
mcp:
|
|
1025
|
-
limits:
|
|
1026
|
-
timeoutMs:
|
|
1027
|
-
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()
|
|
1028
1196
|
}),
|
|
1029
1197
|
/** Present when the runner keeps the conversation's session warm between turns, under `key`. */
|
|
1030
|
-
resident:
|
|
1198
|
+
resident: z7.object({ key: z7.string().min(1), idleMs: z7.number().int().positive() }).optional(),
|
|
1031
1199
|
workspace: TurnWorkspaceSchema
|
|
1032
1200
|
});
|
|
1033
|
-
var TurnWorkSchema =
|
|
1034
|
-
var TurnEventsSchema =
|
|
1035
|
-
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({
|
|
1036
1204
|
exitReason: TurnExitReasonSchema,
|
|
1037
1205
|
status: TurnStatusSchema.nullable(),
|
|
1038
|
-
error:
|
|
1206
|
+
error: z7.string().nullable(),
|
|
1039
1207
|
usage: UsageSchema,
|
|
1040
|
-
costUsd:
|
|
1208
|
+
costUsd: z7.number().nonnegative(),
|
|
1041
1209
|
/** The session's running total after this turn, for CLIs that report one. */
|
|
1042
|
-
sessionCostUsd:
|
|
1210
|
+
sessionCostUsd: z7.number().nonnegative().optional(),
|
|
1043
1211
|
/** The session the CLI used, which differs from the job's for CLIs that assign their own ids. */
|
|
1044
|
-
session:
|
|
1212
|
+
session: z7.string().min(1),
|
|
1045
1213
|
/** The model the CLI reported, when it reported one. */
|
|
1046
|
-
model:
|
|
1214
|
+
model: z7.string().nullable(),
|
|
1047
1215
|
work: TurnWorkSchema.nullable(),
|
|
1048
1216
|
/**
|
|
1049
1217
|
* Whether the turn's own workspace, a thread's, a channel's, or a task's, holds work on no
|
|
1050
1218
|
* branch after the hand-back: uncommitted changes, or commits no branch contains.
|
|
1051
1219
|
*/
|
|
1052
|
-
leftovers:
|
|
1220
|
+
leftovers: z7.boolean().default(false)
|
|
1053
1221
|
});
|
|
1054
|
-
var TurnAckSchema =
|
|
1055
|
-
var LandRequestSchema =
|
|
1222
|
+
var TurnAckSchema = z7.object({ dropWorkspace: z7.boolean() });
|
|
1223
|
+
var LandRequestSchema = z7.object({
|
|
1056
1224
|
repo: ProjectRepoSchema,
|
|
1057
|
-
branch:
|
|
1225
|
+
branch: z7.string().min(1),
|
|
1058
1226
|
/**
|
|
1059
1227
|
* For a `ghpr` task, the pull request to merge in place of the branch, which must hold the
|
|
1060
1228
|
* branch's files, and the merge commit's subject and body, the pull request's title and an
|
|
1061
1229
|
* empty body when left out.
|
|
1062
1230
|
*/
|
|
1063
|
-
pullRequest:
|
|
1064
|
-
url:
|
|
1065
|
-
subject:
|
|
1066
|
-
body:
|
|
1231
|
+
pullRequest: z7.object({
|
|
1232
|
+
url: z7.string().min(1),
|
|
1233
|
+
subject: z7.string().min(1).optional(),
|
|
1234
|
+
body: z7.string().optional()
|
|
1067
1235
|
}).optional()
|
|
1068
1236
|
});
|
|
1069
|
-
var CloseRequestSchema =
|
|
1237
|
+
var CloseRequestSchema = z7.object({
|
|
1070
1238
|
repo: ProjectRepoSchema,
|
|
1071
|
-
url:
|
|
1072
|
-
comment:
|
|
1239
|
+
url: z7.string().min(1),
|
|
1240
|
+
comment: z7.string().min(1)
|
|
1073
1241
|
});
|
|
1074
|
-
var MergeOutcomeSchema =
|
|
1075
|
-
var ModelListSchema =
|
|
1242
|
+
var MergeOutcomeSchema = z7.object({ ok: z7.boolean(), detail: z7.string() });
|
|
1243
|
+
var ModelListSchema = z7.array(ModelOptionSchema);
|
|
1076
1244
|
var BRANCH_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
1077
|
-
var BranchPathSchema =
|
|
1078
|
-
var BranchNameSchema =
|
|
1079
|
-
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({
|
|
1080
1248
|
repo: ProjectRepoSchema,
|
|
1081
1249
|
branch: BranchNameSchema,
|
|
1082
1250
|
path: BranchPathSchema
|
|
1083
1251
|
});
|
|
1084
|
-
var BranchCommitSchema =
|
|
1085
|
-
id:
|
|
1252
|
+
var BranchCommitSchema = z7.object({
|
|
1253
|
+
id: z7.string().min(1),
|
|
1086
1254
|
at: IsoDateTimeSchema,
|
|
1087
|
-
author:
|
|
1255
|
+
author: z7.string()
|
|
1088
1256
|
});
|
|
1089
|
-
var BranchFileSchema =
|
|
1090
|
-
|
|
1091
|
-
kind:
|
|
1257
|
+
var BranchFileSchema = z7.discriminatedUnion("kind", [
|
|
1258
|
+
z7.object({
|
|
1259
|
+
kind: z7.literal("file"),
|
|
1092
1260
|
path: BranchPathSchema,
|
|
1093
1261
|
commit: BranchCommitSchema,
|
|
1094
|
-
size:
|
|
1095
|
-
content:
|
|
1262
|
+
size: z7.number().int().nonnegative(),
|
|
1263
|
+
content: z7.string().nullable()
|
|
1096
1264
|
}),
|
|
1097
|
-
|
|
1098
|
-
kind:
|
|
1265
|
+
z7.object({
|
|
1266
|
+
kind: z7.literal("dir"),
|
|
1099
1267
|
path: BranchPathSchema,
|
|
1100
1268
|
commit: BranchCommitSchema,
|
|
1101
|
-
entries:
|
|
1102
|
-
name:
|
|
1103
|
-
kind:
|
|
1104
|
-
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()
|
|
1105
1273
|
}))
|
|
1106
1274
|
})
|
|
1107
1275
|
]);
|
|
1108
|
-
var BranchChangesReadSchema =
|
|
1276
|
+
var BranchChangesReadSchema = z7.object({
|
|
1109
1277
|
repo: ProjectRepoSchema,
|
|
1110
1278
|
branch: BranchNameSchema
|
|
1111
1279
|
});
|
|
1112
|
-
var BranchFileChangeSchema =
|
|
1113
|
-
path:
|
|
1114
|
-
status:
|
|
1280
|
+
var BranchFileChangeSchema = z7.object({
|
|
1281
|
+
path: z7.string().min(1),
|
|
1282
|
+
status: z7.enum(["added", "modified", "deleted"]),
|
|
1115
1283
|
/** Lines added and removed, or null for a binary file. */
|
|
1116
|
-
added:
|
|
1117
|
-
removed:
|
|
1284
|
+
added: z7.number().int().nonnegative().nullable(),
|
|
1285
|
+
removed: z7.number().int().nonnegative().nullable(),
|
|
1118
1286
|
/** Absent when the file changed only through a merge of another branch. */
|
|
1119
|
-
lastChange:
|
|
1287
|
+
lastChange: z7.object({ author: z7.string(), at: IsoDateTimeSchema }).optional()
|
|
1120
1288
|
});
|
|
1121
|
-
var BranchChangesSchema =
|
|
1289
|
+
var BranchChangesSchema = z7.object({
|
|
1122
1290
|
head: BranchCommitSchema,
|
|
1123
|
-
files:
|
|
1124
|
-
total:
|
|
1291
|
+
files: z7.array(BranchFileChangeSchema),
|
|
1292
|
+
total: z7.number().int().nonnegative()
|
|
1125
1293
|
});
|
|
1126
|
-
var WorkspaceConversationSchema =
|
|
1127
|
-
|
|
1128
|
-
|
|
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 })
|
|
1129
1297
|
]);
|
|
1130
|
-
var HeldWorkspaceSchema =
|
|
1298
|
+
var HeldWorkspaceSchema = z7.object({
|
|
1131
1299
|
agent: NameSchema,
|
|
1132
1300
|
scope: NameSchema,
|
|
1133
1301
|
conversation: WorkspaceConversationSchema
|
|
1134
1302
|
});
|
|
1135
|
-
var HeldWorkspacesSchema =
|
|
1136
|
-
var SweepResultSchema =
|
|
1137
|
-
var TurnSteerSchema =
|
|
1138
|
-
var RunnerMessageSchema =
|
|
1139
|
-
|
|
1140
|
-
|
|
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 }),
|
|
1141
1309
|
/** Answered with a `MergeOutcome`. */
|
|
1142
|
-
|
|
1143
|
-
|
|
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 }),
|
|
1144
1312
|
/** Answered with a `BranchFile`, or null when the branch or the path does not exist. */
|
|
1145
|
-
|
|
1313
|
+
z7.object({ type: z7.literal("file"), request: z7.string().min(1), read: BranchReadSchema }),
|
|
1146
1314
|
/** Answered with `BranchChanges`, or null when the repository or the branch does not exist. */
|
|
1147
|
-
|
|
1148
|
-
type:
|
|
1149
|
-
request:
|
|
1315
|
+
z7.object({
|
|
1316
|
+
type: z7.literal("changes"),
|
|
1317
|
+
request: z7.string().min(1),
|
|
1150
1318
|
read: BranchChangesReadSchema
|
|
1151
1319
|
}),
|
|
1152
1320
|
/** Answered with whether the CLI accepted it; that it took it comes as a `steered` event. */
|
|
1153
|
-
|
|
1154
|
-
type:
|
|
1155
|
-
request:
|
|
1321
|
+
z7.object({
|
|
1322
|
+
type: z7.literal("steer"),
|
|
1323
|
+
request: z7.string().min(1),
|
|
1156
1324
|
turnId: UlidSchema,
|
|
1157
1325
|
steer: TurnSteerSchema
|
|
1158
1326
|
}),
|
|
1159
1327
|
/** Answered with whether the turn was there to stop; it ends `stopped`. */
|
|
1160
|
-
|
|
1328
|
+
z7.object({ type: z7.literal("stop"), request: z7.string().min(1), turnId: UlidSchema }),
|
|
1161
1329
|
/**
|
|
1162
1330
|
* A conversation ended: the runner removes every citizen's workspace for it but those a turn
|
|
1163
1331
|
* still uses, and answers with a `SweepResult` naming those it kept for holding work on no branch.
|
|
1164
1332
|
*/
|
|
1165
|
-
|
|
1166
|
-
type:
|
|
1167
|
-
request:
|
|
1333
|
+
z7.object({
|
|
1334
|
+
type: z7.literal("sweep"),
|
|
1335
|
+
request: z7.string().min(1),
|
|
1168
1336
|
conversation: WorkspaceConversationSchema
|
|
1169
1337
|
})
|
|
1170
1338
|
]);
|
|
1171
|
-
var RunnerAnswerSchema =
|
|
1172
|
-
|
|
1173
|
-
|
|
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() })
|
|
1174
1342
|
]);
|
|
1175
|
-
var WarmSessionsSchema =
|
|
1176
|
-
var FileManifestSchema =
|
|
1177
|
-
var FileReadSchema =
|
|
1178
|
-
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()) });
|
|
1179
1347
|
function isSafeRelativePath(file) {
|
|
1180
1348
|
if (file.length === 0 || file.startsWith("/") || file.includes("\\") || file.includes("\0")) {
|
|
1181
1349
|
return false;
|
|
@@ -1184,7 +1352,7 @@ function isSafeRelativePath(file) {
|
|
|
1184
1352
|
}
|
|
1185
1353
|
|
|
1186
1354
|
// packages/shared/dist/turns.js
|
|
1187
|
-
var TriggerKindSchema =
|
|
1355
|
+
var TriggerKindSchema = z8.enum([
|
|
1188
1356
|
"mention",
|
|
1189
1357
|
"stage",
|
|
1190
1358
|
"task_done",
|
|
@@ -1195,27 +1363,32 @@ var TriggerKindSchema = z7.enum([
|
|
|
1195
1363
|
"manual",
|
|
1196
1364
|
"ops_event",
|
|
1197
1365
|
"user_post",
|
|
1198
|
-
"closing"
|
|
1366
|
+
"closing",
|
|
1367
|
+
"cron"
|
|
1199
1368
|
]);
|
|
1200
|
-
var TriggerSchema =
|
|
1369
|
+
var TriggerSchema = z8.object({
|
|
1201
1370
|
kind: TriggerKindSchema,
|
|
1202
1371
|
from: NameSchema.optional(),
|
|
1203
|
-
fromUser:
|
|
1204
|
-
reason:
|
|
1372
|
+
fromUser: z8.boolean().default(false),
|
|
1373
|
+
reason: z8.string().default(""),
|
|
1205
1374
|
taskId: UlidSchema.optional(),
|
|
1206
1375
|
messageId: UlidSchema.optional(),
|
|
1207
1376
|
/** The channel the waking message was posted in. */
|
|
1208
|
-
channel: ChannelRefSchema.optional()
|
|
1377
|
+
channel: ChannelRefSchema.optional(),
|
|
1378
|
+
/** The cron that came due, for a `cron` wake. */
|
|
1379
|
+
cronId: UlidSchema.optional()
|
|
1209
1380
|
});
|
|
1210
|
-
var TurnThreadSchema =
|
|
1211
|
-
var TurnDispatchSchema =
|
|
1381
|
+
var TurnThreadSchema = z8.object({ id: UlidSchema, task: z8.boolean() });
|
|
1382
|
+
var TurnDispatchSchema = z8.object({
|
|
1212
1383
|
agent: NameSchema,
|
|
1213
1384
|
project: NameSchema,
|
|
1214
1385
|
thread: TurnThreadSchema.optional(),
|
|
1215
1386
|
channel: NameSchema.optional(),
|
|
1216
1387
|
trigger: TriggerSchema,
|
|
1217
|
-
priority:
|
|
1218
|
-
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()
|
|
1219
1392
|
});
|
|
1220
1393
|
function conversationPart(conversation) {
|
|
1221
1394
|
return conversation.thread ?? (conversation.channel === void 0 ? void 0 : `#${conversation.channel}`);
|
|
@@ -1226,7 +1399,7 @@ function channelConversation(scope, channel) {
|
|
|
1226
1399
|
const name = at < 0 ? channel : channel.slice(at + 1);
|
|
1227
1400
|
return place === scope && name !== "general" ? name : void 0;
|
|
1228
1401
|
}
|
|
1229
|
-
var TurnRecordSchema =
|
|
1402
|
+
var TurnRecordSchema = z8.object({
|
|
1230
1403
|
/** Assigned by the board when the turn begins; the turn's transcript is filed under it. */
|
|
1231
1404
|
id: UlidSchema.optional(),
|
|
1232
1405
|
agent: NameSchema,
|
|
@@ -1237,29 +1410,31 @@ var TurnRecordSchema = z7.object({
|
|
|
1237
1410
|
channel: NameSchema.optional(),
|
|
1238
1411
|
runner: NameSchema,
|
|
1239
1412
|
cli: CliKindSchema.nullable(),
|
|
1240
|
-
session:
|
|
1413
|
+
session: z8.string().nullable(),
|
|
1241
1414
|
trigger: TriggerSchema,
|
|
1242
1415
|
startedAt: IsoDateTimeSchema,
|
|
1243
1416
|
endedAt: IsoDateTimeSchema.nullable(),
|
|
1244
1417
|
exitReason: TurnExitReasonSchema.nullable(),
|
|
1245
1418
|
status: TurnStatusSchema.nullable(),
|
|
1246
|
-
error:
|
|
1419
|
+
error: z8.string().nullable(),
|
|
1247
1420
|
usage: UsageSchema.nullable(),
|
|
1248
|
-
costUsd:
|
|
1421
|
+
costUsd: z8.number().nonnegative().default(0),
|
|
1249
1422
|
/**
|
|
1250
1423
|
* The running total the CLI reported for the session after this turn, when it reports one. A
|
|
1251
1424
|
* resumed session starts from it, so the next turn's own cost is measured from here.
|
|
1252
1425
|
*/
|
|
1253
|
-
sessionCostUsd:
|
|
1254
|
-
toolCalls:
|
|
1426
|
+
sessionCostUsd: z8.number().nonnegative().optional(),
|
|
1427
|
+
toolCalls: z8.number().int().nonnegative().default(0),
|
|
1255
1428
|
/** The model the CLI reported for this turn, else the model configured for the agent, else null. */
|
|
1256
|
-
model:
|
|
1429
|
+
model: z8.string().nullable().default(null),
|
|
1257
1430
|
/** The task branch the turn left work on, with its commit, as the runner reported it. */
|
|
1258
1431
|
work: TurnWorkSchema.optional(),
|
|
1259
1432
|
/** Who stopped the turn, for one that ended `stopped`. */
|
|
1260
|
-
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()
|
|
1261
1436
|
});
|
|
1262
|
-
var RunningTurnSchema =
|
|
1437
|
+
var RunningTurnSchema = z8.object({
|
|
1263
1438
|
turnId: UlidSchema,
|
|
1264
1439
|
agent: NameSchema,
|
|
1265
1440
|
scope: NameSchema,
|
|
@@ -1267,49 +1442,49 @@ var RunningTurnSchema = z7.object({
|
|
|
1267
1442
|
channel: NameSchema.optional(),
|
|
1268
1443
|
cli: CliKindSchema,
|
|
1269
1444
|
askedIn: ChannelRefSchema.optional(),
|
|
1270
|
-
steerable:
|
|
1271
|
-
stoppable:
|
|
1445
|
+
steerable: z8.boolean(),
|
|
1446
|
+
stoppable: z8.boolean()
|
|
1272
1447
|
});
|
|
1273
|
-
var SessionsFileSchema =
|
|
1274
|
-
claude:
|
|
1275
|
-
codex:
|
|
1448
|
+
var SessionsFileSchema = z8.object({
|
|
1449
|
+
claude: z8.string().optional(),
|
|
1450
|
+
codex: z8.string().optional(),
|
|
1276
1451
|
runner: NameSchema.optional()
|
|
1277
1452
|
});
|
|
1278
|
-
var WakeRequestSchema =
|
|
1453
|
+
var WakeRequestSchema = z8.object({
|
|
1279
1454
|
agent: NameSchema,
|
|
1280
1455
|
project: NameSchema,
|
|
1281
|
-
reason:
|
|
1456
|
+
reason: z8.string().default("manual wake"),
|
|
1282
1457
|
/** A manual wake, or a reflection turn requested ahead of the cadence. */
|
|
1283
|
-
kind:
|
|
1458
|
+
kind: z8.enum(["manual", "reflection"]).default("manual")
|
|
1284
1459
|
});
|
|
1285
|
-
var TurnHistoryEntrySchema =
|
|
1460
|
+
var TurnHistoryEntrySchema = z8.object({
|
|
1286
1461
|
/** The id of the event that ended the turn. */
|
|
1287
1462
|
id: UlidSchema,
|
|
1288
1463
|
/** The turn's own id, when it has a transcript to open; turns before transcripts have none. */
|
|
1289
1464
|
turnId: UlidSchema.optional(),
|
|
1290
1465
|
ts: IsoDateTimeSchema,
|
|
1291
|
-
outcome:
|
|
1466
|
+
outcome: z8.enum(["completed", "failed"]),
|
|
1292
1467
|
/** A project slug, or the society scope. */
|
|
1293
1468
|
project: NameSchema,
|
|
1294
1469
|
/** The thread whose conversation the turn was in; absent for the home conversation. */
|
|
1295
1470
|
thread: UlidSchema.optional(),
|
|
1296
1471
|
/** The channel, never general, whose conversation the turn was in. */
|
|
1297
1472
|
channel: NameSchema.optional(),
|
|
1298
|
-
trigger:
|
|
1299
|
-
exitReason:
|
|
1300
|
-
costUsd:
|
|
1473
|
+
trigger: z8.string(),
|
|
1474
|
+
exitReason: z8.string().nullable(),
|
|
1475
|
+
costUsd: z8.number(),
|
|
1301
1476
|
/** The tokens the turn used; absent for turns logged before turns recorded them. */
|
|
1302
1477
|
usage: UsageSchema.optional(),
|
|
1303
|
-
model:
|
|
1304
|
-
summary:
|
|
1305
|
-
error:
|
|
1478
|
+
model: z8.string().nullable(),
|
|
1479
|
+
summary: z8.string().nullable(),
|
|
1480
|
+
error: z8.string().nullable(),
|
|
1306
1481
|
/** When the log recorded the turn's start; absent when the start is missing from the log. */
|
|
1307
1482
|
startedAt: IsoDateTimeSchema.optional(),
|
|
1308
|
-
toolCalls:
|
|
1483
|
+
toolCalls: z8.number().int().nonnegative().optional()
|
|
1309
1484
|
});
|
|
1310
1485
|
|
|
1311
1486
|
// packages/shared/dist/ops.js
|
|
1312
|
-
import { z as
|
|
1487
|
+
import { z as z9 } from "zod";
|
|
1313
1488
|
var OPS_SIGNAL_KINDS = [
|
|
1314
1489
|
"waiting_stage",
|
|
1315
1490
|
"backlog",
|
|
@@ -1322,18 +1497,19 @@ var OPS_SIGNAL_KINDS = [
|
|
|
1322
1497
|
"scaled",
|
|
1323
1498
|
"runner",
|
|
1324
1499
|
"home_conflict",
|
|
1325
|
-
"stuck_workspace"
|
|
1500
|
+
"stuck_workspace",
|
|
1501
|
+
"cron_failing"
|
|
1326
1502
|
];
|
|
1327
|
-
var OpsSignalKindSchema =
|
|
1328
|
-
var OpsSignalSchema =
|
|
1503
|
+
var OpsSignalKindSchema = z9.enum(OPS_SIGNAL_KINDS);
|
|
1504
|
+
var OpsSignalSchema = z9.object({
|
|
1329
1505
|
kind: OpsSignalKindSchema,
|
|
1330
1506
|
/** Identifies the condition, for example `backlog:demo:engineer`, so a persisting one is not logged again every pass. */
|
|
1331
|
-
key:
|
|
1507
|
+
key: z9.string().min(1),
|
|
1332
1508
|
/** One line a reader can act on. */
|
|
1333
|
-
summary:
|
|
1509
|
+
summary: z9.string().min(1),
|
|
1334
1510
|
/** The counter or timer value behind the signal. */
|
|
1335
|
-
value:
|
|
1336
|
-
threshold:
|
|
1511
|
+
value: z9.number(),
|
|
1512
|
+
threshold: z9.number().optional(),
|
|
1337
1513
|
project: NameSchema.optional(),
|
|
1338
1514
|
agent: NameSchema.optional(),
|
|
1339
1515
|
role: NameSchema.optional(),
|
|
@@ -1341,8 +1517,8 @@ var OpsSignalSchema = z8.object({
|
|
|
1341
1517
|
});
|
|
1342
1518
|
|
|
1343
1519
|
// packages/shared/dist/metrics.js
|
|
1344
|
-
import { z as
|
|
1345
|
-
var MetricsWindowSchema =
|
|
1520
|
+
import { z as z10 } from "zod";
|
|
1521
|
+
var MetricsWindowSchema = z10.enum(["24h", "7d", "all"]);
|
|
1346
1522
|
var METRICS_WINDOW_MS = {
|
|
1347
1523
|
"24h": 24 * 36e5,
|
|
1348
1524
|
"7d": 7 * 24 * 36e5,
|
|
@@ -1353,9 +1529,9 @@ function isBoardAction(tool) {
|
|
|
1353
1529
|
const verb = /^mcp__board__(.+)$/.exec(tool)?.[1];
|
|
1354
1530
|
return verb !== void 0 && !READ_VERBS.some((read2) => read2 === verb);
|
|
1355
1531
|
}
|
|
1356
|
-
var CountSchema =
|
|
1357
|
-
var MsSchema =
|
|
1358
|
-
var MetricsSchema =
|
|
1532
|
+
var CountSchema = z10.number().int().nonnegative();
|
|
1533
|
+
var MsSchema = z10.number().nonnegative();
|
|
1534
|
+
var MetricsSchema = z10.object({
|
|
1359
1535
|
window: MetricsWindowSchema,
|
|
1360
1536
|
since: IsoDateTimeSchema.nullable(),
|
|
1361
1537
|
until: IsoDateTimeSchema,
|
|
@@ -1363,12 +1539,12 @@ var MetricsSchema = z9.object({
|
|
|
1363
1539
|
* Finished turns that changed nothing on the board. Reflections are left out, since they write
|
|
1364
1540
|
* memory, not the board; turns without a transcript are counted apart, their actions unknown.
|
|
1365
1541
|
*/
|
|
1366
|
-
idle:
|
|
1542
|
+
idle: z10.object({
|
|
1367
1543
|
turns: CountSchema,
|
|
1368
1544
|
idle: CountSchema,
|
|
1369
1545
|
unknown: CountSchema,
|
|
1370
|
-
byTrigger:
|
|
1371
|
-
byAgent:
|
|
1546
|
+
byTrigger: z10.array(z10.object({ trigger: z10.string(), turns: CountSchema, idle: CountSchema })),
|
|
1547
|
+
byAgent: z10.array(z10.object({
|
|
1372
1548
|
agent: NameSchema,
|
|
1373
1549
|
role: NameSchema.nullable(),
|
|
1374
1550
|
turns: CountSchema,
|
|
@@ -1376,33 +1552,33 @@ var MetricsSchema = z9.object({
|
|
|
1376
1552
|
}))
|
|
1377
1553
|
}),
|
|
1378
1554
|
/** Work sent back to an earlier stage: over the tasks finished in the window, and by where. */
|
|
1379
|
-
sentBack:
|
|
1555
|
+
sentBack: z10.object({
|
|
1380
1556
|
finished: CountSchema,
|
|
1381
1557
|
sendBacks: CountSchema,
|
|
1382
1558
|
tasksSentBack: CountSchema,
|
|
1383
|
-
byStage:
|
|
1384
|
-
bySender:
|
|
1559
|
+
byStage: z10.array(z10.object({ project: NameSchema, stage: z10.string(), count: CountSchema })),
|
|
1560
|
+
bySender: z10.array(z10.object({ agent: NameSchema, count: CountSchema }))
|
|
1385
1561
|
}),
|
|
1386
1562
|
/** Posts in the threads of the tasks finished in the window, the board's own notices left out. */
|
|
1387
|
-
messages:
|
|
1563
|
+
messages: z10.object({
|
|
1388
1564
|
finished: CountSchema,
|
|
1389
1565
|
messages: CountSchema,
|
|
1390
|
-
byProject:
|
|
1391
|
-
busiest:
|
|
1566
|
+
byProject: z10.array(z10.object({ project: NameSchema, finished: CountSchema, messages: CountSchema })),
|
|
1567
|
+
busiest: z10.array(z10.object({
|
|
1392
1568
|
taskId: UlidSchema,
|
|
1393
|
-
title:
|
|
1569
|
+
title: z10.string(),
|
|
1394
1570
|
project: NameSchema,
|
|
1395
1571
|
messages: CountSchema
|
|
1396
1572
|
}))
|
|
1397
1573
|
}),
|
|
1398
1574
|
/** Time from a mention to the start of the turn it woke, in the conversation it was made in. */
|
|
1399
|
-
latency:
|
|
1575
|
+
latency: z10.object({
|
|
1400
1576
|
mentions: CountSchema,
|
|
1401
1577
|
/** Mentions no turn has followed yet. */
|
|
1402
1578
|
unanswered: CountSchema,
|
|
1403
1579
|
medianMs: MsSchema.nullable(),
|
|
1404
1580
|
slowestMs: MsSchema.nullable(),
|
|
1405
|
-
byAgent:
|
|
1581
|
+
byAgent: z10.array(z10.object({
|
|
1406
1582
|
agent: NameSchema,
|
|
1407
1583
|
mentions: CountSchema,
|
|
1408
1584
|
medianMs: MsSchema,
|
|
@@ -1410,81 +1586,81 @@ var MetricsSchema = z9.object({
|
|
|
1410
1586
|
}))
|
|
1411
1587
|
}),
|
|
1412
1588
|
/** The user's decisions: proposals decided, questions answered, and stages passed or sent back. */
|
|
1413
|
-
decisions:
|
|
1589
|
+
decisions: z10.object({
|
|
1414
1590
|
total: CountSchema,
|
|
1415
|
-
byDay:
|
|
1416
|
-
day:
|
|
1591
|
+
byDay: z10.array(z10.object({
|
|
1592
|
+
day: z10.string(),
|
|
1417
1593
|
proposals: CountSchema,
|
|
1418
1594
|
answers: CountSchema,
|
|
1419
1595
|
stages: CountSchema
|
|
1420
1596
|
}))
|
|
1421
1597
|
}),
|
|
1422
1598
|
/** Tasks that needed a capability no connected runner offered, and whether they still do. */
|
|
1423
|
-
blocked:
|
|
1599
|
+
blocked: z10.array(z10.object({
|
|
1424
1600
|
taskId: UlidSchema,
|
|
1425
|
-
title:
|
|
1601
|
+
title: z10.string().nullable(),
|
|
1426
1602
|
project: NameSchema.nullable(),
|
|
1427
|
-
summary:
|
|
1603
|
+
summary: z10.string(),
|
|
1428
1604
|
firstAt: IsoDateTimeSchema,
|
|
1429
1605
|
lastAt: IsoDateTimeSchema,
|
|
1430
|
-
holds:
|
|
1606
|
+
holds: z10.boolean()
|
|
1431
1607
|
}))
|
|
1432
1608
|
});
|
|
1433
1609
|
|
|
1434
1610
|
// packages/shared/dist/config.js
|
|
1435
|
-
import { z as
|
|
1436
|
-
var LogLevelSchema =
|
|
1437
|
-
var GithubSignInSchema =
|
|
1438
|
-
clientId:
|
|
1439
|
-
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),
|
|
1440
1616
|
/** Logins, lowercased; GitHub logins are case-insensitive. */
|
|
1441
|
-
users:
|
|
1617
|
+
users: z11.array(z11.string().min(1)).min(1)
|
|
1442
1618
|
});
|
|
1443
|
-
var ServerConfigSchema =
|
|
1444
|
-
dataDir:
|
|
1445
|
-
host:
|
|
1446
|
-
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),
|
|
1447
1623
|
/**
|
|
1448
1624
|
* An address every runner's agents reach this server at, for example https://board.example, or
|
|
1449
1625
|
* null for each runner to use the address it reaches the server at itself.
|
|
1450
1626
|
*/
|
|
1451
|
-
publicUrl:
|
|
1627
|
+
publicUrl: z11.string().min(1).nullable(),
|
|
1452
1628
|
logLevel: LogLevelSchema,
|
|
1453
1629
|
/** Turns running at once across every runner, or null for no limit beyond each runner's own. */
|
|
1454
|
-
concurrency:
|
|
1630
|
+
concurrency: z11.number().int().positive().nullable(),
|
|
1455
1631
|
/** How long one turn may run before it is stopped, or null for no limit. */
|
|
1456
|
-
turnTimeoutMs:
|
|
1632
|
+
turnTimeoutMs: z11.number().int().positive().nullable(),
|
|
1457
1633
|
/** Rounds of tool calls one Claude turn may take, or null for no limit. */
|
|
1458
|
-
toolRounds:
|
|
1634
|
+
toolRounds: z11.number().int().positive().nullable(),
|
|
1459
1635
|
/** How long a resident session stays warm after its last turn. */
|
|
1460
|
-
residentIdleMs:
|
|
1636
|
+
residentIdleMs: z11.number().int().positive(),
|
|
1461
1637
|
/** Signing in to the board with GitHub, or null when only a token lets anyone in. */
|
|
1462
1638
|
github: GithubSignInSchema.nullable(),
|
|
1463
1639
|
/**
|
|
1464
1640
|
* Reverse proxies in front of the server whose X-Forwarded-For entries name the client, for the
|
|
1465
1641
|
* rate limits on what anyone may ask without a token: 0 when clients connect directly.
|
|
1466
1642
|
*/
|
|
1467
|
-
trustedProxies:
|
|
1643
|
+
trustedProxies: z11.number().int().min(0).max(16)
|
|
1468
1644
|
});
|
|
1469
|
-
var RunnerConfigSchema =
|
|
1645
|
+
var RunnerConfigSchema = z11.object({
|
|
1470
1646
|
/** The board server, for example http://127.0.0.1:4700. */
|
|
1471
|
-
serverUrl:
|
|
1647
|
+
serverUrl: z11.string().url(),
|
|
1472
1648
|
/**
|
|
1473
1649
|
* The runner's token, from `runner add`, or null for the one it saved when it was enrolled, or
|
|
1474
1650
|
* to enroll it.
|
|
1475
1651
|
*/
|
|
1476
|
-
token:
|
|
1652
|
+
token: z11.string().min(1).nullable(),
|
|
1477
1653
|
/** The runner's own data directory: the board mirror, agent homes, repositories, worktrees. */
|
|
1478
|
-
dataDir:
|
|
1654
|
+
dataDir: z11.string().min(1),
|
|
1479
1655
|
logLevel: LogLevelSchema,
|
|
1480
1656
|
/** Turns this machine runs at once, or null for no limit. */
|
|
1481
|
-
slots:
|
|
1482
|
-
clis:
|
|
1483
|
-
capabilities:
|
|
1657
|
+
slots: z11.number().int().positive().nullable(),
|
|
1658
|
+
clis: z11.array(CliKindSchema).min(1),
|
|
1659
|
+
capabilities: z11.array(z11.string())
|
|
1484
1660
|
});
|
|
1485
1661
|
|
|
1486
1662
|
// packages/shared/dist/version.js
|
|
1487
|
-
var STELLARIS_VERSION = "0.3.
|
|
1663
|
+
var STELLARIS_VERSION = "0.3.14";
|
|
1488
1664
|
|
|
1489
1665
|
// packages/board-core/dist/errors.js
|
|
1490
1666
|
var BoardError = class extends Error {
|
|
@@ -2029,32 +2205,32 @@ function filesOf(lines, status) {
|
|
|
2029
2205
|
}
|
|
2030
2206
|
|
|
2031
2207
|
// packages/board-core/dist/metrics.js
|
|
2032
|
-
import { z as
|
|
2033
|
-
var TurnPayload =
|
|
2034
|
-
turnId:
|
|
2035
|
-
project:
|
|
2036
|
-
thread:
|
|
2037
|
-
channel:
|
|
2038
|
-
trigger:
|
|
2039
|
-
});
|
|
2040
|
-
var TaskPayload =
|
|
2041
|
-
taskId:
|
|
2042
|
-
project:
|
|
2043
|
-
from:
|
|
2044
|
-
});
|
|
2045
|
-
var MessagePayload =
|
|
2046
|
-
id:
|
|
2047
|
-
channel:
|
|
2048
|
-
thread:
|
|
2049
|
-
mentions:
|
|
2050
|
-
});
|
|
2051
|
-
var SteeredPayload =
|
|
2052
|
-
var SignalPayload =
|
|
2053
|
-
kind:
|
|
2054
|
-
key:
|
|
2055
|
-
summary:
|
|
2056
|
-
taskId:
|
|
2057
|
-
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()
|
|
2058
2234
|
});
|
|
2059
2235
|
function median(values) {
|
|
2060
2236
|
const sorted = values.toSorted((a, b) => a - b);
|
|
@@ -2423,6 +2599,13 @@ var BoardPaths = class {
|
|
|
2423
2599
|
members() {
|
|
2424
2600
|
return path5.join(this.society, "members");
|
|
2425
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
|
+
}
|
|
2426
2609
|
member(name) {
|
|
2427
2610
|
return path5.join(this.members(), `${name}.md`);
|
|
2428
2611
|
}
|
|
@@ -2569,41 +2752,41 @@ function namesList(names) {
|
|
|
2569
2752
|
return names.length <= 1 ? names[0] ?? "" : `${names.slice(0, -1).join(", ")} and ${names.at(-1) ?? ""}`;
|
|
2570
2753
|
}
|
|
2571
2754
|
var SKIPPED_IN_HOME = /* @__PURE__ */ new Set([".git", "turns", ".claude", ".codex"]);
|
|
2572
|
-
var RunnerTokensSchema =
|
|
2573
|
-
runners:
|
|
2755
|
+
var RunnerTokensSchema = z13.object({
|
|
2756
|
+
runners: z13.record(z13.string(), z13.object({ tokenHash: z13.string().min(1) }))
|
|
2574
2757
|
});
|
|
2575
|
-
var SignInsSchema =
|
|
2576
|
-
signIns:
|
|
2577
|
-
tokenHash:
|
|
2578
|
-
login:
|
|
2758
|
+
var SignInsSchema = z13.object({
|
|
2759
|
+
signIns: z13.array(z13.object({
|
|
2760
|
+
tokenHash: z13.string().min(1),
|
|
2761
|
+
login: z13.string().min(1),
|
|
2579
2762
|
/** The account's picture at the identity provider; sign-ins from before it have none. */
|
|
2580
|
-
avatarUrl:
|
|
2763
|
+
avatarUrl: z13.url().optional(),
|
|
2581
2764
|
createdAt: IsoDateTimeSchema,
|
|
2582
2765
|
expiresAt: IsoDateTimeSchema
|
|
2583
2766
|
})).default([])
|
|
2584
2767
|
});
|
|
2585
|
-
var TurnHistoryPayloadSchema =
|
|
2768
|
+
var TurnHistoryPayloadSchema = z13.object({
|
|
2586
2769
|
project: NameSchema,
|
|
2587
2770
|
thread: UlidSchema.optional(),
|
|
2588
2771
|
channel: NameSchema.optional(),
|
|
2589
|
-
trigger:
|
|
2590
|
-
exitReason:
|
|
2591
|
-
costUsd:
|
|
2772
|
+
trigger: z13.string().default("manual"),
|
|
2773
|
+
exitReason: z13.string().nullable().default(null),
|
|
2774
|
+
costUsd: z13.number().default(0),
|
|
2592
2775
|
usage: UsageSchema.optional(),
|
|
2593
|
-
model:
|
|
2594
|
-
summary:
|
|
2595
|
-
error:
|
|
2596
|
-
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(),
|
|
2597
2780
|
turnId: UlidSchema.optional()
|
|
2598
2781
|
});
|
|
2599
|
-
var TurnStartPayloadSchema =
|
|
2600
|
-
var CursorsSchema =
|
|
2601
|
-
digest:
|
|
2602
|
-
scopes:
|
|
2603
|
-
threadsFrom:
|
|
2604
|
-
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({}),
|
|
2605
2788
|
/** How far a work role has read news, which goes to whichever of its general conversations reads first. */
|
|
2606
|
-
news:
|
|
2789
|
+
news: z13.string().optional()
|
|
2607
2790
|
});
|
|
2608
2791
|
var NO_CURSORS = { digest: null, scopes: {}, threadsFrom: {}, channelsFrom: {} };
|
|
2609
2792
|
function conversationKey(scope, part) {
|
|
@@ -2642,7 +2825,7 @@ function byId(a, b) {
|
|
|
2642
2825
|
function turnTime(record) {
|
|
2643
2826
|
return record.endedAt ?? record.startedAt;
|
|
2644
2827
|
}
|
|
2645
|
-
var PausedSchema =
|
|
2828
|
+
var PausedSchema = z13.object({ paused: z13.boolean() });
|
|
2646
2829
|
var DEFAULT_LEASE_MS = 30 * 60 * 1e3;
|
|
2647
2830
|
var MENTION_PATTERN = /(^|[^\w@])@([a-z0-9][a-z0-9-]{0,31})(?![\w-])/g;
|
|
2648
2831
|
function withoutMentions(text) {
|
|
@@ -2654,7 +2837,7 @@ var OPS_WAKE_TRIGGER = "ops_event";
|
|
|
2654
2837
|
var FRONT_DESK_TRIGGER = "user_post";
|
|
2655
2838
|
var OPS_CHANNELS = ["governance"];
|
|
2656
2839
|
var FRONT_DESK_CHANNELS = [ASK_CHANNEL];
|
|
2657
|
-
var AlignmentsSchema =
|
|
2840
|
+
var AlignmentsSchema = z13.object({ applied: z13.array(z13.string()).default([]) });
|
|
2658
2841
|
var FOLLOW_DECISIONS = "ops-readers-follow-decisions";
|
|
2659
2842
|
var THREAD_RECORDS = "threads-as-records";
|
|
2660
2843
|
var DIGEST_CURSORS = "digest-replaces-inbox";
|
|
@@ -2666,6 +2849,8 @@ var ASKS_CHANNEL_OPENED = "asks-channel-opened";
|
|
|
2666
2849
|
var KNOWLEDGE_REMOVAL = "knowledge-removal-granted";
|
|
2667
2850
|
var CHANNEL_CONVERSATIONS = "channel-conversations";
|
|
2668
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"];
|
|
2669
2854
|
var OLD_DASHBOARD_LINE = "Agents may edit this file. It is rendered by the board UI.";
|
|
2670
2855
|
var DASHBOARD_LINE = "The project's members keep this page current with update_dashboard.";
|
|
2671
2856
|
var PLANNING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
@@ -2867,7 +3052,8 @@ var Board = class _Board {
|
|
|
2867
3052
|
ASKS_CHANNEL_OPENED,
|
|
2868
3053
|
KNOWLEDGE_REMOVAL,
|
|
2869
3054
|
CHANNEL_CONVERSATIONS,
|
|
2870
|
-
DASHBOARD_UPDATES
|
|
3055
|
+
DASHBOARD_UPDATES,
|
|
3056
|
+
CRON_VERBS
|
|
2871
3057
|
].filter((name) => !applied.includes(name));
|
|
2872
3058
|
if (pending.length === 0) {
|
|
2873
3059
|
return;
|
|
@@ -2906,6 +3092,9 @@ var Board = class _Board {
|
|
|
2906
3092
|
await this.grantBesideKnowledge("update_dashboard");
|
|
2907
3093
|
await this.rewordDashboards();
|
|
2908
3094
|
}
|
|
3095
|
+
if (pending.includes(CRON_VERBS)) {
|
|
3096
|
+
await this.grantToEveryRole(CRON_VERB_NAMES);
|
|
3097
|
+
}
|
|
2909
3098
|
await writeJson(file, { applied: [...applied, ...pending] });
|
|
2910
3099
|
}
|
|
2911
3100
|
/**
|
|
@@ -2919,6 +3108,15 @@ var Board = class _Board {
|
|
|
2919
3108
|
}
|
|
2920
3109
|
}
|
|
2921
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
|
+
}
|
|
2922
3120
|
/** A dashboard still saying agents may edit its file says how it changes now. */
|
|
2923
3121
|
async rewordDashboards() {
|
|
2924
3122
|
for (const slug of await listDirs(this.paths.projects())) {
|
|
@@ -3063,7 +3261,7 @@ var Board = class _Board {
|
|
|
3063
3261
|
}
|
|
3064
3262
|
}
|
|
3065
3263
|
for (const file of await listFiles(this.paths.tasks(slug))) {
|
|
3066
|
-
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()));
|
|
3067
3265
|
const legacy = raw.data["thread"];
|
|
3068
3266
|
if (legacy === void 0) {
|
|
3069
3267
|
continue;
|
|
@@ -3141,7 +3339,7 @@ var Board = class _Board {
|
|
|
3141
3339
|
* turns, so it follows nothing now.
|
|
3142
3340
|
*/
|
|
3143
3341
|
async renameInboxCursors() {
|
|
3144
|
-
const Legacy =
|
|
3342
|
+
const Legacy = z13.object({ inbox: z13.string().nullable() });
|
|
3145
3343
|
for (const agent2 of await this.listAgents()) {
|
|
3146
3344
|
const file = this.paths.agentCursors(agent2.name);
|
|
3147
3345
|
if (await exists(file)) {
|
|
@@ -3187,7 +3385,7 @@ ${charter.purpose}
|
|
|
3187
3385
|
*/
|
|
3188
3386
|
async alignSeedRole(seed) {
|
|
3189
3387
|
const file = this.paths.role(seed.name);
|
|
3190
|
-
const raw = await readMarkdown(file,
|
|
3388
|
+
const raw = await readMarkdown(file, z13.record(z13.string(), z13.unknown()));
|
|
3191
3389
|
const current = RoleCharterSchema.parse(raw.data);
|
|
3192
3390
|
const verbsAdded = seed.verbs.filter((verb) => !current.verbs.includes(verb));
|
|
3193
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));
|
|
@@ -3500,14 +3698,14 @@ ${charter.purpose}
|
|
|
3500
3698
|
/** The society's norms, the one knowledge topic every turn loads. Empty until the steward writes it. */
|
|
3501
3699
|
async readSocietyNorms() {
|
|
3502
3700
|
const file = this.paths.societyKnowledgeFile("norms");
|
|
3503
|
-
return await exists(file) ? (await readMarkdown(file,
|
|
3701
|
+
return await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
|
|
3504
3702
|
}
|
|
3505
3703
|
/** Knowledge topics of a project, or of the society when the project is null. */
|
|
3506
3704
|
async listKnowledge(project2) {
|
|
3507
3705
|
const dir = project2 === null ? this.paths.societyKnowledge() : this.paths.projectKnowledge(project2);
|
|
3508
3706
|
const topics = [];
|
|
3509
3707
|
for (const file of await listFiles(dir)) {
|
|
3510
|
-
const doc = await readMarkdown(path6.join(dir, file),
|
|
3708
|
+
const doc = await readMarkdown(path6.join(dir, file), z13.record(z13.string(), z13.unknown()));
|
|
3511
3709
|
const parsed = KnowledgeSchema.safeParse({
|
|
3512
3710
|
...doc.data,
|
|
3513
3711
|
project: project2,
|
|
@@ -3907,6 +4105,21 @@ ${DASHBOARD_LINE}
|
|
|
3907
4105
|
await this.events.append("paused.changed", actor.name, { paused });
|
|
3908
4106
|
});
|
|
3909
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
|
+
}
|
|
3910
4123
|
/** Releases every claim whose lease has expired. Called by the scheduler on its tick. */
|
|
3911
4124
|
async expireLeases() {
|
|
3912
4125
|
return this.mutex.run(async () => {
|
|
@@ -4329,6 +4542,241 @@ ${current.body}`);
|
|
|
4329
4542
|
return updated;
|
|
4330
4543
|
});
|
|
4331
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
|
+
}
|
|
4332
4780
|
async openThread(actor, input) {
|
|
4333
4781
|
const args = VerbInputs.open_thread.parse(input);
|
|
4334
4782
|
await this.authorize(actor, "open_thread");
|
|
@@ -4545,6 +4993,7 @@ ${args.summary}`);
|
|
|
4545
4993
|
threadId: thread2.id,
|
|
4546
4994
|
channel: thread2.channel
|
|
4547
4995
|
});
|
|
4996
|
+
await this.endCronsUnlocked(actor.name, "its thread closed", (cron2) => cron2.thread === thread2.id);
|
|
4548
4997
|
return summary;
|
|
4549
4998
|
});
|
|
4550
4999
|
}
|
|
@@ -5224,6 +5673,7 @@ ${rationale}`}`);
|
|
|
5224
5673
|
members: project2.members.filter((member) => member !== target)
|
|
5225
5674
|
}));
|
|
5226
5675
|
await this.refreshMember(target);
|
|
5676
|
+
await this.endCronsUnlocked(actor.name, `${target} left ${args.project}`, (cron2) => cron2.agent === target && cron2.scope === args.project);
|
|
5227
5677
|
await this.events.append("agent.left", actor.name, {
|
|
5228
5678
|
name: target,
|
|
5229
5679
|
project: args.project,
|
|
@@ -5292,6 +5742,12 @@ ${rationale}`}`);
|
|
|
5292
5742
|
return this.archiveChannel(actor, VerbInputs.archive_channel.parse(input));
|
|
5293
5743
|
case "update_dashboard":
|
|
5294
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));
|
|
5295
5751
|
default:
|
|
5296
5752
|
throw new BoardError("VALIDATION", `unknown verb ${String(verb)}`);
|
|
5297
5753
|
}
|
|
@@ -5338,7 +5794,7 @@ ${rationale}`}`);
|
|
|
5338
5794
|
}
|
|
5339
5795
|
async readAgentRoleBody(agent2) {
|
|
5340
5796
|
const file = this.paths.agentRole(agent2);
|
|
5341
|
-
return await exists(file) ? (await readMarkdown(file,
|
|
5797
|
+
return await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
|
|
5342
5798
|
}
|
|
5343
5799
|
/** The steps of one finished turn, as the runner handed them over when it ended. */
|
|
5344
5800
|
/**
|
|
@@ -5516,6 +5972,9 @@ ${rationale}`}`);
|
|
|
5516
5972
|
}
|
|
5517
5973
|
await this.writeTurnRecord(parsed);
|
|
5518
5974
|
await this.refreshMember(parsed.agent);
|
|
5975
|
+
for (const id of parsed.crons ?? []) {
|
|
5976
|
+
await this.recordCronRun(id, parsed, failed);
|
|
5977
|
+
}
|
|
5519
5978
|
if (parsed.exitReason === "stopped") {
|
|
5520
5979
|
await this.events.append("turn.stopped", parsed.stoppedBy ?? USER_NAME, {
|
|
5521
5980
|
...parsed.id === void 0 ? {} : { turnId: parsed.id },
|
|
@@ -5597,6 +6056,45 @@ ${rationale}`}`);
|
|
|
5597
6056
|
return true;
|
|
5598
6057
|
});
|
|
5599
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
|
+
}
|
|
5600
6098
|
/**
|
|
5601
6099
|
* Whether a conversation with a workspace of its own has ended: a thread closed, a task ended
|
|
5602
6100
|
* (for one from before tasks had threads), or a channel archived.
|
|
@@ -5696,7 +6194,7 @@ ${rationale}`}`);
|
|
|
5696
6194
|
return { project: slug, revision: 0, body: "" };
|
|
5697
6195
|
}
|
|
5698
6196
|
const { data, body } = await readLooseMarkdown(file);
|
|
5699
|
-
const revision =
|
|
6197
|
+
const revision = z13.number().int().min(0).safeParse(data["revision"]);
|
|
5700
6198
|
const updatedBy = NameSchema.safeParse(data["updatedBy"]);
|
|
5701
6199
|
const updatedAt = IsoDateTimeSchema.safeParse(data["updatedAt"]);
|
|
5702
6200
|
return {
|
|
@@ -5987,7 +6485,7 @@ ${seed}
|
|
|
5987
6485
|
continue;
|
|
5988
6486
|
}
|
|
5989
6487
|
const file = this.paths.agentRole(agent2.name);
|
|
5990
|
-
const body = await exists(file) ? (await readMarkdown(file,
|
|
6488
|
+
const body = await exists(file) ? (await readMarkdown(file, z13.record(z13.string(), z13.unknown()))).body : "";
|
|
5991
6489
|
const at = body.indexOf(`
|
|
5992
6490
|
${SEED_INSTRUCTIONS_HEADING}
|
|
5993
6491
|
`);
|
|
@@ -6431,6 +6929,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
|
|
|
6431
6929
|
archivedChannels: [...current.archivedChannels, archived]
|
|
6432
6930
|
}));
|
|
6433
6931
|
}
|
|
6932
|
+
await this.endCronsUnlocked(by, `${ref} was archived`, (cron2) => cron2.scope === scope && cron2.channel === channel || cron2.thread !== void 0 && threadsClosed.includes(cron2.thread));
|
|
6434
6933
|
await this.events.append("channel.archived", by, {
|
|
6435
6934
|
channel: ref,
|
|
6436
6935
|
reason,
|
|
@@ -6535,6 +7034,7 @@ ${charter.purpose}
|
|
|
6535
7034
|
});
|
|
6536
7035
|
threadsClosed.push(doc.data.id);
|
|
6537
7036
|
}
|
|
7037
|
+
await this.endCronsUnlocked(by, `project ${slug} was archived`, (cron2) => cron2.scope === slug || cron2.thread !== void 0 && threadsClosed.includes(cron2.thread));
|
|
6538
7038
|
const project2 = await this.updateProject(slug, (p) => ({
|
|
6539
7039
|
...p,
|
|
6540
7040
|
members: [],
|
|
@@ -6599,6 +7099,7 @@ ${charter.purpose}
|
|
|
6599
7099
|
}
|
|
6600
7100
|
this.tokenIndex.delete(current.tokenHash);
|
|
6601
7101
|
await this.refreshMember(name);
|
|
7102
|
+
await this.endCronsUnlocked(by, `${name} was retired`, (cron2) => cron2.agent === name);
|
|
6602
7103
|
await this.events.append("agent.retired", by, {
|
|
6603
7104
|
name,
|
|
6604
7105
|
role: agent2.role,
|
|
@@ -6910,6 +7411,7 @@ ${charter.purpose}
|
|
|
6910
7411
|
channel: found.thread.channel,
|
|
6911
7412
|
ended
|
|
6912
7413
|
});
|
|
7414
|
+
await this.endCronsUnlocked(by, `its thread closed: its ${found.thread.subject?.kind ?? "subject"} was ${ended}`, (cron2) => cron2.thread === subjectId);
|
|
6913
7415
|
}
|
|
6914
7416
|
async readMessagesIn(dir, since) {
|
|
6915
7417
|
const messages = [];
|
|
@@ -7345,6 +7847,66 @@ program.command("resume").description("Allow wakeups again").action(async () =>
|
|
|
7345
7847
|
await board.setPaused(board.userActor(), false);
|
|
7346
7848
|
print({ paused: false }, () => "Society resumed");
|
|
7347
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
|
+
});
|
|
7348
7910
|
var knowledge = program.command("knowledge").description("Shared knowledge, by project or society");
|
|
7349
7911
|
knowledge.command("list [project]").description("List knowledge topics of a project, or of the society when no project is given").action(async (projectSlug) => {
|
|
7350
7912
|
const board = await open();
|