@kubitnodes/stellaris 0.3.6 → 0.3.7
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 +92 -14
- package/dist/server/index.js +161 -30
- package/package.json +1 -1
- package/web/dist/assets/{BackLink-Cr1RWb4K.js → BackLink-BW0sb-M0.js} +1 -1
- package/web/dist/assets/{ChannelView-iK3ug1lV.js → ChannelView-DmTE9aUA.js} +1 -1
- package/web/dist/assets/CitizenView-lrA6vZDK.js +2 -0
- package/web/dist/assets/{CitizensView-CFrD6f7m.js → CitizensView-MRVffBnU.js} +1 -1
- package/web/dist/assets/KnowledgeView-C2N-x5yS.js +1 -0
- package/web/dist/assets/{Markdown-CYcTTxcR.js → Markdown-CS3NCze-.js} +1 -1
- package/web/dist/assets/{MessageItem-DDC_1Jqn.js → MessageItem-CL3XDi7l.js} +1 -1
- package/web/dist/assets/MetricsView-CR5u5k5n.js +1 -0
- package/web/dist/assets/{NeedsYouView-I7j_cnda.js → NeedsYouView-BwUvwyVt.js} +1 -1
- package/web/dist/assets/{Overview-DI7LPNEY.js → Overview-DxFOFviB.js} +1 -1
- package/web/dist/assets/{Pane-BNjb76U4.js → Pane-voa6GU0F.js} +1 -1
- package/web/dist/assets/{ProjectView-DYDcDsWg.js → ProjectView-BkNG_qdh.js} +1 -1
- package/web/dist/assets/{ProposalView-DrP3Cv9y.js → ProposalView-G89UmLF5.js} +1 -1
- package/web/dist/assets/{ProposalsView-90rufsQN.js → ProposalsView-CzpL-POB.js} +1 -1
- package/web/dist/assets/{RunnersView-iMbemuCe.js → RunnersView-SKNIDsjR.js} +1 -1
- package/web/dist/assets/{SocietyView-C9FdIFZY.js → SocietyView-CCFXEYZW.js} +1 -1
- package/web/dist/assets/{SubjectThread-Bw6wEJpH.js → SubjectThread-JQG2FxkA.js} +1 -1
- package/web/dist/assets/{TaskFileView-D0n7kzt2.js → TaskFileView-BKoGmHGz.js} +1 -1
- package/web/dist/assets/{TaskView-BZ6U_XpQ.js → TaskView-BIYqIFU4.js} +1 -1
- package/web/dist/assets/{TasksView-CtGF3i-n.js → TasksView-n2ssgQOL.js} +1 -1
- package/web/dist/assets/{ThreadCard-FPtAzvNM.js → ThreadCard-CzQXJXSd.js} +1 -1
- package/web/dist/assets/{ThreadForms-LFw2EF6r.js → ThreadForms-0LpUc2jJ.js} +1 -1
- package/web/dist/assets/{ThreadView-DnDx2SCv.js → ThreadView-_LgGISSH.js} +1 -1
- package/web/dist/assets/{citizen-Bm_r8vBY.js → citizen-DvJr6Ibu.js} +1 -1
- package/web/dist/assets/{format-Cj7NW9Y9.js → format-C7bqWt6N.js} +1 -1
- package/web/dist/assets/index-DM00hlle.js +23 -0
- package/web/dist/assets/{tasks-tFehkpRz.js → tasks-B4bx7Gg3.js} +1 -1
- package/web/dist/assets/{useStickyScroll-XrG6M-hT.js → useStickyScroll-DCsF4GYJ.js} +1 -1
- package/web/dist/index.html +3 -3
- package/web/dist/assets/CitizenView-BiaYYmCh.js +0 -2
- package/web/dist/assets/KnowledgeView-CR3cXdj0.js +0 -1
- package/web/dist/assets/MetricsView-xdvE7pFA.js +0 -1
- package/web/dist/assets/index-CthEgIzf.js +0 -23
package/dist/cli/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import path7 from "node:path";
|
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// packages/board-core/dist/board.js
|
|
9
|
-
import { readdir as readdir3, readFile as readFile5, rename as rename3, stat as stat2 } from "node:fs/promises";
|
|
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
12
|
import { z as z12 } from "zod";
|
|
@@ -56,7 +56,8 @@ var VERB_NAMES = [
|
|
|
56
56
|
"plan_task",
|
|
57
57
|
"advance_task",
|
|
58
58
|
"configure_project",
|
|
59
|
-
"archive_project"
|
|
59
|
+
"archive_project",
|
|
60
|
+
"remove_knowledge"
|
|
60
61
|
];
|
|
61
62
|
var VerbNameSchema = z2.enum(VERB_NAMES);
|
|
62
63
|
var CharterTriggerSchema = z2.enum(["user_post", "ops_event", "heartbeat"]);
|
|
@@ -100,6 +101,7 @@ var MEMBER_VERBS = [
|
|
|
100
101
|
"join_project",
|
|
101
102
|
"leave_project",
|
|
102
103
|
"write_knowledge",
|
|
104
|
+
"remove_knowledge",
|
|
103
105
|
"plan_task",
|
|
104
106
|
"advance_task"
|
|
105
107
|
];
|
|
@@ -314,6 +316,11 @@ var ModelOptionSchema = z3.object({
|
|
|
314
316
|
efforts: z3.array(EffortOptionSchema).default([]),
|
|
315
317
|
defaultEffort: EffortSchema.optional()
|
|
316
318
|
});
|
|
319
|
+
var RunnerModelsSchema = z3.object({
|
|
320
|
+
runner: NameSchema,
|
|
321
|
+
cli: CliKindSchema,
|
|
322
|
+
models: z3.array(ModelOptionSchema)
|
|
323
|
+
});
|
|
317
324
|
var AgentStatusSchema = z3.enum(["active", "retired"]);
|
|
318
325
|
var AgentSchema = z3.object({
|
|
319
326
|
name: NameSchema,
|
|
@@ -534,6 +541,12 @@ var KnowledgeSchema = z3.object({
|
|
|
534
541
|
updatedBy: NameSchema,
|
|
535
542
|
updatedAt: IsoDateTimeSchema
|
|
536
543
|
});
|
|
544
|
+
var RemovedKnowledgeSchema = z3.object({
|
|
545
|
+
topic: NameSchema,
|
|
546
|
+
project: NameSchema.nullable(),
|
|
547
|
+
removedBy: NameSchema,
|
|
548
|
+
removedAt: IsoDateTimeSchema
|
|
549
|
+
});
|
|
537
550
|
var ProposalFrontmatterSchema = z3.object({
|
|
538
551
|
id: UlidSchema,
|
|
539
552
|
kind: ProposalKindSchema,
|
|
@@ -630,6 +643,11 @@ var VerbInputs = {
|
|
|
630
643
|
topic: NameSchema,
|
|
631
644
|
body: z4.string().min(1)
|
|
632
645
|
}),
|
|
646
|
+
remove_knowledge: z4.object({
|
|
647
|
+
/** As for write_knowledge: a project you belong to, or null for society knowledge. */
|
|
648
|
+
project: NameSchema.nullable().default(null),
|
|
649
|
+
topic: NameSchema
|
|
650
|
+
}),
|
|
633
651
|
plan_task: z4.object({
|
|
634
652
|
task_id: UlidSchema,
|
|
635
653
|
stages: z4.array(PlanEditStageSchema),
|
|
@@ -679,6 +697,7 @@ var BOARD_EVENT_TYPES = [
|
|
|
679
697
|
"agent.joined",
|
|
680
698
|
"agent.left",
|
|
681
699
|
"knowledge.written",
|
|
700
|
+
"knowledge.removed",
|
|
682
701
|
"skill.promoted",
|
|
683
702
|
"runner.added",
|
|
684
703
|
"user.signed_in",
|
|
@@ -1276,7 +1295,7 @@ var RunnerConfigSchema = z10.object({
|
|
|
1276
1295
|
});
|
|
1277
1296
|
|
|
1278
1297
|
// packages/shared/dist/version.js
|
|
1279
|
-
var STELLARIS_VERSION = "0.3.
|
|
1298
|
+
var STELLARIS_VERSION = "0.3.7";
|
|
1280
1299
|
|
|
1281
1300
|
// packages/board-core/dist/errors.js
|
|
1282
1301
|
var BoardError = class extends Error {
|
|
@@ -2309,6 +2328,10 @@ var BoardPaths = class {
|
|
|
2309
2328
|
pausedFile() {
|
|
2310
2329
|
return path5.join(this.state(), "paused.json");
|
|
2311
2330
|
}
|
|
2331
|
+
/** Removed knowledge topics, set aside outside the projection, by scope. */
|
|
2332
|
+
removedKnowledge(scope) {
|
|
2333
|
+
return path5.join(this.state(), "removed-knowledge", scope);
|
|
2334
|
+
}
|
|
2312
2335
|
};
|
|
2313
2336
|
|
|
2314
2337
|
// packages/board-core/dist/tokens.js
|
|
@@ -2402,6 +2425,7 @@ var OPS_CHANNEL_RETIRED = "ops-channel-retired";
|
|
|
2402
2425
|
var DECISIONS_CHANNEL_RETIRED = "decisions-channel-retired";
|
|
2403
2426
|
var THREAD_CONVERSATIONS = "thread-conversations";
|
|
2404
2427
|
var ASKS_CHANNEL_OPENED = "asks-channel-opened";
|
|
2428
|
+
var KNOWLEDGE_REMOVAL = "knowledge-removal-granted";
|
|
2405
2429
|
var PLANNING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
2406
2430
|
var REALLOCATING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
2407
2431
|
var PROFILE_TEMPLATE = "# Profile\n\nOne short paragraph, kept current: what I do well, what I am working on, and what to send my way. The board projects this into the roster the front desk reads.\n";
|
|
@@ -2591,7 +2615,8 @@ var Board = class _Board {
|
|
|
2591
2615
|
OPS_CHANNEL_RETIRED,
|
|
2592
2616
|
DECISIONS_CHANNEL_RETIRED,
|
|
2593
2617
|
THREAD_CONVERSATIONS,
|
|
2594
|
-
ASKS_CHANNEL_OPENED
|
|
2618
|
+
ASKS_CHANNEL_OPENED,
|
|
2619
|
+
KNOWLEDGE_REMOVAL
|
|
2595
2620
|
].filter((name) => !applied.includes(name));
|
|
2596
2621
|
if (pending.length === 0) {
|
|
2597
2622
|
return;
|
|
@@ -2620,8 +2645,22 @@ var Board = class _Board {
|
|
|
2620
2645
|
if (pending.includes(ASKS_CHANNEL_OPENED)) {
|
|
2621
2646
|
await this.openAsksChannel();
|
|
2622
2647
|
}
|
|
2648
|
+
if (pending.includes(KNOWLEDGE_REMOVAL)) {
|
|
2649
|
+
await this.grantKnowledgeRemoval();
|
|
2650
|
+
}
|
|
2623
2651
|
await writeJson(file, { applied: [...applied, ...pending] });
|
|
2624
2652
|
}
|
|
2653
|
+
/**
|
|
2654
|
+
* Whoever may write a scope's knowledge may remove it, so every charter granting
|
|
2655
|
+
* `write_knowledge` is granted `remove_knowledge` once; seed roles have it from their seed.
|
|
2656
|
+
*/
|
|
2657
|
+
async grantKnowledgeRemoval() {
|
|
2658
|
+
for (const charter of await this.listRoles()) {
|
|
2659
|
+
if (charter.verbs.includes("write_knowledge") && !charter.verbs.includes("remove_knowledge")) {
|
|
2660
|
+
await this.writeRoleUnlocked(USER_NAME, { ...charter, verbs: [...charter.verbs, "remove_knowledge"] }, { verbsAdded: ["remove_knowledge"] });
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2625
2664
|
/**
|
|
2626
2665
|
* A task sent back before tasks recorded it kept no trace outside the event log. A task still
|
|
2627
2666
|
* short of the stage its latest `task.moved` came from is in that rework, and records it.
|
|
@@ -3853,16 +3892,7 @@ ${task2.body}`;
|
|
|
3853
3892
|
const args = VerbInputs.write_knowledge.parse(input);
|
|
3854
3893
|
await this.authorize(actor, "write_knowledge");
|
|
3855
3894
|
return this.mutex.run(async () => {
|
|
3856
|
-
|
|
3857
|
-
if (!CURATING_ROLES.includes(actor.role)) {
|
|
3858
|
-
throw new BoardError("FORBIDDEN", "society knowledge is curated by the steward and the user");
|
|
3859
|
-
}
|
|
3860
|
-
} else {
|
|
3861
|
-
const project2 = await this.readActiveProject(args.project);
|
|
3862
|
-
if (!CURATING_ROLES.includes(actor.role) && !project2.members.includes(actor.name)) {
|
|
3863
|
-
throw new BoardError("FORBIDDEN", `${actor.name} is not a member of ${args.project}`);
|
|
3864
|
-
}
|
|
3865
|
-
}
|
|
3895
|
+
await this.assertMayCurate(actor, args.project);
|
|
3866
3896
|
const data = KnowledgeSchema.parse({
|
|
3867
3897
|
topic: args.topic,
|
|
3868
3898
|
project: args.project,
|
|
@@ -3882,6 +3912,52 @@ ${task2.body}`;
|
|
|
3882
3912
|
return { ...data, body: args.body };
|
|
3883
3913
|
});
|
|
3884
3914
|
}
|
|
3915
|
+
/**
|
|
3916
|
+
* Retires a topic, by whoever may write it. Its file leaves the knowledge directory, so no turn,
|
|
3917
|
+
* search, or digest finds it again, and is set aside under `state/removed-knowledge`, outside
|
|
3918
|
+
* the projection, for the user to restore by hand. The note in the matching general channel
|
|
3919
|
+
* carries no mention, as a write's does.
|
|
3920
|
+
*/
|
|
3921
|
+
async removeKnowledge(actor, input) {
|
|
3922
|
+
const args = VerbInputs.remove_knowledge.parse(input);
|
|
3923
|
+
await this.authorize(actor, "remove_knowledge");
|
|
3924
|
+
return this.mutex.run(async () => {
|
|
3925
|
+
await this.assertMayCurate(actor, args.project);
|
|
3926
|
+
const file = args.project === null ? this.paths.societyKnowledgeFile(args.topic) : this.paths.projectKnowledgeFile(args.project, args.topic);
|
|
3927
|
+
if (!await exists(file)) {
|
|
3928
|
+
throw new BoardError("NOT_FOUND", `${args.project ?? "the society"} has no knowledge topic ${args.topic}`);
|
|
3929
|
+
}
|
|
3930
|
+
const removed = RemovedKnowledgeSchema.parse({
|
|
3931
|
+
topic: args.topic,
|
|
3932
|
+
project: args.project,
|
|
3933
|
+
removedBy: actor.name,
|
|
3934
|
+
removedAt: this.now().toISOString()
|
|
3935
|
+
});
|
|
3936
|
+
const aside = this.paths.removedKnowledge(args.project ?? SOCIETY_SCOPE);
|
|
3937
|
+
await mkdir4(aside, { recursive: true });
|
|
3938
|
+
await rename3(file, path6.join(aside, `${args.topic}.${removed.removedAt.replaceAll(":", "-")}.md`));
|
|
3939
|
+
await this.events.append("knowledge.removed", actor.name, {
|
|
3940
|
+
topic: args.topic,
|
|
3941
|
+
project: args.project
|
|
3942
|
+
});
|
|
3943
|
+
const where = args.project === null ? "general" : channelRef(args.project, "general");
|
|
3944
|
+
await this.appendMessage(actor.name, where, `Knowledge removed: ${args.topic}. It is no longer under knowledge/${args.topic}.md${args.project === null ? " of the society" : ""}.`);
|
|
3945
|
+
return removed;
|
|
3946
|
+
});
|
|
3947
|
+
}
|
|
3948
|
+
/** Society knowledge is the steward's and the user's; a project's, its members' too. */
|
|
3949
|
+
async assertMayCurate(actor, project2) {
|
|
3950
|
+
if (project2 === null) {
|
|
3951
|
+
if (!CURATING_ROLES.includes(actor.role)) {
|
|
3952
|
+
throw new BoardError("FORBIDDEN", "society knowledge is curated by the steward and the user");
|
|
3953
|
+
}
|
|
3954
|
+
return;
|
|
3955
|
+
}
|
|
3956
|
+
const active = await this.readActiveProject(project2);
|
|
3957
|
+
if (!CURATING_ROLES.includes(actor.role) && !active.members.includes(actor.name)) {
|
|
3958
|
+
throw new BoardError("FORBIDDEN", `${actor.name} is not a member of ${project2}`);
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3885
3961
|
async openThread(actor, input) {
|
|
3886
3962
|
const args = VerbInputs.open_thread.parse(input);
|
|
3887
3963
|
await this.authorize(actor, "open_thread");
|
|
@@ -4708,6 +4784,8 @@ ${rationale}`}`);
|
|
|
4708
4784
|
return this.leaveProject(actor, VerbInputs.leave_project.parse(input));
|
|
4709
4785
|
case "write_knowledge":
|
|
4710
4786
|
return this.writeKnowledge(actor, VerbInputs.write_knowledge.parse(input));
|
|
4787
|
+
case "remove_knowledge":
|
|
4788
|
+
return this.removeKnowledge(actor, VerbInputs.remove_knowledge.parse(input));
|
|
4711
4789
|
case "plan_task":
|
|
4712
4790
|
return this.planTask(actor, VerbInputs.plan_task.parse(input));
|
|
4713
4791
|
case "advance_task":
|
package/dist/server/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import path9 from "node:path";
|
|
|
6
6
|
import { serve } from "@hono/node-server";
|
|
7
7
|
|
|
8
8
|
// packages/board-core/dist/board.js
|
|
9
|
-
import { readdir as readdir3, readFile as readFile5, rename as rename3, stat as stat2 } from "node:fs/promises";
|
|
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
12
|
import { z as z12 } from "zod";
|
|
@@ -56,7 +56,8 @@ var VERB_NAMES = [
|
|
|
56
56
|
"plan_task",
|
|
57
57
|
"advance_task",
|
|
58
58
|
"configure_project",
|
|
59
|
-
"archive_project"
|
|
59
|
+
"archive_project",
|
|
60
|
+
"remove_knowledge"
|
|
60
61
|
];
|
|
61
62
|
var VerbNameSchema = z2.enum(VERB_NAMES);
|
|
62
63
|
var CharterTriggerSchema = z2.enum(["user_post", "ops_event", "heartbeat"]);
|
|
@@ -100,6 +101,7 @@ var MEMBER_VERBS = [
|
|
|
100
101
|
"join_project",
|
|
101
102
|
"leave_project",
|
|
102
103
|
"write_knowledge",
|
|
104
|
+
"remove_knowledge",
|
|
103
105
|
"plan_task",
|
|
104
106
|
"advance_task"
|
|
105
107
|
];
|
|
@@ -314,6 +316,11 @@ var ModelOptionSchema = z3.object({
|
|
|
314
316
|
efforts: z3.array(EffortOptionSchema).default([]),
|
|
315
317
|
defaultEffort: EffortSchema.optional()
|
|
316
318
|
});
|
|
319
|
+
var RunnerModelsSchema = z3.object({
|
|
320
|
+
runner: NameSchema,
|
|
321
|
+
cli: CliKindSchema,
|
|
322
|
+
models: z3.array(ModelOptionSchema)
|
|
323
|
+
});
|
|
317
324
|
var AgentStatusSchema = z3.enum(["active", "retired"]);
|
|
318
325
|
var AgentSchema = z3.object({
|
|
319
326
|
name: NameSchema,
|
|
@@ -534,6 +541,12 @@ var KnowledgeSchema = z3.object({
|
|
|
534
541
|
updatedBy: NameSchema,
|
|
535
542
|
updatedAt: IsoDateTimeSchema
|
|
536
543
|
});
|
|
544
|
+
var RemovedKnowledgeSchema = z3.object({
|
|
545
|
+
topic: NameSchema,
|
|
546
|
+
project: NameSchema.nullable(),
|
|
547
|
+
removedBy: NameSchema,
|
|
548
|
+
removedAt: IsoDateTimeSchema
|
|
549
|
+
});
|
|
537
550
|
var ProposalFrontmatterSchema = z3.object({
|
|
538
551
|
id: UlidSchema,
|
|
539
552
|
kind: ProposalKindSchema,
|
|
@@ -630,6 +643,11 @@ var VerbInputs = {
|
|
|
630
643
|
topic: NameSchema,
|
|
631
644
|
body: z4.string().min(1)
|
|
632
645
|
}),
|
|
646
|
+
remove_knowledge: z4.object({
|
|
647
|
+
/** As for write_knowledge: a project you belong to, or null for society knowledge. */
|
|
648
|
+
project: NameSchema.nullable().default(null),
|
|
649
|
+
topic: NameSchema
|
|
650
|
+
}),
|
|
633
651
|
plan_task: z4.object({
|
|
634
652
|
task_id: UlidSchema,
|
|
635
653
|
stages: z4.array(PlanEditStageSchema),
|
|
@@ -663,6 +681,7 @@ var VERB_DESCRIPTIONS = {
|
|
|
663
681
|
join_project: "Join a project, or add another citizen to one when your role allows it. Membership gives the pair a worktree and an onboarding turn.",
|
|
664
682
|
leave_project: "Leave a project, or remove another citizen from one when your role allows it.",
|
|
665
683
|
write_knowledge: "Write or replace a knowledge topic: durable facts every member of a project should know, or with project null the society's shared knowledge (steward and user). Not a message; use post_message for those.",
|
|
684
|
+
remove_knowledge: "Remove a knowledge topic that is wrong, superseded, or folded into another, so no turn or search finds it again. Whoever may write the topic may remove it; the board keeps its text aside for the user to restore.",
|
|
666
685
|
plan_task: "Reshape a task's plan from the current stage onward, or after it while someone holds it: keep a stage by passing its id, drop it by leaving it out, add one without an id. Gated stages and on_done: user, steward, concierge.",
|
|
667
686
|
advance_task: "Finish the stage you hold, with a note for the task's thread on what you did. The next stage becomes current; past the last one the task is done.",
|
|
668
687
|
configure_project: "Set a project's completion effect: none, or merge to land each finished task's branch on the default branch.",
|
|
@@ -704,6 +723,7 @@ var BOARD_EVENT_TYPES = [
|
|
|
704
723
|
"agent.joined",
|
|
705
724
|
"agent.left",
|
|
706
725
|
"knowledge.written",
|
|
726
|
+
"knowledge.removed",
|
|
707
727
|
"skill.promoted",
|
|
708
728
|
"runner.added",
|
|
709
729
|
"user.signed_in",
|
|
@@ -1356,7 +1376,7 @@ var RunnerConfigSchema = z10.object({
|
|
|
1356
1376
|
});
|
|
1357
1377
|
|
|
1358
1378
|
// packages/shared/dist/version.js
|
|
1359
|
-
var STELLARIS_VERSION = "0.3.
|
|
1379
|
+
var STELLARIS_VERSION = "0.3.7";
|
|
1360
1380
|
|
|
1361
1381
|
// packages/board-core/dist/errors.js
|
|
1362
1382
|
var BoardError = class extends Error {
|
|
@@ -2392,6 +2412,10 @@ var BoardPaths = class {
|
|
|
2392
2412
|
pausedFile() {
|
|
2393
2413
|
return path5.join(this.state(), "paused.json");
|
|
2394
2414
|
}
|
|
2415
|
+
/** Removed knowledge topics, set aside outside the projection, by scope. */
|
|
2416
|
+
removedKnowledge(scope) {
|
|
2417
|
+
return path5.join(this.state(), "removed-knowledge", scope);
|
|
2418
|
+
}
|
|
2395
2419
|
};
|
|
2396
2420
|
|
|
2397
2421
|
// packages/board-core/dist/tokens.js
|
|
@@ -2485,6 +2509,7 @@ var OPS_CHANNEL_RETIRED = "ops-channel-retired";
|
|
|
2485
2509
|
var DECISIONS_CHANNEL_RETIRED = "decisions-channel-retired";
|
|
2486
2510
|
var THREAD_CONVERSATIONS = "thread-conversations";
|
|
2487
2511
|
var ASKS_CHANNEL_OPENED = "asks-channel-opened";
|
|
2512
|
+
var KNOWLEDGE_REMOVAL = "knowledge-removal-granted";
|
|
2488
2513
|
var PLANNING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
2489
2514
|
var REALLOCATING_ROLES = [USER_ROLE, "steward", "concierge"];
|
|
2490
2515
|
var PROFILE_TEMPLATE = "# Profile\n\nOne short paragraph, kept current: what I do well, what I am working on, and what to send my way. The board projects this into the roster the front desk reads.\n";
|
|
@@ -2674,7 +2699,8 @@ var Board = class _Board {
|
|
|
2674
2699
|
OPS_CHANNEL_RETIRED,
|
|
2675
2700
|
DECISIONS_CHANNEL_RETIRED,
|
|
2676
2701
|
THREAD_CONVERSATIONS,
|
|
2677
|
-
ASKS_CHANNEL_OPENED
|
|
2702
|
+
ASKS_CHANNEL_OPENED,
|
|
2703
|
+
KNOWLEDGE_REMOVAL
|
|
2678
2704
|
].filter((name) => !applied.includes(name));
|
|
2679
2705
|
if (pending.length === 0) {
|
|
2680
2706
|
return;
|
|
@@ -2703,8 +2729,22 @@ var Board = class _Board {
|
|
|
2703
2729
|
if (pending.includes(ASKS_CHANNEL_OPENED)) {
|
|
2704
2730
|
await this.openAsksChannel();
|
|
2705
2731
|
}
|
|
2732
|
+
if (pending.includes(KNOWLEDGE_REMOVAL)) {
|
|
2733
|
+
await this.grantKnowledgeRemoval();
|
|
2734
|
+
}
|
|
2706
2735
|
await writeJson(file, { applied: [...applied, ...pending] });
|
|
2707
2736
|
}
|
|
2737
|
+
/**
|
|
2738
|
+
* Whoever may write a scope's knowledge may remove it, so every charter granting
|
|
2739
|
+
* `write_knowledge` is granted `remove_knowledge` once; seed roles have it from their seed.
|
|
2740
|
+
*/
|
|
2741
|
+
async grantKnowledgeRemoval() {
|
|
2742
|
+
for (const charter of await this.listRoles()) {
|
|
2743
|
+
if (charter.verbs.includes("write_knowledge") && !charter.verbs.includes("remove_knowledge")) {
|
|
2744
|
+
await this.writeRoleUnlocked(USER_NAME, { ...charter, verbs: [...charter.verbs, "remove_knowledge"] }, { verbsAdded: ["remove_knowledge"] });
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2708
2748
|
/**
|
|
2709
2749
|
* A task sent back before tasks recorded it kept no trace outside the event log. A task still
|
|
2710
2750
|
* short of the stage its latest `task.moved` came from is in that rework, and records it.
|
|
@@ -3936,16 +3976,7 @@ ${task.body}`;
|
|
|
3936
3976
|
const args = VerbInputs.write_knowledge.parse(input);
|
|
3937
3977
|
await this.authorize(actor, "write_knowledge");
|
|
3938
3978
|
return this.mutex.run(async () => {
|
|
3939
|
-
|
|
3940
|
-
if (!CURATING_ROLES.includes(actor.role)) {
|
|
3941
|
-
throw new BoardError("FORBIDDEN", "society knowledge is curated by the steward and the user");
|
|
3942
|
-
}
|
|
3943
|
-
} else {
|
|
3944
|
-
const project = await this.readActiveProject(args.project);
|
|
3945
|
-
if (!CURATING_ROLES.includes(actor.role) && !project.members.includes(actor.name)) {
|
|
3946
|
-
throw new BoardError("FORBIDDEN", `${actor.name} is not a member of ${args.project}`);
|
|
3947
|
-
}
|
|
3948
|
-
}
|
|
3979
|
+
await this.assertMayCurate(actor, args.project);
|
|
3949
3980
|
const data = KnowledgeSchema.parse({
|
|
3950
3981
|
topic: args.topic,
|
|
3951
3982
|
project: args.project,
|
|
@@ -3965,6 +3996,52 @@ ${task.body}`;
|
|
|
3965
3996
|
return { ...data, body: args.body };
|
|
3966
3997
|
});
|
|
3967
3998
|
}
|
|
3999
|
+
/**
|
|
4000
|
+
* Retires a topic, by whoever may write it. Its file leaves the knowledge directory, so no turn,
|
|
4001
|
+
* search, or digest finds it again, and is set aside under `state/removed-knowledge`, outside
|
|
4002
|
+
* the projection, for the user to restore by hand. The note in the matching general channel
|
|
4003
|
+
* carries no mention, as a write's does.
|
|
4004
|
+
*/
|
|
4005
|
+
async removeKnowledge(actor, input) {
|
|
4006
|
+
const args = VerbInputs.remove_knowledge.parse(input);
|
|
4007
|
+
await this.authorize(actor, "remove_knowledge");
|
|
4008
|
+
return this.mutex.run(async () => {
|
|
4009
|
+
await this.assertMayCurate(actor, args.project);
|
|
4010
|
+
const file = args.project === null ? this.paths.societyKnowledgeFile(args.topic) : this.paths.projectKnowledgeFile(args.project, args.topic);
|
|
4011
|
+
if (!await exists(file)) {
|
|
4012
|
+
throw new BoardError("NOT_FOUND", `${args.project ?? "the society"} has no knowledge topic ${args.topic}`);
|
|
4013
|
+
}
|
|
4014
|
+
const removed = RemovedKnowledgeSchema.parse({
|
|
4015
|
+
topic: args.topic,
|
|
4016
|
+
project: args.project,
|
|
4017
|
+
removedBy: actor.name,
|
|
4018
|
+
removedAt: this.now().toISOString()
|
|
4019
|
+
});
|
|
4020
|
+
const aside = this.paths.removedKnowledge(args.project ?? SOCIETY_SCOPE);
|
|
4021
|
+
await mkdir4(aside, { recursive: true });
|
|
4022
|
+
await rename3(file, path6.join(aside, `${args.topic}.${removed.removedAt.replaceAll(":", "-")}.md`));
|
|
4023
|
+
await this.events.append("knowledge.removed", actor.name, {
|
|
4024
|
+
topic: args.topic,
|
|
4025
|
+
project: args.project
|
|
4026
|
+
});
|
|
4027
|
+
const where = args.project === null ? "general" : channelRef(args.project, "general");
|
|
4028
|
+
await this.appendMessage(actor.name, where, `Knowledge removed: ${args.topic}. It is no longer under knowledge/${args.topic}.md${args.project === null ? " of the society" : ""}.`);
|
|
4029
|
+
return removed;
|
|
4030
|
+
});
|
|
4031
|
+
}
|
|
4032
|
+
/** Society knowledge is the steward's and the user's; a project's, its members' too. */
|
|
4033
|
+
async assertMayCurate(actor, project) {
|
|
4034
|
+
if (project === null) {
|
|
4035
|
+
if (!CURATING_ROLES.includes(actor.role)) {
|
|
4036
|
+
throw new BoardError("FORBIDDEN", "society knowledge is curated by the steward and the user");
|
|
4037
|
+
}
|
|
4038
|
+
return;
|
|
4039
|
+
}
|
|
4040
|
+
const active = await this.readActiveProject(project);
|
|
4041
|
+
if (!CURATING_ROLES.includes(actor.role) && !active.members.includes(actor.name)) {
|
|
4042
|
+
throw new BoardError("FORBIDDEN", `${actor.name} is not a member of ${project}`);
|
|
4043
|
+
}
|
|
4044
|
+
}
|
|
3968
4045
|
async openThread(actor, input) {
|
|
3969
4046
|
const args = VerbInputs.open_thread.parse(input);
|
|
3970
4047
|
await this.authorize(actor, "open_thread");
|
|
@@ -4791,6 +4868,8 @@ ${rationale}`}`);
|
|
|
4791
4868
|
return this.leaveProject(actor, VerbInputs.leave_project.parse(input));
|
|
4792
4869
|
case "write_knowledge":
|
|
4793
4870
|
return this.writeKnowledge(actor, VerbInputs.write_knowledge.parse(input));
|
|
4871
|
+
case "remove_knowledge":
|
|
4872
|
+
return this.removeKnowledge(actor, VerbInputs.remove_knowledge.parse(input));
|
|
4794
4873
|
case "plan_task":
|
|
4795
4874
|
return this.planTask(actor, VerbInputs.plan_task.parse(input));
|
|
4796
4875
|
case "advance_task":
|
|
@@ -8473,6 +8552,7 @@ var RunnerHub = class {
|
|
|
8473
8552
|
pending = /* @__PURE__ */ new Map();
|
|
8474
8553
|
/** Disconnects being recorded, which `close` waits for. */
|
|
8475
8554
|
detaching = /* @__PURE__ */ new Set();
|
|
8555
|
+
runnerListeners = /* @__PURE__ */ new Set();
|
|
8476
8556
|
constructor(options) {
|
|
8477
8557
|
this.board = options.board;
|
|
8478
8558
|
this.host = options.host;
|
|
@@ -8528,6 +8608,7 @@ var RunnerHub = class {
|
|
|
8528
8608
|
clis: hello.clis,
|
|
8529
8609
|
capabilities: hello.capabilities
|
|
8530
8610
|
});
|
|
8611
|
+
this.runnerChanged(name);
|
|
8531
8612
|
this.log.info({ runner: name, clis: hello.clis, slots: hello.slots }, "runner registered");
|
|
8532
8613
|
return { name, version: this.version };
|
|
8533
8614
|
}
|
|
@@ -8544,6 +8625,7 @@ var RunnerHub = class {
|
|
|
8544
8625
|
seat.grace = null;
|
|
8545
8626
|
}
|
|
8546
8627
|
await this.board.markRunner(name, { status: "connected" });
|
|
8628
|
+
this.runnerChanged(name);
|
|
8547
8629
|
return async () => {
|
|
8548
8630
|
if (seat.send !== send) {
|
|
8549
8631
|
return;
|
|
@@ -8816,16 +8898,37 @@ var RunnerHub = class {
|
|
|
8816
8898
|
return changes;
|
|
8817
8899
|
}
|
|
8818
8900
|
/** The models a CLI offers, asked of a connected runner that has it; none when no runner has it. */
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8901
|
+
/**
|
|
8902
|
+
* The connected runner a model list for `cli` comes from: the first of `prefer` that has the CLI,
|
|
8903
|
+
* else the least busy that does; null when no connected runner has it.
|
|
8904
|
+
*/
|
|
8905
|
+
modelRunner(cli, prefer = []) {
|
|
8906
|
+
const able = (seat) => seat !== void 0 && seat.send !== null && seat.hello.clis.includes(cli);
|
|
8907
|
+
const preferred = prefer.map((name) => this.seats.get(name)).find(able);
|
|
8908
|
+
return (preferred ?? this.byLoad().find(able))?.name ?? null;
|
|
8909
|
+
}
|
|
8910
|
+
/** The models `cli` lists on a runner, `modelRunner`'s choice when none is named. */
|
|
8911
|
+
async models(cli, runner) {
|
|
8912
|
+
const name = runner ?? this.modelRunner(cli);
|
|
8913
|
+
const seat = name === null ? void 0 : this.seats.get(name);
|
|
8914
|
+
if (seat === void 0 || seat.send === null || !seat.hello.clis.includes(cli)) {
|
|
8915
|
+
throw new RunnerAwayError(runner === void 0 ? `no connected runner has ${cli}` : `runner ${runner} is not connected with ${cli}`);
|
|
8823
8916
|
}
|
|
8824
8917
|
return ModelListSchema.parse(await this.request(seat, (request) => ({ type: "models", request, cli })));
|
|
8825
8918
|
}
|
|
8919
|
+
/** Calls `listener` whenever a runner registers, connects, or goes away. */
|
|
8920
|
+
onRunnerChange(listener) {
|
|
8921
|
+
this.runnerListeners.add(listener);
|
|
8922
|
+
}
|
|
8923
|
+
runnerChanged(runner) {
|
|
8924
|
+
for (const listener of this.runnerListeners) {
|
|
8925
|
+
listener(runner);
|
|
8926
|
+
}
|
|
8927
|
+
}
|
|
8826
8928
|
// -------------------------------------------------------------------------------------------
|
|
8827
8929
|
async detach(seat) {
|
|
8828
8930
|
seat.send = null;
|
|
8931
|
+
this.runnerChanged(seat.name);
|
|
8829
8932
|
seat.disconnectedAt = this.now().getTime();
|
|
8830
8933
|
for (const [id, pending] of this.pending) {
|
|
8831
8934
|
if (pending.runner === seat.name) {
|
|
@@ -9632,7 +9735,19 @@ function createApp(deps) {
|
|
|
9632
9735
|
});
|
|
9633
9736
|
api.get("/models/:cli", async (c) => {
|
|
9634
9737
|
const cli = CliKindSchema.parse(c.req.param("cli"));
|
|
9635
|
-
return c.json(models === void 0 ? [] : await models.list(cli).
|
|
9738
|
+
return c.json(models === void 0 ? [] : await models.list(cli).then((listed) => listed.models, () => []));
|
|
9739
|
+
});
|
|
9740
|
+
api.get("/agents/:name/models", async (c) => {
|
|
9741
|
+
const agent = await board2.readAgent(c.req.param("name"));
|
|
9742
|
+
if (agent.cli === null) {
|
|
9743
|
+
throw new BoardError("INVALID_STATE", `${agent.name} takes no turns, so it has no models`);
|
|
9744
|
+
}
|
|
9745
|
+
if (models === void 0) {
|
|
9746
|
+
throw new RunnerAwayError("this server asks no runner for models");
|
|
9747
|
+
}
|
|
9748
|
+
const placed = new Map((await board2.listProjects()).map((project) => [project.slug, project.runner]));
|
|
9749
|
+
const prefer = [agent.homeRunner, ...agent.memberships.map((slug) => placed.get(slug))].filter((runner) => runner !== void 0);
|
|
9750
|
+
return c.json(await models.list(agent.cli, prefer));
|
|
9636
9751
|
});
|
|
9637
9752
|
api.put("/roles/:name", async (c) => {
|
|
9638
9753
|
const charter = RoleCharterSchema.parse({
|
|
@@ -9915,26 +10030,42 @@ var EnrollmentDesk = class {
|
|
|
9915
10030
|
// apps/server/dist/models.js
|
|
9916
10031
|
var HOUR = 60 * 6e4;
|
|
9917
10032
|
var ModelCatalog = class {
|
|
9918
|
-
|
|
10033
|
+
runners;
|
|
9919
10034
|
ttlMs;
|
|
9920
10035
|
now;
|
|
9921
10036
|
kept = /* @__PURE__ */ new Map();
|
|
9922
|
-
constructor(
|
|
9923
|
-
this.
|
|
10037
|
+
constructor(runners2, ttlMs = HOUR, now = Date.now) {
|
|
10038
|
+
this.runners = runners2;
|
|
9924
10039
|
this.ttlMs = ttlMs;
|
|
9925
10040
|
this.now = now;
|
|
10041
|
+
runners2.onRunnerChange((runner) => this.forget(runner));
|
|
9926
10042
|
}
|
|
9927
|
-
list(cli) {
|
|
9928
|
-
const
|
|
10043
|
+
async list(cli, prefer = []) {
|
|
10044
|
+
const runner = this.runners.modelRunner(cli, prefer);
|
|
10045
|
+
if (runner === null) {
|
|
10046
|
+
throw new RunnerAwayError(`no connected runner has ${cli}`);
|
|
10047
|
+
}
|
|
10048
|
+
const key = `${runner}/${cli}`;
|
|
10049
|
+
const kept = this.kept.get(key);
|
|
9929
10050
|
if (kept !== void 0 && this.now() - kept.at < this.ttlMs) {
|
|
9930
|
-
return kept.models;
|
|
10051
|
+
return { runner, cli, models: await kept.models };
|
|
9931
10052
|
}
|
|
9932
|
-
const models = this.
|
|
9933
|
-
this.kept.set(
|
|
10053
|
+
const models = this.runners.models(cli, runner);
|
|
10054
|
+
this.kept.set(key, { at: this.now(), models });
|
|
9934
10055
|
models.catch(() => {
|
|
9935
|
-
this.kept.
|
|
10056
|
+
if (this.kept.get(key)?.models === models) {
|
|
10057
|
+
this.kept.delete(key);
|
|
10058
|
+
}
|
|
9936
10059
|
});
|
|
9937
|
-
return models;
|
|
10060
|
+
return { runner, cli, models: await models };
|
|
10061
|
+
}
|
|
10062
|
+
/** Drops every list a runner gave. */
|
|
10063
|
+
forget(runner) {
|
|
10064
|
+
for (const key of this.kept.keys()) {
|
|
10065
|
+
if (key.startsWith(`${runner}/`)) {
|
|
10066
|
+
this.kept.delete(key);
|
|
10067
|
+
}
|
|
10068
|
+
}
|
|
9938
10069
|
}
|
|
9939
10070
|
};
|
|
9940
10071
|
|
|
@@ -10042,7 +10173,7 @@ var app = createApp({
|
|
|
10042
10173
|
version: VERSION,
|
|
10043
10174
|
turns,
|
|
10044
10175
|
scheduler: view,
|
|
10045
|
-
models: new ModelCatalog(
|
|
10176
|
+
models: new ModelCatalog(runners),
|
|
10046
10177
|
runners,
|
|
10047
10178
|
webDir,
|
|
10048
10179
|
signIn: { github: config.github, log },
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{b as e,ln as t,wt as n}from"./format-
|
|
1
|
+
import{b as e,ln as t,wt as n}from"./format-C7bqWt6N.js";import{L as r,S as i,n as a,r as o,t as s}from"./index-DM00hlle.js";var c=t(),l=`grid size-7 shrink-0 place-items-center rounded-lg text-fg-tertiary hover:bg-surface-2/70 hover:text-fg-primary`;function u({fallback:t}){let u=n(),d=r({select:e=>e.location.state.__TSR_index}),f=o(),p=i(),m=e(),h=a(f,d);if(h===null)return t;let g=s(h,{entity:e=>p.get(e)?.title,project:e=>m.data?.find(t=>t.slug===e)?.name});return(0,c.jsx)(`button`,{type:`button`,onClick:()=>u.history.back(),"aria-label":`Back to ${g}`,title:`Back to ${g}`,className:l,children:`←`})}export{u as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{D as e,F as t,Ht as n,R as r,b as i,
|
|
1
|
+
import{D as e,F as t,Ht as n,R as r,b as i,g as a,ln as o,pn as s,un as c,y as l}from"./format-C7bqWt6N.js";import{I as u,V as d,c as f,i as p}from"./index-DM00hlle.js";import{t as m}from"./MessageItem-CL3XDi7l.js";import{n as h,t as g}from"./Pane-voa6GU0F.js";import{n as _}from"./ThreadCard-CzQXJXSd.js";import{i as v}from"./ThreadForms-0LpUc2jJ.js";import{t as y}from"./useStickyScroll-DCsF4GYJ.js";var b=s(c(),1),x=o();function S(){let{_splat:e=`general`}=d({from:`/c/$`});return(0,x.jsx)(C,{channel:e},e)}function C({channel:o}){let{api:s}=e(),c=n({queryKey:[`channel`,o],queryFn:()=>s.channel(o)}),d=t(),S=a(),C=i(),w=l(3e4),[T,E]=(0,b.useState)(!1),D=(0,b.useMemo)(()=>{let e=(c.data??[]).map(e=>({kind:`message`,at:e.ts,id:e.id,message:e})),t=(d.data??[]).filter(e=>e.channel===o).map(e=>({kind:`thread`,at:e.openedAt,id:e.id,thread:e}));return[...e,...t].toSorted((e,t)=>e.at.localeCompare(t.at)||e.id.localeCompare(t.id))},[c.data,d.data,o]),O=c.data?.at(-1)?.id??null;(0,b.useEffect)(()=>u(o,O),[o,O]);let{ref:k,onScroll:A}=y(),[j,M]=o.split(`/`),N=M??j??o,P=M===void 0?void 0:C.data?.find(e=>e.slug===j),F=M===void 0?`society`:P?.name??j,I=P?.archived!==void 0,L=D.filter(e=>e.kind===`thread`&&e.thread.state===`open`);return(0,x.jsxs)(x.Fragment,{children:[(0,x.jsx)(g,{title:`# ${N}`,subtitle:`${F} · ${c.data?.length??0} messages${L.length>0?` · ${L.length} open threads`:``}`,trailing:T||I?null:(0,x.jsx)(f,{onClick:()=>E(!0),children:`New thread`})}),T?(0,x.jsx)(v,{channel:o,onDone:()=>E(!1)}):null,(0,x.jsx)(`div`,{ref:k,onScroll:A,className:`flex-1 overflow-y-auto py-2`,children:c.error instanceof r&&c.error.status===404?(0,x.jsxs)(h,{children:[`There is no channel `,o,`.`]}):c.data===void 0?(0,x.jsx)(h,{children:`Reading the channel…`}):D.length===0?(0,x.jsx)(h,{children:`Nothing has been said here yet.`}):D.map(e=>e.kind===`message`?(0,x.jsx)(m,{message:e.message,members:S.data,now:w},e.id):(0,x.jsx)(_,{thread:e.thread,now:w},e.id))}),c.data===void 0?null:I?(0,x.jsxs)(`p`,{className:`border-t border-line px-4 py-3 text-meta`,children:[F,` is archived, so its channels are read-only.`]}):(0,x.jsx)(p,{target:{channel:o},placeholder:`Message #${N}`})]})}export{S as ChannelView};
|