@rivet-dev/pi 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.
@@ -0,0 +1,185 @@
1
+ import { posix } from "node:path";
2
+ import { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, } from "@earendil-works/pi-coding-agent";
3
+ const MAX_FIND_RESULTS = 1_000;
4
+ const MAX_GREP_MATCHES = 1_000;
5
+ const MAX_GREP_OUTPUT_BYTES = 512 * 1024;
6
+ const IGNORED_DIRECTORIES = [".git", "node_modules"];
7
+ /**
8
+ * Builds Pi's seven built-in coding tools with every file and shell operation
9
+ * routed into the sandbox. Passed as `customTools`, they replace the built-in
10
+ * tools of the same names.
11
+ */
12
+ export function createSandboxTools(sandbox) {
13
+ const root = normalizeRoot(sandbox.cwd);
14
+ const resolvePath = (path) => resolveSandboxPath(root, path);
15
+ const access = async (path) => {
16
+ const resolved = resolvePath(path);
17
+ if (!(await sandbox.exists(resolved))) {
18
+ throw new Error(`Path not found: ${resolved}`);
19
+ }
20
+ };
21
+ const readFile = async (path) => Buffer.from(await sandbox.readFile(resolvePath(path)));
22
+ const writeFile = (path, content) => sandbox.writeFile(resolvePath(path), content);
23
+ const read = createReadToolDefinition(root, {
24
+ operations: {
25
+ access,
26
+ readFile,
27
+ detectImageMimeType: async (path) => imageMimeType(path),
28
+ },
29
+ });
30
+ const write = createWriteToolDefinition(root, {
31
+ operations: {
32
+ writeFile,
33
+ mkdir: (path) => sandbox.mkdir(resolvePath(path)),
34
+ },
35
+ });
36
+ const edit = createEditToolDefinition(root, {
37
+ operations: { access, readFile, writeFile },
38
+ });
39
+ const bash = createBashToolDefinition(root, {
40
+ operations: createSandboxBashOperations(sandbox),
41
+ });
42
+ const ls = createLsToolDefinition(root, {
43
+ operations: {
44
+ exists: (path) => sandbox.exists(resolvePath(path)),
45
+ readdir: (path) => sandbox.readdir(resolvePath(path)),
46
+ stat: async (path) => {
47
+ const stat = await sandbox.stat(resolvePath(path));
48
+ return { isDirectory: () => stat.isDirectory };
49
+ },
50
+ },
51
+ });
52
+ const find = createFindToolDefinition(root, {
53
+ operations: {
54
+ exists: (path) => sandbox.exists(resolvePath(path)),
55
+ glob: async (pattern, searchDirectory, options) => {
56
+ const searchRoot = resolvePath(searchDirectory);
57
+ const result = await sandbox.exec(`find . -type f ${IGNORED_DIRECTORIES.map((directory) => `-not -path ${shellQuote(`*/${directory}/*`)}`).join(" ")}`, { cwd: searchRoot });
58
+ if (result.exitCode !== 0) {
59
+ throw new Error(result.stderr.trim() || `find exited with ${result.exitCode}`);
60
+ }
61
+ return result.stdout
62
+ .split("\n")
63
+ .map(stripDotSlash)
64
+ .filter((path) => path.length > 0 && matchesToolGlob(path, pattern))
65
+ .slice(0, Math.min(options.limit, MAX_FIND_RESULTS));
66
+ },
67
+ },
68
+ });
69
+ const grepBase = createGrepToolDefinition(root);
70
+ const grep = {
71
+ ...grepBase,
72
+ execute: async (_toolCallId, params, signal) => {
73
+ const searchPath = resolvePath(params.path ?? ".");
74
+ const limit = Math.min(Math.max(1, params.limit ?? 100), MAX_GREP_MATCHES);
75
+ const args = ["-r", "-n", "-I", "-H"];
76
+ if (params.ignoreCase)
77
+ args.push("-i");
78
+ if (params.literal)
79
+ args.push("-F");
80
+ if (params.glob)
81
+ args.push(`--include=${params.glob}`);
82
+ if (params.context && params.context > 0) {
83
+ args.push("-C", String(Math.floor(params.context)));
84
+ }
85
+ for (const directory of IGNORED_DIRECTORIES) {
86
+ args.push(`--exclude-dir=${directory}`);
87
+ }
88
+ args.push("-e", params.pattern, "--", posix.relative(root, searchPath) || ".");
89
+ const result = await sandbox.exec(`grep ${args.map(shellQuote).join(" ")}`, {
90
+ cwd: root,
91
+ signal,
92
+ });
93
+ if (result.exitCode !== 0 && result.exitCode !== 1) {
94
+ throw new Error(result.stderr.trim() || `grep exited with ${result.exitCode}`);
95
+ }
96
+ const lines = result.stdout.replace(/\n$/, "").split("\n");
97
+ const matched = lines[0] === "" ? [] : lines.map(stripDotSlash);
98
+ const selected = matched.slice(0, limit);
99
+ let output = selected.join("\n");
100
+ let linesTruncated = false;
101
+ if (Buffer.byteLength(output) > MAX_GREP_OUTPUT_BYTES) {
102
+ output = Buffer.from(output).subarray(0, MAX_GREP_OUTPUT_BYTES).toString();
103
+ linesTruncated = true;
104
+ }
105
+ const details = {};
106
+ if (matched.length > selected.length)
107
+ details.matchLimitReached = limit;
108
+ if (linesTruncated)
109
+ details.linesTruncated = true;
110
+ return {
111
+ content: [{ type: "text", text: output || "No matches found" }],
112
+ details: Object.keys(details).length > 0 ? details : undefined,
113
+ };
114
+ },
115
+ };
116
+ return [read, bash, edit, write, grep, find, ls];
117
+ }
118
+ /**
119
+ * Command execution for Pi's bash tool and `executeBash`, inside the sandbox.
120
+ * Follows Pi's `BashOperations` contract: `timeout` is in seconds, and abort
121
+ * and timeout reject with `aborted` and `timeout:<seconds>`.
122
+ *
123
+ * Pi passes the actor host's environment as `options.env`. It is not
124
+ * forwarded, because it holds the host's provider keys; commands run with the
125
+ * sandbox's own environment.
126
+ */
127
+ export function createSandboxBashOperations(sandbox) {
128
+ const root = normalizeRoot(sandbox.cwd);
129
+ return {
130
+ async exec(command, requestedCwd, options) {
131
+ const result = await sandbox.exec(command, {
132
+ cwd: resolveSandboxPath(root, requestedCwd),
133
+ timeoutMs: options.timeout === undefined ? undefined : options.timeout * 1000,
134
+ signal: options.signal,
135
+ onData: (chunk) => options.onData(Buffer.from(chunk)),
136
+ });
137
+ if (result.timedOut) {
138
+ throw new Error(`timeout:${options.timeout}`);
139
+ }
140
+ return { exitCode: result.exitCode };
141
+ },
142
+ };
143
+ }
144
+ /** Resolves `path` against `root` and rejects anything outside it. */
145
+ export function resolveSandboxPath(root, path) {
146
+ const normalizedRoot = normalizeRoot(root);
147
+ const resolved = posix.resolve(normalizedRoot, path);
148
+ const prefix = normalizedRoot === "/" ? "/" : `${normalizedRoot}/`;
149
+ if (resolved !== normalizedRoot && !resolved.startsWith(prefix)) {
150
+ throw new Error(`Path escapes the sandbox working directory: ${path}`);
151
+ }
152
+ return resolved;
153
+ }
154
+ function normalizeRoot(path) {
155
+ if (!posix.isAbsolute(path)) {
156
+ throw new Error(`sandbox cwd must be an absolute POSIX path, received ${path}`);
157
+ }
158
+ return posix.normalize(path);
159
+ }
160
+ /** Pi's find semantics: a pattern without a slash matches the file name at any depth. */
161
+ function matchesToolGlob(relativePath, pattern) {
162
+ if (pattern.includes("/")) {
163
+ return (posix.matchesGlob(relativePath, pattern) ||
164
+ posix.matchesGlob(relativePath, `**/${pattern}`));
165
+ }
166
+ return posix.matchesGlob(posix.basename(relativePath), pattern);
167
+ }
168
+ function stripDotSlash(path) {
169
+ return path.startsWith("./") ? path.slice(2) : path;
170
+ }
171
+ function imageMimeType(path) {
172
+ const extension = posix.extname(path).toLowerCase();
173
+ return {
174
+ ".bmp": "image/bmp",
175
+ ".gif": "image/gif",
176
+ ".jpeg": "image/jpeg",
177
+ ".jpg": "image/jpeg",
178
+ ".png": "image/png",
179
+ ".webp": "image/webp",
180
+ }[extension];
181
+ }
182
+ function shellQuote(value) {
183
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
184
+ }
185
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { FileEntry, SessionEntry, SessionHeader, SettingsManager } from "@earendil-works/pi-coding-agent";
2
+ import type { RawAccess } from "rivetkit/db";
3
+ export type PiDatabase = Pick<RawAccess, "execute">;
4
+ export type PiSettings = ReturnType<SettingsManager["getGlobalSettings"]>;
5
+ /** A Pi session as stored in the actor's SQLite database. */
6
+ export interface StoredPiSession {
7
+ sessionId: string;
8
+ cwd: string;
9
+ header: SessionHeader;
10
+ entries: SessionEntry[];
11
+ settings: PiSettings | undefined;
12
+ }
13
+ /**
14
+ * The sandbox a Pi actor's tools run in. It is stored on its own row, written
15
+ * as soon as the provider creates it, so a failed first start never loses it.
16
+ */
17
+ export interface StoredSandbox {
18
+ provider: string;
19
+ id: string;
20
+ }
21
+ /**
22
+ * One row per Pi session entry, inserted as Pi appends them. The rows are Pi's
23
+ * own JSON, so `SessionManager.inMemory(cwd, { id }, [header, ...entries])`
24
+ * restores the session and Pi migrates old entry versions itself.
25
+ */
26
+ export declare const migratePiTables: (db: import("rivetkit/unstable/migrations").MigrationDatabase) => Promise<void>;
27
+ export declare function loadPiSession(db: PiDatabase): Promise<StoredPiSession | undefined>;
28
+ export declare function createPiSession(db: PiDatabase, session: {
29
+ header: SessionHeader;
30
+ cwd: string;
31
+ settings: PiSettings;
32
+ }): Promise<void>;
33
+ export declare function loadPiSandbox(db: PiDatabase): Promise<StoredSandbox | undefined>;
34
+ export declare function savePiSandbox(db: PiDatabase, sandbox: StoredSandbox): Promise<void>;
35
+ export declare function appendPiEntry(db: PiDatabase, entry: SessionEntry): Promise<void>;
36
+ export declare function savePiSettings(db: PiDatabase, settings: PiSettings): Promise<void>;
37
+ /** Builds the entry list `SessionManager.inMemory` expects: header first. */
38
+ export declare function toFileEntries(stored: StoredPiSession): FileEntry[];
39
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,SAAS,EACT,YAAY,EACZ,aAAa,EACb,eAAe,EACf,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AAEpD,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC,CAAC;AAE1E,6DAA6D;AAC7D,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACX;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,iFAiC1B,CAAC;AAEH,wBAAsB,aAAa,CAClC,EAAE,EAAE,UAAU,GACZ,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CA6BtC;AAED,wBAAsB,eAAe,CACpC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE;IACR,MAAM,EAAE,aAAa,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,UAAU,CAAC;CACrB,GACC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,aAAa,CAClC,EAAE,EAAE,UAAU,GACZ,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAMpC;AAED,wBAAsB,aAAa,CAClC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,aAAa,GACpB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,aAAa,CAClC,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,YAAY,GACjB,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAsB,cAAc,CACnC,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,EAAE,CAElE"}
@@ -0,0 +1,87 @@
1
+ import { migrations } from "rivetkit/unstable/migrations";
2
+ /**
3
+ * One row per Pi session entry, inserted as Pi appends them. The rows are Pi's
4
+ * own JSON, so `SessionManager.inMemory(cwd, { id }, [header, ...entries])`
5
+ * restores the session and Pi migrates old entry versions itself.
6
+ */
7
+ export const migratePiTables = migrations({
8
+ tableName: "pi_schema_version",
9
+ migrations: [
10
+ {
11
+ version: 1,
12
+ sql: `
13
+ CREATE TABLE pi_session (
14
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
15
+ session_id TEXT NOT NULL CHECK (length(session_id) > 0),
16
+ cwd TEXT NOT NULL CHECK (substr(cwd, 1, 1) = '/'),
17
+ header_json TEXT NOT NULL CHECK (json_valid(header_json)),
18
+ settings_json TEXT CHECK (
19
+ settings_json IS NULL OR json_valid(settings_json)
20
+ ),
21
+ created_at INTEGER NOT NULL,
22
+ updated_at INTEGER NOT NULL
23
+ ) STRICT;
24
+
25
+ CREATE TABLE pi_sandbox (
26
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
27
+ provider TEXT NOT NULL,
28
+ sandbox_id TEXT NOT NULL,
29
+ created_at INTEGER NOT NULL
30
+ ) STRICT;
31
+
32
+ CREATE TABLE pi_entry (
33
+ seq INTEGER PRIMARY KEY,
34
+ entry_id TEXT NOT NULL UNIQUE,
35
+ entry_json TEXT NOT NULL CHECK (json_valid(entry_json))
36
+ ) STRICT;
37
+ `,
38
+ },
39
+ ],
40
+ });
41
+ export async function loadPiSession(db) {
42
+ const rows = await db.execute(`SELECT session_id, cwd, header_json, settings_json
43
+ FROM pi_session
44
+ WHERE singleton = 1`);
45
+ const row = rows[0];
46
+ if (!row)
47
+ return undefined;
48
+ const entryRows = await db.execute(`SELECT entry_json FROM pi_entry ORDER BY seq`);
49
+ return {
50
+ sessionId: row.session_id,
51
+ cwd: row.cwd,
52
+ header: JSON.parse(row.header_json),
53
+ entries: entryRows.map((entry) => JSON.parse(entry.entry_json)),
54
+ settings: row.settings_json === null
55
+ ? undefined
56
+ : JSON.parse(row.settings_json),
57
+ };
58
+ }
59
+ export async function createPiSession(db, session) {
60
+ const now = Date.now();
61
+ await db.execute(`INSERT INTO pi_session (
62
+ singleton, session_id, cwd, header_json, settings_json, created_at, updated_at
63
+ ) VALUES (1, ?, ?, ?, ?, ?, ?)`, session.header.id, session.cwd, JSON.stringify(session.header), JSON.stringify(session.settings), now, now);
64
+ }
65
+ export async function loadPiSandbox(db) {
66
+ const rows = await db.execute(`SELECT provider, sandbox_id FROM pi_sandbox WHERE singleton = 1`);
67
+ const row = rows[0];
68
+ return row && { provider: row.provider, id: row.sandbox_id };
69
+ }
70
+ export async function savePiSandbox(db, sandbox) {
71
+ await db.execute(`INSERT INTO pi_sandbox (singleton, provider, sandbox_id, created_at) VALUES (1, ?, ?, ?)
72
+ ON CONFLICT (singleton) DO UPDATE SET
73
+ provider = excluded.provider,
74
+ sandbox_id = excluded.sandbox_id,
75
+ created_at = excluded.created_at`, sandbox.provider, sandbox.id, Date.now());
76
+ }
77
+ export async function appendPiEntry(db, entry) {
78
+ await db.execute(`INSERT OR IGNORE INTO pi_entry (entry_id, entry_json) VALUES (?, ?)`, entry.id, JSON.stringify(entry));
79
+ }
80
+ export async function savePiSettings(db, settings) {
81
+ await db.execute(`UPDATE pi_session SET settings_json = ?, updated_at = ? WHERE singleton = 1`, JSON.stringify(settings), Date.now());
82
+ }
83
+ /** Builds the entry list `SessionManager.inMemory` expects: header first. */
84
+ export function toFileEntries(stored) {
85
+ return [stored.header, ...stored.entries];
86
+ }
87
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { AgentSession } from "@earendil-works/pi-coding-agent";
2
+ /**
3
+ * Runs one Pi run inside an `invoke_agent pi` span, a child of the active span,
4
+ * which is the `prompt` action span when RivetKit tracing is on. The action
5
+ * span cannot show the outcome, because a model error does not reject the run,
6
+ * so this span records it with OpenTelemetry GenAI attributes. Prompt text,
7
+ * tool arguments, and tool results are not recorded.
8
+ */
9
+ export declare function traceRun(session: AgentSession, actorId: string, run: () => Promise<void>): Promise<void>;
10
+ //# sourceMappingURL=tracing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAGpE;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAC7B,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GACtB,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
@@ -0,0 +1,39 @@
1
+ import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
2
+ /**
3
+ * Runs one Pi run inside an `invoke_agent pi` span, a child of the active span,
4
+ * which is the `prompt` action span when RivetKit tracing is on. The action
5
+ * span cannot show the outcome, because a model error does not reject the run,
6
+ * so this span records it with OpenTelemetry GenAI attributes. Prompt text,
7
+ * tool arguments, and tool results are not recorded.
8
+ */
9
+ export async function traceRun(session, actorId, run) {
10
+ if (session.isStreaming)
11
+ return run();
12
+ const span = trace.getTracer("@rivet-dev/pi").startSpan("invoke_agent pi", {
13
+ kind: SpanKind.INTERNAL,
14
+ attributes: {
15
+ "gen_ai.operation.name": "invoke_agent",
16
+ "gen_ai.agent.name": "pi",
17
+ "gen_ai.conversation.id": session.sessionId,
18
+ "gen_ai.request.model": session.model?.id,
19
+ "rivet.actor.id": actorId,
20
+ },
21
+ });
22
+ try {
23
+ await context.with(trace.setSpan(context.active(), span), run);
24
+ const last = session.messages.findLast((message) => message.role === "assistant");
25
+ if (last?.role === "assistant" && last.stopReason === "error") {
26
+ span.setAttribute("error.type", "model_error");
27
+ span.setStatus({ code: SpanStatusCode.ERROR, message: last.errorMessage });
28
+ }
29
+ }
30
+ catch (error) {
31
+ span.setAttribute("error.type", "run_error");
32
+ span.setStatus({ code: SpanStatusCode.ERROR });
33
+ throw error;
34
+ }
35
+ finally {
36
+ span.end();
37
+ }
38
+ }
39
+ //# sourceMappingURL=tracing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing.js","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC7B,OAAqB,EACrB,OAAe,EACf,GAAwB;IAExB,IAAI,OAAO,CAAC,WAAW;QAAE,OAAO,GAAG,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,SAAS,CAAC,iBAAiB,EAAE;QAC1E,IAAI,EAAE,QAAQ,CAAC,QAAQ;QACvB,UAAU,EAAE;YACX,uBAAuB,EAAE,cAAc;YACvC,mBAAmB,EAAE,IAAI;YACzB,wBAAwB,EAAE,OAAO,CAAC,SAAS;YAC3C,sBAAsB,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE;YACzC,gBAAgB,EAAE,OAAO;SACzB;KACD,CAAC,CAAC;IACH,IAAI,CAAC;QACJ,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;QAClF,IAAI,IAAI,EAAE,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;YAC/D,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;YAC/C,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QAC5E,CAAC;IACF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/C,MAAM,KAAK,CAAC;IACb,CAAC;YAAS,CAAC;QACV,IAAI,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC;AACF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@rivet-dev/pi",
3
+ "version": "0.1.0",
4
+ "description": "Run the Pi coding agent as a durable Rivet Actor",
5
+ "license": "Apache-2.0",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "files": [
11
+ "dist",
12
+ "package.json"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "main": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.build.json",
24
+ "check-types": "tsc --noEmit",
25
+ "test": "vitest run"
26
+ },
27
+ "dependencies": {
28
+ "@earendil-works/pi-coding-agent": "1.0.0",
29
+ "@opentelemetry/api": "^1.1.0",
30
+ "@rivet-dev/sandbox-adapter": "0.1.0",
31
+ "rivetkit": "^2.3.22"
32
+ },
33
+ "devDependencies": {
34
+ "@earendil-works/pi-ai": "1.0.0",
35
+ "@opentelemetry/sdk-trace-node": "2.11.0",
36
+ "@types/node": "^24.0.0",
37
+ "typescript": "^5.9.2",
38
+ "vitest": "^3.2.4"
39
+ },
40
+ "engines": {
41
+ "node": ">=22.19.0"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/rivet-dev/rivet.git",
46
+ "directory": "integrations/pi"
47
+ }
48
+ }