aibroker 0.28.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/iterm/sessions.d.ts +8 -0
- package/dist/adapters/iterm/sessions.d.ts.map +1 -1
- package/dist/adapters/iterm/sessions.js +31 -0
- package/dist/adapters/iterm/sessions.js.map +1 -1
- package/dist/core/persistence.d.ts +20 -0
- package/dist/core/persistence.d.ts.map +1 -1
- package/dist/core/persistence.js +35 -0
- package/dist/core/persistence.js.map +1 -1
- package/dist/daemon/core-handlers.d.ts.map +1 -1
- package/dist/daemon/core-handlers.js +20 -1
- package/dist/daemon/core-handlers.js.map +1 -1
- package/dist/daemon/dispatch.d.ts +14 -2
- package/dist/daemon/dispatch.d.ts.map +1 -1
- package/dist/daemon/dispatch.js +113 -26
- package/dist/daemon/dispatch.js.map +1 -1
- package/dist/daemon/pai-projects.d.ts +23 -8
- package/dist/daemon/pai-projects.d.ts.map +1 -1
- package/dist/daemon/pai-projects.js +113 -7
- package/dist/daemon/pai-projects.js.map +1 -1
- package/dist/daemon/sessions.d.ts.map +1 -1
- package/dist/daemon/sessions.js +43 -2
- package/dist/daemon/sessions.js.map +1 -1
- package/dist/daemon/terminal-screen.d.ts +1 -1
- package/dist/daemon/terminal-screen.d.ts.map +1 -1
- package/dist/daemon/terminal-screen.js +1 -1
- package/dist/daemon/terminal-screen.js.map +1 -1
- package/dist/daemon/todoist-webhook.d.ts.map +1 -1
- package/dist/daemon/todoist-webhook.js +14 -0
- package/dist/daemon/todoist-webhook.js.map +1 -1
- package/dist/transport/screen.d.ts +20 -0
- package/dist/transport/screen.d.ts.map +1 -1
- package/dist/transport/screen.js +26 -0
- package/dist/transport/screen.js.map +1 -1
- package/package.json +1 -1
package/dist/daemon/dispatch.js
CHANGED
|
@@ -27,10 +27,13 @@
|
|
|
27
27
|
* missing alias, the second is a session that failed to come up. Collapsing
|
|
28
28
|
* them sends whoever reads the result looking in the wrong place.
|
|
29
29
|
*/
|
|
30
|
+
import { mkdirSync, writeFileSync, readdirSync, statSync, unlinkSync } from "node:fs";
|
|
31
|
+
import { homedir } from "node:os";
|
|
32
|
+
import { join } from "node:path";
|
|
30
33
|
import { snapshotAllSessions } from "../transport/sync-facade.js";
|
|
31
34
|
import { findCuratedPaiProject, launchResolvedPaiProject, invalidatePaiProjectCache, } from "./pai-projects.js";
|
|
32
35
|
import { getAllPersistentSessionNames, lookupPersistentName } from "../core/persistence.js";
|
|
33
|
-
import { flatten, isClaudeReady, hasBeenSubmitted, realIO, } from "./terminal-screen.js";
|
|
36
|
+
import { flatten, isClaudeReady, isInputBoxEmpty, hasBeenSubmitted, realIO, } from "./terminal-screen.js";
|
|
34
37
|
import { log } from "../core/log.js";
|
|
35
38
|
import { matchSession } from "../core/session-match.js";
|
|
36
39
|
/** A spawned Claude needs to boot and run its `/Name … go` preamble first. */
|
|
@@ -84,7 +87,17 @@ function liveSessions() {
|
|
|
84
87
|
* `/Name … go` preamble and stays busy for minutes; waiting for the screen to
|
|
85
88
|
* settle times out on a session that is perfectly healthy — which is exactly
|
|
86
89
|
* what the first version did. Claude Code queues typed input while it works, so
|
|
87
|
-
*
|
|
90
|
+
* idleness is the wrong gate.
|
|
91
|
+
*
|
|
92
|
+
* But "the box is drawn" was too weak. The preamble is typed into that box and
|
|
93
|
+
* sits there unsubmitted while it is drawn, so a dispatcher that fired on the
|
|
94
|
+
* first drawn box appended its work order to `/Name Jobs Grazyna` and `go` —
|
|
95
|
+
* three inputs racing in one box, with the user's own typing landing in the
|
|
96
|
+
* middle of it. Reported live on 2026-08-04.
|
|
97
|
+
*
|
|
98
|
+
* The gate is therefore drawn AND empty: the preamble has been submitted and
|
|
99
|
+
* the box is free. A busy session still qualifies, which preserves the point of
|
|
100
|
+
* the paragraph above.
|
|
88
101
|
*/
|
|
89
102
|
export async function waitForReady(sessionId, timeoutMs, io = realIO) {
|
|
90
103
|
const deadline = io.now() + timeoutMs;
|
|
@@ -93,7 +106,7 @@ export async function waitForReady(sessionId, timeoutMs, io = realIO) {
|
|
|
93
106
|
const frame = io.capture(sessionId);
|
|
94
107
|
if (frame === null)
|
|
95
108
|
continue;
|
|
96
|
-
if (isClaudeReady(frame))
|
|
109
|
+
if (isClaudeReady(frame) && isInputBoxEmpty(frame))
|
|
97
110
|
return true;
|
|
98
111
|
}
|
|
99
112
|
return false;
|
|
@@ -263,9 +276,36 @@ export async function dispatch(projectName, message, opts = {}, deps = realDeps)
|
|
|
263
276
|
`Raise the timeout.`,
|
|
264
277
|
};
|
|
265
278
|
}
|
|
279
|
+
// Hand the work order over IN the launch, not by typing afterwards.
|
|
280
|
+
//
|
|
281
|
+
// See the long note in pai-projects.ts: a freshly launched session holds its
|
|
282
|
+
// `/Name … go` preamble as queued prompts that are not rendered anywhere, so
|
|
283
|
+
// "the session looks ready" is true for ~8 seconds before those prompts run.
|
|
284
|
+
// Typing into that window interleaves the work order with the rename and the
|
|
285
|
+
// resume. The queue is ordered and nothing else writes to it, so passing the
|
|
286
|
+
// order as the second queued prompt is race-free by construction.
|
|
287
|
+
//
|
|
288
|
+
// Via a file because the queue separator is a newline: a multi-line body
|
|
289
|
+
// passed inline would arrive as several unrelated prompts. One line pointing
|
|
290
|
+
// at the body keeps the body arbitrarily long and the instruction atomic.
|
|
291
|
+
let orderPath;
|
|
292
|
+
try {
|
|
293
|
+
orderPath = writeWorkOrder(label, body);
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
return {
|
|
297
|
+
outcome: "unreachable",
|
|
298
|
+
project: label,
|
|
299
|
+
session: "",
|
|
300
|
+
reason: `Could not stage the work order: ${err instanceof Error ? err.message : String(err)}`,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
266
303
|
let itermSessionId;
|
|
267
304
|
try {
|
|
268
|
-
({ itermSessionId } = await deps.launch(project
|
|
305
|
+
({ itermSessionId } = await deps.launch(project, {
|
|
306
|
+
initialPrompt: `${opts.prefix ?? TASK_PREFIX} Your work order is in ${orderPath} — read that file and carry it out. ` +
|
|
307
|
+
`It was written for this session only; delete it once you have read it.`,
|
|
308
|
+
}));
|
|
269
309
|
}
|
|
270
310
|
catch (err) {
|
|
271
311
|
return {
|
|
@@ -292,29 +332,76 @@ export async function dispatch(projectName, message, opts = {}, deps = realDeps)
|
|
|
292
332
|
: `. The tab is open — check why it did not start.`),
|
|
293
333
|
};
|
|
294
334
|
}
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
335
|
+
// No budget check here any more, and its absence is the point.
|
|
336
|
+
//
|
|
337
|
+
// While the work order was TYPED after boot, a boot that ate the budget left
|
|
338
|
+
// nothing to deliver in, and `unreachable` was the honest answer. Now the
|
|
339
|
+
// order is queued by the launch itself, so once the tab exists the work is
|
|
340
|
+
// handed over whether or not the clock ran out. Reporting failure at this
|
|
341
|
+
// point would tell PAI to retry something that is already going to run — the
|
|
342
|
+
// duplicate-dispatch failure, reintroduced from the other side.
|
|
343
|
+
//
|
|
344
|
+
// A slow boot is now a slow boot, not a lost task.
|
|
345
|
+
// No delivery step: the order was queued by the launch and Claude Code runs
|
|
346
|
+
// its queue in order. Readiness above is now a LIVENESS check — did a Claude
|
|
347
|
+
// actually come up in that tab — rather than permission to start typing.
|
|
348
|
+
log(`dispatch: "${label}" came up; work order was queued at launch (${orderPath})`);
|
|
349
|
+
return { outcome: "spawned", project: label, session: label, reason: "" };
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Stage a work order on disk for a session that is about to be launched.
|
|
353
|
+
*
|
|
354
|
+
* Under ~/.aibroker so it survives nothing in particular — it is meant to be
|
|
355
|
+
* short-lived, and the receiving session is told to delete it. Named after the
|
|
356
|
+
* project and the clock so two dispatches for one project cannot overwrite each
|
|
357
|
+
* other mid-launch.
|
|
358
|
+
*/
|
|
359
|
+
function writeWorkOrder(label, body) {
|
|
360
|
+
const dir = join(homedir(), ".aibroker", "work-orders");
|
|
361
|
+
mkdirSync(dir, { recursive: true });
|
|
362
|
+
pruneWorkOrders(dir);
|
|
363
|
+
const slug = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "task";
|
|
364
|
+
const path = join(dir, `${slug}-${Date.now()}.md`);
|
|
365
|
+
writeFileSync(path, body.endsWith("\n") ? body : `${body}\n`, "utf8");
|
|
366
|
+
return path;
|
|
367
|
+
}
|
|
368
|
+
/** How long a staged work order is kept before it is assumed abandoned. */
|
|
369
|
+
const WORK_ORDER_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
370
|
+
/**
|
|
371
|
+
* Drop work orders old enough that nobody is coming back for them.
|
|
372
|
+
*
|
|
373
|
+
* The receiving session is told to delete its own order, and a healthy dispatch
|
|
374
|
+
* does. Nothing deletes the others: a dispatch that fails after staging, a
|
|
375
|
+
* session killed mid-run, a tab closed before it read the file. Each leaves a
|
|
376
|
+
* copy of a work order — which is a copy of task content — sitting in the home
|
|
377
|
+
* directory indefinitely.
|
|
378
|
+
*
|
|
379
|
+
* Swept on write rather than on a timer: the only moment this directory is
|
|
380
|
+
* certainly in use is when something is being added to it, and a sweep that
|
|
381
|
+
* needs its own schedule is one more thing that can stop running silently.
|
|
382
|
+
*
|
|
383
|
+
* A week, because the point is to bound the pile, not to race the reader. An
|
|
384
|
+
* order still unread after seven days is not about to be read.
|
|
385
|
+
*/
|
|
386
|
+
function pruneWorkOrders(dir) {
|
|
387
|
+
try {
|
|
388
|
+
const cutoff = Date.now() - WORK_ORDER_TTL_MS;
|
|
389
|
+
for (const name of readdirSync(dir)) {
|
|
390
|
+
if (!name.endsWith(".md"))
|
|
391
|
+
continue;
|
|
392
|
+
const p = join(dir, name);
|
|
393
|
+
try {
|
|
394
|
+
if (statSync(p).mtimeMs < cutoff)
|
|
395
|
+
unlinkSync(p);
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
/* vanished under us, or not ours to remove — either way, skip it */
|
|
399
|
+
}
|
|
400
|
+
}
|
|
306
401
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
402
|
+
catch {
|
|
403
|
+
// Housekeeping must never cost a dispatch. A directory that cannot be read
|
|
404
|
+
// is a reason to skip the sweep, not to fail the work order.
|
|
310
405
|
}
|
|
311
|
-
// Deliberately NOT "spawned": the tab exists but the work order never landed,
|
|
312
|
-
// and reporting success here is how a task silently disappears.
|
|
313
|
-
return {
|
|
314
|
-
outcome: "unreachable",
|
|
315
|
-
project: label,
|
|
316
|
-
session: label,
|
|
317
|
-
reason: `Spawned a session but ${ackReason(res)}.`,
|
|
318
|
-
};
|
|
319
406
|
}
|
|
320
407
|
//# sourceMappingURL=dispatch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/daemon/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,yBAAyB,GAE1B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE5F,OAAO,EAGL,OAAO,EAEP,aAAa,EACb,gBAAgB,EAChB,MAAM,GAGP,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AA8DxD,8EAA8E;AAC9E,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,0BAA0B,GAAG,OAAO,CAAC;AAC3C,MAAM,aAAa,GAAG,KAAK,CAAC;AAE5B;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC;AAEpC,6EAA6E;AAC7E,mEAAmE;AACnE,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,OAAO,EACP,MAAM,GAEP,MAAM,sBAAsB,CAAC;AAE9B;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAmB,EACnB,QAAgE;IAEhE,2EAA2E;IAC3E,4EAA4E;IAC5E,4EAA4E;IAC5E,MAAM,GAAG,GAAG,YAAY,CACtB,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,EACnE,QAAQ,EACR,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,CACnC,CAAC;IACF,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/D,CAAC;AAED,0EAA0E;AAC1E,SAAS,YAAY;IACnB,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,MAAM,UAAU,GAAG,4BAA4B,EAAE,CAAC;IAClD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,oBAAoB,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC;KAC9D,CAAC,CAAC,CAAC;AACN,CAAC;AAKD
|
|
1
|
+
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/daemon/dispatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -80,16 +80,31 @@ export declare function launchPaiProject(name: string): Promise<{
|
|
|
80
80
|
sessionId: string;
|
|
81
81
|
}>;
|
|
82
82
|
/**
|
|
83
|
-
*
|
|
83
|
+
* A launch that cannot succeed no matter how often it is retried.
|
|
84
84
|
*
|
|
85
|
-
*
|
|
86
|
-
* this
|
|
87
|
-
*
|
|
88
|
-
* opening a different project that happens to share the display name — there
|
|
89
|
-
* are three "Glidr" rows at three different paths. Once resolution has picked
|
|
90
|
-
* a row, that row is what gets launched.
|
|
85
|
+
* Carries `permanent` so the caller — and PAI's task poller across the IPC
|
|
86
|
+
* boundary — can tell "this will never work" from "this did not work yet" and
|
|
87
|
+
* park the task instead of re-dispatching it every fifteen minutes forever.
|
|
91
88
|
*/
|
|
92
|
-
export declare
|
|
89
|
+
export declare class ProjectRootMissingError extends Error {
|
|
90
|
+
readonly projectName: string;
|
|
91
|
+
readonly rootPath: string;
|
|
92
|
+
readonly code = "project_root_missing";
|
|
93
|
+
readonly permanent = true;
|
|
94
|
+
constructor(projectName: string, rootPath: string);
|
|
95
|
+
}
|
|
96
|
+
export declare function launchResolvedPaiProject(project: PaiProject, opts?: {
|
|
97
|
+
/**
|
|
98
|
+
* What the session should do instead of resuming.
|
|
99
|
+
*
|
|
100
|
+
* Queued as the second initial-prompt line, so it runs immediately after
|
|
101
|
+
* `/Name` and before anything else can reach the session. MUST be a single
|
|
102
|
+
* line: Claude Code splits the initial-prompt argument on newlines and
|
|
103
|
+
* treats each as a separate queued prompt, so an embedded newline silently
|
|
104
|
+
* fragments the instruction into several.
|
|
105
|
+
*/
|
|
106
|
+
initialPrompt?: string;
|
|
107
|
+
}): Promise<{
|
|
93
108
|
itermSessionId: string;
|
|
94
109
|
sessionId: string;
|
|
95
110
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pai-projects.d.ts","sourceRoot":"","sources":["../../src/daemon/pai-projects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"pai-projects.d.ts","sourceRoot":"","sources":["../../src/daemon/pai-projects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAwBH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAaD,0EAA0E;AAC1E,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD;AA+DD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,GAAG,UAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAWxE;AAcD;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAEzF;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAMlF;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,SAAS,CAAC,CAWb;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAMxD;AAwCD;;;;;;GAMG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAI9C,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAJ3B,QAAQ,CAAC,IAAI,0BAA0B;IACvC,QAAQ,CAAC,SAAS,QAAQ;gBAEf,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM;CAU5B;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,UAAU,EACnB,IAAI,GAAE;IACJ;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACnB,GACL,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA8FxD"}
|
|
@@ -11,9 +11,21 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { execFile } from "node:child_process";
|
|
13
13
|
import { promisify } from "node:util";
|
|
14
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { join } from "node:path";
|
|
14
17
|
import { log } from "../core/log.js";
|
|
15
18
|
import { createClaudeSession } from "../adapters/iterm/sessions.js";
|
|
16
19
|
const execFileAsync = promisify(execFile);
|
|
20
|
+
/**
|
|
21
|
+
* The restore manifest, read directly rather than through daemon/sessions.ts.
|
|
22
|
+
*
|
|
23
|
+
* sessions.ts owns this file and is the only writer; it is imported here by
|
|
24
|
+
* path instead of by module because sessions.ts pulls in daemon/index.js, and
|
|
25
|
+
* daemon/index → core-handlers → pai-projects closes an import cycle. A
|
|
26
|
+
* six-line read is the cheaper of the two couplings.
|
|
27
|
+
*/
|
|
28
|
+
const RESTORE_MANIFEST = join(homedir(), ".aibroker", "session-restore.json");
|
|
17
29
|
const CACHE_TTL_MS = 30_000;
|
|
18
30
|
let _cache = null; // curated shortlist
|
|
19
31
|
let _cacheAll = null; // all active projects (--all)
|
|
@@ -182,8 +194,84 @@ export async function launchPaiProject(name) {
|
|
|
182
194
|
* are three "Glidr" rows at three different paths. Once resolution has picked
|
|
183
195
|
* a row, that row is what gets launched.
|
|
184
196
|
*/
|
|
185
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Where a session of this name was last actually seen running, if it still exists.
|
|
199
|
+
*
|
|
200
|
+
* PAI's project registry records where a project was DECLARED to live; the
|
|
201
|
+
* restore manifest records where its sessions were OBSERVED to live. Renaming a
|
|
202
|
+
* directory goes unnoticed by the first and is picked up within five minutes by
|
|
203
|
+
* the second, so when the declared path is gone the manifest is the natural
|
|
204
|
+
* place to look for what it became.
|
|
205
|
+
*/
|
|
206
|
+
function lastSeenCwdForName(name) {
|
|
207
|
+
try {
|
|
208
|
+
if (!existsSync(RESTORE_MANIFEST))
|
|
209
|
+
return null;
|
|
210
|
+
const raw = JSON.parse(readFileSync(RESTORE_MANIFEST, "utf-8"));
|
|
211
|
+
const entries = (Array.isArray(raw) ? raw : raw.entries);
|
|
212
|
+
if (!Array.isArray(entries))
|
|
213
|
+
return null;
|
|
214
|
+
const hit = entries.find((e) => e?.name === name && typeof e.cwd === "string" && existsSync(e.cwd));
|
|
215
|
+
return hit?.cwd ?? null;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// The manifest is a hint, not a dependency. A bad one must not turn a
|
|
219
|
+
// clear "directory is gone" error into an unrelated parse failure.
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* A launch that cannot succeed no matter how often it is retried.
|
|
225
|
+
*
|
|
226
|
+
* Carries `permanent` so the caller — and PAI's task poller across the IPC
|
|
227
|
+
* boundary — can tell "this will never work" from "this did not work yet" and
|
|
228
|
+
* park the task instead of re-dispatching it every fifteen minutes forever.
|
|
229
|
+
*/
|
|
230
|
+
export class ProjectRootMissingError extends Error {
|
|
231
|
+
projectName;
|
|
232
|
+
rootPath;
|
|
233
|
+
code = "project_root_missing";
|
|
234
|
+
permanent = true;
|
|
235
|
+
constructor(projectName, rootPath) {
|
|
236
|
+
super(`PAI project "${projectName}" points at "${rootPath}", which does not exist, ` +
|
|
237
|
+
`and no session of that name has been seen in a directory that does. ` +
|
|
238
|
+
`Run \`pai project health\` to find where it went, then ` +
|
|
239
|
+
`\`pai project move <slug> <new-path>\` to make the registry agree.`);
|
|
240
|
+
this.projectName = projectName;
|
|
241
|
+
this.rootPath = rootPath;
|
|
242
|
+
this.name = "ProjectRootMissingError";
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
export async function launchResolvedPaiProject(project, opts = {}) {
|
|
186
246
|
const name = project.name;
|
|
247
|
+
// Never type a `cd` into a terminal without knowing it can succeed.
|
|
248
|
+
//
|
|
249
|
+
// The command below is handed to a fresh iTerm2 tab as one `cd … && claude …`
|
|
250
|
+
// string. If the `cd` fails the tab stays open on a bare shell, `claude` never
|
|
251
|
+
// runs, and the readiness probe waits out its full 90s before reporting the
|
|
252
|
+
// session "unreachable" — indistinguishable, from the outside, from a slow
|
|
253
|
+
// start. On 2026-08-04 a renamed directory turned that into a terminal window
|
|
254
|
+
// every fifteen minutes for nine hours.
|
|
255
|
+
//
|
|
256
|
+
// When the declared path is gone, the restore manifest is asked where the
|
|
257
|
+
// session actually ran. That is not a guess: the 5-minute snapshot writes it
|
|
258
|
+
// from observation, so it is the one record that follows a directory when it
|
|
259
|
+
// is renamed. Launching from it recovers automatically, which is the whole
|
|
260
|
+
// point — but the registry stays stale until somebody fixes it, so the log
|
|
261
|
+
// says exactly what to run. Silent divergence is what produced this bug.
|
|
262
|
+
let rootPath = project.rootPath;
|
|
263
|
+
if (!rootPath || !existsSync(rootPath)) {
|
|
264
|
+
const observed = lastSeenCwdForName(project.displayName || name);
|
|
265
|
+
if (!observed) {
|
|
266
|
+
const err = new ProjectRootMissingError(name, rootPath);
|
|
267
|
+
log(`pai-projects: refusing to launch "${name}" — ${err.message}`);
|
|
268
|
+
throw err;
|
|
269
|
+
}
|
|
270
|
+
log(`pai-projects: "${name}" is registered at "${rootPath}", which is gone; ` +
|
|
271
|
+
`launching from the last observed directory "${observed}" instead. ` +
|
|
272
|
+
`Make it permanent with: pai project move ${project.slug} ${JSON.stringify(observed)}`);
|
|
273
|
+
rootPath = observed;
|
|
274
|
+
}
|
|
187
275
|
// Get effective config (project overrides global defaults)
|
|
188
276
|
const effective = await getEffectiveConfig(name) ?? project.sessionConfig ?? {};
|
|
189
277
|
const flags = effective.flags ?? "";
|
|
@@ -193,15 +281,33 @@ export async function launchResolvedPaiProject(project) {
|
|
|
193
281
|
for (const [key, value] of Object.entries(env)) {
|
|
194
282
|
parts.push(`export ${key}=${shellEscape(value)}`);
|
|
195
283
|
}
|
|
196
|
-
parts.push(`cd ${shellEscape(
|
|
284
|
+
parts.push(`cd ${shellEscape(rootPath)}`);
|
|
197
285
|
// Replicate PAI's launch: `--name` sets the session label, and the single
|
|
198
|
-
// initial-prompt arg `$'/Name <name>\
|
|
199
|
-
// (tab + /resume label) then
|
|
286
|
+
// initial-prompt arg `$'/Name <name>\n<next>'` advance-enters the /Name skill
|
|
287
|
+
// (tab + /resume label) and then does one more thing.
|
|
288
|
+
//
|
|
289
|
+
// That second line is normally `go` — resume from TODO.md. A caller with work
|
|
290
|
+
// to hand over replaces it, and that replacement is the whole point:
|
|
291
|
+
//
|
|
292
|
+
// Claude Code holds initial-prompt lines as QUEUED PROMPTS, and a queued
|
|
293
|
+
// prompt is not on the screen anywhere. Measured 2026-08-04: from t≈6s the
|
|
294
|
+
// input box renders empty and every readiness check passes, while `/Name` and
|
|
295
|
+
// `go` sit invisibly pending until t≈14s. A dispatcher that waited for
|
|
296
|
+
// "ready" and then TYPED its work order landed it inside that window, so the
|
|
297
|
+
// task, the rename and the resume all raced in one input. No amount of screen
|
|
298
|
+
// reading can close that gap — the state is not rendered.
|
|
299
|
+
//
|
|
300
|
+
// Putting the work order in the launch removes the race by construction
|
|
301
|
+
// rather than by timing: the queue is ordered, nothing else types, and there
|
|
302
|
+
// is no window to lose.
|
|
200
303
|
const label = project.displayName || project.name;
|
|
201
304
|
const ansiC = label.replace(/'/g, "");
|
|
305
|
+
// Single line only — see `initialPrompt` above. Newlines are what separate
|
|
306
|
+
// queued prompts, so one embedded here would split the instruction in two.
|
|
307
|
+
const next = (opts.initialPrompt ?? "go").replace(/[\r\n]+/g, " ").replace(/'/g, "");
|
|
202
308
|
// Double backslash — collapsed to `\n` by AppleScript, then to a real newline
|
|
203
|
-
// by zsh's $'...', so
|
|
204
|
-
const prompt = `$'/Name ${ansiC}\\\\
|
|
309
|
+
// by zsh's $'...', so the two lines arrive as two queued inputs.
|
|
310
|
+
const prompt = `$'/Name ${ansiC}\\\\n${next}'`;
|
|
205
311
|
const claudeFlags = flags.includes("--dangerously-skip-permissions")
|
|
206
312
|
? flags
|
|
207
313
|
: `--dangerously-skip-permissions ${flags}`.trim();
|
|
@@ -209,7 +315,7 @@ export async function launchResolvedPaiProject(project) {
|
|
|
209
315
|
const command = parts.join(" && ");
|
|
210
316
|
const sessionId = `pai-${project.slug}-${Date.now()}`;
|
|
211
317
|
log(`pai-projects: launching visual session for "${project.name}" ` +
|
|
212
|
-
`in "${
|
|
318
|
+
`in "${rootPath}" (command: ${command})`);
|
|
213
319
|
// Open new iTerm2 tab and run the command
|
|
214
320
|
const itermSessionId = createClaudeSession(command);
|
|
215
321
|
if (!itermSessionId) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pai-projects.js","sourceRoot":"","sources":["../../src/daemon/pai-projects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEpE,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"pai-projects.js","sourceRoot":"","sources":["../../src/daemon/pai-projects.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/daemon/sessions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/daemon/sessions.ts"],"names":[],"mappings":"AAqDA,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAyHD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,EAAE,CA0BpF;AAyJD,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,CAAC;AAErE,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACvC,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,OAAO,MAAM,CAAC;AAC3B,uEAAuE;AACvE,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,oFAAoF;AACpF,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC5C,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,SAAS,CAAC,CAoCpB;AA+JD,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAU/D"}
|
package/dist/daemon/sessions.js
CHANGED
|
@@ -37,6 +37,7 @@ import { createHash } from "node:crypto";
|
|
|
37
37
|
import { WatcherClient } from "../ipc/client.js";
|
|
38
38
|
import { captureSession, typeIntoSession } from "../transport/sync-facade.js";
|
|
39
39
|
import { DAEMON_SOCKET_PATH } from "./index.js";
|
|
40
|
+
import { pruneSessionNames } from "../core/persistence.js";
|
|
40
41
|
const HOME = homedir();
|
|
41
42
|
const AIBROKER_DIR = join(HOME, ".aibroker");
|
|
42
43
|
const MANIFEST = join(AIBROKER_DIR, "session-restore.json");
|
|
@@ -267,12 +268,37 @@ function ago(iso) {
|
|
|
267
268
|
* Merge the currently-open sessions into the manifest. Never removes anything.
|
|
268
269
|
* Returns null when the live set could not be determined (nothing was written).
|
|
269
270
|
*/
|
|
271
|
+
/**
|
|
272
|
+
* The name this directory is already known by, when there is exactly one.
|
|
273
|
+
*
|
|
274
|
+
* A live session carries a `paiName` only if session-names.json has an entry
|
|
275
|
+
* for its CURRENT iTerm UUID. A fresh tab, a session not yet `/Name`d, or one
|
|
276
|
+
* whose name was recorded under an older UUID all arrive nameless — and the
|
|
277
|
+
* snapshot used to invent `basename(cwd)` for them. That invention does not
|
|
278
|
+
* stay local: restore relaunches with `--name <invented>` and a `/Name
|
|
279
|
+
* <invented>` prompt, which writes it back into session-names.json, so the
|
|
280
|
+
* guess becomes the truth. "Jobs Matthias" became "09 - Job Search" this way on
|
|
281
|
+
* 2026-08-04, and paperfull/Paperfull the same way before it.
|
|
282
|
+
*
|
|
283
|
+
* The manifest already knows what the user calls this directory. Asking it
|
|
284
|
+
* first costs nothing and keeps a transient UUID gap from renaming a session.
|
|
285
|
+
*
|
|
286
|
+
* Only an UNAMBIGUOUS answer counts. Several sessions legitimately share a
|
|
287
|
+
* directory — Home and Solar both live in $HOME — and picking one of them
|
|
288
|
+
* would misattribute rather than merely guess. Where the manifest cannot say,
|
|
289
|
+
* the basename is still the fallback.
|
|
290
|
+
*/
|
|
291
|
+
function knownNameForCwd(entries, cwd) {
|
|
292
|
+
const matches = entries.filter((e) => e.cwd === cwd && !e.pinned);
|
|
293
|
+
return matches.length === 1 ? matches[0].name : null;
|
|
294
|
+
}
|
|
270
295
|
async function doSnapshot() {
|
|
271
296
|
const sessions = await liveSessions();
|
|
272
297
|
if (sessions === null) {
|
|
273
298
|
console.error("Daemon unreachable — manifest left untouched.");
|
|
274
299
|
return null;
|
|
275
300
|
}
|
|
301
|
+
const known = readManifest().entries;
|
|
276
302
|
const ttys = ttyMap();
|
|
277
303
|
const now = new Date().toISOString();
|
|
278
304
|
const fresh = [];
|
|
@@ -285,9 +311,24 @@ async function doSnapshot() {
|
|
|
285
311
|
const cwd = cwdForTty(tty);
|
|
286
312
|
if (!cwd)
|
|
287
313
|
continue;
|
|
288
|
-
|
|
314
|
+
const name = s.paiName || knownNameForCwd(known, cwd) || basename(cwd);
|
|
315
|
+
fresh.push({ name, cwd, lastSeen: now, addedAt: now });
|
|
316
|
+
}
|
|
317
|
+
// Garbage-collect the name store while we have an authoritative live set.
|
|
318
|
+
//
|
|
319
|
+
// This is the only routine that both runs regularly and knows every open
|
|
320
|
+
// session, which makes it the only honest place to decide a name is dead.
|
|
321
|
+
// pruneSessionNames refuses to act on an empty set, so a failed enumeration
|
|
322
|
+
// costs nothing rather than erasing every name the user has assigned.
|
|
323
|
+
try {
|
|
324
|
+
const dropped = pruneSessionNames(sessions.map((s) => s.sessionId));
|
|
325
|
+
if (dropped > 0)
|
|
326
|
+
console.error(`Pruned ${dropped} stale session name(s).`);
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Housekeeping must never cost the snapshot — the restore list is the point.
|
|
289
330
|
}
|
|
290
|
-
const merged = mergeEntries(
|
|
331
|
+
const merged = mergeEntries(known, fresh, now);
|
|
291
332
|
writeManifest(merged);
|
|
292
333
|
return { merged, seen: sessions.filter((s) => s.kind === "claude").length };
|
|
293
334
|
}
|