@foothill/agent-move 1.0.8 → 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 (122) hide show
  1. package/README.md +46 -9
  2. package/package.json +2 -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 -5
  16. package/packages/server/dist/config.js.map +1 -1
  17. package/packages/server/dist/hooks/hook-event-manager.js +0 -4
  18. package/packages/server/dist/hooks/hook-installer.js +0 -4
  19. package/packages/server/dist/index.d.ts.map +1 -1
  20. package/packages/server/dist/index.js +792 -856
  21. package/packages/server/dist/index.js.map +1 -1
  22. package/packages/server/dist/routes/api.js +0 -4
  23. package/packages/server/dist/state/activity-processor.d.ts +1 -1
  24. package/packages/server/dist/state/activity-processor.d.ts.map +1 -1
  25. package/packages/server/dist/state/activity-processor.js +1 -5
  26. package/packages/server/dist/state/activity-processor.js.map +1 -1
  27. package/packages/server/dist/state/agent-state-manager.d.ts +1 -2
  28. package/packages/server/dist/state/agent-state-manager.d.ts.map +1 -1
  29. package/packages/server/dist/state/agent-state-manager.js +87 -6
  30. package/packages/server/dist/state/agent-state-manager.js.map +1 -1
  31. package/packages/server/dist/state/anomaly-detector.js +0 -4
  32. package/packages/server/dist/state/identity-manager.js +0 -4
  33. package/packages/server/dist/state/role-resolver.d.ts +1 -2
  34. package/packages/server/dist/state/role-resolver.d.ts.map +1 -1
  35. package/packages/server/dist/state/role-resolver.js +0 -4
  36. package/packages/server/dist/state/role-resolver.js.map +1 -1
  37. package/packages/server/dist/state/task-graph-manager.d.ts +12 -0
  38. package/packages/server/dist/state/task-graph-manager.d.ts.map +1 -1
  39. package/packages/server/dist/state/task-graph-manager.js +80 -4
  40. package/packages/server/dist/state/task-graph-manager.js.map +1 -1
  41. package/packages/server/dist/state/tool-chain-tracker.js +0 -4
  42. package/packages/server/dist/watcher/agent-watcher.js +0 -5
  43. package/packages/server/dist/watcher/claude/claude-paths.d.ts +18 -0
  44. package/packages/server/dist/watcher/claude/claude-paths.d.ts.map +1 -0
  45. package/packages/server/dist/watcher/{claude-paths.js → claude/claude-paths.js} +47 -59
  46. package/packages/server/dist/watcher/claude/claude-paths.js.map +1 -0
  47. package/packages/server/dist/watcher/{file-watcher.d.ts → claude/claude-watcher.d.ts} +3 -3
  48. package/packages/server/dist/watcher/claude/claude-watcher.d.ts.map +1 -0
  49. package/packages/server/dist/watcher/{file-watcher.js → claude/claude-watcher.js} +59 -69
  50. package/packages/server/dist/watcher/claude/claude-watcher.js.map +1 -0
  51. package/packages/server/dist/watcher/claude/jsonl-parser.d.ts +6 -0
  52. package/packages/server/dist/watcher/claude/jsonl-parser.d.ts.map +1 -0
  53. package/packages/server/dist/watcher/{jsonl-parser.js → claude/jsonl-parser.js} +1 -5
  54. package/packages/server/dist/watcher/claude/jsonl-parser.js.map +1 -0
  55. package/packages/server/dist/watcher/codex/codex-parser.d.ts +30 -0
  56. package/packages/server/dist/watcher/codex/codex-parser.d.ts.map +1 -0
  57. package/packages/server/dist/watcher/codex/codex-parser.js +326 -0
  58. package/packages/server/dist/watcher/codex/codex-parser.js.map +1 -0
  59. package/packages/server/dist/watcher/codex/codex-paths.d.ts +35 -0
  60. package/packages/server/dist/watcher/codex/codex-paths.d.ts.map +1 -0
  61. package/packages/server/dist/watcher/codex/codex-paths.js +46 -0
  62. package/packages/server/dist/watcher/codex/codex-paths.js.map +1 -0
  63. package/packages/server/dist/watcher/codex/codex-watcher.d.ts +42 -0
  64. package/packages/server/dist/watcher/codex/codex-watcher.d.ts.map +1 -0
  65. package/packages/server/dist/watcher/codex/codex-watcher.js +577 -0
  66. package/packages/server/dist/watcher/codex/codex-watcher.js.map +1 -0
  67. package/packages/server/dist/watcher/git-info.js +0 -4
  68. package/packages/server/dist/watcher/opencode/opencode-parser.d.ts +1 -1
  69. package/packages/server/dist/watcher/opencode/opencode-parser.d.ts.map +1 -1
  70. package/packages/server/dist/watcher/opencode/opencode-parser.js +31 -6
  71. package/packages/server/dist/watcher/opencode/opencode-paths.d.ts +1 -1
  72. package/packages/server/dist/watcher/opencode/opencode-paths.d.ts.map +1 -1
  73. package/packages/server/dist/watcher/opencode/opencode-paths.js +1 -4
  74. package/packages/server/dist/watcher/opencode/opencode-paths.js.map +1 -1
  75. package/packages/server/dist/watcher/opencode/opencode-watcher.d.ts.map +1 -1
  76. package/packages/server/dist/watcher/opencode/opencode-watcher.js +50 -789
  77. package/packages/server/dist/watcher/opencode/opencode-watcher.js.map +1 -1
  78. package/packages/server/dist/watcher/path-utils.d.ts +10 -0
  79. package/packages/server/dist/watcher/path-utils.d.ts.map +1 -0
  80. package/packages/server/dist/watcher/path-utils.js +38 -0
  81. package/packages/server/dist/watcher/path-utils.js.map +1 -0
  82. package/packages/server/dist/watcher/pi/pi-parser.d.ts +19 -0
  83. package/packages/server/dist/watcher/pi/pi-parser.d.ts.map +1 -0
  84. package/packages/server/dist/watcher/pi/pi-parser.js +307 -0
  85. package/packages/server/dist/watcher/pi/pi-parser.js.map +1 -0
  86. package/packages/server/dist/watcher/pi/pi-paths.d.ts +28 -0
  87. package/packages/server/dist/watcher/pi/pi-paths.d.ts.map +1 -0
  88. package/packages/server/dist/watcher/pi/pi-paths.js +86 -0
  89. package/packages/server/dist/watcher/pi/pi-paths.js.map +1 -0
  90. package/packages/server/dist/watcher/pi/pi-watcher.d.ts +36 -0
  91. package/packages/server/dist/watcher/pi/pi-watcher.d.ts.map +1 -0
  92. package/packages/server/dist/watcher/pi/pi-watcher.js +593 -0
  93. package/packages/server/dist/watcher/pi/pi-watcher.js.map +1 -0
  94. package/packages/server/dist/watcher/session-scanner.d.ts +9 -3
  95. package/packages/server/dist/watcher/session-scanner.d.ts.map +1 -1
  96. package/packages/server/dist/watcher/session-scanner.js +11 -13
  97. package/packages/server/dist/watcher/session-scanner.js.map +1 -1
  98. package/packages/server/dist/watcher/types.d.ts +30 -0
  99. package/packages/server/dist/watcher/types.d.ts.map +1 -0
  100. package/packages/server/dist/watcher/types.js +14 -0
  101. package/packages/server/dist/watcher/types.js.map +1 -0
  102. package/packages/server/dist/ws/broadcaster.js +0 -4
  103. package/packages/server/dist/ws/ws-handler.js +0 -4
  104. package/packages/shared/dist/constants/colors.d.ts +1 -1
  105. package/packages/shared/dist/constants/colors.js +1 -1
  106. package/packages/shared/dist/constants/colors.js.map +1 -1
  107. package/packages/shared/dist/constants/tools.d.ts.map +1 -1
  108. package/packages/shared/dist/constants/tools.js +30 -1
  109. package/packages/shared/dist/constants/tools.js.map +1 -1
  110. package/packages/shared/dist/index.d.ts +1 -1
  111. package/packages/shared/dist/index.d.ts.map +1 -1
  112. package/packages/shared/dist/types/agent.d.ts +3 -0
  113. package/packages/shared/dist/types/agent.d.ts.map +1 -1
  114. package/packages/client/dist/assets/index-Dh8yWoLP.js +0 -711
  115. package/packages/server/dist/watcher/claude-paths.d.ts +0 -32
  116. package/packages/server/dist/watcher/claude-paths.d.ts.map +0 -1
  117. package/packages/server/dist/watcher/claude-paths.js.map +0 -1
  118. package/packages/server/dist/watcher/file-watcher.d.ts.map +0 -1
  119. package/packages/server/dist/watcher/file-watcher.js.map +0 -1
  120. package/packages/server/dist/watcher/jsonl-parser.d.ts +0 -21
  121. package/packages/server/dist/watcher/jsonl-parser.d.ts.map +0 -1
  122. package/packages/server/dist/watcher/jsonl-parser.js.map +0 -1
@@ -1,9 +1,6 @@
1
- import { createRequire } from 'module'; const require = createRequire(import.meta.url);
2
-
3
1
  // dist/index.js
4
- import { createRequire } from "module";
5
2
  import { fileURLToPath } from "url";
6
- import { dirname, join as join6 } from "path";
3
+ import { dirname as dirname2, join as join10 } from "path";
7
4
  import Fastify from "fastify";
8
5
  import cors from "@fastify/cors";
9
6
  import websocket from "@fastify/websocket";
@@ -13,793 +10,32 @@ import { join } from "path";
13
10
  import chokidar from "chokidar";
14
11
  import { stat as stat2, open } from "fs/promises";
15
12
  import { join as join4, basename } from "path";
16
- import { existsSync } from "fs";
17
13
  import { join as join2 } from "path";
14
+ import { existsSync } from "fs";
18
15
  import { readdir, stat } from "fs/promises";
19
16
  import { join as join3 } from "path";
20
17
  import chokidar2 from "chokidar";
18
+ import Database from "better-sqlite3";
21
19
  import { homedir as homedir2 } from "os";
22
20
  import { join as join5 } from "path";
23
21
  import { existsSync as existsSync2 } from "fs";
22
+ import chokidar3 from "chokidar";
23
+ import { stat as stat3, open as open2 } from "fs/promises";
24
+ import { join as join7, basename as basename2, dirname } from "path";
25
+ import { homedir as homedir3 } from "os";
26
+ import { join as join6 } from "path";
27
+ import { existsSync as existsSync3 } from "fs";
28
+ import chokidar4 from "chokidar";
29
+ import { stat as stat4, open as open3, readdir as readdir2 } from "fs/promises";
30
+ import { join as join9, basename as basename4 } from "path";
31
+ import { homedir as homedir4 } from "os";
32
+ import { join as join8, basename as basename3 } from "path";
33
+ import { existsSync as existsSync4 } from "fs";
24
34
  import { EventEmitter as EventEmitter2 } from "events";
25
35
  import { EventEmitter } from "events";
26
36
  import { execSync } from "child_process";
27
37
  import { EventEmitter as EventEmitter3 } from "events";
28
38
  import { randomUUID } from "crypto";
29
- var require2 = createRequire(import.meta.url);
30
- var __create = Object.create;
31
- var __defProp = Object.defineProperty;
32
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
33
- var __getOwnPropNames = Object.getOwnPropertyNames;
34
- var __getProtoOf = Object.getPrototypeOf;
35
- var __hasOwnProp = Object.prototype.hasOwnProperty;
36
- var __require = /* @__PURE__ */ ((x) => typeof require2 !== "undefined" ? require2 : typeof Proxy !== "undefined" ? new Proxy(x, {
37
- get: (a, b) => (typeof require2 !== "undefined" ? require2 : a)[b]
38
- }) : x)(function(x) {
39
- if (typeof require2 !== "undefined") return require2.apply(this, arguments);
40
- throw Error('Dynamic require of "' + x + '" is not supported');
41
- });
42
- var __commonJS = (cb, mod) => function __require2() {
43
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
44
- };
45
- var __copyProps = (to, from, except, desc) => {
46
- if (from && typeof from === "object" || typeof from === "function") {
47
- for (let key of __getOwnPropNames(from))
48
- if (!__hasOwnProp.call(to, key) && key !== except)
49
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
50
- }
51
- return to;
52
- };
53
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
54
- // If the importer is in node compatibility mode or this is not an ESM
55
- // file that has been converted to a CommonJS file using a Babel-
56
- // compatible transform (i.e. "__esModule" has not been set), then set
57
- // "default" to the CommonJS "module.exports" for node compatibility.
58
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
59
- mod
60
- ));
61
- var require_util = __commonJS({
62
- "../../node_modules/better-sqlite3/lib/util.js"(exports) {
63
- "use strict";
64
- exports.getBooleanOption = (options, key) => {
65
- let value = false;
66
- if (key in options && typeof (value = options[key]) !== "boolean") {
67
- throw new TypeError(`Expected the "${key}" option to be a boolean`);
68
- }
69
- return value;
70
- };
71
- exports.cppdb = /* @__PURE__ */ Symbol();
72
- exports.inspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
73
- }
74
- });
75
- var require_sqlite_error = __commonJS({
76
- "../../node_modules/better-sqlite3/lib/sqlite-error.js"(exports, module) {
77
- "use strict";
78
- var descriptor = { value: "SqliteError", writable: true, enumerable: false, configurable: true };
79
- function SqliteError(message, code) {
80
- if (new.target !== SqliteError) {
81
- return new SqliteError(message, code);
82
- }
83
- if (typeof code !== "string") {
84
- throw new TypeError("Expected second argument to be a string");
85
- }
86
- Error.call(this, message);
87
- descriptor.value = "" + message;
88
- Object.defineProperty(this, "message", descriptor);
89
- Error.captureStackTrace(this, SqliteError);
90
- this.code = code;
91
- }
92
- Object.setPrototypeOf(SqliteError, Error);
93
- Object.setPrototypeOf(SqliteError.prototype, Error.prototype);
94
- Object.defineProperty(SqliteError.prototype, "name", descriptor);
95
- module.exports = SqliteError;
96
- }
97
- });
98
- var require_file_uri_to_path = __commonJS({
99
- "../../node_modules/file-uri-to-path/index.js"(exports, module) {
100
- var sep = __require("path").sep || "/";
101
- module.exports = fileUriToPath;
102
- function fileUriToPath(uri) {
103
- if ("string" != typeof uri || uri.length <= 7 || "file://" != uri.substring(0, 7)) {
104
- throw new TypeError("must pass in a file:// URI to convert to a file path");
105
- }
106
- var rest = decodeURI(uri.substring(7));
107
- var firstSlash = rest.indexOf("/");
108
- var host = rest.substring(0, firstSlash);
109
- var path = rest.substring(firstSlash + 1);
110
- if ("localhost" == host) host = "";
111
- if (host) {
112
- host = sep + sep + host;
113
- }
114
- path = path.replace(/^(.+)\|/, "$1:");
115
- if (sep == "\\") {
116
- path = path.replace(/\//g, "\\");
117
- }
118
- if (/^.+\:/.test(path)) {
119
- } else {
120
- path = sep + path;
121
- }
122
- return host + path;
123
- }
124
- }
125
- });
126
- var require_bindings = __commonJS({
127
- "../../node_modules/bindings/bindings.js"(exports, module) {
128
- var fs = __require("fs");
129
- var path = __require("path");
130
- var fileURLToPath2 = require_file_uri_to_path();
131
- var join7 = path.join;
132
- var dirname2 = path.dirname;
133
- var exists = fs.accessSync && function(path2) {
134
- try {
135
- fs.accessSync(path2);
136
- } catch (e) {
137
- return false;
138
- }
139
- return true;
140
- } || fs.existsSync || path.existsSync;
141
- var defaults = {
142
- arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
143
- compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
144
- platform: process.platform,
145
- arch: process.arch,
146
- nodePreGyp: "node-v" + process.versions.modules + "-" + process.platform + "-" + process.arch,
147
- version: process.versions.node,
148
- bindings: "bindings.node",
149
- try: [
150
- // node-gyp's linked version in the "build" dir
151
- ["module_root", "build", "bindings"],
152
- // node-waf and gyp_addon (a.k.a node-gyp)
153
- ["module_root", "build", "Debug", "bindings"],
154
- ["module_root", "build", "Release", "bindings"],
155
- // Debug files, for development (legacy behavior, remove for node v0.9)
156
- ["module_root", "out", "Debug", "bindings"],
157
- ["module_root", "Debug", "bindings"],
158
- // Release files, but manually compiled (legacy behavior, remove for node v0.9)
159
- ["module_root", "out", "Release", "bindings"],
160
- ["module_root", "Release", "bindings"],
161
- // Legacy from node-waf, node <= 0.4.x
162
- ["module_root", "build", "default", "bindings"],
163
- // Production "Release" buildtype binary (meh...)
164
- ["module_root", "compiled", "version", "platform", "arch", "bindings"],
165
- // node-qbs builds
166
- ["module_root", "addon-build", "release", "install-root", "bindings"],
167
- ["module_root", "addon-build", "debug", "install-root", "bindings"],
168
- ["module_root", "addon-build", "default", "install-root", "bindings"],
169
- // node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}
170
- ["module_root", "lib", "binding", "nodePreGyp", "bindings"]
171
- ]
172
- };
173
- function bindings(opts) {
174
- if (typeof opts == "string") {
175
- opts = { bindings: opts };
176
- } else if (!opts) {
177
- opts = {};
178
- }
179
- Object.keys(defaults).map(function(i2) {
180
- if (!(i2 in opts)) opts[i2] = defaults[i2];
181
- });
182
- if (!opts.module_root) {
183
- opts.module_root = exports.getRoot(exports.getFileName());
184
- }
185
- if (path.extname(opts.bindings) != ".node") {
186
- opts.bindings += ".node";
187
- }
188
- var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
189
- var tries = [], i = 0, l = opts.try.length, n, b, err;
190
- for (; i < l; i++) {
191
- n = join7.apply(
192
- null,
193
- opts.try[i].map(function(p) {
194
- return opts[p] || p;
195
- })
196
- );
197
- tries.push(n);
198
- try {
199
- b = opts.path ? requireFunc.resolve(n) : requireFunc(n);
200
- if (!opts.path) {
201
- b.path = n;
202
- }
203
- return b;
204
- } catch (e) {
205
- if (e.code !== "MODULE_NOT_FOUND" && e.code !== "QUALIFIED_PATH_RESOLUTION_FAILED" && !/not find/i.test(e.message)) {
206
- throw e;
207
- }
208
- }
209
- }
210
- err = new Error(
211
- "Could not locate the bindings file. Tried:\n" + tries.map(function(a) {
212
- return opts.arrow + a;
213
- }).join("\n")
214
- );
215
- err.tries = tries;
216
- throw err;
217
- }
218
- module.exports = exports = bindings;
219
- exports.getFileName = function getFileName(calling_file) {
220
- var origPST = Error.prepareStackTrace, origSTL = Error.stackTraceLimit, dummy = {}, fileName;
221
- Error.stackTraceLimit = 10;
222
- Error.prepareStackTrace = function(e, st) {
223
- for (var i = 0, l = st.length; i < l; i++) {
224
- fileName = st[i].getFileName();
225
- if (fileName !== __filename) {
226
- if (calling_file) {
227
- if (fileName !== calling_file) {
228
- return;
229
- }
230
- } else {
231
- return;
232
- }
233
- }
234
- }
235
- };
236
- Error.captureStackTrace(dummy);
237
- dummy.stack;
238
- Error.prepareStackTrace = origPST;
239
- Error.stackTraceLimit = origSTL;
240
- var fileSchema = "file://";
241
- if (fileName.indexOf(fileSchema) === 0) {
242
- fileName = fileURLToPath2(fileName);
243
- }
244
- return fileName;
245
- };
246
- exports.getRoot = function getRoot(file) {
247
- var dir = dirname2(file), prev;
248
- while (true) {
249
- if (dir === ".") {
250
- dir = process.cwd();
251
- }
252
- if (exists(join7(dir, "package.json")) || exists(join7(dir, "node_modules"))) {
253
- return dir;
254
- }
255
- if (prev === dir) {
256
- throw new Error(
257
- 'Could not find module root given file: "' + file + '". Do you have a `package.json` file? '
258
- );
259
- }
260
- prev = dir;
261
- dir = join7(dir, "..");
262
- }
263
- };
264
- }
265
- });
266
- var require_wrappers = __commonJS({
267
- "../../node_modules/better-sqlite3/lib/methods/wrappers.js"(exports) {
268
- "use strict";
269
- var { cppdb } = require_util();
270
- exports.prepare = function prepare(sql) {
271
- return this[cppdb].prepare(sql, this, false);
272
- };
273
- exports.exec = function exec(sql) {
274
- this[cppdb].exec(sql);
275
- return this;
276
- };
277
- exports.close = function close() {
278
- this[cppdb].close();
279
- return this;
280
- };
281
- exports.loadExtension = function loadExtension(...args) {
282
- this[cppdb].loadExtension(...args);
283
- return this;
284
- };
285
- exports.defaultSafeIntegers = function defaultSafeIntegers(...args) {
286
- this[cppdb].defaultSafeIntegers(...args);
287
- return this;
288
- };
289
- exports.unsafeMode = function unsafeMode(...args) {
290
- this[cppdb].unsafeMode(...args);
291
- return this;
292
- };
293
- exports.getters = {
294
- name: {
295
- get: function name() {
296
- return this[cppdb].name;
297
- },
298
- enumerable: true
299
- },
300
- open: {
301
- get: function open2() {
302
- return this[cppdb].open;
303
- },
304
- enumerable: true
305
- },
306
- inTransaction: {
307
- get: function inTransaction() {
308
- return this[cppdb].inTransaction;
309
- },
310
- enumerable: true
311
- },
312
- readonly: {
313
- get: function readonly() {
314
- return this[cppdb].readonly;
315
- },
316
- enumerable: true
317
- },
318
- memory: {
319
- get: function memory() {
320
- return this[cppdb].memory;
321
- },
322
- enumerable: true
323
- }
324
- };
325
- }
326
- });
327
- var require_transaction = __commonJS({
328
- "../../node_modules/better-sqlite3/lib/methods/transaction.js"(exports, module) {
329
- "use strict";
330
- var { cppdb } = require_util();
331
- var controllers = /* @__PURE__ */ new WeakMap();
332
- module.exports = function transaction(fn) {
333
- if (typeof fn !== "function") throw new TypeError("Expected first argument to be a function");
334
- const db = this[cppdb];
335
- const controller = getController(db, this);
336
- const { apply } = Function.prototype;
337
- const properties = {
338
- default: { value: wrapTransaction(apply, fn, db, controller.default) },
339
- deferred: { value: wrapTransaction(apply, fn, db, controller.deferred) },
340
- immediate: { value: wrapTransaction(apply, fn, db, controller.immediate) },
341
- exclusive: { value: wrapTransaction(apply, fn, db, controller.exclusive) },
342
- database: { value: this, enumerable: true }
343
- };
344
- Object.defineProperties(properties.default.value, properties);
345
- Object.defineProperties(properties.deferred.value, properties);
346
- Object.defineProperties(properties.immediate.value, properties);
347
- Object.defineProperties(properties.exclusive.value, properties);
348
- return properties.default.value;
349
- };
350
- var getController = (db, self) => {
351
- let controller = controllers.get(db);
352
- if (!controller) {
353
- const shared = {
354
- commit: db.prepare("COMMIT", self, false),
355
- rollback: db.prepare("ROLLBACK", self, false),
356
- savepoint: db.prepare("SAVEPOINT ` _bs3. `", self, false),
357
- release: db.prepare("RELEASE ` _bs3. `", self, false),
358
- rollbackTo: db.prepare("ROLLBACK TO ` _bs3. `", self, false)
359
- };
360
- controllers.set(db, controller = {
361
- default: Object.assign({ begin: db.prepare("BEGIN", self, false) }, shared),
362
- deferred: Object.assign({ begin: db.prepare("BEGIN DEFERRED", self, false) }, shared),
363
- immediate: Object.assign({ begin: db.prepare("BEGIN IMMEDIATE", self, false) }, shared),
364
- exclusive: Object.assign({ begin: db.prepare("BEGIN EXCLUSIVE", self, false) }, shared)
365
- });
366
- }
367
- return controller;
368
- };
369
- var wrapTransaction = (apply, fn, db, { begin, commit, rollback, savepoint, release, rollbackTo }) => function sqliteTransaction() {
370
- let before, after, undo;
371
- if (db.inTransaction) {
372
- before = savepoint;
373
- after = release;
374
- undo = rollbackTo;
375
- } else {
376
- before = begin;
377
- after = commit;
378
- undo = rollback;
379
- }
380
- before.run();
381
- try {
382
- const result = apply.call(fn, this, arguments);
383
- if (result && typeof result.then === "function") {
384
- throw new TypeError("Transaction function cannot return a promise");
385
- }
386
- after.run();
387
- return result;
388
- } catch (ex) {
389
- if (db.inTransaction) {
390
- undo.run();
391
- if (undo !== rollback) after.run();
392
- }
393
- throw ex;
394
- }
395
- };
396
- }
397
- });
398
- var require_pragma = __commonJS({
399
- "../../node_modules/better-sqlite3/lib/methods/pragma.js"(exports, module) {
400
- "use strict";
401
- var { getBooleanOption, cppdb } = require_util();
402
- module.exports = function pragma(source, options) {
403
- if (options == null) options = {};
404
- if (typeof source !== "string") throw new TypeError("Expected first argument to be a string");
405
- if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
406
- const simple = getBooleanOption(options, "simple");
407
- const stmt = this[cppdb].prepare(`PRAGMA ${source}`, this, true);
408
- return simple ? stmt.pluck().get() : stmt.all();
409
- };
410
- }
411
- });
412
- var require_backup = __commonJS({
413
- "../../node_modules/better-sqlite3/lib/methods/backup.js"(exports, module) {
414
- "use strict";
415
- var fs = __require("fs");
416
- var path = __require("path");
417
- var { promisify } = __require("util");
418
- var { cppdb } = require_util();
419
- var fsAccess = promisify(fs.access);
420
- module.exports = async function backup(filename, options) {
421
- if (options == null) options = {};
422
- if (typeof filename !== "string") throw new TypeError("Expected first argument to be a string");
423
- if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
424
- filename = filename.trim();
425
- const attachedName = "attached" in options ? options.attached : "main";
426
- const handler = "progress" in options ? options.progress : null;
427
- if (!filename) throw new TypeError("Backup filename cannot be an empty string");
428
- if (filename === ":memory:") throw new TypeError('Invalid backup filename ":memory:"');
429
- if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
430
- if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
431
- if (handler != null && typeof handler !== "function") throw new TypeError('Expected the "progress" option to be a function');
432
- await fsAccess(path.dirname(filename)).catch(() => {
433
- throw new TypeError("Cannot save backup because the directory does not exist");
434
- });
435
- const isNewFile = await fsAccess(filename).then(() => false, () => true);
436
- return runBackup(this[cppdb].backup(this, attachedName, filename, isNewFile), handler || null);
437
- };
438
- var runBackup = (backup, handler) => {
439
- let rate = 0;
440
- let useDefault = true;
441
- return new Promise((resolve, reject) => {
442
- setImmediate(function step() {
443
- try {
444
- const progress = backup.transfer(rate);
445
- if (!progress.remainingPages) {
446
- backup.close();
447
- resolve(progress);
448
- return;
449
- }
450
- if (useDefault) {
451
- useDefault = false;
452
- rate = 100;
453
- }
454
- if (handler) {
455
- const ret = handler(progress);
456
- if (ret !== void 0) {
457
- if (typeof ret === "number" && ret === ret) rate = Math.max(0, Math.min(2147483647, Math.round(ret)));
458
- else throw new TypeError("Expected progress callback to return a number or undefined");
459
- }
460
- }
461
- setImmediate(step);
462
- } catch (err) {
463
- backup.close();
464
- reject(err);
465
- }
466
- });
467
- });
468
- };
469
- }
470
- });
471
- var require_serialize = __commonJS({
472
- "../../node_modules/better-sqlite3/lib/methods/serialize.js"(exports, module) {
473
- "use strict";
474
- var { cppdb } = require_util();
475
- module.exports = function serialize(options) {
476
- if (options == null) options = {};
477
- if (typeof options !== "object") throw new TypeError("Expected first argument to be an options object");
478
- const attachedName = "attached" in options ? options.attached : "main";
479
- if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
480
- if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
481
- return this[cppdb].serialize(attachedName);
482
- };
483
- }
484
- });
485
- var require_function = __commonJS({
486
- "../../node_modules/better-sqlite3/lib/methods/function.js"(exports, module) {
487
- "use strict";
488
- var { getBooleanOption, cppdb } = require_util();
489
- module.exports = function defineFunction(name, options, fn) {
490
- if (options == null) options = {};
491
- if (typeof options === "function") {
492
- fn = options;
493
- options = {};
494
- }
495
- if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
496
- if (typeof fn !== "function") throw new TypeError("Expected last argument to be a function");
497
- if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
498
- if (!name) throw new TypeError("User-defined function name cannot be an empty string");
499
- const safeIntegers = "safeIntegers" in options ? +getBooleanOption(options, "safeIntegers") : 2;
500
- const deterministic = getBooleanOption(options, "deterministic");
501
- const directOnly = getBooleanOption(options, "directOnly");
502
- const varargs = getBooleanOption(options, "varargs");
503
- let argCount = -1;
504
- if (!varargs) {
505
- argCount = fn.length;
506
- if (!Number.isInteger(argCount) || argCount < 0) throw new TypeError("Expected function.length to be a positive integer");
507
- if (argCount > 100) throw new RangeError("User-defined functions cannot have more than 100 arguments");
508
- }
509
- this[cppdb].function(fn, name, argCount, safeIntegers, deterministic, directOnly);
510
- return this;
511
- };
512
- }
513
- });
514
- var require_aggregate = __commonJS({
515
- "../../node_modules/better-sqlite3/lib/methods/aggregate.js"(exports, module) {
516
- "use strict";
517
- var { getBooleanOption, cppdb } = require_util();
518
- module.exports = function defineAggregate(name, options) {
519
- if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
520
- if (typeof options !== "object" || options === null) throw new TypeError("Expected second argument to be an options object");
521
- if (!name) throw new TypeError("User-defined function name cannot be an empty string");
522
- const start = "start" in options ? options.start : null;
523
- const step = getFunctionOption(options, "step", true);
524
- const inverse = getFunctionOption(options, "inverse", false);
525
- const result = getFunctionOption(options, "result", false);
526
- const safeIntegers = "safeIntegers" in options ? +getBooleanOption(options, "safeIntegers") : 2;
527
- const deterministic = getBooleanOption(options, "deterministic");
528
- const directOnly = getBooleanOption(options, "directOnly");
529
- const varargs = getBooleanOption(options, "varargs");
530
- let argCount = -1;
531
- if (!varargs) {
532
- argCount = Math.max(getLength(step), inverse ? getLength(inverse) : 0);
533
- if (argCount > 0) argCount -= 1;
534
- if (argCount > 100) throw new RangeError("User-defined functions cannot have more than 100 arguments");
535
- }
536
- this[cppdb].aggregate(start, step, inverse, result, name, argCount, safeIntegers, deterministic, directOnly);
537
- return this;
538
- };
539
- var getFunctionOption = (options, key, required) => {
540
- const value = key in options ? options[key] : null;
541
- if (typeof value === "function") return value;
542
- if (value != null) throw new TypeError(`Expected the "${key}" option to be a function`);
543
- if (required) throw new TypeError(`Missing required option "${key}"`);
544
- return null;
545
- };
546
- var getLength = ({ length }) => {
547
- if (Number.isInteger(length) && length >= 0) return length;
548
- throw new TypeError("Expected function.length to be a positive integer");
549
- };
550
- }
551
- });
552
- var require_table = __commonJS({
553
- "../../node_modules/better-sqlite3/lib/methods/table.js"(exports, module) {
554
- "use strict";
555
- var { cppdb } = require_util();
556
- module.exports = function defineTable(name, factory) {
557
- if (typeof name !== "string") throw new TypeError("Expected first argument to be a string");
558
- if (!name) throw new TypeError("Virtual table module name cannot be an empty string");
559
- let eponymous = false;
560
- if (typeof factory === "object" && factory !== null) {
561
- eponymous = true;
562
- factory = defer(parseTableDefinition(factory, "used", name));
563
- } else {
564
- if (typeof factory !== "function") throw new TypeError("Expected second argument to be a function or a table definition object");
565
- factory = wrapFactory(factory);
566
- }
567
- this[cppdb].table(factory, name, eponymous);
568
- return this;
569
- };
570
- function wrapFactory(factory) {
571
- return function virtualTableFactory(moduleName, databaseName, tableName, ...args) {
572
- const thisObject = {
573
- module: moduleName,
574
- database: databaseName,
575
- table: tableName
576
- };
577
- const def = apply.call(factory, thisObject, args);
578
- if (typeof def !== "object" || def === null) {
579
- throw new TypeError(`Virtual table module "${moduleName}" did not return a table definition object`);
580
- }
581
- return parseTableDefinition(def, "returned", moduleName);
582
- };
583
- }
584
- function parseTableDefinition(def, verb, moduleName) {
585
- if (!hasOwnProperty.call(def, "rows")) {
586
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "rows" property`);
587
- }
588
- if (!hasOwnProperty.call(def, "columns")) {
589
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition without a "columns" property`);
590
- }
591
- const rows = def.rows;
592
- if (typeof rows !== "function" || Object.getPrototypeOf(rows) !== GeneratorFunctionPrototype) {
593
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "rows" property (should be a generator function)`);
594
- }
595
- let columns = def.columns;
596
- if (!Array.isArray(columns) || !(columns = [...columns]).every((x) => typeof x === "string")) {
597
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "columns" property (should be an array of strings)`);
598
- }
599
- if (columns.length !== new Set(columns).size) {
600
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate column names`);
601
- }
602
- if (!columns.length) {
603
- throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with zero columns`);
604
- }
605
- let parameters;
606
- if (hasOwnProperty.call(def, "parameters")) {
607
- parameters = def.parameters;
608
- if (!Array.isArray(parameters) || !(parameters = [...parameters]).every((x) => typeof x === "string")) {
609
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "parameters" property (should be an array of strings)`);
610
- }
611
- } else {
612
- parameters = inferParameters(rows);
613
- }
614
- if (parameters.length !== new Set(parameters).size) {
615
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with duplicate parameter names`);
616
- }
617
- if (parameters.length > 32) {
618
- throw new RangeError(`Virtual table module "${moduleName}" ${verb} a table definition with more than the maximum number of 32 parameters`);
619
- }
620
- for (const parameter of parameters) {
621
- if (columns.includes(parameter)) {
622
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with column "${parameter}" which was ambiguously defined as both a column and parameter`);
623
- }
624
- }
625
- let safeIntegers = 2;
626
- if (hasOwnProperty.call(def, "safeIntegers")) {
627
- const bool = def.safeIntegers;
628
- if (typeof bool !== "boolean") {
629
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "safeIntegers" property (should be a boolean)`);
630
- }
631
- safeIntegers = +bool;
632
- }
633
- let directOnly = false;
634
- if (hasOwnProperty.call(def, "directOnly")) {
635
- directOnly = def.directOnly;
636
- if (typeof directOnly !== "boolean") {
637
- throw new TypeError(`Virtual table module "${moduleName}" ${verb} a table definition with an invalid "directOnly" property (should be a boolean)`);
638
- }
639
- }
640
- const columnDefinitions = [
641
- ...parameters.map(identifier).map((str) => `${str} HIDDEN`),
642
- ...columns.map(identifier)
643
- ];
644
- return [
645
- `CREATE TABLE x(${columnDefinitions.join(", ")});`,
646
- wrapGenerator(rows, new Map(columns.map((x, i) => [x, parameters.length + i])), moduleName),
647
- parameters,
648
- safeIntegers,
649
- directOnly
650
- ];
651
- }
652
- function wrapGenerator(generator, columnMap, moduleName) {
653
- return function* virtualTable(...args) {
654
- const output = args.map((x) => Buffer.isBuffer(x) ? Buffer.from(x) : x);
655
- for (let i = 0; i < columnMap.size; ++i) {
656
- output.push(null);
657
- }
658
- for (const row of generator(...args)) {
659
- if (Array.isArray(row)) {
660
- extractRowArray(row, output, columnMap.size, moduleName);
661
- yield output;
662
- } else if (typeof row === "object" && row !== null) {
663
- extractRowObject(row, output, columnMap, moduleName);
664
- yield output;
665
- } else {
666
- throw new TypeError(`Virtual table module "${moduleName}" yielded something that isn't a valid row object`);
667
- }
668
- }
669
- };
670
- }
671
- function extractRowArray(row, output, columnCount, moduleName) {
672
- if (row.length !== columnCount) {
673
- throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an incorrect number of columns`);
674
- }
675
- const offset = output.length - columnCount;
676
- for (let i = 0; i < columnCount; ++i) {
677
- output[i + offset] = row[i];
678
- }
679
- }
680
- function extractRowObject(row, output, columnMap, moduleName) {
681
- let count = 0;
682
- for (const key of Object.keys(row)) {
683
- const index = columnMap.get(key);
684
- if (index === void 0) {
685
- throw new TypeError(`Virtual table module "${moduleName}" yielded a row with an undeclared column "${key}"`);
686
- }
687
- output[index] = row[key];
688
- count += 1;
689
- }
690
- if (count !== columnMap.size) {
691
- throw new TypeError(`Virtual table module "${moduleName}" yielded a row with missing columns`);
692
- }
693
- }
694
- function inferParameters({ length }) {
695
- if (!Number.isInteger(length) || length < 0) {
696
- throw new TypeError("Expected function.length to be a positive integer");
697
- }
698
- const params = [];
699
- for (let i = 0; i < length; ++i) {
700
- params.push(`$${i + 1}`);
701
- }
702
- return params;
703
- }
704
- var { hasOwnProperty } = Object.prototype;
705
- var { apply } = Function.prototype;
706
- var GeneratorFunctionPrototype = Object.getPrototypeOf(function* () {
707
- });
708
- var identifier = (str) => `"${str.replace(/"/g, '""')}"`;
709
- var defer = (x) => () => x;
710
- }
711
- });
712
- var require_inspect = __commonJS({
713
- "../../node_modules/better-sqlite3/lib/methods/inspect.js"(exports, module) {
714
- "use strict";
715
- var DatabaseInspection = function Database2() {
716
- };
717
- module.exports = function inspect(depth, opts) {
718
- return Object.assign(new DatabaseInspection(), this);
719
- };
720
- }
721
- });
722
- var require_database = __commonJS({
723
- "../../node_modules/better-sqlite3/lib/database.js"(exports, module) {
724
- "use strict";
725
- var fs = __require("fs");
726
- var path = __require("path");
727
- var util = require_util();
728
- var SqliteError = require_sqlite_error();
729
- var DEFAULT_ADDON;
730
- function Database2(filenameGiven, options) {
731
- if (new.target == null) {
732
- return new Database2(filenameGiven, options);
733
- }
734
- let buffer;
735
- if (Buffer.isBuffer(filenameGiven)) {
736
- buffer = filenameGiven;
737
- filenameGiven = ":memory:";
738
- }
739
- if (filenameGiven == null) filenameGiven = "";
740
- if (options == null) options = {};
741
- if (typeof filenameGiven !== "string") throw new TypeError("Expected first argument to be a string");
742
- if (typeof options !== "object") throw new TypeError("Expected second argument to be an options object");
743
- if ("readOnly" in options) throw new TypeError('Misspelled option "readOnly" should be "readonly"');
744
- if ("memory" in options) throw new TypeError('Option "memory" was removed in v7.0.0 (use ":memory:" filename instead)');
745
- const filename = filenameGiven.trim();
746
- const anonymous = filename === "" || filename === ":memory:";
747
- const readonly = util.getBooleanOption(options, "readonly");
748
- const fileMustExist = util.getBooleanOption(options, "fileMustExist");
749
- const timeout = "timeout" in options ? options.timeout : 5e3;
750
- const verbose = "verbose" in options ? options.verbose : null;
751
- const nativeBinding = "nativeBinding" in options ? options.nativeBinding : null;
752
- if (readonly && anonymous && !buffer) throw new TypeError("In-memory/temporary databases cannot be readonly");
753
- if (!Number.isInteger(timeout) || timeout < 0) throw new TypeError('Expected the "timeout" option to be a positive integer');
754
- if (timeout > 2147483647) throw new RangeError('Option "timeout" cannot be greater than 2147483647');
755
- if (verbose != null && typeof verbose !== "function") throw new TypeError('Expected the "verbose" option to be a function');
756
- if (nativeBinding != null && typeof nativeBinding !== "string" && typeof nativeBinding !== "object") throw new TypeError('Expected the "nativeBinding" option to be a string or addon object');
757
- let addon;
758
- if (nativeBinding == null) {
759
- addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
760
- } else if (typeof nativeBinding === "string") {
761
- const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : __require;
762
- addon = requireFunc(path.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
763
- } else {
764
- addon = nativeBinding;
765
- }
766
- if (!addon.isInitialized) {
767
- addon.setErrorConstructor(SqliteError);
768
- addon.isInitialized = true;
769
- }
770
- if (!anonymous && !filename.startsWith("file:") && !fs.existsSync(path.dirname(filename))) {
771
- throw new TypeError("Cannot open database because the directory does not exist");
772
- }
773
- Object.defineProperties(this, {
774
- [util.cppdb]: { value: new addon.Database(filename, filenameGiven, anonymous, readonly, fileMustExist, timeout, verbose || null, buffer || null) },
775
- ...wrappers.getters
776
- });
777
- }
778
- var wrappers = require_wrappers();
779
- Database2.prototype.prepare = wrappers.prepare;
780
- Database2.prototype.transaction = require_transaction();
781
- Database2.prototype.pragma = require_pragma();
782
- Database2.prototype.backup = require_backup();
783
- Database2.prototype.serialize = require_serialize();
784
- Database2.prototype.function = require_function();
785
- Database2.prototype.aggregate = require_aggregate();
786
- Database2.prototype.table = require_table();
787
- Database2.prototype.loadExtension = wrappers.loadExtension;
788
- Database2.prototype.exec = wrappers.exec;
789
- Database2.prototype.close = wrappers.close;
790
- Database2.prototype.defaultSafeIntegers = wrappers.defaultSafeIntegers;
791
- Database2.prototype.unsafeMode = wrappers.unsafeMode;
792
- Database2.prototype[util.inspect] = require_inspect();
793
- module.exports = Database2;
794
- }
795
- });
796
- var require_lib = __commonJS({
797
- "../../node_modules/better-sqlite3/lib/index.js"(exports, module) {
798
- "use strict";
799
- module.exports = require_database();
800
- module.exports.SqliteError = require_sqlite_error();
801
- }
802
- });
803
39
  var config = {
804
40
  port: parseInt(process.env.AGENT_MOVE_PORT || "3333", 10),
805
41
  claudeHome: join(homedir(), ".claude"),
@@ -811,7 +47,11 @@ var config = {
811
47
  activeThresholdMs: 10 * 60 * 1e3,
812
48
  // 10 minutes
813
49
  /** Enable OpenCode session watching (auto-detected if storage dir exists) */
814
- enableOpenCode: process.env.AGENT_MOVE_OPENCODE !== "false"
50
+ enableOpenCode: process.env.AGENT_MOVE_OPENCODE !== "false",
51
+ /** Enable pi coding agent session watching (auto-detected if sessions dir exists) */
52
+ enablePi: process.env.AGENT_MOVE_PI !== "false",
53
+ /** Enable Codex CLI session watching (auto-detected if sessions dir exists) */
54
+ enableCodex: process.env.AGENT_MOVE_CODEX !== "false"
815
55
  };
816
56
  var JsonlParser = class {
817
57
  parseLine(line) {
@@ -904,6 +144,38 @@ var JsonlParser = class {
904
144
  return null;
905
145
  }
906
146
  };
147
+ function resolveEncodedPath(root, segments) {
148
+ try {
149
+ const parts = segments.split("-").filter(Boolean);
150
+ let currentPath = root;
151
+ let lastName = "";
152
+ let i = 0;
153
+ while (i < parts.length) {
154
+ let found = false;
155
+ const maxLen = Math.min(parts.length - i, 6);
156
+ for (let len = 1; len <= maxLen; len++) {
157
+ const segment = parts.slice(i, i + len).join("-");
158
+ for (const prefix of ["", "."]) {
159
+ const testPath = join2(currentPath, prefix + segment);
160
+ if (existsSync(testPath)) {
161
+ currentPath = testPath;
162
+ lastName = prefix + segment;
163
+ i += len;
164
+ found = true;
165
+ break;
166
+ }
167
+ }
168
+ if (found)
169
+ break;
170
+ }
171
+ if (!found)
172
+ break;
173
+ }
174
+ return lastName || null;
175
+ } catch {
176
+ return null;
177
+ }
178
+ }
907
179
  var ClaudePaths = class {
908
180
  /**
909
181
  * Parse a JSONL session file path to extract project info.
@@ -916,6 +188,7 @@ var ClaudePaths = class {
916
188
  const projectsIdx = parts.indexOf("projects");
917
189
  if (projectsIdx === -1 || projectsIdx + 1 >= parts.length) {
918
190
  return {
191
+ agentType: "claude",
919
192
  projectPath: "unknown",
920
193
  projectName: "Unknown",
921
194
  isSubagent: false,
@@ -929,6 +202,7 @@ var ClaudePaths = class {
929
202
  const isSubagent = depthAfterProject > 1;
930
203
  const parentSessionId = isSubagent ? parts[projectsIdx + 2] : null;
931
204
  return {
205
+ agentType: "claude",
932
206
  projectPath: encodedProjectName,
933
207
  projectName,
934
208
  isSubagent,
@@ -942,80 +216,37 @@ var ClaudePaths = class {
942
216
  * e.g., "C--projects-fts-temp-agent-move" → "agent-move"
943
217
  */
944
218
  decodeProjectName(encoded) {
945
- const resolved = this.resolveToFolderName(encoded);
946
- if (resolved)
947
- return resolved;
219
+ const driveMatch = encoded.match(/^([A-Za-z])--(.*)/);
220
+ const unixMatch = !driveMatch && encoded.match(/^-(.*)/);
221
+ if (driveMatch) {
222
+ const resolved = resolveEncodedPath(driveMatch[1] + ":/", driveMatch[2]);
223
+ if (resolved)
224
+ return resolved;
225
+ } else if (unixMatch) {
226
+ const resolved = resolveEncodedPath("/", unixMatch[1]);
227
+ if (resolved)
228
+ return resolved;
229
+ }
948
230
  const parts = encoded.split("-").filter((p) => p.length > 0);
949
231
  if (parts.length <= 2)
950
232
  return parts.join("/");
951
233
  return parts.slice(-2).join("/");
952
234
  }
953
- /**
954
- * Greedily resolve the encoded path against the filesystem.
955
- * Tries each dash-segment as a directory, joining multiple segments
956
- * when a single one doesn't exist (to handle dashes in folder names).
957
- */
958
- resolveToFolderName(encoded) {
959
- try {
960
- let root;
961
- let rest;
962
- const driveMatch = encoded.match(/^([A-Za-z])--(.*)/);
963
- const unixMatch = !driveMatch && encoded.match(/^-(.*)/);
964
- if (driveMatch) {
965
- root = driveMatch[1] + ":/";
966
- rest = driveMatch[2];
967
- } else if (unixMatch) {
968
- root = "/";
969
- rest = unixMatch[1];
970
- } else {
971
- return null;
972
- }
973
- const parts = rest.split("-").filter(Boolean);
974
- let currentPath = root;
975
- let lastName = "";
976
- let i = 0;
977
- while (i < parts.length) {
978
- let found = false;
979
- const maxLen = Math.min(parts.length - i, 6);
980
- for (let len = 1; len <= maxLen; len++) {
981
- const segment = parts.slice(i, i + len).join("-");
982
- for (const prefix of ["", "."]) {
983
- const testPath = join2(currentPath, prefix + segment);
984
- if (existsSync(testPath)) {
985
- currentPath = testPath;
986
- lastName = prefix + segment;
987
- i += len;
988
- found = true;
989
- break;
990
- }
991
- }
992
- if (found)
993
- break;
994
- }
995
- if (!found)
996
- break;
997
- }
998
- return lastName || null;
999
- } catch {
1000
- return null;
1001
- }
1002
- }
1003
235
  };
1004
236
  var claudePaths = new ClaudePaths();
1005
237
  var SessionScanner = class {
1006
- claudeHome;
1007
- constructor(claudeHome) {
1008
- this.claudeHome = claudeHome;
238
+ rootDir;
239
+ constructor(rootDir) {
240
+ this.rootDir = rootDir;
1009
241
  }
1010
- /** Find all recently active JSONL session files */
242
+ /** Find the most recently modified JSONL per project subdirectory */
1011
243
  async scan() {
1012
244
  const results = [];
1013
- const projectsDir = join3(this.claudeHome, "projects");
1014
245
  try {
1015
- const projects = await readdir(projectsDir);
246
+ const projects = await readdir(this.rootDir);
1016
247
  const now = Date.now();
1017
248
  for (const project of projects) {
1018
- const projectDir = join3(projectsDir, project);
249
+ const projectDir = join3(this.rootDir, project);
1019
250
  try {
1020
251
  const projectStat = await stat(projectDir);
1021
252
  if (!projectStat.isDirectory())
@@ -1045,7 +276,6 @@ var SessionScanner = class {
1045
276
  }
1046
277
  }
1047
278
  } catch {
1048
- console.log("No projects directory found \u2014 will wait for new sessions");
1049
279
  }
1050
280
  return results;
1051
281
  }
@@ -1063,7 +293,7 @@ var FileWatcher = class {
1063
293
  this.stateManager = stateManager;
1064
294
  }
1065
295
  async start() {
1066
- const scanner = new SessionScanner(this.claudeHome);
296
+ const scanner = new SessionScanner(join4(this.claudeHome, "projects"));
1067
297
  const existingFiles = await scanner.scan();
1068
298
  for (const file of existingFiles) {
1069
299
  await this.processFile(file);
@@ -1134,7 +364,16 @@ var FileWatcher = class {
1134
364
  }
1135
365
  }
1136
366
  };
1137
- var import_better_sqlite3 = __toESM(require_lib(), 1);
367
+ function createFallbackSession(agentType, name) {
368
+ return {
369
+ agentType,
370
+ projectPath: name,
371
+ projectName: name,
372
+ isSubagent: false,
373
+ projectDir: name,
374
+ parentSessionId: null
375
+ };
376
+ }
1138
377
  function getOpenCodeDbPath() {
1139
378
  const home = homedir2();
1140
379
  const candidates = [
@@ -1153,6 +392,7 @@ function parseOpenCodeSession(row) {
1153
392
  const segments = row.directory.replace(/\\/g, "/").split("/").filter(Boolean);
1154
393
  const projectName = segments[segments.length - 1] || "opencode";
1155
394
  return {
395
+ agentType: "opencode",
1156
396
  // Use the actual directory as projectPath so getGitBranch() gets a valid cwd.
1157
397
  // projectDir uses the project_id hash to group agents belonging to the same project.
1158
398
  projectPath: row.directory || row.project_id,
@@ -1212,7 +452,7 @@ function getZoneForTool(toolName) {
1212
452
  return TOOL_ZONE_MAP[toolName] ?? "thinking";
1213
453
  }
1214
454
  var TOOL_NAME_MAP = {
1215
- // OpenCode lowercase → canonical PascalCase
455
+ // OpenCode / pi lowercase → canonical PascalCase
1216
456
  read: "Read",
1217
457
  write: "Write",
1218
458
  edit: "Edit",
@@ -1223,7 +463,36 @@ var TOOL_NAME_MAP = {
1223
463
  websearch: "WebSearch",
1224
464
  webfetch: "WebFetch",
1225
465
  todoread: "TodoRead",
1226
- todowrite: "TodoWrite"
466
+ todowrite: "TodoWrite",
467
+ // pi-specific tool names
468
+ "edit-diff": "Patch",
469
+ find: "Glob",
470
+ ls: "Bash",
471
+ truncate: "Write",
472
+ // Codex CLI tool names
473
+ shell_command: "Bash",
474
+ exec_command: "Bash",
475
+ read_file: "Read",
476
+ apply_patch: "Patch",
477
+ list_dir: "Bash",
478
+ grep_files: "Grep",
479
+ web_search: "WebSearch",
480
+ js_repl: "Bash",
481
+ js_repl_reset: "Bash",
482
+ spawn_agent: "Agent",
483
+ send_input: "Agent",
484
+ wait: "Agent",
485
+ close_agent: "Agent",
486
+ resume_agent: "Agent",
487
+ spawn_agents_on_csv: "Agent",
488
+ report_agent_job_result: "Agent",
489
+ request_user_input: "AskUserQuestion",
490
+ request_permissions: "AskUserQuestion",
491
+ update_plan: "TodoWrite",
492
+ view_image: "Read",
493
+ image_generation: "Write",
494
+ write_stdin: "Bash",
495
+ search_apps: "WebSearch"
1227
496
  };
1228
497
  function normalizeToolName(name) {
1229
498
  return TOOL_NAME_MAP[name] ?? name;
@@ -1520,7 +789,7 @@ var OpenCodeWatcher = class {
1520
789
  }
1521
790
  console.log(`[opencode] Database found at ${dbPath}`);
1522
791
  try {
1523
- this.db = new import_better_sqlite3.default(dbPath, { readonly: true, fileMustExist: true });
792
+ this.db = new Database(dbPath, { readonly: true, fileMustExist: true });
1524
793
  this.prepareStatements();
1525
794
  } catch (err) {
1526
795
  console.error("[opencode] Failed to open database:", err);
@@ -1684,13 +953,597 @@ var OpenCodeWatcher = class {
1684
953
  return id.startsWith("oc:") ? id : `oc:${id}`;
1685
954
  }
1686
955
  fallbackSession() {
1687
- return {
1688
- projectPath: "opencode",
1689
- projectName: "opencode",
1690
- isSubagent: false,
1691
- projectDir: "opencode",
1692
- parentSessionId: null
956
+ return createFallbackSession("opencode", "opencode");
957
+ }
958
+ };
959
+ var PiParser = class {
960
+ /**
961
+ * Parse a single JSONL line from a pi session file into a raw object.
962
+ * Returns the parsed JSON, or null on parse error.
963
+ */
964
+ parseRaw(line) {
965
+ try {
966
+ return JSON.parse(line);
967
+ } catch {
968
+ return null;
969
+ }
970
+ }
971
+ /**
972
+ * Extract a ParsedActivity from a pre-parsed JSONL entry.
973
+ * Returns null for non-actionable entries (session header, user messages, etc.).
974
+ */
975
+ parseEntry(entry) {
976
+ if (entry.type !== "message")
977
+ return null;
978
+ const msg = entry.message;
979
+ if (!msg || msg.role !== "assistant")
980
+ return null;
981
+ return this.parseAssistantMessage(msg);
982
+ }
983
+ /**
984
+ * Check if a pre-parsed entry is a session header.
985
+ */
986
+ isSessionHeader(entry) {
987
+ return entry.type === "session";
988
+ }
989
+ parseAssistantMessage(msg) {
990
+ const content = msg.content;
991
+ if (!Array.isArray(content))
992
+ return null;
993
+ for (const block of content) {
994
+ if (block.type === "toolCall") {
995
+ const tool = block;
996
+ return {
997
+ type: "tool_use",
998
+ toolName: normalizeToolName(tool.name),
999
+ toolInput: normalizeToolInput(tool.arguments ?? {}),
1000
+ model: msg.model,
1001
+ inputTokens: msg.usage?.input,
1002
+ outputTokens: msg.usage?.output,
1003
+ cacheReadTokens: msg.usage?.cacheRead,
1004
+ cacheCreationTokens: msg.usage?.cacheWrite
1005
+ };
1006
+ }
1007
+ }
1008
+ for (const block of content) {
1009
+ if (block.type === "text") {
1010
+ const text = block.text?.trim() ?? "";
1011
+ if (text.length > 0 && text.length < 200) {
1012
+ return {
1013
+ type: "text",
1014
+ text,
1015
+ model: msg.model,
1016
+ inputTokens: msg.usage?.input,
1017
+ outputTokens: msg.usage?.output,
1018
+ cacheReadTokens: msg.usage?.cacheRead,
1019
+ cacheCreationTokens: msg.usage?.cacheWrite
1020
+ };
1021
+ }
1022
+ }
1023
+ }
1024
+ for (const block of content) {
1025
+ if (block.type === "thinking") {
1026
+ const thinking = block.thinking?.trim() ?? "";
1027
+ return {
1028
+ type: "tool_use",
1029
+ toolName: "thinking",
1030
+ toolInput: thinking.length > 0 ? { thought: thinking.slice(0, 120) } : void 0,
1031
+ model: msg.model,
1032
+ inputTokens: msg.usage?.input,
1033
+ outputTokens: msg.usage?.output,
1034
+ cacheReadTokens: msg.usage?.cacheRead,
1035
+ cacheCreationTokens: msg.usage?.cacheWrite
1036
+ };
1037
+ }
1038
+ }
1039
+ if (msg.usage && (msg.usage.input || msg.usage.output)) {
1040
+ return {
1041
+ type: "token_usage",
1042
+ inputTokens: msg.usage.input,
1043
+ outputTokens: msg.usage.output,
1044
+ cacheReadTokens: msg.usage.cacheRead,
1045
+ cacheCreationTokens: msg.usage.cacheWrite,
1046
+ model: msg.model
1047
+ };
1048
+ }
1049
+ return null;
1050
+ }
1051
+ };
1052
+ function getPiSessionsDir() {
1053
+ const candidate = join6(homedir3(), ".pi", "agent", "sessions");
1054
+ return existsSync3(candidate) ? candidate : null;
1055
+ }
1056
+ function decodePiProjectDir(encoded) {
1057
+ let inner = encoded;
1058
+ if (inner.startsWith("--") && inner.endsWith("--")) {
1059
+ inner = inner.slice(2, -2);
1060
+ }
1061
+ const driveMatch = inner.match(/^([A-Za-z])--(.*)/);
1062
+ if (driveMatch) {
1063
+ const resolved = resolveEncodedPath(driveMatch[1] + ":/", driveMatch[2]);
1064
+ if (resolved)
1065
+ return resolved;
1066
+ } else {
1067
+ const resolved = resolveEncodedPath("/", inner);
1068
+ if (resolved)
1069
+ return resolved;
1070
+ }
1071
+ const parts = inner.split("-").filter(Boolean);
1072
+ if (parts.length <= 2)
1073
+ return parts.join("/");
1074
+ return parts.slice(-2).join("-");
1075
+ }
1076
+ function parsePiSessionInfo(header, dirName) {
1077
+ const projectName = decodePiProjectDir(dirName);
1078
+ return {
1079
+ agentType: "pi",
1080
+ projectPath: header.cwd || dirName,
1081
+ projectName,
1082
+ isSubagent: !!header.parentSession,
1083
+ projectDir: dirName,
1084
+ parentSessionId: header.parentSession ? extractSessionIdFromPath(header.parentSession) : null
1085
+ };
1086
+ }
1087
+ function extractSessionIdFromPath(filePath) {
1088
+ const match = filePath.match(/([^/\\]+)\.jsonl$/);
1089
+ if (!match)
1090
+ return null;
1091
+ const parts = match[1].split("_");
1092
+ const uuid = parts[parts.length - 1];
1093
+ return uuid ? `pi:${uuid}` : null;
1094
+ }
1095
+ var PiWatcher = class {
1096
+ stateManager;
1097
+ watcher = null;
1098
+ byteOffsets = /* @__PURE__ */ new Map();
1099
+ parser = new PiParser();
1100
+ /** Per-file lock to prevent concurrent processFile calls */
1101
+ fileLocks = /* @__PURE__ */ new Map();
1102
+ /** Cached session info per file (parsed from session header) */
1103
+ sessionInfoCache = /* @__PURE__ */ new Map();
1104
+ constructor(stateManager) {
1105
+ this.stateManager = stateManager;
1106
+ }
1107
+ async start() {
1108
+ const sessionsDir = getPiSessionsDir();
1109
+ if (!sessionsDir) {
1110
+ console.log("[pi] No sessions directory found \u2014 pi not installed or not yet used");
1111
+ return;
1112
+ }
1113
+ console.log(`[pi] Sessions directory found at ${sessionsDir}`);
1114
+ const scanner = new SessionScanner(sessionsDir);
1115
+ const existingFiles = await scanner.scan();
1116
+ for (const file of existingFiles) {
1117
+ await this.processFile(file);
1118
+ }
1119
+ const pattern = join7(sessionsDir, "**", "*.jsonl");
1120
+ this.watcher = chokidar3.watch(pattern, {
1121
+ persistent: true,
1122
+ ignoreInitial: true,
1123
+ awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
1124
+ });
1125
+ this.watcher.on("add", (filePath) => {
1126
+ console.log(`[pi] New session file: ${filePath}`);
1127
+ this.processFile(filePath);
1128
+ });
1129
+ this.watcher.on("change", (filePath) => {
1130
+ this.processFile(filePath);
1131
+ });
1132
+ console.log(`[pi] Watching for JSONL files in ${sessionsDir}`);
1133
+ }
1134
+ stop() {
1135
+ this.watcher?.close();
1136
+ this.byteOffsets.clear();
1137
+ this.fileLocks.clear();
1138
+ this.sessionInfoCache.clear();
1139
+ }
1140
+ processFile(filePath) {
1141
+ const prev = this.fileLocks.get(filePath) ?? Promise.resolve();
1142
+ const next = prev.then(() => this.doProcessFile(filePath)).catch(() => {
1143
+ }).finally(() => {
1144
+ if (this.fileLocks.get(filePath) === next) {
1145
+ this.fileLocks.delete(filePath);
1146
+ }
1147
+ });
1148
+ this.fileLocks.set(filePath, next);
1149
+ }
1150
+ async doProcessFile(filePath) {
1151
+ try {
1152
+ const fileStats = await stat3(filePath);
1153
+ const currentOffset = this.byteOffsets.get(filePath) ?? 0;
1154
+ if (fileStats.size <= currentOffset)
1155
+ return;
1156
+ const handle = await open2(filePath, "r");
1157
+ try {
1158
+ const buffer = Buffer.alloc(fileStats.size - currentOffset);
1159
+ await handle.read(buffer, 0, buffer.length, currentOffset);
1160
+ this.byteOffsets.set(filePath, fileStats.size);
1161
+ const newContent = buffer.toString("utf-8");
1162
+ const lines = newContent.split("\n").filter((l) => l.trim());
1163
+ const sessionId = this.extractSessionId(filePath);
1164
+ let sessionInfo = this.sessionInfoCache.get(filePath);
1165
+ let hadParsedActivity = false;
1166
+ for (const line of lines) {
1167
+ const raw = this.parser.parseRaw(line);
1168
+ if (!raw)
1169
+ continue;
1170
+ if (!sessionInfo && this.parser.isSessionHeader(raw)) {
1171
+ const dirName = this.getProjectDirName(filePath);
1172
+ sessionInfo = parsePiSessionInfo(raw, dirName);
1173
+ this.sessionInfoCache.set(filePath, sessionInfo);
1174
+ continue;
1175
+ }
1176
+ const parsed = this.parser.parseEntry(raw);
1177
+ if (parsed) {
1178
+ hadParsedActivity = true;
1179
+ if (!sessionInfo) {
1180
+ sessionInfo = this.buildFallbackSession(filePath);
1181
+ this.sessionInfoCache.set(filePath, sessionInfo);
1182
+ }
1183
+ this.stateManager.processMessage(sessionId, parsed, sessionInfo);
1184
+ }
1185
+ }
1186
+ if (!hadParsedActivity && lines.length > 0) {
1187
+ this.stateManager.heartbeat(sessionId);
1188
+ }
1189
+ } finally {
1190
+ await handle.close();
1191
+ }
1192
+ } catch (err) {
1193
+ if (err.code !== "ENOENT") {
1194
+ console.error(`[pi] Error processing ${filePath}:`, err);
1195
+ }
1196
+ }
1197
+ }
1198
+ /**
1199
+ * Extract a prefixed session ID from a pi session file path.
1200
+ * Filename format: {timestamp}_{uuid}.jsonl
1201
+ */
1202
+ extractSessionId(filePath) {
1203
+ const name = basename2(filePath, ".jsonl");
1204
+ return `pi:${name}`;
1205
+ }
1206
+ buildFallbackSession(filePath) {
1207
+ const dirName = this.getProjectDirName(filePath);
1208
+ const name = dirName.replace(/^--|--$/g, "") || "pi";
1209
+ return createFallbackSession("pi", name);
1210
+ }
1211
+ /**
1212
+ * Get the encoded project directory name from a session file path.
1213
+ * Path: .../sessions/--encoded-path--/{timestamp}_{uuid}.jsonl
1214
+ */
1215
+ getProjectDirName(filePath) {
1216
+ const dir = dirname(filePath).replace(/\\/g, "/");
1217
+ const parts = dir.split("/");
1218
+ return parts[parts.length - 1] || "unknown";
1219
+ }
1220
+ };
1221
+ var CodexParser = class {
1222
+ /**
1223
+ * Parse a single JSONL line into the envelope structure.
1224
+ */
1225
+ parseRaw(line) {
1226
+ try {
1227
+ const obj = JSON.parse(line);
1228
+ if (obj && typeof obj.type === "string") {
1229
+ return obj;
1230
+ }
1231
+ return null;
1232
+ } catch {
1233
+ return null;
1234
+ }
1235
+ }
1236
+ /**
1237
+ * Try to extract session_meta payload. Returns null if not a session_meta envelope.
1238
+ */
1239
+ tryGetSessionMeta(envelope) {
1240
+ if (envelope.type !== "session_meta")
1241
+ return null;
1242
+ return envelope.payload;
1243
+ }
1244
+ /**
1245
+ * Try to extract model from a turn_context envelope. Returns null otherwise.
1246
+ */
1247
+ tryGetModel(envelope) {
1248
+ if (envelope.type !== "turn_context")
1249
+ return null;
1250
+ const model = envelope.payload?.model;
1251
+ return model ?? null;
1252
+ }
1253
+ /**
1254
+ * Parse a Codex JSONL envelope into a ParsedActivity.
1255
+ * Model is passed in from the watcher (tracked per-file).
1256
+ * Returns null for non-actionable entries.
1257
+ */
1258
+ parseEntry(envelope, model) {
1259
+ if (envelope.type === "response_item") {
1260
+ return this.parseResponseItem(envelope.payload, model);
1261
+ }
1262
+ if (envelope.type === "event_msg") {
1263
+ return this.parseEventMsg(envelope.payload, model);
1264
+ }
1265
+ return null;
1266
+ }
1267
+ parseResponseItem(payload, model) {
1268
+ const itemType = payload.type;
1269
+ if (itemType === "function_call") {
1270
+ const fc = payload;
1271
+ let toolInput = {};
1272
+ try {
1273
+ toolInput = JSON.parse(fc.arguments);
1274
+ } catch {
1275
+ }
1276
+ return {
1277
+ type: "tool_use",
1278
+ toolName: normalizeToolName(fc.name),
1279
+ toolInput: normalizeToolInput(toolInput),
1280
+ model: model ?? void 0
1281
+ };
1282
+ }
1283
+ if (itemType.endsWith("_call") && itemType !== "function_call") {
1284
+ const nativeName = itemType.replace(/_call$/, "");
1285
+ const toolInput = {};
1286
+ const action = payload.action;
1287
+ if (action?.query)
1288
+ toolInput.query = action.query;
1289
+ return {
1290
+ type: "tool_use",
1291
+ toolName: normalizeToolName(nativeName),
1292
+ toolInput,
1293
+ model: model ?? void 0
1294
+ };
1295
+ }
1296
+ return null;
1297
+ }
1298
+ parseEventMsg(payload, model) {
1299
+ const eventType = payload.type;
1300
+ if (eventType === "token_count") {
1301
+ const info = payload.info;
1302
+ if (!info?.last_token_usage)
1303
+ return null;
1304
+ const usage = info.last_token_usage;
1305
+ return {
1306
+ type: "token_usage",
1307
+ inputTokens: usage.input_tokens,
1308
+ outputTokens: (usage.output_tokens ?? 0) + (usage.reasoning_output_tokens ?? 0),
1309
+ cacheReadTokens: usage.cached_input_tokens,
1310
+ model: model ?? void 0
1311
+ };
1312
+ }
1313
+ if (eventType === "agent_message") {
1314
+ const text = payload.message?.trim();
1315
+ if (text && text.length > 0 && text.length < 200) {
1316
+ return { type: "text", text, model: model ?? void 0 };
1317
+ }
1318
+ }
1319
+ if (eventType === "agent_reasoning") {
1320
+ const text = payload.text?.trim();
1321
+ if (!text)
1322
+ return null;
1323
+ return {
1324
+ type: "tool_use",
1325
+ toolName: "thinking",
1326
+ toolInput: { thought: text.slice(0, 120) },
1327
+ model: model ?? void 0
1328
+ };
1329
+ }
1330
+ return null;
1331
+ }
1332
+ };
1333
+ function getCodexSessionsDir() {
1334
+ const candidate = join8(homedir4(), ".codex", "sessions");
1335
+ return existsSync4(candidate) ? candidate : null;
1336
+ }
1337
+ var UUID_RE = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/;
1338
+ function extractCodexSessionId(filePath) {
1339
+ const name = basename3(filePath, ".jsonl");
1340
+ const match = name.match(UUID_RE);
1341
+ if (match) {
1342
+ return `codex:${match[1]}`;
1343
+ }
1344
+ return `codex:${name}`;
1345
+ }
1346
+ function parseCodexSessionInfo(meta) {
1347
+ const cwd = meta.cwd || "codex";
1348
+ const parts = cwd.replace(/\\/g, "/").split("/").filter(Boolean);
1349
+ const projectName = parts[parts.length - 1] || "codex";
1350
+ return {
1351
+ agentType: "codex",
1352
+ projectPath: cwd,
1353
+ projectName,
1354
+ isSubagent: false,
1355
+ projectDir: cwd,
1356
+ parentSessionId: null
1357
+ };
1358
+ }
1359
+ function createCodexSubagentSession(parentSessionId, parentInfo) {
1360
+ return {
1361
+ agentType: "codex",
1362
+ projectPath: parentInfo.projectPath,
1363
+ projectName: parentInfo.projectName,
1364
+ isSubagent: true,
1365
+ projectDir: parentInfo.projectDir,
1366
+ parentSessionId
1367
+ };
1368
+ }
1369
+ var CodexWatcher = class {
1370
+ stateManager;
1371
+ watcher = null;
1372
+ byteOffsets = /* @__PURE__ */ new Map();
1373
+ parser = new CodexParser();
1374
+ /** Per-file lock to prevent concurrent processFile calls */
1375
+ fileLocks = /* @__PURE__ */ new Map();
1376
+ /** Cached session info per file (parsed from session_meta) */
1377
+ sessionInfoCache = /* @__PURE__ */ new Map();
1378
+ /** Per-file model tracking (from turn_context events) */
1379
+ fileModels = /* @__PURE__ */ new Map();
1380
+ /** Deduplication of spawn_agent call IDs already processed */
1381
+ seenSubagentCalls = /* @__PURE__ */ new Set();
1382
+ /** Counter for generating unique subagent session IDs */
1383
+ subagentCounter = 0;
1384
+ constructor(stateManager) {
1385
+ this.stateManager = stateManager;
1386
+ }
1387
+ async start() {
1388
+ const sessionsDir = getCodexSessionsDir();
1389
+ if (!sessionsDir) {
1390
+ console.log("[codex] No sessions directory found \u2014 Codex CLI not installed or not yet used");
1391
+ return;
1392
+ }
1393
+ console.log(`[codex] Sessions directory found at ${sessionsDir}`);
1394
+ const existingFiles = await this.scanDeep(sessionsDir);
1395
+ for (const file of existingFiles) {
1396
+ await this.processFile(file);
1397
+ }
1398
+ const pattern = join9(sessionsDir, "**", "*.jsonl");
1399
+ const usePolling = process.platform === "win32";
1400
+ this.watcher = chokidar4.watch(pattern, {
1401
+ persistent: true,
1402
+ ignoreInitial: true,
1403
+ usePolling,
1404
+ interval: usePolling ? 500 : void 0,
1405
+ awaitWriteFinish: usePolling ? false : { stabilityThreshold: 200, pollInterval: 50 }
1406
+ });
1407
+ this.watcher.on("add", (filePath) => {
1408
+ console.log(`[codex] New session file: ${filePath}`);
1409
+ this.processFile(filePath);
1410
+ });
1411
+ this.watcher.on("change", (filePath) => {
1412
+ this.processFile(filePath);
1413
+ });
1414
+ console.log(`[codex] Watching for JSONL files in ${sessionsDir} (polling: ${usePolling})`);
1415
+ }
1416
+ stop() {
1417
+ this.watcher?.close();
1418
+ this.byteOffsets.clear();
1419
+ this.fileLocks.clear();
1420
+ this.sessionInfoCache.clear();
1421
+ this.fileModels.clear();
1422
+ this.seenSubagentCalls.clear();
1423
+ }
1424
+ /**
1425
+ * Recursively scan for recently-modified JSONL files under the sessions dir.
1426
+ * Codex nests files as sessions/YYYY/MM/DD/rollout-*.jsonl.
1427
+ */
1428
+ async scanDeep(dir) {
1429
+ const results = [];
1430
+ const now = Date.now();
1431
+ const walk = async (current) => {
1432
+ try {
1433
+ const entries = await readdir2(current, { withFileTypes: true });
1434
+ for (const entry of entries) {
1435
+ const full = join9(current, entry.name);
1436
+ if (entry.isDirectory()) {
1437
+ await walk(full);
1438
+ } else if (entry.name.endsWith(".jsonl")) {
1439
+ try {
1440
+ const s = await stat4(full);
1441
+ if (now - s.mtimeMs < config.activeThresholdMs) {
1442
+ results.push(full);
1443
+ }
1444
+ } catch {
1445
+ }
1446
+ }
1447
+ }
1448
+ } catch {
1449
+ }
1693
1450
  };
1451
+ await walk(dir);
1452
+ return results;
1453
+ }
1454
+ processFile(filePath) {
1455
+ const prev = this.fileLocks.get(filePath) ?? Promise.resolve();
1456
+ const next = prev.then(() => this.doProcessFile(filePath)).catch(() => {
1457
+ }).finally(() => {
1458
+ if (this.fileLocks.get(filePath) === next) {
1459
+ this.fileLocks.delete(filePath);
1460
+ }
1461
+ });
1462
+ this.fileLocks.set(filePath, next);
1463
+ }
1464
+ async doProcessFile(filePath) {
1465
+ try {
1466
+ const fileStats = await stat4(filePath);
1467
+ const currentOffset = this.byteOffsets.get(filePath) ?? 0;
1468
+ if (fileStats.size <= currentOffset)
1469
+ return;
1470
+ const handle = await open3(filePath, "r");
1471
+ try {
1472
+ const buffer = Buffer.alloc(fileStats.size - currentOffset);
1473
+ await handle.read(buffer, 0, buffer.length, currentOffset);
1474
+ this.byteOffsets.set(filePath, fileStats.size);
1475
+ const newContent = buffer.toString("utf-8");
1476
+ const lines = newContent.split("\n").filter((l) => l.trim());
1477
+ const sessionId = extractCodexSessionId(filePath);
1478
+ let sessionInfo = this.sessionInfoCache.get(filePath);
1479
+ let hadParsedActivity = false;
1480
+ for (const line of lines) {
1481
+ const envelope = this.parser.parseRaw(line);
1482
+ if (!envelope)
1483
+ continue;
1484
+ if (!sessionInfo) {
1485
+ const meta = this.parser.tryGetSessionMeta(envelope);
1486
+ if (meta) {
1487
+ sessionInfo = parseCodexSessionInfo(meta);
1488
+ this.sessionInfoCache.set(filePath, sessionInfo);
1489
+ continue;
1490
+ }
1491
+ }
1492
+ const model = this.parser.tryGetModel(envelope);
1493
+ if (model) {
1494
+ this.fileModels.set(filePath, model);
1495
+ continue;
1496
+ }
1497
+ const currentModel = this.fileModels.get(filePath) ?? null;
1498
+ const parsed = this.parser.parseEntry(envelope, currentModel);
1499
+ if (parsed) {
1500
+ hadParsedActivity = true;
1501
+ if (!sessionInfo) {
1502
+ sessionInfo = this.buildFallbackSession(filePath);
1503
+ this.sessionInfoCache.set(filePath, sessionInfo);
1504
+ }
1505
+ if (parsed.type === "tool_use" && parsed.toolName === "Agent") {
1506
+ this.handleSubagentSpawn(sessionId, sessionInfo, parsed, envelope.payload);
1507
+ }
1508
+ this.stateManager.processMessage(sessionId, parsed, sessionInfo);
1509
+ }
1510
+ }
1511
+ if (!hadParsedActivity && lines.length > 0) {
1512
+ this.stateManager.heartbeat(sessionId);
1513
+ }
1514
+ } finally {
1515
+ await handle.close();
1516
+ }
1517
+ } catch (err) {
1518
+ if (err.code !== "ENOENT") {
1519
+ console.error(`[codex] Error processing ${filePath}:`, err);
1520
+ }
1521
+ }
1522
+ }
1523
+ /**
1524
+ * When a spawn_agent tool call is detected, create a synthetic subagent session
1525
+ * so the visualization shows the child agent.
1526
+ */
1527
+ handleSubagentSpawn(parentSessionId, parentInfo, parsed, payload) {
1528
+ const callId = payload.call_id;
1529
+ if (!callId || this.seenSubagentCalls.has(callId))
1530
+ return;
1531
+ this.subagentCounter++;
1532
+ this.seenSubagentCalls.add(callId);
1533
+ const subSessionId = `codex:sub-${parentSessionId.replace("codex:", "")}-${this.subagentCounter}`;
1534
+ const subInfo = createCodexSubagentSession(parentSessionId, parentInfo);
1535
+ const agentName = parsed.toolInput?.agent_type || parsed.toolInput?.message?.slice(0, 30) || `agent-${this.subagentCounter}`;
1536
+ this.stateManager.processMessage(subSessionId, {
1537
+ type: "tool_use",
1538
+ toolName: "thinking",
1539
+ toolInput: { thought: `Subagent: ${agentName}` },
1540
+ model: parsed.model,
1541
+ agentName
1542
+ }, subInfo);
1543
+ }
1544
+ buildFallbackSession(filePath) {
1545
+ const name = basename4(filePath, ".jsonl");
1546
+ return createFallbackSession("codex", name);
1694
1547
  }
1695
1548
  };
1696
1549
  var COOLDOWN_MS = 6e4;
@@ -1946,6 +1799,9 @@ var TaskGraphManager = class {
1946
1799
  if (toolName === "TaskUpdate") {
1947
1800
  return this.handleUpdate(agentId, agentName, toolInput, projectName, root);
1948
1801
  }
1802
+ if (toolName === "TodoWrite" || toolName === "update_plan") {
1803
+ return this.handlePlanUpdate(agentId, agentName, toolInput, projectName, root);
1804
+ }
1949
1805
  return false;
1950
1806
  }
1951
1807
  handleCreate(agentId, agentName, toolInput, projectName, root) {
@@ -2051,6 +1907,83 @@ var TaskGraphManager = class {
2051
1907
  }
2052
1908
  return changed;
2053
1909
  }
1910
+ /**
1911
+ * Handle plan/todo updates from multiple formats:
1912
+ * - Codex update_plan: {plan: [{step: "...", status: "pending"|"in_progress"|"completed"}, ...]}
1913
+ * - Claude Code / OpenCode TodoWrite: {todos: [{id, content, status}, ...]}
1914
+ * Each call replaces the full plan — we sync to create/update tasks accordingly.
1915
+ */
1916
+ handlePlanUpdate(agentId, agentName, toolInput, projectName, root) {
1917
+ const input = toolInput;
1918
+ if (!input)
1919
+ return false;
1920
+ const steps = this.extractPlanSteps(input);
1921
+ if (steps.length === 0)
1922
+ return false;
1923
+ let changed = false;
1924
+ const existingBySubject = /* @__PURE__ */ new Map();
1925
+ for (const [key, task] of this.tasks) {
1926
+ if (key.startsWith(root + "::")) {
1927
+ existingBySubject.set(task.subject, task);
1928
+ }
1929
+ }
1930
+ for (const { subject, status } of steps) {
1931
+ const existing = existingBySubject.get(subject);
1932
+ if (existing) {
1933
+ if (existing.status !== status) {
1934
+ existing.status = status;
1935
+ changed = true;
1936
+ }
1937
+ } else {
1938
+ const count = (this.counters.get(root) ?? 0) + 1;
1939
+ this.counters.set(root, count);
1940
+ const shortId = String(count);
1941
+ const key = this.scopedKey(root, shortId);
1942
+ const node = {
1943
+ id: shortId,
1944
+ subject,
1945
+ status,
1946
+ owner: void 0,
1947
+ agentId,
1948
+ agentName,
1949
+ projectName,
1950
+ blocks: [],
1951
+ blockedBy: [],
1952
+ timestamp: Date.now(),
1953
+ _rootKey: key
1954
+ };
1955
+ this.tasks.set(key, node);
1956
+ existingBySubject.set(subject, node);
1957
+ changed = true;
1958
+ }
1959
+ }
1960
+ return changed;
1961
+ }
1962
+ /**
1963
+ * Extract plan steps from various tool input formats into a common shape.
1964
+ */
1965
+ extractPlanSteps(input) {
1966
+ const planSteps = input.plan;
1967
+ if (Array.isArray(planSteps) && planSteps.length > 0) {
1968
+ return planSteps.filter((s) => s.step?.trim()).map((s) => ({ subject: s.step.trim(), status: this.normalizePlanStatus(s.status) }));
1969
+ }
1970
+ const todos = input.todos;
1971
+ if (Array.isArray(todos) && todos.length > 0) {
1972
+ return todos.filter((t) => t.content?.trim()).map((t) => ({ subject: t.content.trim(), status: this.normalizePlanStatus(t.status) }));
1973
+ }
1974
+ return [];
1975
+ }
1976
+ normalizePlanStatus(status) {
1977
+ if (!status)
1978
+ return "pending";
1979
+ if (status === "in_progress" || status === "in-progress")
1980
+ return "in_progress";
1981
+ if (status === "completed" || status === "done")
1982
+ return "completed";
1983
+ if (status === "deleted" || status === "cancelled")
1984
+ return "deleted";
1985
+ return "pending";
1986
+ }
2054
1987
  /**
2055
1988
  * Mark a task as completed (hook-sourced — TaskCompleted event).
2056
1989
  * Returns true if the status actually changed.
@@ -2272,7 +2205,7 @@ function processToolUseActivity(deps, agent, activity, now) {
2272
2205
  anomalyDetector.setAgentName(agentId, agent.agentName ?? agent.projectName ?? agentId.slice(0, 10));
2273
2206
  anomalyDetector.checkToolUse(agentId, toolName);
2274
2207
  toolChainTracker.recordToolUse(agentId, toolName);
2275
- if (toolName === "TaskCreate" || toolName === "TaskUpdate") {
2208
+ if (toolName === "TaskCreate" || toolName === "TaskUpdate" || toolName === "TodoWrite") {
2276
2209
  const graphChanged = taskGraphManager.processToolUse(agentId, agent.agentName ?? agentId.slice(0, 10), toolName, activity.toolInput, agent.projectName, agent.rootSessionId);
2277
2210
  if (graphChanged) {
2278
2211
  emit("taskgraph:changed", { data: taskGraphManager.getSnapshot(), timestamp: Date.now() });
@@ -2696,6 +2629,7 @@ var AgentStateManager = class extends EventEmitter2 {
2696
2629
  agent = {
2697
2630
  id: sessionId,
2698
2631
  sessionId,
2632
+ agentType: sessionInfo.agentType,
2699
2633
  rootSessionId,
2700
2634
  projectPath: sessionInfo.projectPath,
2701
2635
  projectName: sessionInfo.projectName,
@@ -3459,12 +3393,12 @@ function buildResponse(decision) {
3459
3393
  }
3460
3394
  };
3461
3395
  }
3462
- var __dirname = dirname(fileURLToPath(import.meta.url));
3396
+ var __dirname = dirname2(fileURLToPath(import.meta.url));
3463
3397
  async function main() {
3464
3398
  const app = Fastify({ logger: { level: "info" } });
3465
3399
  await app.register(cors, { origin: true });
3466
3400
  await app.register(websocket);
3467
- const clientDist = join6(__dirname, "..", "..", "client", "dist");
3401
+ const clientDist = join10(__dirname, "..", "..", "client", "dist");
3468
3402
  await app.register(fastifyStatic, {
3469
3403
  root: clientDist,
3470
3404
  prefix: "/",
@@ -3496,7 +3430,9 @@ async function main() {
3496
3430
  });
3497
3431
  const watchers = [
3498
3432
  new FileWatcher(config.claudeHome, stateManager),
3499
- ...config.enableOpenCode ? [new OpenCodeWatcher(stateManager)] : []
3433
+ ...config.enableOpenCode ? [new OpenCodeWatcher(stateManager)] : [],
3434
+ ...config.enablePi ? [new PiWatcher(stateManager)] : [],
3435
+ ...config.enableCodex ? [new CodexWatcher(stateManager)] : []
3500
3436
  ];
3501
3437
  for (const w of watchers) {
3502
3438
  await w.start();