bom-pi 0.0.0-stage → 0.0.2

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.
Files changed (131) hide show
  1. package/README.md +33 -2
  2. package/apps/README.md +7 -0
  3. package/apps/web/README.md +21 -0
  4. package/apps/web/pocket-adapter.mjs +323 -0
  5. package/apps/web/public/LICENSE.pocket +21 -0
  6. package/apps/web/public/activity-fold.js +28 -0
  7. package/apps/web/public/agent-now.js +37 -0
  8. package/apps/web/public/agent-tile.js +79 -0
  9. package/apps/web/public/app.js +975 -0
  10. package/apps/web/public/apple-touch-icon.png +0 -0
  11. package/apps/web/public/artifact-bridge.js +687 -0
  12. package/apps/web/public/avatar.js +87 -0
  13. package/apps/web/public/back.js +554 -0
  14. package/apps/web/public/box-keys.js +216 -0
  15. package/apps/web/public/browser-panel.js +1097 -0
  16. package/apps/web/public/browser.js +68 -0
  17. package/apps/web/public/calls.js +139 -0
  18. package/apps/web/public/chat.js +919 -0
  19. package/apps/web/public/clock.js +34 -0
  20. package/apps/web/public/commands.js +578 -0
  21. package/apps/web/public/composer.js +2068 -0
  22. package/apps/web/public/diff-parse.js +672 -0
  23. package/apps/web/public/diff.js +1646 -0
  24. package/apps/web/public/dock.js +299 -0
  25. package/apps/web/public/extension-ui.js +2734 -0
  26. package/apps/web/public/files-panel.js +775 -0
  27. package/apps/web/public/files.js +368 -0
  28. package/apps/web/public/fonts/FiraCode-VF.woff2 +0 -0
  29. package/apps/web/public/fonts/Geist.woff2 +0 -0
  30. package/apps/web/public/fonts/IBMPlexMono-Variable-Italic.woff2 +0 -0
  31. package/apps/web/public/fonts/IBMPlexMono-Variable.woff2 +0 -0
  32. package/apps/web/public/fonts/InterVariable-Italic.woff2 +0 -0
  33. package/apps/web/public/fonts/InterVariable.woff2 +0 -0
  34. package/apps/web/public/fonts/Iosevka-Bold.woff2 +0 -0
  35. package/apps/web/public/fonts/Iosevka-BoldItalic.woff2 +0 -0
  36. package/apps/web/public/fonts/Iosevka-Italic.woff2 +0 -0
  37. package/apps/web/public/fonts/Iosevka-Regular.woff2 +0 -0
  38. package/apps/web/public/fonts/Iosevka-SemiBold.woff2 +0 -0
  39. package/apps/web/public/fonts/Iosevka-SemiBoldItalic.woff2 +0 -0
  40. package/apps/web/public/fonts/JetBrainsMono-Italic.woff2 +0 -0
  41. package/apps/web/public/fonts/JetBrainsMono.woff2 +0 -0
  42. package/apps/web/public/fonts/NerdFontsSymbolsMono.woff2 +0 -0
  43. package/apps/web/public/fonts/SOURCES.md +69 -0
  44. package/apps/web/public/gestures.js +610 -0
  45. package/apps/web/public/highlight.js +480 -0
  46. package/apps/web/public/history.js +70 -0
  47. package/apps/web/public/home.js +169 -0
  48. package/apps/web/public/icon-192.png +0 -0
  49. package/apps/web/public/icon-512.png +0 -0
  50. package/apps/web/public/icon-maskable-512.png +0 -0
  51. package/apps/web/public/icon.svg +16 -0
  52. package/apps/web/public/index.html +71 -0
  53. package/apps/web/public/launcher.js +1293 -0
  54. package/apps/web/public/linked-sessions.js +55 -0
  55. package/apps/web/public/live.js +39 -0
  56. package/apps/web/public/manifest.webmanifest +32 -0
  57. package/apps/web/public/model-refresh.js +73 -0
  58. package/apps/web/public/notify.js +251 -0
  59. package/apps/web/public/peeks.js +920 -0
  60. package/apps/web/public/pip-attachments.js +182 -0
  61. package/apps/web/public/pip-window.js +141 -0
  62. package/apps/web/public/pip.js +1217 -0
  63. package/apps/web/public/quote-reply.js +109 -0
  64. package/apps/web/public/quote-text.js +31 -0
  65. package/apps/web/public/rich.js +403 -0
  66. package/apps/web/public/run-frame.js +106 -0
  67. package/apps/web/public/safe-mode.js +71 -0
  68. package/apps/web/public/seen.js +169 -0
  69. package/apps/web/public/session-tree.js +117 -0
  70. package/apps/web/public/sessions.js +2695 -0
  71. package/apps/web/public/share.js +126 -0
  72. package/apps/web/public/sheets/appearance.js +820 -0
  73. package/apps/web/public/sheets/branch.js +353 -0
  74. package/apps/web/public/sheets/extensions.js +818 -0
  75. package/apps/web/public/sheets/file.js +318 -0
  76. package/apps/web/public/sheets/find.js +250 -0
  77. package/apps/web/public/sheets/folder.js +680 -0
  78. package/apps/web/public/sheets/gallery.js +2340 -0
  79. package/apps/web/public/sheets/hooks.js +350 -0
  80. package/apps/web/public/sheets/memory.js +531 -0
  81. package/apps/web/public/sheets/menu.js +580 -0
  82. package/apps/web/public/sheets/message.js +220 -0
  83. package/apps/web/public/sheets/model.js +412 -0
  84. package/apps/web/public/sheets/people.js +414 -0
  85. package/apps/web/public/sheets/pi-sessions.js +264 -0
  86. package/apps/web/public/sheets/providers.js +525 -0
  87. package/apps/web/public/sheets/running.js +130 -0
  88. package/apps/web/public/sheets/schedules.js +71 -0
  89. package/apps/web/public/sheets/session.js +249 -0
  90. package/apps/web/public/sheets/shortcuts.js +101 -0
  91. package/apps/web/public/sheets/spend.js +150 -0
  92. package/apps/web/public/sheets/text.js +53 -0
  93. package/apps/web/public/sheets/viewers.js +363 -0
  94. package/apps/web/public/sheets/worktree.js +50 -0
  95. package/apps/web/public/sheets.js +410 -0
  96. package/apps/web/public/signin.js +57 -0
  97. package/apps/web/public/slots.js +208 -0
  98. package/apps/web/public/store.js +1760 -0
  99. package/apps/web/public/style.css +18625 -0
  100. package/apps/web/public/subagents-board.js +879 -0
  101. package/apps/web/public/subagents.js +273 -0
  102. package/apps/web/public/sw.js +169 -0
  103. package/apps/web/public/taps.js +26 -0
  104. package/apps/web/public/theme-surface.d.ts +4 -0
  105. package/apps/web/public/theme-surface.js +266 -0
  106. package/apps/web/public/theme.js +845 -0
  107. package/apps/web/public/themes/lantern/colors.toml +25 -0
  108. package/apps/web/public/themes/lantern/direction.css +60 -0
  109. package/apps/web/public/themes/lantern/frame.css +254 -0
  110. package/apps/web/public/themes/lantern/theme.css +10 -0
  111. package/apps/web/public/themes/lantern/theme.json +7 -0
  112. package/apps/web/public/themes/pocket/theme.css +189 -0
  113. package/apps/web/public/themes/pocket/theme.json +6 -0
  114. package/apps/web/public/themes.js +556 -0
  115. package/apps/web/public/transcript.js +3348 -0
  116. package/apps/web/public/trust.js +295 -0
  117. package/apps/web/public/turns.js +507 -0
  118. package/apps/web/public/ui.js +1910 -0
  119. package/apps/web/public/voice.js +345 -0
  120. package/apps/web/server.mjs +774 -0
  121. package/package.json +43 -4
  122. package/packages/cli/src/bom-pi.mjs +452 -0
  123. package/packages/cli/templates/blank.factory.json +7 -0
  124. package/packages/cli/templates/dev.factory.json +11 -0
  125. package/packages/cli/templates/docs.factory.json +12 -0
  126. package/packages/cli/templates/ops.factory.json +14 -0
  127. package/packages/cli/templates/triage.factory.json +10 -0
  128. package/packages/core/src/daemon.mjs +2047 -0
  129. package/packages/core/src/limit-gate.mjs +192 -0
  130. package/packages/core/src/rpc-client.mjs +66 -0
  131. package/systemd/README.md +11 -0
@@ -0,0 +1,295 @@
1
+ // Project trust gates .agents/skills and model defaults from .pi/settings.json. The bar asks the owner when a
2
+ // session's project has either and nobody decided yet; /trust shows and changes the decision shared with Pi's CLI.
3
+ // Trusting model defaults affects only later new sessions, never the open session.
4
+ import { useEffect } from "preact/hooks";
5
+ import { actions, attempt, closeSheet, notify, openSheet, store } from "./store.js";
6
+ import { html, Icon, Loader, Sheet, shortPath } from "./ui.js";
7
+
8
+ /** Only the owner decides which projects Pi trusts: the answer goes into Pi's own settings. */
9
+ export const trustAvailable = () => store.state.me?.role === "owner";
10
+
11
+ /** The folders whose question the owner put off in this tab ("Not now"): the sheet and `/trust` still answer it. */
12
+ const LATER_KEY = "pocket.trustLater";
13
+
14
+ function later() {
15
+ try {
16
+ return new Set(JSON.parse(sessionStorage.getItem(LATER_KEY) ?? "[]"));
17
+ } catch {
18
+ return new Set();
19
+ }
20
+ }
21
+
22
+ function putOff(folder) {
23
+ sessionStorage.setItem(LATER_KEY, JSON.stringify([...later(), folder]));
24
+ store.set({});
25
+ }
26
+
27
+ /** What the server said about the open conversation's project, when it was for its folder now. */
28
+ export function currentTrust() {
29
+ const { trust, conversationId, view } = store.state;
30
+
31
+ return trust?.conversationId === conversationId && trust.cwd === view.agent?.cwd
32
+ ? trust.info
33
+ : null;
34
+ }
35
+
36
+ /** Fetch whether Pi trusts the open conversation's project, unless that is known for its folder (or `force`). */
37
+ export function loadTrust({ force = false } = {}) {
38
+ const { conversationId, view, trust } = store.state;
39
+ const cwd = view.agent?.cwd;
40
+ const known = trust?.conversationId === conversationId && trust.cwd === cwd;
41
+
42
+ if (conversationId === null || !cwd || (known && !force)) {
43
+ return;
44
+ }
45
+
46
+ // Asked for, which nothing shows until the answer: noted without a draw (each draw is the whole app's). The answer
47
+ // draws only when it says something new.
48
+ if (!known) {
49
+ store.note({ trust: { conversationId, cwd, info: null } });
50
+ }
51
+
52
+ actions.trust().then(
53
+ (info) => {
54
+ const now = store.state.trust;
55
+
56
+ if (
57
+ now?.conversationId === conversationId &&
58
+ now.cwd === cwd &&
59
+ JSON.stringify(info) !== JSON.stringify(now.info)
60
+ ) {
61
+ store.set({ trust: { ...now, info } });
62
+ }
63
+ },
64
+ () => {},
65
+ );
66
+ }
67
+
68
+ /** The skills' names, a few of them: "deploy, review, and 3 more". */
69
+ function skillNames(skills, most = 3) {
70
+ const names = skills.map((skill) => skill.name);
71
+
72
+ if (names.length <= most) {
73
+ return names.join(", ");
74
+ }
75
+
76
+ return `${names.slice(0, most).join(", ")}, and ${names.length - most} more`;
77
+ }
78
+
79
+ /** Save the owner's answer, and show it: the bar goes, and the skills join the slash commands. */
80
+ async function decide(choice) {
81
+ const { conversationId, view, server } = store.state;
82
+ const cwd = view.agent?.cwd;
83
+ const info = await actions.setTrust(choice);
84
+
85
+ // Saved for that session's folder; this tab shows another session now: its own trust stays as it is.
86
+ if (store.state.conversationId !== conversationId || store.state.view.agent?.cwd !== cwd) {
87
+ return;
88
+ }
89
+
90
+ store.set({ trust: { conversationId, cwd, info }, templates: null });
91
+
92
+ if (!info.trusted) {
93
+ notify(
94
+ "info",
95
+ "Not trusted: project model defaults and its own skills stay off. /trust changes that.",
96
+ );
97
+
98
+ return;
99
+ }
100
+
101
+ const where =
102
+ choice === "trust-parent"
103
+ ? `${shortPath(info.parent, server?.home)} and the folders in it`
104
+ : "this project";
105
+ const skills =
106
+ info.skills.length > 0 ? ` Pi has its skills from now on: ${skillNames(info.skills)}.` : "";
107
+
108
+ const defaults = info.settings
109
+ ? " Project model defaults apply only to later new sessions; this session will not change."
110
+ : "";
111
+
112
+ notify("info", `Trusted ${where}.${skills}${defaults}`);
113
+ }
114
+
115
+ /** Above the message box, for the owner: this project's skills or settings wait for a trust decision. */
116
+ export function TrustBar() {
117
+ const { conversationId } = store.state;
118
+ const cwd = store.state.view.agent?.cwd;
119
+ const mine = trustAvailable();
120
+
121
+ useEffect(() => {
122
+ if (mine) {
123
+ loadTrust();
124
+ }
125
+ }, [conversationId, cwd, mine]);
126
+ const info = currentTrust();
127
+
128
+ if (!mine || !info?.ask || later().has(info.folder)) {
129
+ return null;
130
+ }
131
+
132
+ return html`<div class="trust-bar">
133
+ <span class="grow">
134
+ <strong>Trust this project?</strong>${" "}${
135
+ info.settings
136
+ ? `Its .pi/settings.json can set the model and thinking level of later new sessions. This session will not change.${info.skills.length > 0 ? ` Its skills in .agents/skills (${skillNames(info.skills)}) also need trust.` : ""}`
137
+ : `Its skills in .agents/skills (${skillNames(info.skills)}) load only in a project Pi trusts.`
138
+ }
139
+ </span>
140
+ <span class="trust-actions">
141
+ <button class="link small" onClick=${() => attempt(() => decide("distrust"))}>
142
+ Don't trust
143
+ </button>
144
+ <button class="button small primary" onClick=${() => openSheet({ type: "trust" })}>
145
+ Review
146
+ </button>
147
+ <button
148
+ class="icon-button"
149
+ aria-label="Not now"
150
+ title="Not now: ask again in a new tab"
151
+ onClick=${() => putOff(info.folder)}
152
+ >
153
+ <${Icon} name="close" size=${13} />
154
+ </button>
155
+ </span>
156
+ </div>`;
157
+ }
158
+
159
+ /** An answer in the sheet: what it does, the folder it is about when that is not this one, and whether it is saved. */
160
+ function Choice({ label, detail, current, onClick }) {
161
+ return html`<button class=${`list-item trust-choice ${current ? "active" : ""}`} onClick=${onClick}>
162
+ <span class="trust-radio" aria-hidden="true"></span>
163
+ <span class="trust-choice-text">
164
+ <span>${label}</span>
165
+ ${detail && html`<span class="muted small mono">${detail}</span>`}
166
+ </span>
167
+ ${current && html`<span class="trust-current">✓ current</span>`}
168
+ </button>`;
169
+ }
170
+
171
+ /**
172
+ * Whether Pi was told not to trust the project: a saved "no", or its setting for undecided projects. A trust store that
173
+ * cannot be read decides nothing (as on the server, `skills.ts`), so the project's .pi/skills load.
174
+ */
175
+ const distrusted = (info) =>
176
+ !info.trusted &&
177
+ !info.unreadable &&
178
+ (info.saved?.trusted === false ||
179
+ (info.saved === null && info.defaultProjectTrust === "never"));
180
+
181
+ /** What the project's own skills do here, a sentence for each place they are in. */
182
+ function ownSkillsText(info) {
183
+ const list = (skills) => skillNames(skills, 8);
184
+ const texts = [];
185
+
186
+ if (info.skills.length > 0) {
187
+ texts.push(
188
+ `In .agents/skills: ${list(info.skills)}. ${info.trusted ? "Pi has them." : "They load once you trust it."}`,
189
+ );
190
+ }
191
+
192
+ if (info.ownSkills.length > 0) {
193
+ texts.push(
194
+ `In .pi/skills: ${list(info.ownSkills)}. ${distrusted(info) ? "They stay off while it is not trusted." : "They load unless you choose Don't trust."}`,
195
+ );
196
+ }
197
+
198
+ return texts.length > 0
199
+ ? texts
200
+ : [
201
+ info.settings
202
+ ? "It has no skills of its own."
203
+ : "It has no skills of its own, so this changes nothing in Pi Pocket here.",
204
+ ];
205
+ }
206
+
207
+ /** Where the decision comes from, in a sentence. */
208
+ function status(info, short) {
209
+ if (info.unreadable) {
210
+ return "Pi's trust store (~/.pi/agent/trust.json) cannot be read, so no project is trusted, and no answer can be saved until it is mended or deleted.";
211
+ }
212
+
213
+ if (info.saved === null) {
214
+ if (info.defaultProjectTrust === "always") {
215
+ return "Trusted: no decision is saved for it, and Pi's defaultProjectTrust setting trusts every project.";
216
+ }
217
+
218
+ if (info.defaultProjectTrust === "never") {
219
+ return "Not trusted: no decision is saved for it, and Pi's defaultProjectTrust setting trusts no project.";
220
+ }
221
+
222
+ return info.skills.length > 0
223
+ ? "No decision yet. Until there is one, Pi Pocket leaves its skills in .agents/skills off."
224
+ : "No decision yet.";
225
+ }
226
+
227
+ const what = info.saved.trusted ? "Trusted" : "Not trusted";
228
+
229
+ return info.saved.path === info.folder
230
+ ? `${what}.`
231
+ : `${what}, as the folder it is in: ${short(info.saved.path)}.`;
232
+ }
233
+
234
+ /** The project's trust, and the three answers Pi's `/trust` offers. */
235
+ export function TrustSheet() {
236
+ const { server } = store.state;
237
+ const short = (path) => shortPath(path, server?.home);
238
+
239
+ useEffect(() => loadTrust({ force: true }), []);
240
+ const info = currentTrust();
241
+
242
+ if (!info) {
243
+ return html`<${Sheet} title="Project trust" onClose=${closeSheet}>
244
+ <${Loader} label="Loading" />
245
+ <//>`;
246
+ }
247
+
248
+ const choose = (choice) =>
249
+ attempt(async () => {
250
+ await decide(choice);
251
+ closeSheet();
252
+ });
253
+ const saved = (path, trusted) => info.saved?.path === path && info.saved.trusted === trusted;
254
+
255
+ return html`<${Sheet} title="Project trust" onClose=${closeSheet}>
256
+ <p class="mono small">${short(info.folder)}</p>
257
+ <p>${status(info, short)}</p>
258
+ ${ownSkillsText(info).map((text) => html`<p>${text}</p>`)}
259
+ ${
260
+ info.settings &&
261
+ html`<p>
262
+ In .pi/settings.json: model and thinking defaults apply only to later new sessions, after trust. This session will not change. The owner's Default model still wins; clear it in Menu → Default model to use project defaults. No project setting can grant trust to itself. Extra skill and prompt paths and per-project run policy are not supported yet.
263
+ </p>`
264
+ }
265
+ <p class="muted small">
266
+ Pi's CLI goes by the same decision, saved in ~/.pi/agent/trust.json. There, trusting a project also loads its .pi settings, extensions, and packages, which run code on this machine. Trust only folders whose contents you trust.
267
+ </p>
268
+ ${
269
+ !trustAvailable()
270
+ ? html`<p class="muted small">Only the owner can change this.</p>`
271
+ : !info.unreadable &&
272
+ html`<div class="group">
273
+ <${Choice}
274
+ label="Trust this folder"
275
+ current=${saved(info.folder, true)}
276
+ onClick=${() => choose("trust")}
277
+ />
278
+ ${
279
+ info.parent &&
280
+ html`<${Choice}
281
+ label="Trust the folder above it"
282
+ detail=${short(info.parent)}
283
+ current=${saved(info.parent, true)}
284
+ onClick=${() => choose("trust-parent")}
285
+ />`
286
+ }
287
+ <${Choice}
288
+ label="Don't trust"
289
+ current=${saved(info.folder, false)}
290
+ onClick=${() => choose("distrust")}
291
+ />
292
+ </div>`
293
+ }
294
+ <//>`;
295
+ }