Skip to content
96 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

The docs service answers every artifacts call: docs can be made, listed, shared, moved, trashed, restored, searched, versioned and edited live in their own rooms, agents read, write and recall them only where their person and everyone in the conversation can, and folio events go out on the bus, while Docs' pages keep working as before.1import assert from "node:assert/strict";
2import { test } from "node:test";
3
4import { aclChain, type FolioAclNode, type FolioGrant, type Person, type SpaceRules } from "../access.ts";
5import { agentMayFind, agentReach, audienceRule, type AudienceRule } from "./agents.ts";
6
7// The leak rules of docs/ARTIFACTS_MODE.md section 4.3.
8
9const ana: Person = { user_id: "ana", owner: false, teams: new Set(["web"]) };
10const bo: Person = { user_id: "bo", owner: false, teams: new Set() };
11const cy: Person = { user_id: "cy", owner: false, teams: new Set(["web"]) };
12
13const open: SpaceRules = { kind: "workspace", team: null, default_role: "edit", members: [] };
14const team: SpaceRules = { kind: "team", team: "web", default_role: "edit", members: [] };
15
16function node(id: string, over: Partial<FolioAclNode> = {}): FolioAclNode {
17 return { id, owner: "user:ana", parent_id: null, space_id: null, inherit: true, general_access: "none", general_role: null, ...over };
18}
19
20function reach(n: FolioAclNode, grants: Record<string, FolioGrant[]>, space: SpaceRules | null, rule: AudienceRule, people: Person[] = [], visits: string[] = [], asker = ana) {
21 return agentReach({
22 chain: aclChain(n.id, new Map([[n.id, n]])),
23 grants: new Map(Object.entries(grants)),
24 space,
25 asker,
26 askerVisited: visits.includes(asker.user_id),
27 rule,
28 people,
29 visited: (p) => visits.includes(p.user_id),
30 agent_mode: "edit",
31 });
32}
33
34test("audience rules: none or only the asker is the asker; more than 20 people is the workspace", () => {
35 assert.deepEqual(audienceRule(null, "ana"), { kind: "asker" });
36 assert.deepEqual(audienceRule({ kind: "people", user_ids: ["ana"] }, "ana"), { kind: "asker" });
37 assert.deepEqual(audienceRule({ kind: "people", user_ids: ["ana", "bo", "bo"] }, "ana"), { kind: "people", user_ids: ["bo"] });
38 assert.deepEqual(audienceRule({ kind: "workspace" }, "ana"), { kind: "workspace" });
39 const crowd = Array.from({ length: 21 }, (_, i) => `u${i}`);
40 assert.deepEqual(audienceRule({ kind: "people", user_ids: crowd }, "ana"), { kind: "workspace" });
41 const twenty = Array.from({ length: 19 }, (_, i) => `u${i}`);
42 assert.equal(audienceRule({ kind: "people", user_ids: twenty }, "ana").kind, "people");
43});
44
45test("rule 1: a public channel finds only open-space and workspace-wide folios", () => {
46 const ws: AudienceRule = { kind: "workspace" };
47 assert.equal(agentMayFind(reach(node("a", { space_id: "open" }), {}, open, ws)), true);
48 assert.equal(agentMayFind(reach(node("a", { general_access: "workspace", general_role: "view" }), {}, null, ws)), true);
49 // Private, shared, team, link: never in a public channel.
50 assert.equal(agentMayFind(reach(node("a"), {}, null, ws)), false);
51 assert.equal(agentMayFind(reach(node("a"), { a: [{ principal: "user:bo", role: "view" }] }, null, ws)), false);
52 assert.equal(agentMayFind(reach(node("a", { space_id: "team" }), {}, team, ws)), false);
53 assert.equal(agentMayFind(reach(node("a", { general_access: "link", general_role: "view" }), {}, null, ws, [], ["ana"])), false);
54});
55
56test("rule 1: a conversation finds only what everyone in it can read", () => {
57 const withBo: AudienceRule = { kind: "people", user_ids: ["bo"] };
58 const shared = node("a");
59 assert.equal(agentMayFind(reach(shared, { a: [{ principal: "user:bo", role: "view" }] }, null, withBo, [bo])), true);
60 assert.equal(agentMayFind(reach(shared, {}, null, withBo, [bo])), false);
61 // A team space: Cy is in the team, Bo isn't.
62 const t = node("t", { space_id: "team" });
63 assert.equal(agentMayFind(reach(t, {}, team, { kind: "people", user_ids: ["cy"] }, [cy])), true);
64 assert.equal(agentMayFind(reach(t, {}, team, withBo, [bo])), false);
65 // A link folio: only when everyone opened it.
66 const link = node("l", { general_access: "link", general_role: "view" });
67 assert.equal(agentMayFind(reach(link, {}, null, withBo, [bo], ["ana"])), false);
68 assert.equal(agentMayFind(reach(link, {}, null, withBo, [bo], ["ana", "bo"])), true);
69});
70
71test("rule 1: the asker's own Private is theirs alone", () => {
72 assert.equal(agentMayFind(reach(node("p"), {}, null, { kind: "asker" })), true);
73 assert.equal(agentMayFind(reach(node("p"), {}, null, { kind: "people", user_ids: ["bo"] }, [bo])), false);
74 // Someone else's Private: not even for its asker.
75 assert.equal(agentMayFind(reach(node("p", { owner: "user:bo" }), {}, null, { kind: "asker" })), false);
76});
77
78test("rule 2: reading what the audience can't all read says so", () => {
79 const r = reach(node("p"), {}, null, { kind: "workspace" });
80 assert.equal(r.asker_role, "manage");
81 assert.equal(r.audience_can_read, false);
82 const fine = reach(node("o", { space_id: "open" }), {}, open, { kind: "workspace" });
83 assert.equal(fine.audience_can_read, true);
84});
85
86test("an agent's grant never widens what it can do for its asker", () => {
87 const n = node("f", { owner: "user:cy" });
88 const grants = { f: [{ principal: "agent:ag1", role: "manage" as const }, { principal: "user:bo", role: "comment" as const }] };
89 const r = reach(n, grants, null, { kind: "asker" }, [], [], bo);
90 assert.equal(r.asker_role, "comment");
91 assert.deepEqual(r.can, { read: true, suggest: true, edit: false });
92 const none = reach(n, grants, null, { kind: "asker" }, [], [], ana);
93 assert.equal(none.asker_role, null);
94 assert.deepEqual(none.can, { read: false, suggest: false, edit: false });
95 assert.equal(agentMayFind(none), false);
96});