@kubitnodes/stellaris 0.3.26 → 0.3.27
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 +340 -131
- package/dist/server/index.js +808 -181
- package/package.json +1 -1
- package/web/dist/assets/{BackLink-UTiLF1J4.js → BackLink-qSxOMeEG.js} +1 -1
- package/web/dist/assets/{Button-Cp4cNRUX.js → Button-CxjWy598.js} +1 -1
- package/web/dist/assets/{ChannelForms-DD2B0tey.js → ChannelForms-BVidWMId.js} +1 -1
- package/web/dist/assets/{ChannelView-CY3oG9hZ.js → ChannelView-rqfk1Tyu.js} +1 -1
- package/web/dist/assets/{CitizenView-BNK5ro-6.js → CitizenView-xU99R8hS.js} +1 -1
- package/web/dist/assets/{CitizensView-WPQ6Jhl3.js → CitizensView-Bgw6PRSo.js} +1 -1
- package/web/dist/assets/{CliIcon-C7bwChMP.js → CliIcon-BB2NdLxN.js} +1 -1
- package/web/dist/assets/{CronsView-_pdgwX1I.js → CronsView-BrXrcfC6.js} +1 -1
- package/web/dist/assets/{FileTree-jyDUSu7g.js → FileTree-CHDmrOhY.js} +1 -1
- package/web/dist/assets/{FileView-DcQvlc8V.js → FileView-DUMfKYRW.js} +1 -1
- package/web/dist/assets/{KnowledgeView-DDl0HQPG.js → KnowledgeView-Qog6c92V.js} +1 -1
- package/web/dist/assets/{Markdown-DT3V-EVe.js → Markdown-C7RbUEZE.js} +1 -1
- package/web/dist/assets/{MessageItem-agtEugfG.js → MessageItem-DgWh5pJF.js} +1 -1
- package/web/dist/assets/{MetricsView-DiXyYeWP.js → MetricsView-DuOyISk1.js} +1 -1
- package/web/dist/assets/{NeedsYouView-CKmuYan_.js → NeedsYouView-CrmHXZAn.js} +1 -1
- package/web/dist/assets/{Overview-CKcTqsMM.js → Overview-xatORJK6.js} +1 -1
- package/web/dist/assets/{Pane-BaM0TdE6.js → Pane-BlIRwUtQ.js} +1 -1
- package/web/dist/assets/{Picker-C2ig-0dT.js → Picker-BSOB2JM_.js} +1 -1
- package/web/dist/assets/ProjectView-CVt28SoI.js +1 -0
- package/web/dist/assets/{ProposalView-B5yjTAcn.js → ProposalView-DJ0aJm7J.js} +1 -1
- package/web/dist/assets/{ProposalsView-p4KjwV9p.js → ProposalsView-C0r1ya8_.js} +1 -1
- package/web/dist/assets/{Quota-CfFGIql9.js → Quota-59QJZrPa.js} +1 -1
- package/web/dist/assets/{RunnersView-DdvwoOur.js → RunnersView-CcAAfw6y.js} +1 -1
- package/web/dist/assets/{SkillFileList-CgrCnuaC.js → SkillFileList-Cs_f3RPo.js} +1 -1
- package/web/dist/assets/{SkillView-Di5Z9h2L.js → SkillView-BvVx3LSL.js} +1 -1
- package/web/dist/assets/{SocietyView-BDiVxUC5.js → SocietyView-CJRrypjI.js} +1 -1
- package/web/dist/assets/{StickyList-BaV_KZ8x.js → StickyList-BbGq82Jj.js} +1 -1
- package/web/dist/assets/{SubjectThread-DD_ZCCnr.js → SubjectThread-DdkyhVqK.js} +1 -1
- package/web/dist/assets/{TaskView-CKcs-MOC.js → TaskView-CyEWpCvS.js} +1 -1
- package/web/dist/assets/{TasksView-oZMjncOU.js → TasksView-D10XP1uk.js} +1 -1
- package/web/dist/assets/{ThreadForms-CG5Y926b.js → ThreadForms-D94Jj0ES.js} +1 -1
- package/web/dist/assets/{ThreadItem-D-n2arFA.js → ThreadItem-CYmdCPOf.js} +1 -1
- package/web/dist/assets/{ThreadView-CT9vd0P4.js → ThreadView-D79G2zgF.js} +1 -1
- package/web/dist/assets/{citizen-BiHfph2A.js → citizen-CgA4vJjy.js} +1 -1
- package/web/dist/assets/{compose-DY1WL8zB.js → compose-3ksQaHF6.js} +1 -1
- package/web/dist/assets/format-DpQyO6r1.js +55 -0
- package/web/dist/assets/{governance-C6ez5MLh.js → governance-YialJVYK.js} +1 -1
- package/web/dist/assets/{index-CPXALUV6.js → index-DhuCp-VF.js} +3 -3
- package/web/dist/assets/{mentions-BEL5U6ra.js → mentions-Buc5vs-j.js} +1 -1
- package/web/dist/assets/{tasks-CAI8Eci7.js → tasks-DLTeaHys.js} +1 -1
- package/web/dist/assets/{useMatch-CINzyDWa.js → useMatch-DPEg8ohe.js} +1 -1
- package/web/dist/index.html +8 -8
- package/web/dist/assets/ProjectView-T2OHTh5j.js +0 -1
- package/web/dist/assets/format-CENiwkJC.js +0 -55
package/dist/cli/index.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// apps/cli/dist/index.js
|
|
4
4
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
5
|
-
import
|
|
5
|
+
import path9 from "node:path";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// packages/board-core/dist/board.js
|
|
9
|
-
import { chmod as chmod2, cp, mkdir as
|
|
10
|
-
import
|
|
9
|
+
import { chmod as chmod2, cp, mkdir as mkdir5, readdir as readdir4, readFile as readFile4, rename as rename3, rm as rm3, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
|
|
10
|
+
import path8 from "node:path";
|
|
11
11
|
import { decodeTime, monotonicFactory } from "ulid";
|
|
12
12
|
import { z as z16 } from "zod";
|
|
13
13
|
|
|
@@ -1190,6 +1190,7 @@ var BOARD_EVENT_TYPES = [
|
|
|
1190
1190
|
"project.configured",
|
|
1191
1191
|
"project.archived",
|
|
1192
1192
|
"project.placed",
|
|
1193
|
+
"project.moved",
|
|
1193
1194
|
"agent.added",
|
|
1194
1195
|
"agent.configured",
|
|
1195
1196
|
"agent.placed",
|
|
@@ -1370,6 +1371,11 @@ var RunnerHelloSchema = z9.object({
|
|
|
1370
1371
|
quotaReads: z9.boolean().default(false),
|
|
1371
1372
|
/** Whether the runner reads an image a turn running there uploads; one from before never does. */
|
|
1372
1373
|
imageReads: z9.boolean().default(false),
|
|
1374
|
+
/**
|
|
1375
|
+
* Whether the runner carries a project's repository out to another runner and in from one, and
|
|
1376
|
+
* removes its copy once the project has moved; one from before never does.
|
|
1377
|
+
*/
|
|
1378
|
+
projectMoves: z9.boolean().default(false),
|
|
1373
1379
|
capabilities: z9.array(z9.string()).default([]),
|
|
1374
1380
|
/**
|
|
1375
1381
|
* What the runner's account lacks of the git identity citizens' own commits carry, checked
|
|
@@ -1471,6 +1477,11 @@ var TurnJobSchema = z9.object({
|
|
|
1471
1477
|
session: z9.string().min(1).nullable(),
|
|
1472
1478
|
/** The running total the session reported after its last turn, which a resumed session starts from. */
|
|
1473
1479
|
costSoFarUsd: z9.number().nonnegative(),
|
|
1480
|
+
/**
|
|
1481
|
+
* The session's running usage after its last turn, which a resumed session's own usage is
|
|
1482
|
+
* measured from. Absent when the board holds none for the session, and from a server from before.
|
|
1483
|
+
*/
|
|
1484
|
+
usageSoFar: UsageSchema.optional(),
|
|
1474
1485
|
/** The rendered charter, norms, memory core, and skills index, with `PATH_TOKENS` for places. */
|
|
1475
1486
|
instructions: z9.string(),
|
|
1476
1487
|
prompt: z9.string(),
|
|
@@ -1499,6 +1510,17 @@ var TurnOutcomeSchema = z9.object({
|
|
|
1499
1510
|
costUsd: z9.number().nonnegative(),
|
|
1500
1511
|
/** The session's running total after this turn, for CLIs that report one. */
|
|
1501
1512
|
sessionCostUsd: z9.number().nonnegative().optional(),
|
|
1513
|
+
/**
|
|
1514
|
+
* The session's running usage after this turn, for CLIs that report one, else the job's
|
|
1515
|
+
* `usageSoFar`. A runner from before never sends it.
|
|
1516
|
+
*/
|
|
1517
|
+
sessionUsage: UsageSchema.optional(),
|
|
1518
|
+
/**
|
|
1519
|
+
* The whole input of the turn's latest model call in the session's own thread, cached or not:
|
|
1520
|
+
* how large the session had grown. Sub-agents' calls are left out. A turn that made no call,
|
|
1521
|
+
* and a runner from before, never send it.
|
|
1522
|
+
*/
|
|
1523
|
+
contextTokens: z9.number().int().nonnegative().optional(),
|
|
1502
1524
|
/**
|
|
1503
1525
|
* The session the CLI used, which differs from the job's for CLIs that assign their own ids. A
|
|
1504
1526
|
* turn whose CLI never started, because it failed or was stopped first, reports the job's own,
|
|
@@ -1658,6 +1680,43 @@ var HeldWorkspaceSchema = z9.object({
|
|
|
1658
1680
|
});
|
|
1659
1681
|
var HeldWorkspacesSchema = z9.object({ workspaces: z9.array(HeldWorkspaceSchema) });
|
|
1660
1682
|
var SweepResultSchema = z9.object({ kept: z9.array(HeldWorkspaceSchema) });
|
|
1683
|
+
var MOVE_PACK_LIMIT_BYTES = 256 * 1024 * 1024;
|
|
1684
|
+
var RepoRefsSchema = z9.record(z9.string().regex(/^refs\/(heads|tags)\/\S+$/), z9.string().regex(/^[0-9a-f]{40}([0-9a-f]{24})?$/));
|
|
1685
|
+
var ProjectExportSchema = z9.object({ move: UlidSchema, repo: ProjectRepoSchema });
|
|
1686
|
+
var HeldWorkSchema = z9.object({
|
|
1687
|
+
agent: NameSchema,
|
|
1688
|
+
conversation: WorkspaceConversationSchema.nullable(),
|
|
1689
|
+
uncommitted: z9.number().int().nonnegative(),
|
|
1690
|
+
onNoBranch: z9.number().int().nonnegative()
|
|
1691
|
+
});
|
|
1692
|
+
var ProjectExportedSchema = z9.discriminatedUnion("ok", [
|
|
1693
|
+
z9.object({
|
|
1694
|
+
ok: z9.literal(true),
|
|
1695
|
+
refs: RepoRefsSchema,
|
|
1696
|
+
bytes: z9.number().int().nonnegative()
|
|
1697
|
+
}),
|
|
1698
|
+
z9.object({
|
|
1699
|
+
ok: z9.literal(false),
|
|
1700
|
+
held: z9.array(HeldWorkSchema),
|
|
1701
|
+
bytes: z9.number().int().nonnegative()
|
|
1702
|
+
})
|
|
1703
|
+
]);
|
|
1704
|
+
var ProjectImportSchema = z9.object({
|
|
1705
|
+
move: UlidSchema,
|
|
1706
|
+
repo: ProjectRepoSchema,
|
|
1707
|
+
pullRequests: z9.boolean()
|
|
1708
|
+
});
|
|
1709
|
+
var ProjectImportedSchema = z9.object({ refs: RepoRefsSchema });
|
|
1710
|
+
var ProjectReleaseSchema = z9.object({ slug: NameSchema });
|
|
1711
|
+
var ProjectReleasedSchema = z9.object({
|
|
1712
|
+
worktrees: z9.number().int().nonnegative(),
|
|
1713
|
+
clone: z9.boolean()
|
|
1714
|
+
});
|
|
1715
|
+
var ProjectMovedSchema = z9.object({
|
|
1716
|
+
project: ProjectSchema,
|
|
1717
|
+
carried: z9.number().int().nonnegative().nullable(),
|
|
1718
|
+
released: z9.object({ ok: z9.boolean(), detail: z9.string() }).nullable()
|
|
1719
|
+
});
|
|
1661
1720
|
var TurnSteerSchema = z9.object({ id: z9.uuid(), text: z9.string().min(1) });
|
|
1662
1721
|
var RunnerMessageSchema = z9.discriminatedUnion("type", [
|
|
1663
1722
|
z9.object({ type: z9.literal("turn"), job: TurnJobSchema }),
|
|
@@ -1694,6 +1753,19 @@ var RunnerMessageSchema = z9.discriminatedUnion("type", [
|
|
|
1694
1753
|
}),
|
|
1695
1754
|
/** Answered with whether the turn was there to stop; it ends `stopped`. */
|
|
1696
1755
|
z9.object({ type: z9.literal("stop"), request: z9.string().min(1), turnId: UlidSchema }),
|
|
1756
|
+
/**
|
|
1757
|
+
* Answered with a `ProjectExported`. Sent only to runners whose hello says `projectMoves`, as are
|
|
1758
|
+
* the import and the release.
|
|
1759
|
+
*/
|
|
1760
|
+
z9.object({ type: z9.literal("export"), request: z9.string().min(1), export: ProjectExportSchema }),
|
|
1761
|
+
/** Answered with a `ProjectImported`. */
|
|
1762
|
+
z9.object({ type: z9.literal("import"), request: z9.string().min(1), import: ProjectImportSchema }),
|
|
1763
|
+
/** Answered with a `ProjectReleased`. */
|
|
1764
|
+
z9.object({
|
|
1765
|
+
type: z9.literal("release"),
|
|
1766
|
+
request: z9.string().min(1),
|
|
1767
|
+
release: ProjectReleaseSchema
|
|
1768
|
+
}),
|
|
1697
1769
|
/**
|
|
1698
1770
|
* A conversation ended: the runner removes every citizen's workspace for it but those a turn
|
|
1699
1771
|
* still uses, and answers with a `SweepResult` naming those it kept for holding work on no branch.
|
|
@@ -1808,6 +1880,16 @@ var TurnRecordSchema = z10.object({
|
|
|
1808
1880
|
* resumed session starts from it, so the next turn's own cost is measured from here.
|
|
1809
1881
|
*/
|
|
1810
1882
|
sessionCostUsd: z10.number().nonnegative().optional(),
|
|
1883
|
+
/**
|
|
1884
|
+
* The running usage the CLI reported for the session after this turn, when the runner reports
|
|
1885
|
+
* one. A resumed session's next turn measures its own usage from here.
|
|
1886
|
+
*/
|
|
1887
|
+
sessionUsage: UsageSchema.optional(),
|
|
1888
|
+
/**
|
|
1889
|
+
* The whole input of the turn's latest model call in the session's own thread, as the runner
|
|
1890
|
+
* reported it: how large the session had grown. Absent for a turn that made no call.
|
|
1891
|
+
*/
|
|
1892
|
+
contextTokens: z10.number().int().nonnegative().optional(),
|
|
1811
1893
|
toolCalls: z10.number().int().nonnegative().default(0),
|
|
1812
1894
|
/** The model the CLI reported for this turn, else the model configured for the agent, else null. */
|
|
1813
1895
|
model: z10.string().nullable().default(null),
|
|
@@ -1821,7 +1903,10 @@ var TurnRecordSchema = z10.object({
|
|
|
1821
1903
|
transient: z10.boolean().optional(),
|
|
1822
1904
|
/** How many turns in a row, this one included, could not resume the session for a `retry`. */
|
|
1823
1905
|
resumeRetries: z10.number().int().positive().optional(),
|
|
1824
|
-
/**
|
|
1906
|
+
/**
|
|
1907
|
+
* Whether the server dropped the conversation's session after this turn, which the next starts
|
|
1908
|
+
* anew: one its CLI could not resume, or a society role's home after a reflection completed.
|
|
1909
|
+
*/
|
|
1825
1910
|
sessionDropped: z10.boolean().optional(),
|
|
1826
1911
|
/** The crons whose fire started the turn, which record how it ended. */
|
|
1827
1912
|
crons: z10.array(UlidSchema).optional()
|
|
@@ -2034,11 +2119,17 @@ var TokenUseSchema = z12.object({
|
|
|
2034
2119
|
// packages/shared/dist/config.js
|
|
2035
2120
|
import { z as z13 } from "zod";
|
|
2036
2121
|
var LogLevelSchema = z13.enum(["trace", "debug", "info", "warn", "error", "fatal"]);
|
|
2122
|
+
var AppleAppIdSchema = z13.string().regex(/^[A-Z0-9]{10}\.[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*$/, "not <team id>.<bundle id>");
|
|
2037
2123
|
var GithubSignInSchema = z13.object({
|
|
2038
2124
|
clientId: z13.string().min(1),
|
|
2039
2125
|
clientSecret: z13.string().min(1),
|
|
2040
2126
|
/** Logins, lowercased; GitHub logins are case-insensitive. */
|
|
2041
|
-
users: z13.array(z13.string().min(1)).min(1)
|
|
2127
|
+
users: z13.array(z13.string().min(1)).min(1),
|
|
2128
|
+
/**
|
|
2129
|
+
* The iOS app that may sign in, which Apple's association file names so that only it can take the
|
|
2130
|
+
* board's `https` landing; null when no app signs in.
|
|
2131
|
+
*/
|
|
2132
|
+
appleAppId: AppleAppIdSchema.nullable()
|
|
2042
2133
|
});
|
|
2043
2134
|
var ServerConfigSchema = z13.object({
|
|
2044
2135
|
dataDir: z13.string().min(1),
|
|
@@ -2084,7 +2175,7 @@ var RunnerConfigSchema = z13.object({
|
|
|
2084
2175
|
});
|
|
2085
2176
|
|
|
2086
2177
|
// packages/shared/dist/version.js
|
|
2087
|
-
var STELLARIS_VERSION = "0.3.
|
|
2178
|
+
var STELLARIS_VERSION = "0.3.27";
|
|
2088
2179
|
|
|
2089
2180
|
// packages/shared/dist/mentions.js
|
|
2090
2181
|
import { parse, postprocess, preprocess } from "micromark";
|
|
@@ -2776,6 +2867,41 @@ function filesOf(lines, status) {
|
|
|
2776
2867
|
return [...files.values()];
|
|
2777
2868
|
}
|
|
2778
2869
|
|
|
2870
|
+
// packages/board-core/dist/moves.js
|
|
2871
|
+
import { mkdir as mkdir4, rm as rm2 } from "node:fs/promises";
|
|
2872
|
+
import path5 from "node:path";
|
|
2873
|
+
import { execa as execa2 } from "execa";
|
|
2874
|
+
var MoveRepos = class {
|
|
2875
|
+
root;
|
|
2876
|
+
constructor(root) {
|
|
2877
|
+
this.root = root;
|
|
2878
|
+
}
|
|
2879
|
+
/** Makes the move's empty repository, which checks every object a push brings, and answers its path. */
|
|
2880
|
+
async open(dir) {
|
|
2881
|
+
await mkdir4(this.root, { recursive: true });
|
|
2882
|
+
await git2(["init", "--quiet", "--bare", dir], this.root);
|
|
2883
|
+
await git2(["config", "receive.fsckObjects", "true"], dir);
|
|
2884
|
+
return dir;
|
|
2885
|
+
}
|
|
2886
|
+
async drop(dir) {
|
|
2887
|
+
await rm2(path5.resolve(this.root, dir), { recursive: true, force: true });
|
|
2888
|
+
}
|
|
2889
|
+
/** Removes every move's repository, as the server starts: a move never survives a restart. */
|
|
2890
|
+
async clear() {
|
|
2891
|
+
await rm2(this.root, { recursive: true, force: true });
|
|
2892
|
+
}
|
|
2893
|
+
};
|
|
2894
|
+
async function git2(args, cwd) {
|
|
2895
|
+
const result = await execa2("git", args, {
|
|
2896
|
+
cwd,
|
|
2897
|
+
reject: false,
|
|
2898
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
2899
|
+
});
|
|
2900
|
+
if (result.exitCode !== 0) {
|
|
2901
|
+
throw new Error(`git ${args[0] ?? ""} failed: ${result.stderr.trim()}`);
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2779
2905
|
// packages/board-core/dist/mentions.js
|
|
2780
2906
|
import vm from "node:vm";
|
|
2781
2907
|
var MENTION_PARSE_MS = 500;
|
|
@@ -3189,121 +3315,121 @@ var Mutex = class {
|
|
|
3189
3315
|
};
|
|
3190
3316
|
|
|
3191
3317
|
// packages/board-core/dist/paths.js
|
|
3192
|
-
import
|
|
3318
|
+
import path6 from "node:path";
|
|
3193
3319
|
var BoardPaths = class {
|
|
3194
3320
|
dataDir;
|
|
3195
3321
|
constructor(dataDir) {
|
|
3196
|
-
this.dataDir =
|
|
3322
|
+
this.dataDir = path6.resolve(dataDir);
|
|
3197
3323
|
}
|
|
3198
3324
|
// Board projection, also the storage of record; only the core library writes it.
|
|
3199
3325
|
get board() {
|
|
3200
|
-
return
|
|
3326
|
+
return path6.join(this.dataDir, "board");
|
|
3201
3327
|
}
|
|
3202
3328
|
get society() {
|
|
3203
|
-
return
|
|
3329
|
+
return path6.join(this.board, "society");
|
|
3204
3330
|
}
|
|
3205
3331
|
societyFile() {
|
|
3206
|
-
return
|
|
3332
|
+
return path6.join(this.society, "society.md");
|
|
3207
3333
|
}
|
|
3208
3334
|
societyChannels() {
|
|
3209
|
-
return
|
|
3335
|
+
return path6.join(this.society, "channels");
|
|
3210
3336
|
}
|
|
3211
3337
|
societyChannel(name) {
|
|
3212
|
-
return
|
|
3338
|
+
return path6.join(this.societyChannels(), name);
|
|
3213
3339
|
}
|
|
3214
3340
|
/** Threads whose channel is a society channel; project channels' threads live under the project. */
|
|
3215
3341
|
societyThreads() {
|
|
3216
|
-
return
|
|
3342
|
+
return path6.join(this.society, "threads");
|
|
3217
3343
|
}
|
|
3218
3344
|
societyKnowledge() {
|
|
3219
|
-
return
|
|
3345
|
+
return path6.join(this.society, "knowledge");
|
|
3220
3346
|
}
|
|
3221
3347
|
societyKnowledgeFile(topic) {
|
|
3222
|
-
return
|
|
3348
|
+
return path6.join(this.societyKnowledge(), `${topic}.md`);
|
|
3223
3349
|
}
|
|
3224
3350
|
/** Skills promoted to the society, one directory per skill holding SKILL.md. */
|
|
3225
3351
|
societySkills() {
|
|
3226
|
-
return
|
|
3352
|
+
return path6.join(this.society, "skills");
|
|
3227
3353
|
}
|
|
3228
3354
|
societySkill(name) {
|
|
3229
|
-
return
|
|
3355
|
+
return path6.join(this.societySkills(), name);
|
|
3230
3356
|
}
|
|
3231
3357
|
societySkillFile(name) {
|
|
3232
|
-
return
|
|
3358
|
+
return path6.join(this.societySkill(name), "SKILL.md");
|
|
3233
3359
|
}
|
|
3234
3360
|
roles() {
|
|
3235
|
-
return
|
|
3361
|
+
return path6.join(this.society, "roles");
|
|
3236
3362
|
}
|
|
3237
3363
|
role(name) {
|
|
3238
|
-
return
|
|
3364
|
+
return path6.join(this.roles(), `${name}.md`);
|
|
3239
3365
|
}
|
|
3240
3366
|
proposals() {
|
|
3241
|
-
return
|
|
3367
|
+
return path6.join(this.society, "proposals");
|
|
3242
3368
|
}
|
|
3243
3369
|
proposal(id) {
|
|
3244
|
-
return
|
|
3370
|
+
return path6.join(this.proposals(), `${id}.md`);
|
|
3245
3371
|
}
|
|
3246
3372
|
/** A skill proposal's folder, as the board took it from its proposer's home. */
|
|
3247
3373
|
proposalFiles(id) {
|
|
3248
|
-
return
|
|
3374
|
+
return path6.join(this.proposals(), id);
|
|
3249
3375
|
}
|
|
3250
3376
|
decisions() {
|
|
3251
|
-
return
|
|
3377
|
+
return path6.join(this.society, "decisions");
|
|
3252
3378
|
}
|
|
3253
3379
|
decision(id) {
|
|
3254
|
-
return
|
|
3380
|
+
return path6.join(this.decisions(), `${id}.md`);
|
|
3255
3381
|
}
|
|
3256
3382
|
runners() {
|
|
3257
|
-
return
|
|
3383
|
+
return path6.join(this.society, "runners");
|
|
3258
3384
|
}
|
|
3259
3385
|
runner(name) {
|
|
3260
|
-
return
|
|
3386
|
+
return path6.join(this.runners(), `${name}.md`);
|
|
3261
3387
|
}
|
|
3262
3388
|
members() {
|
|
3263
|
-
return
|
|
3389
|
+
return path6.join(this.society, "members");
|
|
3264
3390
|
}
|
|
3265
3391
|
/** Crons, one file each with its note as the body; ended ones stay. */
|
|
3266
3392
|
crons() {
|
|
3267
|
-
return
|
|
3393
|
+
return path6.join(this.society, "crons");
|
|
3268
3394
|
}
|
|
3269
3395
|
cron(id) {
|
|
3270
|
-
return
|
|
3396
|
+
return path6.join(this.crons(), `${id}.md`);
|
|
3271
3397
|
}
|
|
3272
3398
|
member(name) {
|
|
3273
|
-
return
|
|
3399
|
+
return path6.join(this.members(), `${name}.md`);
|
|
3274
3400
|
}
|
|
3275
3401
|
projects() {
|
|
3276
|
-
return
|
|
3402
|
+
return path6.join(this.board, "projects");
|
|
3277
3403
|
}
|
|
3278
3404
|
project(slug) {
|
|
3279
|
-
return
|
|
3405
|
+
return path6.join(this.projects(), slug);
|
|
3280
3406
|
}
|
|
3281
3407
|
projectFile(slug) {
|
|
3282
|
-
return
|
|
3408
|
+
return path6.join(this.project(slug), "project.md");
|
|
3283
3409
|
}
|
|
3284
3410
|
projectChannels(slug) {
|
|
3285
|
-
return
|
|
3411
|
+
return path6.join(this.project(slug), "channels");
|
|
3286
3412
|
}
|
|
3287
3413
|
projectChannel(slug, name) {
|
|
3288
|
-
return
|
|
3414
|
+
return path6.join(this.projectChannels(slug), name);
|
|
3289
3415
|
}
|
|
3290
3416
|
threads(slug) {
|
|
3291
|
-
return
|
|
3417
|
+
return path6.join(this.project(slug), "threads");
|
|
3292
3418
|
}
|
|
3293
3419
|
tasks(slug) {
|
|
3294
|
-
return
|
|
3420
|
+
return path6.join(this.project(slug), "tasks");
|
|
3295
3421
|
}
|
|
3296
3422
|
task(slug, id) {
|
|
3297
|
-
return
|
|
3423
|
+
return path6.join(this.tasks(slug), `${id}.md`);
|
|
3298
3424
|
}
|
|
3299
3425
|
projectKnowledge(slug) {
|
|
3300
|
-
return
|
|
3426
|
+
return path6.join(this.project(slug), "knowledge");
|
|
3301
3427
|
}
|
|
3302
3428
|
projectKnowledgeFile(slug, topic) {
|
|
3303
|
-
return
|
|
3429
|
+
return path6.join(this.projectKnowledge(slug), `${topic}.md`);
|
|
3304
3430
|
}
|
|
3305
3431
|
dashboard(slug) {
|
|
3306
|
-
return
|
|
3432
|
+
return path6.join(this.project(slug), "dashboard.md");
|
|
3307
3433
|
}
|
|
3308
3434
|
channelDir(ref) {
|
|
3309
3435
|
const parsed = parseChannelRef(ref);
|
|
@@ -3316,57 +3442,57 @@ var BoardPaths = class {
|
|
|
3316
3442
|
}
|
|
3317
3443
|
/** A thread's record, `<id>.md`, beside the directory of its messages, `<id>/`. */
|
|
3318
3444
|
threadFile(threads, id) {
|
|
3319
|
-
return
|
|
3445
|
+
return path6.join(threads, `${id}.md`);
|
|
3320
3446
|
}
|
|
3321
3447
|
threadMessages(threads, id) {
|
|
3322
|
-
return
|
|
3448
|
+
return path6.join(threads, id);
|
|
3323
3449
|
}
|
|
3324
3450
|
messageFile(dir, id, author) {
|
|
3325
|
-
return
|
|
3451
|
+
return path6.join(dir, `${id}-${author}.md`);
|
|
3326
3452
|
}
|
|
3327
3453
|
// Agent homes: authored by the agent, source of truth on the board server.
|
|
3328
3454
|
agents() {
|
|
3329
|
-
return
|
|
3455
|
+
return path6.join(this.dataDir, "agents");
|
|
3330
3456
|
}
|
|
3331
3457
|
agent(name) {
|
|
3332
|
-
return
|
|
3458
|
+
return path6.join(this.agents(), name);
|
|
3333
3459
|
}
|
|
3334
3460
|
agentFile(name) {
|
|
3335
|
-
return
|
|
3461
|
+
return path6.join(this.agent(name), "agent.json");
|
|
3336
3462
|
}
|
|
3337
3463
|
agentRole(name) {
|
|
3338
|
-
return
|
|
3464
|
+
return path6.join(this.agent(name), "role.md");
|
|
3339
3465
|
}
|
|
3340
3466
|
agentProfile(name) {
|
|
3341
|
-
return
|
|
3467
|
+
return path6.join(this.agent(name), "profile.md");
|
|
3342
3468
|
}
|
|
3343
3469
|
agentMemory(name) {
|
|
3344
|
-
return
|
|
3470
|
+
return path6.join(this.agent(name), "memory");
|
|
3345
3471
|
}
|
|
3346
3472
|
agentMemoryCore(name) {
|
|
3347
|
-
return
|
|
3473
|
+
return path6.join(this.agentMemory(name), "core.md");
|
|
3348
3474
|
}
|
|
3349
3475
|
/** Transcripts of the agent's finished turns, one JSON line per step. */
|
|
3350
3476
|
agentTurns(name) {
|
|
3351
|
-
return
|
|
3477
|
+
return path6.join(this.agent(name), "turns");
|
|
3352
3478
|
}
|
|
3353
3479
|
agentTranscript(name, turnId) {
|
|
3354
|
-
return
|
|
3480
|
+
return path6.join(this.agentTurns(name), `${turnId}.jsonl`);
|
|
3355
3481
|
}
|
|
3356
3482
|
agentSkills(name) {
|
|
3357
|
-
return
|
|
3483
|
+
return path6.join(this.agent(name), "skills");
|
|
3358
3484
|
}
|
|
3359
3485
|
agentSkill(name, skill2) {
|
|
3360
|
-
return
|
|
3486
|
+
return path6.join(this.agentSkills(name), skill2);
|
|
3361
3487
|
}
|
|
3362
3488
|
agentSkillFile(name, skill2) {
|
|
3363
|
-
return
|
|
3489
|
+
return path6.join(this.agentSkill(name, skill2), "SKILL.md");
|
|
3364
3490
|
}
|
|
3365
3491
|
agentProjects(name) {
|
|
3366
|
-
return
|
|
3492
|
+
return path6.join(this.agent(name), "projects");
|
|
3367
3493
|
}
|
|
3368
3494
|
agentProject(name, slug) {
|
|
3369
|
-
return
|
|
3495
|
+
return path6.join(this.agentProjects(name), slug);
|
|
3370
3496
|
}
|
|
3371
3497
|
/**
|
|
3372
3498
|
* A thread's, a channel's, or, in the society scope, a project's conversation's session and last
|
|
@@ -3374,46 +3500,56 @@ var BoardPaths = class {
|
|
|
3374
3500
|
*/
|
|
3375
3501
|
agentConversation(name, slug, part) {
|
|
3376
3502
|
if (part.startsWith("#")) {
|
|
3377
|
-
return
|
|
3503
|
+
return path6.join(this.agentProject(name, slug), "channels", part.slice(1));
|
|
3378
3504
|
}
|
|
3379
|
-
return part.startsWith("~") ?
|
|
3505
|
+
return part.startsWith("~") ? path6.join(this.agentProject(name, slug), "places", part.slice(1)) : path6.join(this.agentProject(name, slug), "threads", part);
|
|
3380
3506
|
}
|
|
3381
3507
|
agentCursors(name) {
|
|
3382
|
-
return
|
|
3508
|
+
return path6.join(this.agent(name), "cursors.json");
|
|
3383
3509
|
}
|
|
3384
3510
|
events() {
|
|
3385
|
-
return
|
|
3511
|
+
return path6.join(this.dataDir, "events");
|
|
3386
3512
|
}
|
|
3387
3513
|
eventLog() {
|
|
3388
|
-
return
|
|
3514
|
+
return path6.join(this.events(), "log.jsonl");
|
|
3389
3515
|
}
|
|
3390
3516
|
/** Images citizens uploaded, by id: outside the projection, which every runner mirrors. */
|
|
3391
3517
|
images() {
|
|
3392
|
-
return
|
|
3518
|
+
return path6.join(this.dataDir, "images");
|
|
3393
3519
|
}
|
|
3394
3520
|
image(id) {
|
|
3395
|
-
return
|
|
3521
|
+
return path6.join(this.images(), id);
|
|
3522
|
+
}
|
|
3523
|
+
/**
|
|
3524
|
+
* The repositories projects travel through between runners, one per move while it runs:
|
|
3525
|
+
* outside the projection, which every runner mirrors, and served only to the move's runners.
|
|
3526
|
+
*/
|
|
3527
|
+
moves() {
|
|
3528
|
+
return path6.join(this.dataDir, "moves");
|
|
3529
|
+
}
|
|
3530
|
+
move(id) {
|
|
3531
|
+
return path6.join(this.moves(), `${id}.git`);
|
|
3396
3532
|
}
|
|
3397
3533
|
state() {
|
|
3398
|
-
return
|
|
3534
|
+
return path6.join(this.dataDir, "state");
|
|
3399
3535
|
}
|
|
3400
3536
|
pausedFile() {
|
|
3401
|
-
return
|
|
3537
|
+
return path6.join(this.state(), "paused.json");
|
|
3402
3538
|
}
|
|
3403
3539
|
/** Removed knowledge topics, set aside outside the projection, by scope. */
|
|
3404
3540
|
removedKnowledge(scope) {
|
|
3405
|
-
return
|
|
3541
|
+
return path6.join(this.state(), "removed-knowledge", scope);
|
|
3406
3542
|
}
|
|
3407
3543
|
/** Skills removed from the society, set aside outside the projection, one directory per removal. */
|
|
3408
3544
|
removedSkills() {
|
|
3409
|
-
return
|
|
3545
|
+
return path6.join(this.state(), "removed-skills");
|
|
3410
3546
|
}
|
|
3411
3547
|
};
|
|
3412
3548
|
|
|
3413
3549
|
// packages/board-core/dist/skill-folder.js
|
|
3414
3550
|
import { constants } from "node:fs";
|
|
3415
3551
|
import { lstat, open as open2, readdir as readdir3, readlink, realpath } from "node:fs/promises";
|
|
3416
|
-
import
|
|
3552
|
+
import path7 from "node:path";
|
|
3417
3553
|
var SKILL_TEXT_LIMIT_CHARS = 1e5;
|
|
3418
3554
|
var SKILL_TEXT_LIMIT_BYTES = SKILL_TEXT_LIMIT_CHARS * 4;
|
|
3419
3555
|
function skillFileText(bytes) {
|
|
@@ -3424,8 +3560,8 @@ function skillFileText(bytes) {
|
|
|
3424
3560
|
};
|
|
3425
3561
|
}
|
|
3426
3562
|
function inside(root, file) {
|
|
3427
|
-
const relative =
|
|
3428
|
-
return relative !== ".." && !relative.startsWith(`..${
|
|
3563
|
+
const relative = path7.relative(root, file);
|
|
3564
|
+
return relative !== ".." && !relative.startsWith(`..${path7.sep}`) && !path7.isAbsolute(relative);
|
|
3429
3565
|
}
|
|
3430
3566
|
async function handleInside(root, handle) {
|
|
3431
3567
|
if (process.platform !== "linux") {
|
|
@@ -3435,7 +3571,7 @@ async function handleInside(root, handle) {
|
|
|
3435
3571
|
return file !== null && inside(root, file);
|
|
3436
3572
|
}
|
|
3437
3573
|
async function walk(root, prefix, found) {
|
|
3438
|
-
const entries = await readdir3(
|
|
3574
|
+
const entries = await readdir3(path7.join(root, ...prefix === "" ? [] : prefix.split("/")), {
|
|
3439
3575
|
withFileTypes: true
|
|
3440
3576
|
}).catch(() => []);
|
|
3441
3577
|
for (const entry of entries.toSorted((a, b) => a.name < b.name ? -1 : 1)) {
|
|
@@ -3456,7 +3592,7 @@ async function walk(root, prefix, found) {
|
|
|
3456
3592
|
async function readEntry(root, relative) {
|
|
3457
3593
|
let real;
|
|
3458
3594
|
try {
|
|
3459
|
-
real = await realpath(
|
|
3595
|
+
real = await realpath(path7.join(root, ...relative.split("/")));
|
|
3460
3596
|
} catch {
|
|
3461
3597
|
return null;
|
|
3462
3598
|
}
|
|
@@ -3772,9 +3908,11 @@ var Board = class _Board {
|
|
|
3772
3908
|
signIns = /* @__PURE__ */ new Map();
|
|
3773
3909
|
files = new FileTree();
|
|
3774
3910
|
homes;
|
|
3911
|
+
moves;
|
|
3775
3912
|
constructor(dataDir, options) {
|
|
3776
3913
|
this.paths = new BoardPaths(dataDir);
|
|
3777
|
-
this.homes = new HomeRepos(
|
|
3914
|
+
this.homes = new HomeRepos(path8.join(this.paths.state(), "home-hooks"));
|
|
3915
|
+
this.moves = new MoveRepos(this.paths.moves());
|
|
3778
3916
|
this.leaseMs = options.leaseMs ?? DEFAULT_LEASE_MS;
|
|
3779
3917
|
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
3780
3918
|
this.events = new EventLog(this.paths.eventLog(), () => this.newId(), this.now);
|
|
@@ -3800,6 +3938,7 @@ var Board = class _Board {
|
|
|
3800
3938
|
await board.loadRunnerTokens();
|
|
3801
3939
|
await board.loadSignIns();
|
|
3802
3940
|
await board.homes.installHooks();
|
|
3941
|
+
await board.moves.clear();
|
|
3803
3942
|
return board;
|
|
3804
3943
|
}
|
|
3805
3944
|
/**
|
|
@@ -3809,7 +3948,7 @@ var Board = class _Board {
|
|
|
3809
3948
|
*/
|
|
3810
3949
|
async applyAlignments() {
|
|
3811
3950
|
const alignments = [];
|
|
3812
|
-
const file =
|
|
3951
|
+
const file = path8.join(this.paths.state(), "alignments.json");
|
|
3813
3952
|
const { applied } = await exists(file) ? await readJson(file, AlignmentsSchema) : { applied: [] };
|
|
3814
3953
|
const pending = alignments.filter(({ name }) => !applied.includes(name));
|
|
3815
3954
|
if (pending.length === 0) {
|
|
@@ -4017,7 +4156,7 @@ ${charter.purpose}
|
|
|
4017
4156
|
});
|
|
4018
4157
|
}
|
|
4019
4158
|
signInsFile() {
|
|
4020
|
-
return
|
|
4159
|
+
return path8.join(this.paths.state(), "sign-ins.json");
|
|
4021
4160
|
}
|
|
4022
4161
|
async readSignIns() {
|
|
4023
4162
|
const file = this.signInsFile();
|
|
@@ -4114,7 +4253,7 @@ ${charter.purpose}
|
|
|
4114
4253
|
const files = await listFiles(this.paths.tasks(project2));
|
|
4115
4254
|
const tasks = [];
|
|
4116
4255
|
for (const file of files) {
|
|
4117
|
-
const doc = await readMarkdown(
|
|
4256
|
+
const doc = await readMarkdown(path8.join(this.paths.tasks(project2), file), TaskFrontmatterSchema);
|
|
4118
4257
|
tasks.push({ ...doc.data, body: doc.body });
|
|
4119
4258
|
}
|
|
4120
4259
|
return tasks;
|
|
@@ -4133,7 +4272,7 @@ ${charter.purpose}
|
|
|
4133
4272
|
const files = await listFiles(this.paths.proposals());
|
|
4134
4273
|
const proposals = [];
|
|
4135
4274
|
for (const file of files) {
|
|
4136
|
-
const doc = await readMarkdown(
|
|
4275
|
+
const doc = await readMarkdown(path8.join(this.paths.proposals(), file), ProposalFrontmatterSchema);
|
|
4137
4276
|
proposals.push({ ...doc.data, body: doc.body });
|
|
4138
4277
|
}
|
|
4139
4278
|
return proposals;
|
|
@@ -4160,7 +4299,7 @@ ${charter.purpose}
|
|
|
4160
4299
|
async listMembers() {
|
|
4161
4300
|
const members = [];
|
|
4162
4301
|
for (const file of await listFiles(this.paths.members())) {
|
|
4163
|
-
const doc = await readMarkdown(
|
|
4302
|
+
const doc = await readMarkdown(path8.join(this.paths.members(), file), MemberSchema);
|
|
4164
4303
|
members.push({ ...doc.data, profile: doc.body });
|
|
4165
4304
|
}
|
|
4166
4305
|
return members;
|
|
@@ -4179,7 +4318,7 @@ ${charter.purpose}
|
|
|
4179
4318
|
const dir = project2 === null ? this.paths.societyKnowledge() : this.paths.projectKnowledge(project2);
|
|
4180
4319
|
const topics = [];
|
|
4181
4320
|
for (const file of await listFiles(dir)) {
|
|
4182
|
-
topics.push(await this.knowledgeAt(
|
|
4321
|
+
topics.push(await this.knowledgeAt(path8.join(dir, file), project2, file.replace(/\.md$/, "")));
|
|
4183
4322
|
}
|
|
4184
4323
|
return topics;
|
|
4185
4324
|
}
|
|
@@ -4209,7 +4348,7 @@ ${charter.purpose}
|
|
|
4209
4348
|
const aside = this.paths.removedKnowledge(project2 ?? SOCIETY_SCOPE);
|
|
4210
4349
|
let highest = 0;
|
|
4211
4350
|
for (const file of (await listFiles(aside)).filter((name) => name.startsWith(`${topic}.`))) {
|
|
4212
|
-
const copy = await this.knowledgeAt(
|
|
4351
|
+
const copy = await this.knowledgeAt(path8.join(aside, file), project2, topic);
|
|
4213
4352
|
highest = Math.max(highest, copy.revision);
|
|
4214
4353
|
}
|
|
4215
4354
|
return highest;
|
|
@@ -4225,7 +4364,7 @@ ${charter.purpose}
|
|
|
4225
4364
|
async listSkillsIn(dir, scope) {
|
|
4226
4365
|
const skills = [];
|
|
4227
4366
|
for (const entry of await listDirs(dir)) {
|
|
4228
|
-
const file =
|
|
4367
|
+
const file = path8.join(dir, entry, "SKILL.md");
|
|
4229
4368
|
if (!await exists(file)) {
|
|
4230
4369
|
continue;
|
|
4231
4370
|
}
|
|
@@ -4315,7 +4454,7 @@ ${charter.purpose}
|
|
|
4315
4454
|
if (!wanted.delete(file.slice(0, 26))) {
|
|
4316
4455
|
continue;
|
|
4317
4456
|
}
|
|
4318
|
-
const { data } = await readMarkdown(
|
|
4457
|
+
const { data } = await readMarkdown(path8.join(dir, file), MessageFrontmatterSchema);
|
|
4319
4458
|
found.push(MessageRefSchema.parse(data));
|
|
4320
4459
|
}
|
|
4321
4460
|
if (wanted.size === 0) {
|
|
@@ -4347,7 +4486,7 @@ ${charter.purpose}
|
|
|
4347
4486
|
if (!file.endsWith(".md")) {
|
|
4348
4487
|
continue;
|
|
4349
4488
|
}
|
|
4350
|
-
const doc = await readMarkdown(
|
|
4489
|
+
const doc = await readMarkdown(path8.join(threads, file), ThreadFrontmatterSchema);
|
|
4351
4490
|
if (doc.data.state !== "open") {
|
|
4352
4491
|
continue;
|
|
4353
4492
|
}
|
|
@@ -4374,7 +4513,7 @@ ${charter.purpose}
|
|
|
4374
4513
|
const all = [];
|
|
4375
4514
|
for (const threads of await this.threadScopes()) {
|
|
4376
4515
|
for (const file of await listFiles(threads)) {
|
|
4377
|
-
const doc = await readMarkdown(
|
|
4516
|
+
const doc = await readMarkdown(path8.join(threads, file), ThreadFrontmatterSchema);
|
|
4378
4517
|
const messages = await listFiles(this.paths.threadMessages(threads, doc.data.id));
|
|
4379
4518
|
const last = messages.at(-1);
|
|
4380
4519
|
all.push({
|
|
@@ -4969,7 +5108,7 @@ ${task2.body}`;
|
|
|
4969
5108
|
}
|
|
4970
5109
|
}
|
|
4971
5110
|
for (const file of await listFiles(this.paths.projectKnowledge(project2))) {
|
|
4972
|
-
const content = await readFile4(
|
|
5111
|
+
const content = await readFile4(path8.join(this.paths.projectKnowledge(project2), file), "utf8");
|
|
4973
5112
|
if (content.toLowerCase().includes(needle)) {
|
|
4974
5113
|
hits.push({
|
|
4975
5114
|
kind: "knowledge",
|
|
@@ -4990,7 +5129,7 @@ ${task2.body}`;
|
|
|
4990
5129
|
shared.push({
|
|
4991
5130
|
kind: "knowledge",
|
|
4992
5131
|
ref: `society/knowledge/${file}`,
|
|
4993
|
-
file:
|
|
5132
|
+
file: path8.join(this.paths.societyKnowledge(), file)
|
|
4994
5133
|
});
|
|
4995
5134
|
}
|
|
4996
5135
|
for (const skill2 of await this.listSocietySkills()) {
|
|
@@ -5003,7 +5142,7 @@ ${task2.body}`;
|
|
|
5003
5142
|
shared.push({
|
|
5004
5143
|
kind: "memory",
|
|
5005
5144
|
ref: `memory/${file}`,
|
|
5006
|
-
file:
|
|
5145
|
+
file: path8.join(this.paths.agentMemory(actor.name), file)
|
|
5007
5146
|
});
|
|
5008
5147
|
}
|
|
5009
5148
|
for (const entry of shared) {
|
|
@@ -5092,8 +5231,8 @@ ${current.body}`);
|
|
|
5092
5231
|
removedAt: this.now().toISOString()
|
|
5093
5232
|
});
|
|
5094
5233
|
const aside = this.paths.removedKnowledge(args.project ?? SOCIETY_SCOPE);
|
|
5095
|
-
await
|
|
5096
|
-
await rename3(file,
|
|
5234
|
+
await mkdir5(aside, { recursive: true });
|
|
5235
|
+
await rename3(file, path8.join(aside, `${args.topic}.${removed.removedAt.replaceAll(":", "-")}.md`));
|
|
5097
5236
|
await this.events.append("knowledge.removed", actor.name, {
|
|
5098
5237
|
topic: args.topic,
|
|
5099
5238
|
project: args.project
|
|
@@ -5140,8 +5279,8 @@ ${current.body}`);
|
|
|
5140
5279
|
removedAt: this.now().toISOString()
|
|
5141
5280
|
});
|
|
5142
5281
|
const aside = this.paths.removedSkills();
|
|
5143
|
-
await
|
|
5144
|
-
await rename3(this.paths.societySkill(args.name),
|
|
5282
|
+
await mkdir5(aside, { recursive: true });
|
|
5283
|
+
await rename3(this.paths.societySkill(args.name), path8.join(aside, `${args.name}.${removed.removedAt.replaceAll(":", "-")}`));
|
|
5145
5284
|
await this.events.append("skill.removed", actor.name, {
|
|
5146
5285
|
name: args.name,
|
|
5147
5286
|
reason: args.reason
|
|
@@ -5218,7 +5357,7 @@ ${current.body}`);
|
|
|
5218
5357
|
type,
|
|
5219
5358
|
size: bytes.byteLength
|
|
5220
5359
|
});
|
|
5221
|
-
const name =
|
|
5360
|
+
const name = path8.basename(args.path).replaceAll(/[[\]\\\r\n]/g, "");
|
|
5222
5361
|
return {
|
|
5223
5362
|
id,
|
|
5224
5363
|
type,
|
|
@@ -5244,7 +5383,7 @@ ${current.body}`);
|
|
|
5244
5383
|
async listCrons() {
|
|
5245
5384
|
const crons = [];
|
|
5246
5385
|
for (const file of await listFiles(this.paths.crons())) {
|
|
5247
|
-
crons.push(await this.readCronFile(
|
|
5386
|
+
crons.push(await this.readCronFile(path8.join(this.paths.crons(), file)));
|
|
5248
5387
|
}
|
|
5249
5388
|
return crons;
|
|
5250
5389
|
}
|
|
@@ -5591,7 +5730,7 @@ ${current.body}`);
|
|
|
5591
5730
|
async openThreadIds(dir, keep) {
|
|
5592
5731
|
const ids = /* @__PURE__ */ new Set();
|
|
5593
5732
|
for (const file of await listFiles(dir)) {
|
|
5594
|
-
const doc = await readMarkdown(
|
|
5733
|
+
const doc = await readMarkdown(path8.join(dir, file), ThreadFrontmatterSchema);
|
|
5595
5734
|
if (doc.data.state === "open" && keep({ ...doc.data, body: doc.body })) {
|
|
5596
5735
|
ids.add(doc.data.id);
|
|
5597
5736
|
}
|
|
@@ -5599,12 +5738,12 @@ ${current.body}`);
|
|
|
5599
5738
|
return ids;
|
|
5600
5739
|
}
|
|
5601
5740
|
/**
|
|
5602
|
-
* Refuses to archive a project while a turn runs in it, in a citizen's conversation of
|
|
5603
|
-
* channels in the society scope, or in
|
|
5741
|
+
* Refuses to archive or move a project while a turn runs in it, in a citizen's conversation of
|
|
5742
|
+
* its channels in the society scope, or in one of its open threads.
|
|
5604
5743
|
*/
|
|
5605
|
-
async assertProjectQuiet(actor, slug) {
|
|
5744
|
+
async assertProjectQuiet(actor, slug, what = `project ${slug} cannot be archived`) {
|
|
5606
5745
|
const threads = await this.openThreadIds(this.paths.threads(slug), () => true);
|
|
5607
|
-
this.assertNoTurnCut(actor,
|
|
5746
|
+
this.assertNoTurnCut(actor, what, (turn2) => turn2.scope === slug || turn2.place === slug || turn2.thread !== void 0 && threads.has(turn2.thread));
|
|
5608
5747
|
}
|
|
5609
5748
|
async closeThread(actor, input) {
|
|
5610
5749
|
const args = VerbInputs.close_thread.parse(input);
|
|
@@ -6571,7 +6710,7 @@ ${rationale}`}`);
|
|
|
6571
6710
|
return part === void 0 ? this.paths.agentProject(agent2, scope) : this.paths.agentConversation(agent2, scope, part);
|
|
6572
6711
|
}
|
|
6573
6712
|
async readSessions(agent2, project2, part) {
|
|
6574
|
-
const file =
|
|
6713
|
+
const file = path8.join(this.sessionDir(agent2, project2, part), "sessions.json");
|
|
6575
6714
|
return await exists(file) ? readJson(file, SessionsFileSchema) : {};
|
|
6576
6715
|
}
|
|
6577
6716
|
/**
|
|
@@ -6582,7 +6721,7 @@ ${rationale}`}`);
|
|
|
6582
6721
|
await this.mutex.run(async () => {
|
|
6583
6722
|
const dir = this.sessionDir(agent2, project2, part);
|
|
6584
6723
|
await ensureDir(dir);
|
|
6585
|
-
const file =
|
|
6724
|
+
const file = path8.join(dir, "sessions.json");
|
|
6586
6725
|
const current = await exists(file) ? await readJson(file, SessionsFileSchema) : {};
|
|
6587
6726
|
const kept = current.runner === void 0 || current.runner === runner2 ? current : {};
|
|
6588
6727
|
await writeJson(file, { ...kept, [cli]: sessionId, runner: runner2 });
|
|
@@ -6594,7 +6733,7 @@ ${rationale}`}`);
|
|
|
6594
6733
|
*/
|
|
6595
6734
|
async dropSession(agent2, project2, cli, sessionId, part) {
|
|
6596
6735
|
return this.mutex.run(async () => {
|
|
6597
|
-
const file =
|
|
6736
|
+
const file = path8.join(this.sessionDir(agent2, project2, part), "sessions.json");
|
|
6598
6737
|
if (!await exists(file)) {
|
|
6599
6738
|
return false;
|
|
6600
6739
|
}
|
|
@@ -6609,7 +6748,7 @@ ${rationale}`}`);
|
|
|
6609
6748
|
}
|
|
6610
6749
|
/** The last turn of one conversation: the scope's home, a channel's, a project's, or a thread's. */
|
|
6611
6750
|
async readLastTurn(agent2, project2, part) {
|
|
6612
|
-
const file =
|
|
6751
|
+
const file = path8.join(this.sessionDir(agent2, project2, part), "last-turn.json");
|
|
6613
6752
|
return await exists(file) ? readJson(file, TurnRecordSchema) : null;
|
|
6614
6753
|
}
|
|
6615
6754
|
/** The latest turn a citizen took in a scope, in any of its conversations there. */
|
|
@@ -6617,9 +6756,9 @@ ${rationale}`}`);
|
|
|
6617
6756
|
let latest = await this.readLastTurn(agent2, project2);
|
|
6618
6757
|
const place = this.paths.agentProject(agent2, project2);
|
|
6619
6758
|
const parts = [
|
|
6620
|
-
...await listDirs(
|
|
6621
|
-
...(await listDirs(
|
|
6622
|
-
...(await listDirs(
|
|
6759
|
+
...await listDirs(path8.join(place, "threads")),
|
|
6760
|
+
...(await listDirs(path8.join(place, "channels"))).map((name) => `#${name}`),
|
|
6761
|
+
...(await listDirs(path8.join(place, "places"))).map((name) => `~${name}`)
|
|
6623
6762
|
];
|
|
6624
6763
|
for (const part of parts) {
|
|
6625
6764
|
const turn2 = await this.readLastTurn(agent2, project2, part);
|
|
@@ -6685,6 +6824,7 @@ ${rationale}`}`);
|
|
|
6685
6824
|
exitReason: parsed.exitReason,
|
|
6686
6825
|
costUsd: parsed.costUsd,
|
|
6687
6826
|
...parsed.usage === null ? {} : { usage: parsed.usage },
|
|
6827
|
+
...parsed.contextTokens === void 0 ? {} : { contextTokens: parsed.contextTokens },
|
|
6688
6828
|
toolCalls: parsed.toolCalls,
|
|
6689
6829
|
model: parsed.model,
|
|
6690
6830
|
summary: parsed.status?.summary ?? null,
|
|
@@ -6885,11 +7025,11 @@ ${rationale}`}`);
|
|
|
6885
7025
|
}
|
|
6886
7026
|
/** Small named state files under data/state, for cursors the scheduler must keep across restarts. */
|
|
6887
7027
|
async readState(name, schema, fallback) {
|
|
6888
|
-
const file =
|
|
7028
|
+
const file = path8.join(this.paths.state(), `${name}.json`);
|
|
6889
7029
|
return await exists(file) ? readJson(file, schema) : fallback;
|
|
6890
7030
|
}
|
|
6891
7031
|
async writeState(name, value) {
|
|
6892
|
-
await this.mutex.run(() => writeJson(
|
|
7032
|
+
await this.mutex.run(() => writeJson(path8.join(this.paths.state(), `${name}.json`), value));
|
|
6893
7033
|
}
|
|
6894
7034
|
/** The project's dashboard, empty at revision 0 when it has none. */
|
|
6895
7035
|
async readDashboard(slug) {
|
|
@@ -6920,7 +7060,7 @@ ${rationale}`}`);
|
|
|
6920
7060
|
async listRunners() {
|
|
6921
7061
|
const runners = [];
|
|
6922
7062
|
for (const file of await listFiles(this.paths.runners())) {
|
|
6923
|
-
runners.push((await readMarkdown(
|
|
7063
|
+
runners.push((await readMarkdown(path8.join(this.paths.runners(), file), RunnerSchema)).data);
|
|
6924
7064
|
}
|
|
6925
7065
|
return runners;
|
|
6926
7066
|
}
|
|
@@ -6972,7 +7112,7 @@ ${rationale}`}`);
|
|
|
6972
7112
|
return this.runnerTokens.get(hashToken(token)) ?? null;
|
|
6973
7113
|
}
|
|
6974
7114
|
runnerTokensFile() {
|
|
6975
|
-
return
|
|
7115
|
+
return path8.join(this.paths.state(), "runners.json");
|
|
6976
7116
|
}
|
|
6977
7117
|
async readRunnerTokens() {
|
|
6978
7118
|
const file = this.runnerTokensFile();
|
|
@@ -6986,7 +7126,8 @@ ${rationale}`}`);
|
|
|
6986
7126
|
}
|
|
6987
7127
|
/**
|
|
6988
7128
|
* Places a project on the runner that will hold its repository, unless it already lives on one,
|
|
6989
|
-
* and returns where it lives. Placement is once: every later turn of the project goes there
|
|
7129
|
+
* and returns where it lives. Placement is once: every later turn of the project goes there,
|
|
7130
|
+
* until the user moves the project (`recordProjectMove`).
|
|
6990
7131
|
*/
|
|
6991
7132
|
async placeProject(slug, runner2) {
|
|
6992
7133
|
return this.mutex.run(async () => {
|
|
@@ -7028,12 +7169,12 @@ ${rationale}`}`);
|
|
|
7028
7169
|
}
|
|
7029
7170
|
const relative = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
7030
7171
|
if (entry.isDirectory()) {
|
|
7031
|
-
await walk2(
|
|
7172
|
+
await walk2(path8.join(dir, entry.name), relative);
|
|
7032
7173
|
continue;
|
|
7033
7174
|
}
|
|
7034
7175
|
const file = entry.isFile() ? conflictCopyOf(relative) : null;
|
|
7035
7176
|
if (file !== null) {
|
|
7036
|
-
const since = await this.homes.changedAt(home, relative) ?? (await stat3(
|
|
7177
|
+
const since = await this.homes.changedAt(home, relative) ?? (await stat3(path8.join(home, relative)).catch(() => null))?.mtime.toISOString();
|
|
7037
7178
|
if (since !== void 0) {
|
|
7038
7179
|
found.push({ path: relative, file, since });
|
|
7039
7180
|
}
|
|
@@ -7107,6 +7248,74 @@ ${rationale}`}`);
|
|
|
7107
7248
|
return next;
|
|
7108
7249
|
});
|
|
7109
7250
|
}
|
|
7251
|
+
/**
|
|
7252
|
+
* The board's half of moving a project to another runner, which only the user does, checked
|
|
7253
|
+
* before anything travels and while the hub holds the project's turns: the project is active and
|
|
7254
|
+
* lives on a runner other than `to`, which the board knows; no task there is landing, since its
|
|
7255
|
+
* landing runs on the old runner; and no turn runs in the project. Tasks in play move with their
|
|
7256
|
+
* branches, so the answer lists them for the capabilities the new runner must offer.
|
|
7257
|
+
*/
|
|
7258
|
+
async prepareProjectMove(actor, slug, to) {
|
|
7259
|
+
this.assertUser(actor, "only the user may move a project to another runner");
|
|
7260
|
+
const runner2 = NameSchema.parse(to);
|
|
7261
|
+
return this.mutex.run(async () => {
|
|
7262
|
+
const project2 = await this.readActiveProject(slug);
|
|
7263
|
+
const from = project2.runner;
|
|
7264
|
+
if (from === void 0) {
|
|
7265
|
+
throw new BoardError("INVALID_STATE", `project ${slug} lives on no runner yet; its first turn places it`);
|
|
7266
|
+
}
|
|
7267
|
+
if (from === runner2) {
|
|
7268
|
+
throw new BoardError("INVALID_STATE", `project ${slug} already lives on ${runner2}`);
|
|
7269
|
+
}
|
|
7270
|
+
await this.readRunner(runner2);
|
|
7271
|
+
const inPlay = (await this.listTasks(slug)).filter((task2) => task2.status === "open" || task2.status === "claimed");
|
|
7272
|
+
const landing = inPlay.filter((task2) => task2.completing);
|
|
7273
|
+
if (landing.length > 0) {
|
|
7274
|
+
throw new BoardError("INVALID_STATE", `project ${slug} cannot be moved while ${landing.length} task(s) land on ${from} (${landing.map((task2) => task2.id).join(", ")}); move it once they have landed`);
|
|
7275
|
+
}
|
|
7276
|
+
await this.assertProjectQuiet(actor, slug, `project ${slug} cannot be moved`);
|
|
7277
|
+
return { project: { ...project2, runner: from }, inPlay };
|
|
7278
|
+
});
|
|
7279
|
+
}
|
|
7280
|
+
/**
|
|
7281
|
+
* Records that a project moved, once its new runner holds every branch its old one had, or with
|
|
7282
|
+
* nothing carried when the user moved it while the old runner was away: every later turn of
|
|
7283
|
+
* the project goes to `to`. Refused when the project was archived or moved meanwhile.
|
|
7284
|
+
*/
|
|
7285
|
+
async recordProjectMove(actor, move) {
|
|
7286
|
+
this.assertUser(actor, "only the user may move a project to another runner");
|
|
7287
|
+
return this.mutex.run(async () => {
|
|
7288
|
+
const current = await this.readActiveProject(move.slug);
|
|
7289
|
+
if (current.runner !== move.from) {
|
|
7290
|
+
throw new BoardError("INVALID_STATE", `project ${move.slug} no longer lives on ${move.from}, so this move no longer applies`);
|
|
7291
|
+
}
|
|
7292
|
+
await this.readRunner(move.to);
|
|
7293
|
+
const project2 = await this.updateProject(move.slug, (each) => ({ ...each, runner: move.to }));
|
|
7294
|
+
await this.events.append("project.moved", actor.name, {
|
|
7295
|
+
slug: move.slug,
|
|
7296
|
+
from: move.from,
|
|
7297
|
+
to: move.to,
|
|
7298
|
+
carried: move.carried
|
|
7299
|
+
});
|
|
7300
|
+
return project2;
|
|
7301
|
+
});
|
|
7302
|
+
}
|
|
7303
|
+
/**
|
|
7304
|
+
* Makes the repository a move carries a project through, for the server's git route to serve,
|
|
7305
|
+
* and answers the move's id, which names it; `dropMoveRepo` removes it when the move ends.
|
|
7306
|
+
*/
|
|
7307
|
+
async openMoveRepo() {
|
|
7308
|
+
const id = this.newId();
|
|
7309
|
+
await this.moves.open(this.paths.move(id));
|
|
7310
|
+
return id;
|
|
7311
|
+
}
|
|
7312
|
+
async dropMoveRepo(id) {
|
|
7313
|
+
await this.moves.drop(this.paths.move(UlidSchema.parse(id)));
|
|
7314
|
+
}
|
|
7315
|
+
/** Where the moves' repositories live, which the server's git route serves from. */
|
|
7316
|
+
get moveRepos() {
|
|
7317
|
+
return this.paths.moves();
|
|
7318
|
+
}
|
|
7110
7319
|
/** The projection a runner mirrors for its agents to read, by relative path, with hashes. */
|
|
7111
7320
|
async boardManifest() {
|
|
7112
7321
|
return this.files.manifest(this.paths.board, () => true);
|
|
@@ -7117,7 +7326,7 @@ ${rationale}`}`);
|
|
|
7117
7326
|
async writeTurnRecord(record) {
|
|
7118
7327
|
const dir = this.sessionDir(record.agent, record.project, conversationPart(record));
|
|
7119
7328
|
await ensureDir(dir);
|
|
7120
|
-
await writeJson(
|
|
7329
|
+
await writeJson(path8.join(dir, "last-turn.json"), record);
|
|
7121
7330
|
}
|
|
7122
7331
|
// ---------------------------------------------------------------------------------------------
|
|
7123
7332
|
// Internals
|
|
@@ -7236,8 +7445,8 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
|
|
|
7236
7445
|
async ensureAgentProject(name, slug) {
|
|
7237
7446
|
const dir = this.paths.agentProject(name, slug);
|
|
7238
7447
|
await ensureDir(dir);
|
|
7239
|
-
if (!await exists(
|
|
7240
|
-
await writeJson(
|
|
7448
|
+
if (!await exists(path8.join(dir, "sessions.json"))) {
|
|
7449
|
+
await writeJson(path8.join(dir, "sessions.json"), {});
|
|
7241
7450
|
}
|
|
7242
7451
|
}
|
|
7243
7452
|
/**
|
|
@@ -7479,11 +7688,11 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
|
|
|
7479
7688
|
const file = this.paths.societySkillFile(skill2.name);
|
|
7480
7689
|
const replaced = await exists(file);
|
|
7481
7690
|
const taken = proposal2.skillFiles;
|
|
7482
|
-
const staged =
|
|
7691
|
+
const staged = path8.join(this.paths.proposalFiles(proposal2.id), "SKILL.md");
|
|
7483
7692
|
if (taken === void 0 || !await exists(staged)) {
|
|
7484
7693
|
throw new BoardError("INVALID_STATE", `proposal ${proposal2.id} holds no skill files`);
|
|
7485
7694
|
}
|
|
7486
|
-
await
|
|
7695
|
+
await rm3(dir, { recursive: true, force: true });
|
|
7487
7696
|
await cp(this.paths.proposalFiles(proposal2.id), dir, { recursive: true });
|
|
7488
7697
|
const { body } = await readLooseMarkdown(file);
|
|
7489
7698
|
await writeMarkdown(file, {
|
|
@@ -7536,10 +7745,10 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
|
|
|
7536
7745
|
return `the frontmatter of ${folder}/SKILL.md in ${proposal2.proposedBy}'s home does not parse (${manual.error})`;
|
|
7537
7746
|
}
|
|
7538
7747
|
const dir = this.paths.proposalFiles(proposal2.id);
|
|
7539
|
-
await
|
|
7748
|
+
await rm3(dir, { recursive: true, force: true });
|
|
7540
7749
|
for (const entry of taken.files) {
|
|
7541
|
-
const target =
|
|
7542
|
-
await ensureDir(
|
|
7750
|
+
const target = path8.join(dir, ...entry.path.split("/"));
|
|
7751
|
+
await ensureDir(path8.dirname(target));
|
|
7543
7752
|
await writeFile4(target, contents.get(entry.path) ?? Buffer.alloc(0));
|
|
7544
7753
|
await chmod2(target, entry.executable ? 493 : 420);
|
|
7545
7754
|
}
|
|
@@ -7630,7 +7839,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
|
|
|
7630
7839
|
if (!isSafeRelativePath(entry.path)) {
|
|
7631
7840
|
continue;
|
|
7632
7841
|
}
|
|
7633
|
-
const bytes = await readFile4(
|
|
7842
|
+
const bytes = await readFile4(path8.join(dir, ...entry.path.split("/"))).catch(() => null);
|
|
7634
7843
|
files.push({
|
|
7635
7844
|
...entry,
|
|
7636
7845
|
...bytes === null ? { text: null, truncated: false } : skillFileText(bytes)
|
|
@@ -7779,7 +7988,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
|
|
|
7779
7988
|
this.assertNoTurnCut(actor, `${ref} cannot be archived`, (turn2) => turn2.channel === channel && turn2.scope === scope || turn2.thread !== void 0 && closing.has(turn2.thread));
|
|
7780
7989
|
const threadsClosed = [];
|
|
7781
7990
|
for (const file of await listFiles(threads)) {
|
|
7782
|
-
const doc = await readMarkdown(
|
|
7991
|
+
const doc = await readMarkdown(path8.join(threads, file), ThreadFrontmatterSchema);
|
|
7783
7992
|
if (doc.data.channel !== ref || doc.data.state !== "open") {
|
|
7784
7993
|
continue;
|
|
7785
7994
|
}
|
|
@@ -7911,7 +8120,7 @@ ${charter.purpose}
|
|
|
7911
8120
|
const threads = this.paths.threads(slug);
|
|
7912
8121
|
const threadsClosed = [];
|
|
7913
8122
|
for (const file of await listFiles(threads)) {
|
|
7914
|
-
const doc = await readMarkdown(
|
|
8123
|
+
const doc = await readMarkdown(path8.join(threads, file), ThreadFrontmatterSchema);
|
|
7915
8124
|
if (doc.data.state !== "open") {
|
|
7916
8125
|
continue;
|
|
7917
8126
|
}
|
|
@@ -7986,10 +8195,10 @@ ${charter.purpose}
|
|
|
7986
8195
|
...project2,
|
|
7987
8196
|
members: project2.members.filter((member) => member !== name)
|
|
7988
8197
|
}));
|
|
7989
|
-
const sessions =
|
|
8198
|
+
const sessions = path8.join(this.paths.agentProject(name, slug), "sessions.json");
|
|
7990
8199
|
if (await exists(sessions)) {
|
|
7991
8200
|
const archived = `sessions.archived.${ts.replace(/[:.]/g, "-")}.json`;
|
|
7992
|
-
await rename3(sessions,
|
|
8201
|
+
await rename3(sessions, path8.join(this.paths.agentProject(name, slug), archived));
|
|
7993
8202
|
}
|
|
7994
8203
|
}
|
|
7995
8204
|
this.tokenIndex.delete(current.tokenHash);
|
|
@@ -8330,7 +8539,7 @@ ${charter.purpose}
|
|
|
8330
8539
|
if (since !== null && id <= since || upto !== void 0 && id > upto) {
|
|
8331
8540
|
continue;
|
|
8332
8541
|
}
|
|
8333
|
-
const doc = await readMarkdown(
|
|
8542
|
+
const doc = await readMarkdown(path8.join(dir, file), MessageFrontmatterSchema);
|
|
8334
8543
|
messages.push({ ...doc.data, body: doc.body });
|
|
8335
8544
|
}
|
|
8336
8545
|
return messages;
|
|
@@ -8852,7 +9061,7 @@ knowledge.command("show <topic>").description("Print a knowledge topic").option(
|
|
|
8852
9061
|
});
|
|
8853
9062
|
knowledge.command("write <topic>").description("Write a knowledge topic as the user, from a file or standard input").requiredOption("--revision <n>", "the revision it replaces, as knowledge list shows it; 0 for a new topic").option("--project <slug>", "the project's topic instead of the society's").option("--file <path>", "read the body from this file instead of standard input").option("--as <agent>", "act as this agent instead of the user").action(async (topic, opts) => {
|
|
8854
9063
|
const board = await open3();
|
|
8855
|
-
const body = opts.file === void 0 ? await readStdin() : await readFile5(
|
|
9064
|
+
const body = opts.file === void 0 ? await readStdin() : await readFile5(path9.resolve(opts.file), "utf8");
|
|
8856
9065
|
const written = await board.writeKnowledge(await actorFor(board, opts.as), {
|
|
8857
9066
|
project: opts.project ?? null,
|
|
8858
9067
|
topic,
|