@foothill/agent-move 1.0.9 → 1.0.10

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 (111) hide show
  1. package/README.md +46 -9
  2. package/package.json +1 -1
  3. package/packages/client/dist/assets/{BufferResource-Ddjob236.js → BufferResource-Dfd5uHKt.js} +1 -1
  4. package/packages/client/dist/assets/{CanvasRenderer-B0w6SYyW.js → CanvasRenderer-7Cv6xZVP.js} +1 -1
  5. package/packages/client/dist/assets/{Filter-NcMGuiK-.js → Filter-CBX7EB7j.js} +1 -1
  6. package/packages/client/dist/assets/{RenderTargetSystem-DgAzY5_U.js → RenderTargetSystem-ko-v73NG.js} +1 -1
  7. package/packages/client/dist/assets/{WebGLRenderer-DUWXDPIX.js → WebGLRenderer-vhPQEPUG.js} +1 -1
  8. package/packages/client/dist/assets/{WebGPURenderer-C1HbrllR.js → WebGPURenderer-Dwywvwqe.js} +1 -1
  9. package/packages/client/dist/assets/{browserAll-CaF1Fl0O.js → browserAll-QyCAT8_K.js} +1 -1
  10. package/packages/client/dist/assets/index-BPJtz4FL.js +722 -0
  11. package/packages/client/dist/assets/{webworkerAll-BJ6UhC7r.js → webworkerAll-hM-gNP7L.js} +1 -1
  12. package/packages/client/dist/index.html +1 -1
  13. package/packages/server/dist/config.d.ts +4 -0
  14. package/packages/server/dist/config.d.ts.map +1 -1
  15. package/packages/server/dist/config.js +5 -1
  16. package/packages/server/dist/config.js.map +1 -1
  17. package/packages/server/dist/index.d.ts.map +1 -1
  18. package/packages/server/dist/index.js +790 -77
  19. package/packages/server/dist/index.js.map +1 -1
  20. package/packages/server/dist/state/activity-processor.d.ts +1 -1
  21. package/packages/server/dist/state/activity-processor.d.ts.map +1 -1
  22. package/packages/server/dist/state/activity-processor.js +1 -1
  23. package/packages/server/dist/state/activity-processor.js.map +1 -1
  24. package/packages/server/dist/state/agent-state-manager.d.ts +1 -2
  25. package/packages/server/dist/state/agent-state-manager.d.ts.map +1 -1
  26. package/packages/server/dist/state/agent-state-manager.js +87 -2
  27. package/packages/server/dist/state/agent-state-manager.js.map +1 -1
  28. package/packages/server/dist/state/role-resolver.d.ts +1 -2
  29. package/packages/server/dist/state/role-resolver.d.ts.map +1 -1
  30. package/packages/server/dist/state/role-resolver.js.map +1 -1
  31. package/packages/server/dist/state/task-graph-manager.d.ts +12 -0
  32. package/packages/server/dist/state/task-graph-manager.d.ts.map +1 -1
  33. package/packages/server/dist/state/task-graph-manager.js +80 -0
  34. package/packages/server/dist/state/task-graph-manager.js.map +1 -1
  35. package/packages/server/dist/watcher/claude/claude-paths.d.ts +18 -0
  36. package/packages/server/dist/watcher/claude/claude-paths.d.ts.map +1 -0
  37. package/packages/server/dist/watcher/{claude-paths.js → claude/claude-paths.js} +47 -55
  38. package/packages/server/dist/watcher/claude/claude-paths.js.map +1 -0
  39. package/packages/server/dist/watcher/{file-watcher.d.ts → claude/claude-watcher.d.ts} +3 -3
  40. package/packages/server/dist/watcher/claude/claude-watcher.d.ts.map +1 -0
  41. package/packages/server/dist/watcher/{file-watcher.js → claude/claude-watcher.js} +59 -65
  42. package/packages/server/dist/watcher/claude/claude-watcher.js.map +1 -0
  43. package/packages/server/dist/watcher/claude/jsonl-parser.d.ts +6 -0
  44. package/packages/server/dist/watcher/claude/jsonl-parser.d.ts.map +1 -0
  45. package/packages/server/dist/watcher/{jsonl-parser.js → claude/jsonl-parser.js} +1 -1
  46. package/packages/server/dist/watcher/claude/jsonl-parser.js.map +1 -0
  47. package/packages/server/dist/watcher/codex/codex-parser.d.ts +30 -0
  48. package/packages/server/dist/watcher/codex/codex-parser.d.ts.map +1 -0
  49. package/packages/server/dist/watcher/codex/codex-parser.js +326 -0
  50. package/packages/server/dist/watcher/codex/codex-parser.js.map +1 -0
  51. package/packages/server/dist/watcher/codex/codex-paths.d.ts +35 -0
  52. package/packages/server/dist/watcher/codex/codex-paths.d.ts.map +1 -0
  53. package/packages/server/dist/watcher/codex/codex-paths.js +46 -0
  54. package/packages/server/dist/watcher/codex/codex-paths.js.map +1 -0
  55. package/packages/server/dist/watcher/codex/codex-watcher.d.ts +42 -0
  56. package/packages/server/dist/watcher/codex/codex-watcher.d.ts.map +1 -0
  57. package/packages/server/dist/watcher/codex/codex-watcher.js +577 -0
  58. package/packages/server/dist/watcher/codex/codex-watcher.js.map +1 -0
  59. package/packages/server/dist/watcher/opencode/opencode-parser.d.ts +1 -1
  60. package/packages/server/dist/watcher/opencode/opencode-parser.d.ts.map +1 -1
  61. package/packages/server/dist/watcher/opencode/opencode-parser.js +31 -2
  62. package/packages/server/dist/watcher/opencode/opencode-paths.d.ts +1 -1
  63. package/packages/server/dist/watcher/opencode/opencode-paths.d.ts.map +1 -1
  64. package/packages/server/dist/watcher/opencode/opencode-paths.js +1 -0
  65. package/packages/server/dist/watcher/opencode/opencode-paths.js.map +1 -1
  66. package/packages/server/dist/watcher/opencode/opencode-watcher.d.ts.map +1 -1
  67. package/packages/server/dist/watcher/opencode/opencode-watcher.js +48 -10
  68. package/packages/server/dist/watcher/opencode/opencode-watcher.js.map +1 -1
  69. package/packages/server/dist/watcher/path-utils.d.ts +10 -0
  70. package/packages/server/dist/watcher/path-utils.d.ts.map +1 -0
  71. package/packages/server/dist/watcher/path-utils.js +38 -0
  72. package/packages/server/dist/watcher/path-utils.js.map +1 -0
  73. package/packages/server/dist/watcher/pi/pi-parser.d.ts +19 -0
  74. package/packages/server/dist/watcher/pi/pi-parser.d.ts.map +1 -0
  75. package/packages/server/dist/watcher/pi/pi-parser.js +307 -0
  76. package/packages/server/dist/watcher/pi/pi-parser.js.map +1 -0
  77. package/packages/server/dist/watcher/pi/pi-paths.d.ts +28 -0
  78. package/packages/server/dist/watcher/pi/pi-paths.d.ts.map +1 -0
  79. package/packages/server/dist/watcher/pi/pi-paths.js +86 -0
  80. package/packages/server/dist/watcher/pi/pi-paths.js.map +1 -0
  81. package/packages/server/dist/watcher/pi/pi-watcher.d.ts +36 -0
  82. package/packages/server/dist/watcher/pi/pi-watcher.d.ts.map +1 -0
  83. package/packages/server/dist/watcher/pi/pi-watcher.js +593 -0
  84. package/packages/server/dist/watcher/pi/pi-watcher.js.map +1 -0
  85. package/packages/server/dist/watcher/session-scanner.d.ts +9 -3
  86. package/packages/server/dist/watcher/session-scanner.d.ts.map +1 -1
  87. package/packages/server/dist/watcher/session-scanner.js +11 -9
  88. package/packages/server/dist/watcher/session-scanner.js.map +1 -1
  89. package/packages/server/dist/watcher/types.d.ts +30 -0
  90. package/packages/server/dist/watcher/types.d.ts.map +1 -0
  91. package/packages/server/dist/watcher/types.js +14 -0
  92. package/packages/server/dist/watcher/types.js.map +1 -0
  93. package/packages/shared/dist/constants/colors.d.ts +1 -1
  94. package/packages/shared/dist/constants/colors.js +1 -1
  95. package/packages/shared/dist/constants/colors.js.map +1 -1
  96. package/packages/shared/dist/constants/tools.d.ts.map +1 -1
  97. package/packages/shared/dist/constants/tools.js +30 -1
  98. package/packages/shared/dist/constants/tools.js.map +1 -1
  99. package/packages/shared/dist/index.d.ts +1 -1
  100. package/packages/shared/dist/index.d.ts.map +1 -1
  101. package/packages/shared/dist/types/agent.d.ts +3 -0
  102. package/packages/shared/dist/types/agent.d.ts.map +1 -1
  103. package/packages/client/dist/assets/index-Dh8yWoLP.js +0 -711
  104. package/packages/server/dist/watcher/claude-paths.d.ts +0 -32
  105. package/packages/server/dist/watcher/claude-paths.d.ts.map +0 -1
  106. package/packages/server/dist/watcher/claude-paths.js.map +0 -1
  107. package/packages/server/dist/watcher/file-watcher.d.ts.map +0 -1
  108. package/packages/server/dist/watcher/file-watcher.js.map +0 -1
  109. package/packages/server/dist/watcher/jsonl-parser.d.ts +0 -21
  110. package/packages/server/dist/watcher/jsonl-parser.d.ts.map +0 -1
  111. package/packages/server/dist/watcher/jsonl-parser.js.map +0 -1
@@ -0,0 +1,36 @@
1
+ import type { AgentStateManager } from '../../state/agent-state-manager.js';
2
+ import type { AgentWatcher } from '../agent-watcher.js';
3
+ /**
4
+ * Watches pi coding agent session JSONL files for new activity.
5
+ *
6
+ * Pi stores sessions at: ~/.pi/agent/sessions/--encoded-cwd--/{timestamp}_{uuid}.jsonl
7
+ * The JSONL format uses {type:"message", message:{...}} entries with tool calls
8
+ * as {type:"toolCall"} content blocks inside assistant messages.
9
+ */
10
+ export declare class PiWatcher implements AgentWatcher {
11
+ private stateManager;
12
+ private watcher;
13
+ private byteOffsets;
14
+ private parser;
15
+ /** Per-file lock to prevent concurrent processFile calls */
16
+ private fileLocks;
17
+ /** Cached session info per file (parsed from session header) */
18
+ private sessionInfoCache;
19
+ constructor(stateManager: AgentStateManager);
20
+ start(): Promise<void>;
21
+ stop(): void;
22
+ private processFile;
23
+ private doProcessFile;
24
+ /**
25
+ * Extract a prefixed session ID from a pi session file path.
26
+ * Filename format: {timestamp}_{uuid}.jsonl
27
+ */
28
+ private extractSessionId;
29
+ private buildFallbackSession;
30
+ /**
31
+ * Get the encoded project directory name from a session file path.
32
+ * Path: .../sessions/--encoded-path--/{timestamp}_{uuid}.jsonl
33
+ */
34
+ private getProjectDirName;
35
+ }
36
+ //# sourceMappingURL=pi-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-watcher.d.ts","sourceRoot":"","sources":["../../../src/watcher/pi/pi-watcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAG5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMxD;;;;;;GAMG;AACH,qBAAa,SAAU,YAAW,YAAY;IAShC,OAAO,CAAC,YAAY;IARhC,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,MAAM,CAAkB;IAChC,4DAA4D;IAC5D,OAAO,CAAC,SAAS,CAAoC;IACrD,gEAAgE;IAChE,OAAO,CAAC,gBAAgB,CAAkC;gBAEtC,YAAY,EAAE,iBAAiB;IAE7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC5B,IAAI,IAAI,IAAI;IAOZ,OAAO,CAAC,WAAW;YAaL,aAAa;IAyD3B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,oBAAoB;IAM5B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;CAK1B"}
@@ -0,0 +1,593 @@
1
+ // dist/watcher/pi/pi-watcher.js
2
+ import chokidar from "chokidar";
3
+ import { stat as stat2, open } from "fs/promises";
4
+ import { join as join5, basename, dirname } from "path";
5
+ import { readdir, stat } from "fs/promises";
6
+ import { join as join2 } from "path";
7
+ import { homedir } from "os";
8
+ import { join } from "path";
9
+ import { homedir as homedir2 } from "os";
10
+ import { join as join4 } from "path";
11
+ import { existsSync as existsSync2 } from "fs";
12
+ import { join as join3 } from "path";
13
+ import { existsSync } from "fs";
14
+ function createFallbackSession(agentType, name) {
15
+ return {
16
+ agentType,
17
+ projectPath: name,
18
+ projectName: name,
19
+ isSubagent: false,
20
+ projectDir: name,
21
+ parentSessionId: null
22
+ };
23
+ }
24
+ var TOOL_NAME_MAP = {
25
+ // OpenCode / pi lowercase → canonical PascalCase
26
+ read: "Read",
27
+ write: "Write",
28
+ edit: "Edit",
29
+ patch: "Patch",
30
+ glob: "Glob",
31
+ bash: "Bash",
32
+ grep: "Grep",
33
+ websearch: "WebSearch",
34
+ webfetch: "WebFetch",
35
+ todoread: "TodoRead",
36
+ todowrite: "TodoWrite",
37
+ // pi-specific tool names
38
+ "edit-diff": "Patch",
39
+ find: "Glob",
40
+ ls: "Bash",
41
+ truncate: "Write",
42
+ // Codex CLI tool names
43
+ shell_command: "Bash",
44
+ exec_command: "Bash",
45
+ read_file: "Read",
46
+ apply_patch: "Patch",
47
+ list_dir: "Bash",
48
+ grep_files: "Grep",
49
+ web_search: "WebSearch",
50
+ js_repl: "Bash",
51
+ js_repl_reset: "Bash",
52
+ spawn_agent: "Agent",
53
+ send_input: "Agent",
54
+ wait: "Agent",
55
+ close_agent: "Agent",
56
+ resume_agent: "Agent",
57
+ spawn_agents_on_csv: "Agent",
58
+ report_agent_job_result: "Agent",
59
+ request_user_input: "AskUserQuestion",
60
+ request_permissions: "AskUserQuestion",
61
+ update_plan: "TodoWrite",
62
+ view_image: "Read",
63
+ image_generation: "Write",
64
+ write_stdin: "Bash",
65
+ search_apps: "WebSearch"
66
+ };
67
+ function normalizeToolName(name) {
68
+ return TOOL_NAME_MAP[name] ?? name;
69
+ }
70
+ function normalizeToolInput(input) {
71
+ if (!("filePath" in input) && !("oldString" in input) && !("newString" in input) && !("replaceAll" in input)) {
72
+ return input;
73
+ }
74
+ const out = { ...input };
75
+ if ("filePath" in out) {
76
+ out.file_path = out.filePath;
77
+ delete out.filePath;
78
+ }
79
+ if ("oldString" in out) {
80
+ out.old_string = out.oldString;
81
+ delete out.oldString;
82
+ }
83
+ if ("newString" in out) {
84
+ out.new_string = out.newString;
85
+ delete out.newString;
86
+ }
87
+ if ("replaceAll" in out) {
88
+ out.replace_all = out.replaceAll;
89
+ delete out.replaceAll;
90
+ }
91
+ return out;
92
+ }
93
+ var ZONES = [
94
+ // Row 0
95
+ {
96
+ id: "search",
97
+ label: "Search",
98
+ description: "Grep, WebSearch \u2014 Research & lookup",
99
+ icon: "\u{1F4DA}",
100
+ color: 15381256,
101
+ colStart: 0,
102
+ colSpan: 5,
103
+ rowStart: 0,
104
+ rowSpan: 1,
105
+ x: 0,
106
+ y: 0,
107
+ width: 0,
108
+ height: 0
109
+ },
110
+ {
111
+ id: "terminal",
112
+ label: "Terminal",
113
+ description: "Bash commands \u2014 Server room",
114
+ icon: "\u{1F4BB}",
115
+ color: 2278750,
116
+ colStart: 5,
117
+ colSpan: 3,
118
+ rowStart: 0,
119
+ rowSpan: 1,
120
+ x: 0,
121
+ y: 0,
122
+ width: 0,
123
+ height: 0
124
+ },
125
+ {
126
+ id: "web",
127
+ label: "Web",
128
+ description: "WebFetch, Browser \u2014 Network hub",
129
+ icon: "\u{1F310}",
130
+ color: 9133302,
131
+ colStart: 8,
132
+ colSpan: 4,
133
+ rowStart: 0,
134
+ rowSpan: 1,
135
+ x: 0,
136
+ y: 0,
137
+ width: 0,
138
+ height: 0
139
+ },
140
+ // Row 1
141
+ {
142
+ id: "files",
143
+ label: "Files",
144
+ description: "Read, Write, Edit, Glob \u2014 File storage",
145
+ icon: "\u{1F4C1}",
146
+ color: 3900150,
147
+ colStart: 0,
148
+ colSpan: 4,
149
+ rowStart: 1,
150
+ rowSpan: 1,
151
+ x: 0,
152
+ y: 0,
153
+ width: 0,
154
+ height: 0
155
+ },
156
+ {
157
+ id: "thinking",
158
+ label: "Thinking",
159
+ description: "Planning, Questions \u2014 Conference area",
160
+ icon: "\u{1F4AD}",
161
+ color: 16347926,
162
+ colStart: 4,
163
+ colSpan: 5,
164
+ rowStart: 1,
165
+ rowSpan: 1,
166
+ x: 0,
167
+ y: 0,
168
+ width: 0,
169
+ height: 0
170
+ },
171
+ {
172
+ id: "messaging",
173
+ label: "Messaging",
174
+ description: "SendMessage, Teams \u2014 Chat & relax",
175
+ icon: "\u{1F4AC}",
176
+ color: 15485081,
177
+ colStart: 9,
178
+ colSpan: 3,
179
+ rowStart: 1,
180
+ rowSpan: 1,
181
+ x: 0,
182
+ y: 0,
183
+ width: 0,
184
+ height: 0
185
+ },
186
+ // Row 2
187
+ {
188
+ id: "spawn",
189
+ label: "Spawn",
190
+ description: "Agent spawn/despawn \u2014 Entry portal",
191
+ icon: "\u{1F300}",
192
+ color: 11032055,
193
+ colStart: 0,
194
+ colSpan: 3,
195
+ rowStart: 2,
196
+ rowSpan: 1,
197
+ x: 0,
198
+ y: 0,
199
+ width: 0,
200
+ height: 0
201
+ },
202
+ {
203
+ id: "idle",
204
+ label: "Idle",
205
+ description: "Idle agents rest here \u2014 Kitchen & lounge",
206
+ icon: "\u2615",
207
+ color: 7041664,
208
+ colStart: 3,
209
+ colSpan: 5,
210
+ rowStart: 2,
211
+ rowSpan: 1,
212
+ x: 0,
213
+ y: 0,
214
+ width: 0,
215
+ height: 0
216
+ },
217
+ {
218
+ id: "tasks",
219
+ label: "Tasks",
220
+ description: "TaskCreate, TaskUpdate \u2014 Kanban & planning",
221
+ icon: "\u{1F4CB}",
222
+ color: 1357990,
223
+ colStart: 8,
224
+ colSpan: 4,
225
+ rowStart: 2,
226
+ rowSpan: 1,
227
+ x: 0,
228
+ y: 0,
229
+ width: 0,
230
+ height: 0
231
+ }
232
+ ];
233
+ var ZONE_MAP = new Map(ZONES.map((z) => [z.id, z]));
234
+ var PiParser = class {
235
+ /**
236
+ * Parse a single JSONL line from a pi session file into a raw object.
237
+ * Returns the parsed JSON, or null on parse error.
238
+ */
239
+ parseRaw(line) {
240
+ try {
241
+ return JSON.parse(line);
242
+ } catch {
243
+ return null;
244
+ }
245
+ }
246
+ /**
247
+ * Extract a ParsedActivity from a pre-parsed JSONL entry.
248
+ * Returns null for non-actionable entries (session header, user messages, etc.).
249
+ */
250
+ parseEntry(entry) {
251
+ if (entry.type !== "message")
252
+ return null;
253
+ const msg = entry.message;
254
+ if (!msg || msg.role !== "assistant")
255
+ return null;
256
+ return this.parseAssistantMessage(msg);
257
+ }
258
+ /**
259
+ * Check if a pre-parsed entry is a session header.
260
+ */
261
+ isSessionHeader(entry) {
262
+ return entry.type === "session";
263
+ }
264
+ parseAssistantMessage(msg) {
265
+ const content = msg.content;
266
+ if (!Array.isArray(content))
267
+ return null;
268
+ for (const block of content) {
269
+ if (block.type === "toolCall") {
270
+ const tool = block;
271
+ return {
272
+ type: "tool_use",
273
+ toolName: normalizeToolName(tool.name),
274
+ toolInput: normalizeToolInput(tool.arguments ?? {}),
275
+ model: msg.model,
276
+ inputTokens: msg.usage?.input,
277
+ outputTokens: msg.usage?.output,
278
+ cacheReadTokens: msg.usage?.cacheRead,
279
+ cacheCreationTokens: msg.usage?.cacheWrite
280
+ };
281
+ }
282
+ }
283
+ for (const block of content) {
284
+ if (block.type === "text") {
285
+ const text = block.text?.trim() ?? "";
286
+ if (text.length > 0 && text.length < 200) {
287
+ return {
288
+ type: "text",
289
+ text,
290
+ model: msg.model,
291
+ inputTokens: msg.usage?.input,
292
+ outputTokens: msg.usage?.output,
293
+ cacheReadTokens: msg.usage?.cacheRead,
294
+ cacheCreationTokens: msg.usage?.cacheWrite
295
+ };
296
+ }
297
+ }
298
+ }
299
+ for (const block of content) {
300
+ if (block.type === "thinking") {
301
+ const thinking = block.thinking?.trim() ?? "";
302
+ return {
303
+ type: "tool_use",
304
+ toolName: "thinking",
305
+ toolInput: thinking.length > 0 ? { thought: thinking.slice(0, 120) } : void 0,
306
+ model: msg.model,
307
+ inputTokens: msg.usage?.input,
308
+ outputTokens: msg.usage?.output,
309
+ cacheReadTokens: msg.usage?.cacheRead,
310
+ cacheCreationTokens: msg.usage?.cacheWrite
311
+ };
312
+ }
313
+ }
314
+ if (msg.usage && (msg.usage.input || msg.usage.output)) {
315
+ return {
316
+ type: "token_usage",
317
+ inputTokens: msg.usage.input,
318
+ outputTokens: msg.usage.output,
319
+ cacheReadTokens: msg.usage.cacheRead,
320
+ cacheCreationTokens: msg.usage.cacheWrite,
321
+ model: msg.model
322
+ };
323
+ }
324
+ return null;
325
+ }
326
+ };
327
+ var config = {
328
+ port: parseInt(process.env.AGENT_MOVE_PORT || "3333", 10),
329
+ claudeHome: join(homedir(), ".claude"),
330
+ idleTimeoutMs: 45e3,
331
+ /** How long after going idle before an agent is automatically shutdown/removed */
332
+ shutdownTimeoutMs: 30 * 60 * 1e3,
333
+ // 30 minutes
334
+ /** How recently a session file must be modified to be considered "active" on startup */
335
+ activeThresholdMs: 10 * 60 * 1e3,
336
+ // 10 minutes
337
+ /** Enable OpenCode session watching (auto-detected if storage dir exists) */
338
+ enableOpenCode: process.env.AGENT_MOVE_OPENCODE !== "false",
339
+ /** Enable pi coding agent session watching (auto-detected if sessions dir exists) */
340
+ enablePi: process.env.AGENT_MOVE_PI !== "false",
341
+ /** Enable Codex CLI session watching (auto-detected if sessions dir exists) */
342
+ enableCodex: process.env.AGENT_MOVE_CODEX !== "false"
343
+ };
344
+ var SessionScanner = class {
345
+ rootDir;
346
+ constructor(rootDir) {
347
+ this.rootDir = rootDir;
348
+ }
349
+ /** Find the most recently modified JSONL per project subdirectory */
350
+ async scan() {
351
+ const results = [];
352
+ try {
353
+ const projects = await readdir(this.rootDir);
354
+ const now = Date.now();
355
+ for (const project of projects) {
356
+ const projectDir = join2(this.rootDir, project);
357
+ try {
358
+ const projectStat = await stat(projectDir);
359
+ if (!projectStat.isDirectory())
360
+ continue;
361
+ const files = await readdir(projectDir);
362
+ let newestFile = null;
363
+ let newestMtime = 0;
364
+ for (const file of files) {
365
+ if (!file.endsWith(".jsonl"))
366
+ continue;
367
+ const filePath = join2(projectDir, file);
368
+ try {
369
+ const fileStat = await stat(filePath);
370
+ if (now - fileStat.mtimeMs < config.activeThresholdMs) {
371
+ if (fileStat.mtimeMs > newestMtime) {
372
+ newestMtime = fileStat.mtimeMs;
373
+ newestFile = filePath;
374
+ }
375
+ }
376
+ } catch {
377
+ }
378
+ }
379
+ if (newestFile) {
380
+ results.push(newestFile);
381
+ }
382
+ } catch {
383
+ }
384
+ }
385
+ } catch {
386
+ }
387
+ return results;
388
+ }
389
+ };
390
+ function resolveEncodedPath(root, segments) {
391
+ try {
392
+ const parts = segments.split("-").filter(Boolean);
393
+ let currentPath = root;
394
+ let lastName = "";
395
+ let i = 0;
396
+ while (i < parts.length) {
397
+ let found = false;
398
+ const maxLen = Math.min(parts.length - i, 6);
399
+ for (let len = 1; len <= maxLen; len++) {
400
+ const segment = parts.slice(i, i + len).join("-");
401
+ for (const prefix of ["", "."]) {
402
+ const testPath = join3(currentPath, prefix + segment);
403
+ if (existsSync(testPath)) {
404
+ currentPath = testPath;
405
+ lastName = prefix + segment;
406
+ i += len;
407
+ found = true;
408
+ break;
409
+ }
410
+ }
411
+ if (found)
412
+ break;
413
+ }
414
+ if (!found)
415
+ break;
416
+ }
417
+ return lastName || null;
418
+ } catch {
419
+ return null;
420
+ }
421
+ }
422
+ function getPiSessionsDir() {
423
+ const candidate = join4(homedir2(), ".pi", "agent", "sessions");
424
+ return existsSync2(candidate) ? candidate : null;
425
+ }
426
+ function decodePiProjectDir(encoded) {
427
+ let inner = encoded;
428
+ if (inner.startsWith("--") && inner.endsWith("--")) {
429
+ inner = inner.slice(2, -2);
430
+ }
431
+ const driveMatch = inner.match(/^([A-Za-z])--(.*)/);
432
+ if (driveMatch) {
433
+ const resolved = resolveEncodedPath(driveMatch[1] + ":/", driveMatch[2]);
434
+ if (resolved)
435
+ return resolved;
436
+ } else {
437
+ const resolved = resolveEncodedPath("/", inner);
438
+ if (resolved)
439
+ return resolved;
440
+ }
441
+ const parts = inner.split("-").filter(Boolean);
442
+ if (parts.length <= 2)
443
+ return parts.join("/");
444
+ return parts.slice(-2).join("-");
445
+ }
446
+ function parsePiSessionInfo(header, dirName) {
447
+ const projectName = decodePiProjectDir(dirName);
448
+ return {
449
+ agentType: "pi",
450
+ projectPath: header.cwd || dirName,
451
+ projectName,
452
+ isSubagent: !!header.parentSession,
453
+ projectDir: dirName,
454
+ parentSessionId: header.parentSession ? extractSessionIdFromPath(header.parentSession) : null
455
+ };
456
+ }
457
+ function extractSessionIdFromPath(filePath) {
458
+ const match = filePath.match(/([^/\\]+)\.jsonl$/);
459
+ if (!match)
460
+ return null;
461
+ const parts = match[1].split("_");
462
+ const uuid = parts[parts.length - 1];
463
+ return uuid ? `pi:${uuid}` : null;
464
+ }
465
+ var PiWatcher = class {
466
+ stateManager;
467
+ watcher = null;
468
+ byteOffsets = /* @__PURE__ */ new Map();
469
+ parser = new PiParser();
470
+ /** Per-file lock to prevent concurrent processFile calls */
471
+ fileLocks = /* @__PURE__ */ new Map();
472
+ /** Cached session info per file (parsed from session header) */
473
+ sessionInfoCache = /* @__PURE__ */ new Map();
474
+ constructor(stateManager) {
475
+ this.stateManager = stateManager;
476
+ }
477
+ async start() {
478
+ const sessionsDir = getPiSessionsDir();
479
+ if (!sessionsDir) {
480
+ console.log("[pi] No sessions directory found \u2014 pi not installed or not yet used");
481
+ return;
482
+ }
483
+ console.log(`[pi] Sessions directory found at ${sessionsDir}`);
484
+ const scanner = new SessionScanner(sessionsDir);
485
+ const existingFiles = await scanner.scan();
486
+ for (const file of existingFiles) {
487
+ await this.processFile(file);
488
+ }
489
+ const pattern = join5(sessionsDir, "**", "*.jsonl");
490
+ this.watcher = chokidar.watch(pattern, {
491
+ persistent: true,
492
+ ignoreInitial: true,
493
+ awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
494
+ });
495
+ this.watcher.on("add", (filePath) => {
496
+ console.log(`[pi] New session file: ${filePath}`);
497
+ this.processFile(filePath);
498
+ });
499
+ this.watcher.on("change", (filePath) => {
500
+ this.processFile(filePath);
501
+ });
502
+ console.log(`[pi] Watching for JSONL files in ${sessionsDir}`);
503
+ }
504
+ stop() {
505
+ this.watcher?.close();
506
+ this.byteOffsets.clear();
507
+ this.fileLocks.clear();
508
+ this.sessionInfoCache.clear();
509
+ }
510
+ processFile(filePath) {
511
+ const prev = this.fileLocks.get(filePath) ?? Promise.resolve();
512
+ const next = prev.then(() => this.doProcessFile(filePath)).catch(() => {
513
+ }).finally(() => {
514
+ if (this.fileLocks.get(filePath) === next) {
515
+ this.fileLocks.delete(filePath);
516
+ }
517
+ });
518
+ this.fileLocks.set(filePath, next);
519
+ }
520
+ async doProcessFile(filePath) {
521
+ try {
522
+ const fileStats = await stat2(filePath);
523
+ const currentOffset = this.byteOffsets.get(filePath) ?? 0;
524
+ if (fileStats.size <= currentOffset)
525
+ return;
526
+ const handle = await open(filePath, "r");
527
+ try {
528
+ const buffer = Buffer.alloc(fileStats.size - currentOffset);
529
+ await handle.read(buffer, 0, buffer.length, currentOffset);
530
+ this.byteOffsets.set(filePath, fileStats.size);
531
+ const newContent = buffer.toString("utf-8");
532
+ const lines = newContent.split("\n").filter((l) => l.trim());
533
+ const sessionId = this.extractSessionId(filePath);
534
+ let sessionInfo = this.sessionInfoCache.get(filePath);
535
+ let hadParsedActivity = false;
536
+ for (const line of lines) {
537
+ const raw = this.parser.parseRaw(line);
538
+ if (!raw)
539
+ continue;
540
+ if (!sessionInfo && this.parser.isSessionHeader(raw)) {
541
+ const dirName = this.getProjectDirName(filePath);
542
+ sessionInfo = parsePiSessionInfo(raw, dirName);
543
+ this.sessionInfoCache.set(filePath, sessionInfo);
544
+ continue;
545
+ }
546
+ const parsed = this.parser.parseEntry(raw);
547
+ if (parsed) {
548
+ hadParsedActivity = true;
549
+ if (!sessionInfo) {
550
+ sessionInfo = this.buildFallbackSession(filePath);
551
+ this.sessionInfoCache.set(filePath, sessionInfo);
552
+ }
553
+ this.stateManager.processMessage(sessionId, parsed, sessionInfo);
554
+ }
555
+ }
556
+ if (!hadParsedActivity && lines.length > 0) {
557
+ this.stateManager.heartbeat(sessionId);
558
+ }
559
+ } finally {
560
+ await handle.close();
561
+ }
562
+ } catch (err) {
563
+ if (err.code !== "ENOENT") {
564
+ console.error(`[pi] Error processing ${filePath}:`, err);
565
+ }
566
+ }
567
+ }
568
+ /**
569
+ * Extract a prefixed session ID from a pi session file path.
570
+ * Filename format: {timestamp}_{uuid}.jsonl
571
+ */
572
+ extractSessionId(filePath) {
573
+ const name = basename(filePath, ".jsonl");
574
+ return `pi:${name}`;
575
+ }
576
+ buildFallbackSession(filePath) {
577
+ const dirName = this.getProjectDirName(filePath);
578
+ const name = dirName.replace(/^--|--$/g, "") || "pi";
579
+ return createFallbackSession("pi", name);
580
+ }
581
+ /**
582
+ * Get the encoded project directory name from a session file path.
583
+ * Path: .../sessions/--encoded-path--/{timestamp}_{uuid}.jsonl
584
+ */
585
+ getProjectDirName(filePath) {
586
+ const dir = dirname(filePath).replace(/\\/g, "/");
587
+ const parts = dir.split("/");
588
+ return parts[parts.length - 1] || "unknown";
589
+ }
590
+ };
591
+ export {
592
+ PiWatcher
593
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-watcher.js","sourceRoot":"","sources":["../../../src/watcher/pi/pi-watcher.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,13 @@
1
+ /**
2
+ * Scans a directory of project subdirectories for recently active JSONL files.
3
+ * Used by both Claude and pi watchers — each passes its own root directory.
4
+ *
5
+ * Directory structure: {rootDir}/{project-dir}/*.jsonl
6
+ */
1
7
  export declare class SessionScanner {
2
- private claudeHome;
3
- constructor(claudeHome: string);
4
- /** Find all recently active JSONL session files */
8
+ private rootDir;
9
+ constructor(rootDir: string);
10
+ /** Find the most recently modified JSONL per project subdirectory */
5
11
  scan(): Promise<string[]>;
6
12
  }
7
13
  //# sourceMappingURL=session-scanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-scanner.d.ts","sourceRoot":"","sources":["../../src/watcher/session-scanner.ts"],"names":[],"mappings":"AAIA,qBAAa,cAAc;IACb,OAAO,CAAC,UAAU;gBAAV,UAAU,EAAE,MAAM;IAEtC,mDAAmD;IAC7C,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAkDhC"}
1
+ {"version":3,"file":"session-scanner.d.ts","sourceRoot":"","sources":["../../src/watcher/session-scanner.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,qBAAa,cAAc;IACb,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,MAAM;IAEnC,qEAAqE;IAC/D,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CA+ChC"}