piclaw 0.0.4 → 0.0.6

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 (185) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/dist-D51xeTP2.js +12 -0
  3. package/.output/public/assets/index-B3x2_en6.css +1 -0
  4. package/.output/public/assets/index-SfjxJZSD.js +38 -0
  5. package/.output/public/assets/md4x-CNLJ57xO.wasm +0 -0
  6. package/.output/public/assets/md4x-CyfQToGJ.js +1 -0
  7. package/.output/public/assets/wasm-Cm7RZrwg.js +1 -0
  8. package/.output/public/icon.svg +25 -0
  9. package/.output/public/index.html +3 -2
  10. package/.output/public/manifest.json +1 -1
  11. package/.output/server/_chunks/bun.mjs +49 -0
  12. package/.output/server/_chunks/commands.mjs +280 -0
  13. package/.output/server/_chunks/config.mjs +2 -4
  14. package/.output/server/_chunks/db.mjs +11 -5
  15. package/.output/server/_chunks/logger.mjs +1 -1
  16. package/.output/server/_chunks/node.mjs +31 -0
  17. package/.output/server/_chunks/ntfy.mjs +45 -0
  18. package/.output/server/_chunks/pi.mjs +33 -1
  19. package/.output/server/_chunks/renderer-template.mjs +1 -1
  20. package/.output/server/_chunks/{bootstrap.mjs → server.mjs} +203 -205
  21. package/.output/server/_chunks/session.mjs +249 -40
  22. package/.output/server/_chunks/settings.mjs +1 -1
  23. package/.output/server/_chunks/terminal.mjs +35 -20
  24. package/.output/server/_chunks/virtual.mjs +129 -0
  25. package/.output/server/_jid_.delete.mjs +2 -2
  26. package/.output/server/_jid_.patch.mjs +30 -3
  27. package/.output/server/_jid_2.delete.mjs +2 -2
  28. package/.output/server/_libs/@aws-sdk/client-bedrock-runtime+[...].mjs +13148 -13149
  29. package/.output/server/_libs/@aws-sdk/credential-provider-http+[...].mjs +1 -1
  30. package/.output/server/_libs/@aws-sdk/credential-provider-ini+[...].mjs +2 -2
  31. package/.output/server/_libs/@aws-sdk/credential-provider-process+[...].mjs +1 -1
  32. package/.output/server/_libs/@aws-sdk/credential-provider-sso+[...].mjs +1 -1
  33. package/.output/server/_libs/@aws-sdk/credential-provider-web-identity+[...].mjs +1 -1
  34. package/.output/server/_libs/@google/genai.mjs +2 -2
  35. package/.output/server/_libs/@mariozechner/pi-agent-core+[...].mjs +40 -74
  36. package/.output/server/_libs/@mariozechner/pi-coding-agent+[...].mjs +1315 -1210
  37. package/.output/server/_libs/@smithy/credential-provider-imds+[...].mjs +3 -3
  38. package/.output/server/_libs/_.mjs +3 -2
  39. package/.output/server/_libs/_100.mjs +3 -0
  40. package/.output/server/_libs/_101.mjs +2 -0
  41. package/.output/server/_libs/_102.mjs +3 -0
  42. package/.output/server/_libs/_103.mjs +2 -0
  43. package/.output/server/_libs/_16.mjs +2 -5
  44. package/.output/server/_libs/_17.mjs +2 -3
  45. package/.output/server/_libs/_18.mjs +2 -3
  46. package/.output/server/_libs/_19.mjs +2 -4
  47. package/.output/server/_libs/_2.mjs +2 -3
  48. package/.output/server/_libs/_20.mjs +2 -2
  49. package/.output/server/_libs/_21.mjs +2 -0
  50. package/.output/server/_libs/_22.mjs +2 -0
  51. package/.output/server/_libs/_23.mjs +2 -0
  52. package/.output/server/_libs/_24.mjs +2 -0
  53. package/.output/server/_libs/_25.mjs +2 -0
  54. package/.output/server/_libs/_26.mjs +2 -0
  55. package/.output/server/_libs/_27.mjs +2 -0
  56. package/.output/server/_libs/_28.mjs +2 -0
  57. package/.output/server/_libs/_29.mjs +2 -0
  58. package/.output/server/_libs/_30.mjs +2 -0
  59. package/.output/server/_libs/_31.mjs +2 -0
  60. package/.output/server/_libs/_32.mjs +2 -0
  61. package/.output/server/_libs/_33.mjs +2 -0
  62. package/.output/server/_libs/_34.mjs +2 -0
  63. package/.output/server/_libs/_35.mjs +2 -0
  64. package/.output/server/_libs/_36.mjs +2 -0
  65. package/.output/server/_libs/_37.mjs +2 -0
  66. package/.output/server/_libs/_38.mjs +2 -0
  67. package/.output/server/_libs/_39.mjs +2 -0
  68. package/.output/server/_libs/_40.mjs +2 -0
  69. package/.output/server/_libs/_41.mjs +2 -0
  70. package/.output/server/_libs/_42.mjs +2 -0
  71. package/.output/server/_libs/_43.mjs +2 -0
  72. package/.output/server/_libs/_44.mjs +2 -0
  73. package/.output/server/_libs/_45.mjs +2 -0
  74. package/.output/server/_libs/_46.mjs +2 -0
  75. package/.output/server/_libs/_47.mjs +2 -0
  76. package/.output/server/_libs/_48.mjs +2 -0
  77. package/.output/server/_libs/_49.mjs +2 -0
  78. package/.output/server/_libs/_50.mjs +2 -0
  79. package/.output/server/_libs/_51.mjs +2 -0
  80. package/.output/server/_libs/_52.mjs +2 -0
  81. package/.output/server/_libs/_53.mjs +2 -0
  82. package/.output/server/_libs/_54.mjs +2 -0
  83. package/.output/server/_libs/_55.mjs +2 -0
  84. package/.output/server/_libs/_56.mjs +2 -0
  85. package/.output/server/_libs/_57.mjs +2 -0
  86. package/.output/server/_libs/_58.mjs +2 -0
  87. package/.output/server/_libs/_59.mjs +2 -0
  88. package/.output/server/_libs/_60.mjs +2 -0
  89. package/.output/server/_libs/_61.mjs +2 -0
  90. package/.output/server/_libs/_62.mjs +2 -0
  91. package/.output/server/_libs/_63.mjs +2 -0
  92. package/.output/server/_libs/_64.mjs +2 -0
  93. package/.output/server/_libs/_65.mjs +2 -0
  94. package/.output/server/_libs/_66.mjs +2 -0
  95. package/.output/server/_libs/_67.mjs +2 -0
  96. package/.output/server/_libs/_68.mjs +2 -0
  97. package/.output/server/_libs/_69.mjs +2 -0
  98. package/.output/server/_libs/_70.mjs +2 -0
  99. package/.output/server/_libs/_71.mjs +2 -0
  100. package/.output/server/_libs/_72.mjs +2 -0
  101. package/.output/server/_libs/_73.mjs +2 -0
  102. package/.output/server/_libs/_74.mjs +2 -0
  103. package/.output/server/_libs/_75.mjs +2 -0
  104. package/.output/server/_libs/_76.mjs +2 -0
  105. package/.output/server/_libs/_77.mjs +2 -0
  106. package/.output/server/_libs/_78.mjs +2 -0
  107. package/.output/server/_libs/_79.mjs +2 -0
  108. package/.output/server/_libs/_80.mjs +2 -0
  109. package/.output/server/_libs/_81.mjs +2 -0
  110. package/.output/server/_libs/_82.mjs +2 -0
  111. package/.output/server/_libs/_83.mjs +2 -0
  112. package/.output/server/_libs/_84.mjs +2 -0
  113. package/.output/server/_libs/_85.mjs +2 -0
  114. package/.output/server/_libs/_86.mjs +2 -0
  115. package/.output/server/_libs/_87.mjs +2 -0
  116. package/.output/server/_libs/_88.mjs +2 -0
  117. package/.output/server/_libs/_89.mjs +2 -0
  118. package/.output/server/_libs/_90.mjs +2 -0
  119. package/.output/server/_libs/_91.mjs +2 -0
  120. package/.output/server/_libs/_92.mjs +2 -0
  121. package/.output/server/_libs/_93.mjs +2 -0
  122. package/.output/server/_libs/_94.mjs +2 -0
  123. package/.output/server/_libs/_95.mjs +2 -0
  124. package/.output/server/_libs/_96.mjs +2 -0
  125. package/.output/server/_libs/_97.mjs +2 -0
  126. package/.output/server/_libs/_98.mjs +2 -0
  127. package/.output/server/_libs/_99.mjs +5 -0
  128. package/.output/server/_libs/amdefine.mjs +188 -0
  129. package/.output/server/_libs/aws-sdk__nested-clients.mjs +1 -1
  130. package/.output/server/_libs/compressjs.mjs +50 -0
  131. package/.output/server/_libs/diff.mjs +137 -0
  132. package/.output/server/_libs/get-uri.mjs +1 -1
  133. package/.output/server/_libs/http-proxy-agent.mjs +1 -1
  134. package/.output/server/_libs/https-proxy-agent.mjs +1 -1
  135. package/.output/server/_libs/just-bash+[...].mjs +80359 -0
  136. package/.output/server/_libs/md4x.mjs +73 -0
  137. package/.output/server/_libs/mixmark-io__domino.mjs +14801 -0
  138. package/.output/server/_libs/node-fetch.mjs +1 -1
  139. package/.output/server/_libs/node-liblzma.mjs +1107 -0
  140. package/.output/server/_libs/pac-proxy-agent+[...].mjs +1 -1
  141. package/.output/server/_libs/proxy-agent+proxy-from-env.mjs +1 -1
  142. package/.output/server/_libs/smithy__core.mjs +1 -1
  143. package/.output/server/_routes/api/files/delete.mjs +2 -2
  144. package/.output/server/_routes/api/files/groups.mjs +14 -0
  145. package/.output/server/_routes/api/files/raw.mjs +1 -1
  146. package/.output/server/_routes/api/files/read.mjs +1 -1
  147. package/.output/server/_routes/api/files/watch.mjs +1 -1
  148. package/.output/server/_routes/api/files/write.mjs +3 -2
  149. package/.output/server/_routes/api/files.mjs +1 -1
  150. package/.output/server/_routes/api/groups.mjs +2 -2
  151. package/.output/server/_routes/api/groups2.mjs +3 -2
  152. package/.output/server/_routes/api/health.mjs +2 -2
  153. package/.output/server/_routes/api/logs.mjs +4 -2
  154. package/.output/server/_routes/api/ntfy/setup.mjs +54 -0
  155. package/.output/server/_routes/api/ntfy/status.mjs +12 -0
  156. package/.output/server/_routes/api/pi/apikey.mjs +1 -1
  157. package/.output/server/_routes/api/pi/apikey_providers.mjs +1 -1
  158. package/.output/server/_routes/api/pi/commands.mjs +6 -0
  159. package/.output/server/_routes/api/pi/login/events.mjs +1 -1
  160. package/.output/server/_routes/api/pi/login/respond.mjs +1 -1
  161. package/.output/server/_routes/api/pi/login.mjs +1 -1
  162. package/.output/server/_routes/api/pi/logout.mjs +1 -1
  163. package/.output/server/_routes/api/pi/models.mjs +1 -1
  164. package/.output/server/_routes/api/pi/status.mjs +1 -1
  165. package/.output/server/_routes/api/send.mjs +22 -3
  166. package/.output/server/_routes/api/status.mjs +9 -7
  167. package/.output/server/_routes/api/tasks2.mjs +2 -2
  168. package/.output/server/_routes/api/telegram/setup.mjs +2 -2
  169. package/.output/server/_routes/api/telegram/status.mjs +2 -2
  170. package/.output/server/_routes/api/terminal2.mjs +3 -2
  171. package/.output/server/_utils.mjs +12 -4
  172. package/.output/server/build/md4x.wasm +0 -0
  173. package/.output/server/index.mjs +223 -149
  174. package/.output/server/node_modules/@mongodb-js/zstd/lib/index.js +55 -0
  175. package/.output/server/node_modules/@mongodb-js/zstd/package.json +51 -0
  176. package/.output/server/package.json +1 -0
  177. package/README.md +118 -24
  178. package/bin/piclaw.mjs +39 -1
  179. package/package.json +16 -13
  180. package/.output/public/assets/client-TIs-Ghqj.js +0 -9
  181. package/.output/public/assets/dist-BVjfG3ok.js +0 -12
  182. package/.output/public/assets/dist-DJh8l6yS.js +0 -1
  183. package/.output/public/assets/index-BNNEMkNV.js +0 -39
  184. package/.output/public/assets/index-CdWBxO5V.css +0 -1
  185. package/.output/public/assets/react-DFP7nCmh.js +0 -1
@@ -1,14 +1,109 @@
1
1
  import { o as PI_MODEL, r as GROUPS_DIR, s as PI_PROVIDER, t as AGENT_TIMEOUT, u as SESSIONS_DIR } from "./config.mjs";
2
2
  import { t as createLogger } from "./logger.mjs";
3
- import { a as createCodingTools, i as AuthStorage, n as DefaultResourceLoader, o as SessionManager, r as ModelRegistry, s as SettingsManager, t as createAgentSession } from "../_libs/@mariozechner/pi-coding-agent+[...].mjs";
4
3
  import { t as streamBus } from "./stream.mjs";
4
+ import { a as AuthStorage, i as ModelRegistry, l as SessionManager, n as DefaultResourceLoader, o as createCodingTools, t as createAgentSession, u as SettingsManager } from "../_libs/@mariozechner/pi-coding-agent+[...].mjs";
5
+ import { b as Type } from "../_libs/@mariozechner/pi-agent-core+[...].mjs";
6
+ import { n as getNtfyConfig, r as sendNtfyNotification } from "./ntfy.mjs";
5
7
  import path from "path";
6
- import fs from "fs";
8
+ import os from "node:os";
9
+ import fs from "node:fs/promises";
10
+ import fs$1 from "fs";
11
+ var availableCommands;
12
+ async function getSystemInfo() {
13
+ const info = {
14
+ os: process.platform,
15
+ arch: process.arch,
16
+ nodeVersion: process.versions.node,
17
+ runtime: _getRuntime(),
18
+ freeMemory: os.freemem(),
19
+ totalMemory: os.totalmem(),
20
+ uptime: os.uptime()
21
+ };
22
+ if (process.platform === "linux") {
23
+ info.linuxDistro = await _getLinuxDistro();
24
+ info.isDocker = await _isDocker();
25
+ }
26
+ info.availableDiskSpace = await _getAvailableDiskSpace();
27
+ info.availableCommands = availableCommands ??= await _getAvailableCommands();
28
+ info.processMemory = process.memoryUsage().rss;
29
+ return info;
30
+ }
31
+ async function getSystemInfoText() {
32
+ const info = await getSystemInfo();
33
+ const lines = [];
34
+ let osLine = `${info.os} ${info.arch}`;
35
+ if (info.linuxDistro) osLine += ` (${info.linuxDistro})`;
36
+ if (info.isDocker) osLine += ` [Docker]`;
37
+ lines.push(`OS: ${osLine}`);
38
+ if (info.runtime) lines.push(`Runtime: ${info.runtime}`);
39
+ if (info.freeMemory != null && info.totalMemory != null) {
40
+ const fmt = (b) => `${(b / 1024 / 1024 / 1024).toFixed(1)}GB`;
41
+ let memLine = `Memory: ${fmt(info.freeMemory)} free / ${fmt(info.totalMemory)} total`;
42
+ if (info.processMemory != null) {
43
+ const mb = (info.processMemory / 1024 / 1024).toFixed(0);
44
+ memLine += ` (process: ${mb}MB)`;
45
+ }
46
+ lines.push(memLine);
47
+ }
48
+ if (info.availableDiskSpace != null) lines.push(`Disk: ${(info.availableDiskSpace / 1024 / 1024 / 1024).toFixed(1)}GB available`);
49
+ return lines.map((l) => `- ${l}`).join("\n");
50
+ }
51
+ function _getRuntime() {
52
+ if ("Bun" in globalThis) return `bun v${globalThis.Bun.version}`;
53
+ if ("Deno" in globalThis) return `deno v${globalThis.Deno.version.deno}`;
54
+ return `node v${process.versions.node}`;
55
+ }
56
+ async function _getLinuxDistro() {
57
+ try {
58
+ return (await fs.readFile("/etc/os-release", "utf8")).match(/^PRETTY_NAME="?(.+?)"?\s*$/m)?.[1] ?? null;
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+ async function _isDocker() {
64
+ try {
65
+ await fs.access("/.dockerenv");
66
+ return true;
67
+ } catch {
68
+ try {
69
+ const cgroup = await fs.readFile("/proc/1/cgroup", "utf8");
70
+ return cgroup.includes("docker") || cgroup.includes("containerd");
71
+ } catch {
72
+ return false;
73
+ }
74
+ }
75
+ }
76
+ async function _getAvailableCommands() {
77
+ try {
78
+ const pathEnv = process.env.PATH;
79
+ if (!pathEnv) return void 0;
80
+ const dirs = [...new Set(pathEnv.split(":"))];
81
+ const commands = /* @__PURE__ */ new Set();
82
+ await Promise.all(dirs.map(async (dir) => {
83
+ try {
84
+ const entries = await fs.readdir(dir);
85
+ for (const entry of entries) commands.add(entry);
86
+ } catch {}
87
+ }));
88
+ return [...commands].sort();
89
+ } catch {
90
+ return;
91
+ }
92
+ }
93
+ async function _getAvailableDiskSpace() {
94
+ try {
95
+ const stats = await fs.statfs("/");
96
+ return stats.bsize * stats.bavail;
97
+ } catch {
98
+ return null;
99
+ }
100
+ }
7
101
  var logger$1 = createLogger("pi");
8
102
  function sendPrompt(managed, group, input, onOutput) {
9
103
  return new Promise((resolve) => {
10
104
  const { session } = managed;
11
105
  let fullText = "";
106
+ let thinkingText = "";
12
107
  let resolved = false;
13
108
  let outputChain = Promise.resolve();
14
109
  const timeout = setTimeout(() => {
@@ -52,19 +147,34 @@ function sendPrompt(managed, group, input, onOutput) {
52
147
  }
53
148
  if (ae.type === "text_end" && "content" in ae && onOutput) {
54
149
  const text = ae.content;
150
+ const stored = thinkingText ? `<thinking>${thinkingText}</thinking>\n\n${text}` : text;
151
+ thinkingText = "";
55
152
  outputChain = outputChain.then(async () => {
56
153
  await onOutput({
57
154
  status: "success",
58
- result: text,
155
+ result: stored,
59
156
  newSessionId: session.sessionId
60
157
  });
61
158
  streamBus.emit({
62
159
  type: "text_end",
63
160
  chatJid: jid,
64
- content: text
161
+ content: stored
65
162
  });
66
163
  });
67
164
  }
165
+ if (ae.type === "thinking_delta" && "delta" in ae) {
166
+ thinkingText += ae.delta;
167
+ streamBus.emit({
168
+ type: "thinking_delta",
169
+ chatJid: jid,
170
+ delta: ae.delta
171
+ });
172
+ }
173
+ if (ae.type === "thinking_end" && "content" in ae) streamBus.emit({
174
+ type: "thinking_end",
175
+ chatJid: jid,
176
+ content: ae.content
177
+ });
68
178
  break;
69
179
  }
70
180
  case "agent_end":
@@ -78,7 +188,9 @@ function sendPrompt(managed, group, input, onOutput) {
78
188
  case "tool_execution_start":
79
189
  logger$1.debug({
80
190
  group: group.name,
81
- tool: event.toolName
191
+ tool: event.toolName,
192
+ toolCallId: event.toolCallId,
193
+ argsPreview: previewForLog(event.args)
82
194
  }, "Tool execution started");
83
195
  streamBus.emit({
84
196
  type: "tool_start",
@@ -86,11 +198,24 @@ function sendPrompt(managed, group, input, onOutput) {
86
198
  toolName: event.toolName
87
199
  });
88
200
  break;
89
- case "tool_execution_end":
90
- if (event.isError) logger$1.warn({
201
+ case "tool_execution_update":
202
+ logger$1.debug({
91
203
  group: group.name,
92
- tool: event.toolName
93
- }, "Tool execution error");
204
+ tool: event.toolName,
205
+ toolCallId: event.toolCallId,
206
+ partialResultPreview: previewForLog(event.partialResult)
207
+ }, "Tool execution update");
208
+ break;
209
+ case "tool_execution_end": {
210
+ const payload = {
211
+ group: group.name,
212
+ tool: event.toolName,
213
+ toolCallId: event.toolCallId,
214
+ isError: event.isError,
215
+ resultPreview: previewForLog(event.result)
216
+ };
217
+ if (event.isError) logger$1.warn(payload, "Tool execution error");
218
+ else logger$1.debug(payload, "Tool execution finished");
94
219
  streamBus.emit({
95
220
  type: "tool_end",
96
221
  chatJid: jid,
@@ -98,6 +223,7 @@ function sendPrompt(managed, group, input, onOutput) {
98
223
  isError: event.isError
99
224
  });
100
225
  break;
226
+ }
101
227
  }
102
228
  });
103
229
  session.prompt(input.prompt).catch((err) => {
@@ -110,38 +236,117 @@ function sendPrompt(managed, group, input, onOutput) {
110
236
  });
111
237
  });
112
238
  }
113
- function buildSystemPrompt(assistantName, dir) {
114
- return `You are ${assistantName}, an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.
239
+ var baseSystemPrompt = `
240
+ You live in your own OS and help your human with tasks.
115
241
 
116
242
  Available tools:
117
243
  - read: Read file contents
118
- - bash: Execute bash commands (ls, grep, find, etc.)
119
- - edit: Make surgical edits to files (find exact text and replace)
120
- - write: Create or overwrite files
244
+ - bash: Execute shell commands (ls, rg, find, curl, etc.)
245
+ - edit: Make precise text replacements in existing files
246
+ - write: Create or fully overwrite files
247
+ - notify: Send notifications to user
121
248
 
122
- In addition to the tools above, you may have access to other custom tools depending on the project.
249
+ Execution rules:
250
+ - You operate directly in your own OS/workspace environment autonomously; you do not need to ask for permission to run bash commands; users do not know how to run commands you do it for them.
251
+ - Use tools proactively to do the work; do not just describe what should be done.
252
+ - Use bash for operations (searching, building, testing, git-aware inspection).
253
+ - Do not use cat/sed/awk to read files when read is available.
254
+ - Use read to inspect files before editing them.
255
+ - Prefer edit for targeted changes; use write for new files or full rewrites.
256
+ - Use notify tool as a secondary channel to send important info to the user and for anything you can't send in chat. Do not repeat chat messages in notifications.
257
+ - Summarize results in plain text; do not print summaries via bash commands.
258
+ - Be concise, explicit, and include file paths when referencing changes.
259
+ - Ask a short clarification question when requirements are ambiguous or risky.
260
+ - NEVER say "Use this command", you can just execute commands using bash tool.
261
+ - ALWAYS write necessary info to AGENTS.md, it is your own private memory (do not ask user to do it).
262
+ `;
263
+ async function buildSystemPrompt(ctx) {
264
+ let prompt = `You are ${ctx.assistantName}, an expert assistant.
123
265
 
124
- Guidelines:
125
- - Use bash for file operations like ls, rg, find
126
- - Use read to examine files before editing. You must use this tool instead of cat or sed.
127
- - Use edit for precise changes (old text must match exactly)
128
- - Use write only for new files or complete rewrites
129
- - When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did
130
- - Be concise in your responses
131
- - Show file paths clearly when working with files
266
+ ${baseSystemPrompt}
132
267
 
133
- Current date and time: ${(/* @__PURE__ */ new Date()).toLocaleString("en-US", {
134
- weekday: "long",
135
- year: "numeric",
136
- month: "long",
137
- day: "numeric",
138
- hour: "2-digit",
139
- minute: "2-digit",
140
- second: "2-digit",
141
- timeZoneName: "short"
142
- })}
143
- Current working directory: ${dir}`;
268
+ Current date and time: ${(/* @__PURE__ */ new Date()).toLocaleString()}
269
+ Current model name: ${ctx.modelName}
270
+ Current working directory: ${ctx.dir}`;
271
+ const envInfo = await getSystemInfoText().catch((err) => {
272
+ logger$1.warn({ err }, "Failed to get system info");
273
+ return null;
274
+ });
275
+ if (envInfo) prompt += `\n\nEnvironment:\n${envInfo}`;
276
+ return prompt;
144
277
  }
278
+ function previewForLog(value, maxLength = 1200) {
279
+ try {
280
+ const text = typeof value === "string" ? value : JSON.stringify(value);
281
+ if (!text) return "";
282
+ if (text.length <= maxLength) return text;
283
+ return `${text.slice(0, maxLength)}…(truncated ${text.length - maxLength} chars)`;
284
+ } catch {
285
+ return "[unserializable value]";
286
+ }
287
+ }
288
+ const notifyTool = {
289
+ name: "notify",
290
+ label: "Notify",
291
+ description: [
292
+ "Send a push notification via ntfy. Supports titles, priority levels, emoji tags,",
293
+ "clickable URLs, markdown, scheduled delivery, action buttons, and file attachments.",
294
+ "Requires ntfy to be configured in the Notifications window."
295
+ ].join(" "),
296
+ parameters: Type.Object({
297
+ message: Type.String({ description: "Notification body text" }),
298
+ title: Type.Optional(Type.String({ description: "Notification title" })),
299
+ priority: Type.Optional(Type.Union([
300
+ Type.Literal(1),
301
+ Type.Literal(2),
302
+ Type.Literal(3),
303
+ Type.Literal(4),
304
+ Type.Literal(5)
305
+ ], { description: "1=min, 2=low, 3=default, 4=high, 5=urgent" })),
306
+ tags: Type.Optional(Type.Array(Type.String(), { description: "Emoji shortcodes or labels, e.g. [\"white_check_mark\",\"robot\"]. See https://docs.ntfy.sh/emojis/" })),
307
+ click: Type.Optional(Type.String({ description: "URL opened when notification is tapped" })),
308
+ markdown: Type.Optional(Type.Boolean({ description: "Enable markdown formatting in body (enabled by default)" })),
309
+ delay: Type.Optional(Type.String({ description: "Schedule delivery: duration (\"30m\",\"2h\"), timestamp, or \"tomorrow 9am\" (cannot be combined with email)" })),
310
+ actions: Type.Optional(Type.Array(Type.String(), { description: "Action buttons (max 3). Format: \"view, Label, https://...\" or \"http, Label, https://...\"" })),
311
+ attach: Type.Optional(Type.String({ description: "URL to an external file to attach" })),
312
+ filename: Type.Optional(Type.String({ description: "Override attachment filename" })),
313
+ email: Type.Optional(Type.String({ description: "Also forward notification to this email" }))
314
+ }),
315
+ async execute(_toolCallId, params) {
316
+ const p = params;
317
+ const config = getNtfyConfig();
318
+ if (!config) return {
319
+ content: [{
320
+ type: "text",
321
+ text: "ntfy is not configured. Set it up in the Notifications window."
322
+ }],
323
+ details: {
324
+ ok: false,
325
+ reason: "not_configured"
326
+ }
327
+ };
328
+ await sendNtfyNotification(config, {
329
+ message: p.message,
330
+ title: p.title,
331
+ priority: p.priority,
332
+ tags: p.tags,
333
+ click: p.click,
334
+ markdown: p.markdown ?? true,
335
+ delay: p.delay,
336
+ actions: p.actions,
337
+ attach: p.attach,
338
+ filename: p.filename,
339
+ email: p.email
340
+ });
341
+ return {
342
+ content: [{
343
+ type: "text",
344
+ text: `Notification sent: ${p.title ?? p.message}`
345
+ }],
346
+ details: { ok: true }
347
+ };
348
+ }
349
+ };
145
350
  var logger = createLogger("pi");
146
351
  /**
147
352
  * Model preference order for fallback resolution.
@@ -162,10 +367,8 @@ const sessions = /* @__PURE__ */ new Map();
162
367
  const authStorage = AuthStorage.create();
163
368
  const modelRegistry = new ModelRegistry(authStorage);
164
369
  async function getOrCreateSession(group, input) {
165
- const groupDir = path.join(GROUPS_DIR, group.folder);
166
- fs.mkdirSync(path.join(groupDir, "logs"), { recursive: true });
167
370
  const sessionDir = path.join(SESSIONS_DIR, group.folder);
168
- fs.mkdirSync(sessionDir, { recursive: true });
371
+ fs$1.mkdirSync(sessionDir, { recursive: true });
169
372
  let managed = sessions.get(input.chatJid);
170
373
  if (!managed) {
171
374
  managed = await createManagedSession(group, input, sessionDir);
@@ -267,20 +470,26 @@ async function createManagedSession(group, input, sessionDir) {
267
470
  maxRetries: 3
268
471
  }
269
472
  });
473
+ const systemPrompt = await buildSystemPrompt({
474
+ assistantName: input.assistantName,
475
+ dir: groupDir,
476
+ modelName: `${model?.provider}/${model?.id}`
477
+ });
270
478
  const resourceLoader = new DefaultResourceLoader({
271
479
  cwd: groupDir,
272
480
  settingsManager,
273
- systemPromptOverride: () => buildSystemPrompt(input.assistantName, groupDir)
481
+ systemPromptOverride: () => systemPrompt
274
482
  });
275
483
  await resourceLoader.reload();
276
484
  const sessionManager = input.sessionId ? SessionManager.continueRecent(groupDir, sessionDir) : SessionManager.create(groupDir, sessionDir);
277
485
  const { session, modelFallbackMessage } = await createAgentSession({
278
486
  cwd: groupDir,
279
487
  model,
280
- thinkingLevel: "off",
488
+ thinkingLevel: group.thinkingLevel || "off",
281
489
  authStorage,
282
490
  modelRegistry,
283
491
  tools: createCodingTools(groupDir),
492
+ customTools: [notifyTool],
284
493
  resourceLoader,
285
494
  sessionManager,
286
495
  settingsManager
@@ -317,4 +526,4 @@ function findAvailable(available, provider, modelId) {
317
526
  function fmtModel(m) {
318
527
  return m ? `${m.provider}/${m.id}` : "none";
319
528
  }
320
- export { resolveModel as a, resolveGroupModel as i, getOrCreateSession as n, sessions as o, modelRegistry as r, sendPrompt as s, authStorage as t };
529
+ export { resolveModel as a, getSystemInfo as c, resolveGroupModel as i, getOrCreateSession as n, sessions as o, modelRegistry as r, sendPrompt as s, authStorage as t };
@@ -1,6 +1,6 @@
1
1
  import { join } from "node:path";
2
- import { mkdir, readFile, writeFile } from "node:fs/promises";
3
2
  import { homedir } from "node:os";
3
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  /**
5
5
  * Read/write ~/.pi/agent/settings.json and models.json
6
6
  */
@@ -1,31 +1,48 @@
1
1
  import { t as createLogger } from "./logger.mjs";
2
2
  import crypto from "node:crypto";
3
- import * as pty from "node-pty";
3
+ import os from "node:os";
4
+ var _isBun = typeof globalThis.Bun !== "undefined";
5
+ async function spawnPty(opts) {
6
+ if (_isBun) {
7
+ const { spawnBunPty } = await import("./bun.mjs");
8
+ return spawnBunPty(opts);
9
+ }
10
+ const { spawnNodePty } = await import("./node.mjs");
11
+ return spawnNodePty(opts);
12
+ }
13
+ async function spawnVirtualPty(...args) {
14
+ const { spawnVirtualPty: fn } = await import("./virtual.mjs");
15
+ return fn(...args);
16
+ }
4
17
  var logger = createLogger("terminal");
5
18
  var MAX_SESSIONS = 5;
6
19
  var SESSION_TIMEOUT = 1800 * 1e3;
7
20
  var TerminalManager = class {
8
21
  sessions = /* @__PURE__ */ new Map();
9
- create(opts) {
22
+ async create(opts) {
10
23
  if (this.sessions.size >= MAX_SESSIONS) throw new Error(`Max terminal sessions (${MAX_SESSIONS}) reached`);
11
24
  const id = crypto.randomUUID();
12
25
  const cols = opts?.cols ?? 80;
13
26
  const rows = opts?.rows ?? 24;
14
- const cwd = opts?.cwd ?? process.cwd();
15
- const shell = process.env.SHELL || "/bin/zsh";
16
- const env = { TERM: "xterm-256color" };
17
- for (const [k, v] of Object.entries(process.env)) if (v !== void 0) env[k] = v;
18
- env.TERM = "xterm-256color";
19
- const term = pty.spawn(shell, [], {
20
- name: "xterm-256color",
21
- cols,
22
- rows,
23
- cwd,
24
- env
25
- });
27
+ const cwd = opts?.cwd?.startsWith("~") ? os.homedir() + opts.cwd.slice(1) : opts?.cwd ?? process.cwd();
28
+ let handle;
29
+ if (opts?.virtual) handle = await spawnVirtualPty({ cwd });
30
+ else {
31
+ const shell = process.env.SHELL || "/bin/zsh";
32
+ const env = { TERM: "xterm-256color" };
33
+ for (const [k, v] of Object.entries(process.env)) if (v !== void 0) env[k] = v;
34
+ env.TERM = "xterm-256color";
35
+ handle = await spawnPty({
36
+ shell,
37
+ cols,
38
+ rows,
39
+ cwd,
40
+ env
41
+ });
42
+ }
26
43
  const session = {
27
44
  id,
28
- pty: term,
45
+ pty: handle,
29
46
  createdAt: Date.now(),
30
47
  lastActivity: Date.now(),
31
48
  cols,
@@ -37,11 +54,11 @@ var TerminalManager = class {
37
54
  timeoutTimer: setTimeout(() => this.kill(id), SESSION_TIMEOUT)
38
55
  };
39
56
  this.sessions.set(id, session);
40
- term.onData((data) => {
57
+ handle.onData((data) => {
41
58
  session.lastActivity = Date.now();
42
59
  for (const listener of session.listeners) listener(data);
43
60
  });
44
- term.onExit(() => {
61
+ handle.onExit(() => {
45
62
  this.cleanup(id);
46
63
  });
47
64
  logger.info({
@@ -90,9 +107,7 @@ var TerminalManager = class {
90
107
  kill(id) {
91
108
  const session = this.sessions.get(id);
92
109
  if (!session) return;
93
- try {
94
- session.pty.kill();
95
- } catch {}
110
+ session.pty.kill();
96
111
  this.cleanup(id);
97
112
  }
98
113
  list() {
@@ -0,0 +1,129 @@
1
+ async function spawnVirtualPty(opts) {
2
+ const { Bash } = await import("../_libs/_96.mjs");
3
+ const dataListeners = /* @__PURE__ */ new Set();
4
+ const exitListeners = /* @__PURE__ */ new Set();
5
+ let cwd = opts.cwd || "/";
6
+ let env = {
7
+ HOME: "/home/user",
8
+ PATH: "/usr/bin:/bin",
9
+ ...opts.env
10
+ };
11
+ let line = "";
12
+ let alive = true;
13
+ let busy = false;
14
+ let pending = "";
15
+ const bash = new Bash({
16
+ cwd,
17
+ env,
18
+ files: opts.files
19
+ });
20
+ function emit(data) {
21
+ for (const cb of dataListeners) cb(data);
22
+ }
23
+ function prompt() {
24
+ const home = env.HOME || "";
25
+ emit(`\x1b[1;32m${home && cwd.startsWith(home) ? "~" + cwd.slice(home.length) : cwd}\x1b[0m $ `);
26
+ }
27
+ async function exec(command) {
28
+ const trimmed = command.trim();
29
+ if (!trimmed) {
30
+ prompt();
31
+ return;
32
+ }
33
+ busy = true;
34
+ try {
35
+ const result = await bash.exec(trimmed, {
36
+ cwd,
37
+ env
38
+ });
39
+ if (result.env.PWD) cwd = result.env.PWD;
40
+ for (const [k, v] of Object.entries(result.env)) env[k] = v;
41
+ if (result.stdout) {
42
+ const out = result.stdout.endsWith("\n") ? result.stdout.slice(0, -1) : result.stdout;
43
+ if (out) emit(out.replaceAll("\n", "\r\n") + "\r\n");
44
+ }
45
+ if (result.stderr) {
46
+ const err = result.stderr.endsWith("\n") ? result.stderr.slice(0, -1) : result.stderr;
47
+ if (err) emit(`\x1b[31m${err.replaceAll("\n", "\r\n")}\x1b[0m\r\n`);
48
+ }
49
+ } catch (err) {
50
+ emit(`\x1b[31m${err.message}\x1b[0m\r\n`);
51
+ } finally {
52
+ busy = false;
53
+ }
54
+ prompt();
55
+ if (pending) {
56
+ const queued = pending;
57
+ pending = "";
58
+ handle.write(queued);
59
+ }
60
+ }
61
+ queueMicrotask(() => prompt());
62
+ const handle = {
63
+ write(data) {
64
+ if (!alive) return;
65
+ if (busy) {
66
+ pending += data;
67
+ return;
68
+ }
69
+ for (const ch of data) switch (ch) {
70
+ case "\r":
71
+ case "\n": {
72
+ emit("\r\n");
73
+ const cmd = line;
74
+ line = "";
75
+ exec(cmd);
76
+ break;
77
+ }
78
+ case "":
79
+ case "\b":
80
+ if (line.length > 0) {
81
+ line = line.slice(0, -1);
82
+ emit("\b \b");
83
+ }
84
+ break;
85
+ case "":
86
+ line = "";
87
+ emit("^C\r\n");
88
+ prompt();
89
+ break;
90
+ case "":
91
+ if (line.length === 0) {
92
+ alive = false;
93
+ emit("\r\nexit\r\n");
94
+ for (const cb of exitListeners) cb();
95
+ dataListeners.clear();
96
+ exitListeners.clear();
97
+ }
98
+ break;
99
+ case "\f":
100
+ emit("\x1B[2J\x1B[H");
101
+ prompt();
102
+ emit(line);
103
+ break;
104
+ default: if (ch >= " " || ch === " ") {
105
+ line += ch;
106
+ emit(ch);
107
+ }
108
+ }
109
+ },
110
+ resize() {},
111
+ kill() {
112
+ if (!alive) return;
113
+ alive = false;
114
+ for (const cb of exitListeners) cb();
115
+ dataListeners.clear();
116
+ exitListeners.clear();
117
+ },
118
+ onData(cb) {
119
+ dataListeners.add(cb);
120
+ return () => dataListeners.delete(cb);
121
+ },
122
+ onExit(cb) {
123
+ exitListeners.add(cb);
124
+ return () => exitListeners.delete(cb);
125
+ }
126
+ };
127
+ return handle;
128
+ }
129
+ export { spawnVirtualPty };
@@ -1,7 +1,7 @@
1
1
  import { a as defineHandler, l as getRouterParam } from "./_libs/h3+rou3+srvx.mjs";
2
- import "./_libs/@aws-sdk/client-bedrock-runtime+[...].mjs";
3
2
  import "./_libs/@mariozechner/pi-agent-core+[...].mjs";
4
- import { t as server } from "./_chunks/bootstrap.mjs";
3
+ import "./_libs/@aws-sdk/client-bedrock-runtime+[...].mjs";
4
+ import { t as server } from "./_chunks/server.mjs";
5
5
  var _jid__delete_default = defineHandler((event) => {
6
6
  const jid = decodeURIComponent(getRouterParam(event, "jid"));
7
7
  server.cleanupChat(jid);
@@ -1,14 +1,23 @@
1
1
  import { a as defineHandler, l as getRouterParam, n as HTTPError } from "./_libs/h3+rou3+srvx.mjs";
2
2
  import { t as createLogger } from "./_chunks/logger.mjs";
3
- import "./_libs/@aws-sdk/client-bedrock-runtime+[...].mjs";
4
3
  import "./_libs/@mariozechner/pi-agent-core+[...].mjs";
5
- import { t as server } from "./_chunks/bootstrap.mjs";
4
+ import "./_libs/@aws-sdk/client-bedrock-runtime+[...].mjs";
5
+ import { t as server } from "./_chunks/server.mjs";
6
6
  var logger = createLogger("api:groups");
7
7
  var UPDATABLE_FIELDS = [
8
8
  "name",
9
9
  "trigger",
10
10
  "requiresTrigger",
11
- "model"
11
+ "model",
12
+ "thinkingLevel"
13
+ ];
14
+ var VALID_THINKING_LEVELS = [
15
+ "off",
16
+ "minimal",
17
+ "low",
18
+ "medium",
19
+ "high",
20
+ "xhigh"
12
21
  ];
13
22
  var _jid__patch_default = defineHandler(async (event) => {
14
23
  const jid = decodeURIComponent(getRouterParam(event, "jid"));
@@ -48,6 +57,14 @@ var _jid__patch_default = defineHandler(async (event) => {
48
57
  if (val !== void 0 && val !== null && typeof val !== "string") throw new HTTPError("model must be a string or null", { status: 400 });
49
58
  group.model = val ? String(val) : void 0;
50
59
  }
60
+ const oldThinkingLevel = group.thinkingLevel;
61
+ if ("thinkingLevel" in body) {
62
+ const val = body.thinkingLevel;
63
+ if (val !== void 0 && val !== null) {
64
+ if (typeof val !== "string" || !VALID_THINKING_LEVELS.includes(val)) throw new HTTPError(`thinkingLevel must be one of: ${VALID_THINKING_LEVELS.join(", ")}`, { status: 400 });
65
+ }
66
+ group.thinkingLevel = val === null || val === "off" ? void 0 : val;
67
+ }
51
68
  server.setRegisteredGroup(jid, group);
52
69
  if (oldModel !== group.model) {
53
70
  server.pi.kill(jid);
@@ -59,6 +76,16 @@ var _jid__patch_default = defineHandler(async (event) => {
59
76
  newModel: group.model
60
77
  }, "Model changed, session killed");
61
78
  }
79
+ if (oldThinkingLevel !== group.thinkingLevel) {
80
+ server.pi.kill(jid);
81
+ const display = group.thinkingLevel || "off";
82
+ changes.push(`thinkingLevel → ${display}`);
83
+ logger.info({
84
+ jid,
85
+ oldThinkingLevel,
86
+ newThinkingLevel: group.thinkingLevel
87
+ }, "Thinking level changed, session killed");
88
+ }
62
89
  if (changes.length > 0) {
63
90
  server.sendBotMessage(jid, `Config updated: ${changes.join(", ")}`);
64
91
  logger.info({
@@ -1,7 +1,7 @@
1
1
  import { a as defineHandler, l as getRouterParam } from "./_libs/h3+rou3+srvx.mjs";
2
- import "./_libs/@aws-sdk/client-bedrock-runtime+[...].mjs";
3
2
  import "./_libs/@mariozechner/pi-agent-core+[...].mjs";
4
- import { t as server } from "./_chunks/bootstrap.mjs";
3
+ import "./_libs/@aws-sdk/client-bedrock-runtime+[...].mjs";
4
+ import { t as server } from "./_chunks/server.mjs";
5
5
  var _jid__delete_default = defineHandler((event) => {
6
6
  const jid = decodeURIComponent(getRouterParam(event, "jid"));
7
7
  server.cleanupChat(jid);