@wierdbytes/pi-dream 0.0.0-stage → 0.1.0

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/tools.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * memory_search · memory_read · memory_inbox
3
+ *
4
+ * Descriptions say what memory is for, not how to use it — the model judges
5
+ * what is worth looking up or keeping. memory_inbox never writes memory
6
+ * directly: items wait for the next dream and the user's review.
7
+ */
8
+
9
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
10
+ import { Type } from "typebox";
11
+ import { INBOX_KINDS } from "./inbox.ts";
12
+ import type { Runtime } from "./runtime.ts";
13
+
14
+ /** `{ type: "string", enum }` typed through TypeBox (same JSON as pi-ai's StringEnum). */
15
+ function StringEnum<const T extends readonly string[]>(values: T, options: { description?: string } = {}) {
16
+ return Type.Unsafe<T[number]>({ type: "string", enum: values, ...options });
17
+ }
18
+
19
+ const text = (t: string) => ({ content: [{ type: "text" as const, text: t }], details: {} });
20
+
21
+ export function registerTools(pi: ExtensionAPI, rt: Runtime): void {
22
+ pi.registerTool({
23
+ name: "memory_search",
24
+ label: "Memory search",
25
+ description:
26
+ "Search this user's long-term memory: approved memory notes (global and this project) and digests of past pi sessions. Returns note ids and session references (s:<session>#<entry>) to open with memory_read.",
27
+ promptSnippet: "Search memory notes and past pi sessions of this user",
28
+ promptGuidelines: [
29
+ "Use memory_search when the task depends on what was done, decided or learned in earlier sessions, or on user preferences the <memory> section does not cover.",
30
+ ],
31
+ parameters: Type.Object({
32
+ query: Type.String({ description: "Words to look for: names, commands, paths, topics" }),
33
+ scope: Type.Optional(StringEnum(["all", "notes", "sessions"] as const, { description: "What to search (default: all)" })),
34
+ projects: Type.Optional(StringEnum(["current", "all"] as const, { description: "current = this project + global notes (default); all = every project" })),
35
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 15, description: "Results per kind (default 6)" })),
36
+ }),
37
+ async execute(_id, params, _signal, _onUpdate, ctx) {
38
+ return text(await rt.search(ctx, params.query, params.scope ?? "all", params.projects ?? "current", params.limit ?? 6));
39
+ },
40
+ });
41
+
42
+ pi.registerTool({
43
+ name: "memory_read",
44
+ label: "Memory read",
45
+ description:
46
+ "Open a memory note by id (m-xxxxxx) with its details and evidence, or a past session entry by reference (s:<sessionId>#<entryId>) with the entries around it.",
47
+ promptSnippet: "Open a memory note or a past session entry",
48
+ parameters: Type.Object({
49
+ ref: Type.String({ description: "Note id (m-xxxxxx) or session reference s:<sessionId>#<entryId>" }),
50
+ }),
51
+ async execute(_id, params) {
52
+ return text(await rt.read(params.ref));
53
+ },
54
+ });
55
+
56
+ pi.registerTool({
57
+ name: "memory_inbox",
58
+ label: "Memory inbox",
59
+ description:
60
+ "Keep something for this user's future sessions. Items go to the inbox of pi-dream, which reviews them offline with the session evidence and proposes memory notes for the user to approve; they are not active memory yet.",
61
+ promptSnippet: "Save something future sessions should know for the next memory review",
62
+ promptGuidelines: [
63
+ "Use memory_inbox when the user asks you to remember something, or when you learn something clearly worth keeping for future sessions that cannot be quickly rediscovered from the code — a preference or standing request, a verified procedure, a pitfall and its fix, a decision and its reason.",
64
+ ],
65
+ parameters: Type.Object({
66
+ text: Type.String({ description: "A self-contained statement with the specifics (exact commands, paths, conditions, reasons)" }),
67
+ kind: Type.Optional(StringEnum(INBOX_KINDS, { description: "What kind of knowledge this is" })),
68
+ scope: Type.Optional(StringEnum(["project", "global"] as const, { description: "global = about the user or this machine across projects" })),
69
+ }),
70
+ async execute(_id, params, _signal, _onUpdate, ctx) {
71
+ const id = await rt.inbox(ctx, params.text, "agent", params.kind, params.scope);
72
+ return text(`Saved to the pi-dream inbox (${id}). The next dream reviews it; it becomes memory only after the user approves the change.`);
73
+ },
74
+ });
75
+ }