aibroker 0.34.0 → 0.35.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.map +1 -1
- package/dist/adapters/iterm/sessions.js +36 -10
- package/dist/adapters/iterm/sessions.js.map +1 -1
- package/dist/adapters/pailot/gateway.d.ts.map +1 -1
- package/dist/adapters/pailot/gateway.js +46 -27
- package/dist/adapters/pailot/gateway.js.map +1 -1
- package/dist/adapters/pailot/message-queue.d.ts +1 -1
- package/dist/adapters/pailot/message-queue.d.ts.map +1 -1
- package/dist/adapters/pailot/message-queue.js +86 -2
- package/dist/adapters/pailot/message-queue.js.map +1 -1
- package/dist/core/hybrid.d.ts +59 -2
- package/dist/core/hybrid.d.ts.map +1 -1
- package/dist/core/hybrid.js +112 -6
- package/dist/core/hybrid.js.map +1 -1
- package/dist/core/log.d.ts.map +1 -1
- package/dist/core/log.js +7 -1
- package/dist/core/log.js.map +1 -1
- package/dist/core/mime.d.ts +12 -0
- package/dist/core/mime.d.ts.map +1 -1
- package/dist/core/mime.js +23 -0
- package/dist/core/mime.js.map +1 -1
- package/dist/core/session-discovery.d.ts +43 -0
- package/dist/core/session-discovery.d.ts.map +1 -0
- package/dist/core/session-discovery.js +59 -0
- package/dist/core/session-discovery.js.map +1 -0
- package/dist/daemon/commands.d.ts.map +1 -1
- package/dist/daemon/commands.js +43 -6
- package/dist/daemon/commands.js.map +1 -1
- package/dist/daemon/core-handlers.d.ts.map +1 -1
- package/dist/daemon/core-handlers.js +8 -7
- package/dist/daemon/core-handlers.js.map +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +14 -2
- package/dist/daemon/index.js.map +1 -1
- package/dist/mcp/index.js +6 -14
- package/dist/mcp/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -16,19 +16,45 @@ import { saveJson } from "../../core/json-store.js";
|
|
|
16
16
|
const QUEUE_DIR = join(homedir(), ".aibroker");
|
|
17
17
|
const QUEUE_FILE = join(QUEUE_DIR, "pailot-queue.json");
|
|
18
18
|
const DEFAULT_MAX_SIZE = 500;
|
|
19
|
+
/**
|
|
20
|
+
* A byte ceiling, because a count is not a size.
|
|
21
|
+
*
|
|
22
|
+
* The queue held 500 messages and 194 MB of them: three videos at 29.7 MB each,
|
|
23
|
+
* base64, plus a month of screenshots. A client reconnecting asked for
|
|
24
|
+
* everything it had missed, got all of it, wrote it to its own store, and was
|
|
25
|
+
* killed by the watchdog on the next launch trying to parse it.
|
|
26
|
+
*
|
|
27
|
+
* Counting messages bounds nothing when one message can be tens of megabytes.
|
|
28
|
+
*/
|
|
29
|
+
const DEFAULT_MAX_BYTES = 16 * 1024 * 1024;
|
|
30
|
+
/**
|
|
31
|
+
* The point past which an attachment is not worth replaying.
|
|
32
|
+
*
|
|
33
|
+
* A queue exists so a client that was offline for a while does not lose the
|
|
34
|
+
* thread. Text is what carries the thread; a 30 MB video is not something a
|
|
35
|
+
* reconnecting phone needs handed to it unasked, and it is what breaks the
|
|
36
|
+
* phone when it does. Big payloads are dropped and the caption says so, which
|
|
37
|
+
* leaves the conversation readable and the attachment retrievable on request.
|
|
38
|
+
*/
|
|
39
|
+
const MAX_PAYLOAD_BYTES = 256 * 1024;
|
|
40
|
+
/** Fields that carry bulk. Dropping them leaves the message and its context. */
|
|
41
|
+
const BULK_FIELDS = ["imageBase64", "audioBase64", "data"];
|
|
19
42
|
/** Content types that get persisted to the queue. */
|
|
20
43
|
const CONTENT_TYPES = new Set(["text", "voice", "image"]);
|
|
21
44
|
// --- Module state ---
|
|
22
45
|
let nextSeq = 1;
|
|
23
46
|
let messages = [];
|
|
24
47
|
let maxSize = DEFAULT_MAX_SIZE;
|
|
48
|
+
let maxBytes = DEFAULT_MAX_BYTES;
|
|
25
49
|
let dirty = false;
|
|
26
50
|
let flushTimer = null;
|
|
27
51
|
// --- Persistence ---
|
|
28
52
|
/** Load the queue from disk. Call once at daemon startup. */
|
|
29
|
-
export function loadQueue(maxMessages) {
|
|
53
|
+
export function loadQueue(maxMessages, maxQueueBytes) {
|
|
30
54
|
if (maxMessages)
|
|
31
55
|
maxSize = maxMessages;
|
|
56
|
+
if (maxQueueBytes)
|
|
57
|
+
maxBytes = maxQueueBytes;
|
|
32
58
|
try {
|
|
33
59
|
mkdirSync(QUEUE_DIR, { recursive: true });
|
|
34
60
|
const raw = readFileSync(QUEUE_FILE, "utf-8");
|
|
@@ -39,6 +65,10 @@ export function loadQueue(maxMessages) {
|
|
|
39
65
|
if (Array.isArray(state.messages)) {
|
|
40
66
|
// Trim to maxSize on load (queue file could have been edited)
|
|
41
67
|
messages = state.messages.slice(-maxSize);
|
|
68
|
+
// And to the byte budget: a queue written before this limit existed, or
|
|
69
|
+
// edited by hand, must not survive a restart intact and be replayed.
|
|
70
|
+
messages = messages.map(shrinkIfHuge);
|
|
71
|
+
trimToByteBudget();
|
|
42
72
|
}
|
|
43
73
|
log(`[MQ] loaded ${messages.length} messages, nextSeq=${nextSeq}`);
|
|
44
74
|
}
|
|
@@ -110,14 +140,68 @@ export function enqueue(sessionId, type, payload) {
|
|
|
110
140
|
payload: { ...payload, seq },
|
|
111
141
|
ts: Date.now(),
|
|
112
142
|
};
|
|
113
|
-
messages.push(entry);
|
|
143
|
+
messages.push(shrinkIfHuge(entry));
|
|
114
144
|
// Trim circular buffer
|
|
115
145
|
if (messages.length > maxSize) {
|
|
116
146
|
messages = messages.slice(-maxSize);
|
|
117
147
|
}
|
|
148
|
+
trimToByteBudget();
|
|
118
149
|
scheduleSave();
|
|
119
150
|
return seq;
|
|
120
151
|
}
|
|
152
|
+
/** Size of an entry as it would be stored and replayed. */
|
|
153
|
+
function entryBytes(m) {
|
|
154
|
+
try {
|
|
155
|
+
return JSON.stringify(m).length;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Strip the bulk from an oversized entry, keeping the message itself.
|
|
163
|
+
*
|
|
164
|
+
* Done at ENQUEUE, not at replay: a payload nobody can be handed is not worth
|
|
165
|
+
* carrying on disk either, and stripping once is cheaper than deciding again
|
|
166
|
+
* for every client that reconnects.
|
|
167
|
+
*/
|
|
168
|
+
function shrinkIfHuge(m) {
|
|
169
|
+
if (entryBytes(m) <= MAX_PAYLOAD_BYTES)
|
|
170
|
+
return m;
|
|
171
|
+
const payload = { ...m.payload };
|
|
172
|
+
let dropped = false;
|
|
173
|
+
for (const f of BULK_FIELDS) {
|
|
174
|
+
if (payload[f]) {
|
|
175
|
+
delete payload[f];
|
|
176
|
+
dropped = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!dropped)
|
|
180
|
+
return m;
|
|
181
|
+
const caption = typeof payload.caption === "string" ? payload.caption : "";
|
|
182
|
+
payload.caption = `${caption}${caption ? " " : ""}[attachment too large to replay — ask for it again if you need it]`;
|
|
183
|
+
log(`[MQ] seq=${m.seq} exceeded ${Math.round(MAX_PAYLOAD_BYTES / 1024)} KB — stored without its attachment`);
|
|
184
|
+
return { ...m, payload };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Drop the oldest messages until the queue fits its byte budget.
|
|
188
|
+
*
|
|
189
|
+
* Oldest first, because the queue's purpose is recent continuity: a client
|
|
190
|
+
* that has been away long enough to need the far end of the buffer has lost
|
|
191
|
+
* the thread regardless.
|
|
192
|
+
*/
|
|
193
|
+
function trimToByteBudget() {
|
|
194
|
+
let total = messages.reduce((n, m) => n + entryBytes(m), 0);
|
|
195
|
+
if (total <= maxBytes)
|
|
196
|
+
return;
|
|
197
|
+
let dropped = 0;
|
|
198
|
+
while (messages.length > 1 && total > maxBytes) {
|
|
199
|
+
total -= entryBytes(messages[0]);
|
|
200
|
+
messages.shift();
|
|
201
|
+
dropped++;
|
|
202
|
+
}
|
|
203
|
+
log(`[MQ] byte budget exceeded — dropped ${dropped} oldest message(s), now ${Math.round(total / 1024)} KB`);
|
|
204
|
+
}
|
|
121
205
|
/**
|
|
122
206
|
* Get all messages with seq > afterSeq.
|
|
123
207
|
* Optionally filter by sessionId (returns all sessions if not specified).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-queue.js","sourceRoot":"","sources":["../../../src/adapters/pailot/message-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC;AAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;AACxD,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,qDAAqD;AACrD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAe1D,uBAAuB;AAEvB,IAAI,OAAO,GAAG,CAAC,CAAC;AAChB,IAAI,QAAQ,GAAoB,EAAE,CAAC;AACnC,IAAI,OAAO,GAAG,gBAAgB,CAAC;AAC/B,IAAI,KAAK,GAAG,KAAK,CAAC;AAClB,IAAI,UAAU,GAAyC,IAAI,CAAC;AAE5D,sBAAsB;AAEtB,6DAA6D;AAC7D,MAAM,UAAU,SAAS,CAAC,WAAoB;
|
|
1
|
+
{"version":3,"file":"message-queue.js","sourceRoot":"","sources":["../../../src/adapters/pailot/message-queue.ts"],"names":[],"mappings":"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"}
|
package/dist/core/hybrid.d.ts
CHANGED
|
@@ -30,7 +30,20 @@ export declare class HybridSessionManager {
|
|
|
30
30
|
private readonly sessions;
|
|
31
31
|
private _activeIndex;
|
|
32
32
|
private nextNum;
|
|
33
|
+
private discover?;
|
|
34
|
+
/** True when the last attempt to look could not complete, so the list is last-known. */
|
|
35
|
+
private lastDiscoveryFailed;
|
|
33
36
|
constructor(apiBackend: APIBackend);
|
|
37
|
+
/**
|
|
38
|
+
* Where live sessions come from. Injected rather than imported so this stays
|
|
39
|
+
* a registry rather than growing a dependency on a particular terminal.
|
|
40
|
+
*/
|
|
41
|
+
setDiscovery(fn: () => Array<{
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
paiName?: string | null;
|
|
45
|
+
tabTitle?: string | null;
|
|
46
|
+
}>): void;
|
|
34
47
|
/** Create a new headless (API) session. Delegates to APIBackend. */
|
|
35
48
|
createApiSession(name: string, cwd: string): HybridSession;
|
|
36
49
|
/** Register a visual (iTerm2) session. The transport creates the tab and passes the ID. */
|
|
@@ -50,11 +63,55 @@ export declare class HybridSessionManager {
|
|
|
50
63
|
pruneDeadVisualSessions(liveIds: Set<string>): number;
|
|
51
64
|
/** Update the name of a session identified by its backend ID. */
|
|
52
65
|
updateName(backendSessionId: string, newName: string): void;
|
|
53
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* All sessions in creation order, after checking what is actually running.
|
|
68
|
+
*
|
|
69
|
+
* For anything a person asked for. Discovery is an AppleScript round trip, so
|
|
70
|
+
* this is the wrong call on a path that runs per message — use
|
|
71
|
+
* `knownSessions()` there.
|
|
72
|
+
*/
|
|
54
73
|
listSessions(): HybridSession[];
|
|
74
|
+
/**
|
|
75
|
+
* What the registry already knows, without going out to look.
|
|
76
|
+
*
|
|
77
|
+
* The read for hot paths: resolving a name for a push, checking that an id
|
|
78
|
+
* still exists on the way through. Those run per message, and an enumeration
|
|
79
|
+
* per message would put a terminal round trip in the delivery loop. Callers
|
|
80
|
+
* that need certainty rather than speed should fall back to `listSessions()`
|
|
81
|
+
* when this misses — cheap in the common case, correct in the rare one.
|
|
82
|
+
*/
|
|
83
|
+
knownSessions(): HybridSession[];
|
|
84
|
+
/**
|
|
85
|
+
* Bring the registry in line with what is actually running.
|
|
86
|
+
*
|
|
87
|
+
* Both read paths call this, which is the point: a caller cannot forget to
|
|
88
|
+
* populate, because populating is not the caller's job. Registration by the
|
|
89
|
+
* gateway and by the command that spawns a tab still works and is still
|
|
90
|
+
* useful — it names a session at the moment it is created, before discovery
|
|
91
|
+
* would have anything to go on — this only guarantees the floor.
|
|
92
|
+
*
|
|
93
|
+
* TWO FAILURES THAT ARE NOT THE SAME FACT. Discovery throwing means we could
|
|
94
|
+
* not look; discovery returning nothing means we looked and there is nothing.
|
|
95
|
+
* Folding them together would trade a false "none" for a false "current" —
|
|
96
|
+
* a list that reads as freshly enumerated while being last-known, which is
|
|
97
|
+
* the same lie as a Funnel reporting itself on while refusing connections.
|
|
98
|
+
*
|
|
99
|
+
* So: a throw keeps the known list AND is surfaced to the reader. An honest
|
|
100
|
+
* empty prunes, because a registry that keeps dead rows after looking is
|
|
101
|
+
* showing sessions that do not exist.
|
|
102
|
+
*/
|
|
103
|
+
private syncFromLive;
|
|
55
104
|
/** Get session by 1-based display index. */
|
|
56
105
|
getByIndex(index: number): HybridSession | undefined;
|
|
57
|
-
/**
|
|
106
|
+
/**
|
|
107
|
+
* Format the unified session list for display.
|
|
108
|
+
*
|
|
109
|
+
* Discovers first, so "No sessions." can only ever mean there are none —
|
|
110
|
+
* not that nobody had populated the registry yet. Before this, the answer
|
|
111
|
+
* depended on whether a PAILot client had happened to connect since the last
|
|
112
|
+
* daemon restart: ask over a channel before that and you were told the
|
|
113
|
+
* machine was empty, in the same words it would use if it were.
|
|
114
|
+
*/
|
|
58
115
|
formatSessionList(): string;
|
|
59
116
|
/**
|
|
60
117
|
* Get a status string for the active session.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/core/hybrid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/core/hybrid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,mBAAmB;IACnB,IAAI,EAAE,WAAW,CAAC;IAClB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,oBAAoB;IAC/B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAChD,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAC,CAA+F;IAChH,wFAAwF;IACxF,OAAO,CAAC,mBAAmB,CAAS;gBAExB,UAAU,EAAE,UAAU;IAIlC;;;OAGG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,GAAG,IAAI;IAMpH,oEAAoE;IACpE,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa;IAkB1D,2FAA2F;IAC3F,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,aAAa;IAiBvF,oFAAoF;IACpF,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAYvD,0FAA0F;IAC1F,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAiCvD,6EAA6E;IAC7E,kBAAkB,IAAI,IAAI;IAW1B,0DAA0D;IAC1D,IAAI,aAAa,IAAI,aAAa,GAAG,SAAS,CAE7C;IAED;;;OAGG;IACH,uBAAuB,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM;IAoBrD,iEAAiE;IACjE,UAAU,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ3D;;;;;;OAMG;IACH,YAAY,IAAI,aAAa,EAAE;IAK/B;;;;;;;;OAQG;IACH,aAAa,IAAI,aAAa,EAAE;IAIhC;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,YAAY;IAqCpB,4CAA4C;IAC5C,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAMpD;;;;;;;;OAQG;IACH,iBAAiB,IAAI,MAAM;IAqB3B;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,GAAG,IAAI;CASpC;AAED,wEAAwE;AACxE,eAAO,IAAI,aAAa,EAAE,oBAAoB,GAAG,IAAW,CAAC;AAE7D,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,oBAAoB,GAAG,IAAI,GAAG,IAAI,CAErE"}
|
package/dist/core/hybrid.js
CHANGED
|
@@ -10,14 +10,25 @@
|
|
|
10
10
|
* the active session's kind — the transport decides how to deliver.
|
|
11
11
|
*/
|
|
12
12
|
import { log } from "./log.js";
|
|
13
|
+
import { activeItermSessionId } from "./state.js";
|
|
13
14
|
export class HybridSessionManager {
|
|
14
15
|
apiBackend;
|
|
15
16
|
sessions = [];
|
|
16
17
|
_activeIndex = -1;
|
|
17
18
|
nextNum = 1;
|
|
19
|
+
discover;
|
|
20
|
+
/** True when the last attempt to look could not complete, so the list is last-known. */
|
|
21
|
+
lastDiscoveryFailed = false;
|
|
18
22
|
constructor(apiBackend) {
|
|
19
23
|
this.apiBackend = apiBackend;
|
|
20
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Where live sessions come from. Injected rather than imported so this stays
|
|
27
|
+
* a registry rather than growing a dependency on a particular terminal.
|
|
28
|
+
*/
|
|
29
|
+
setDiscovery(fn) {
|
|
30
|
+
this.discover = fn;
|
|
31
|
+
}
|
|
21
32
|
// ── Session creation ──
|
|
22
33
|
/** Create a new headless (API) session. Delegates to APIBackend. */
|
|
23
34
|
createApiSession(name, cwd) {
|
|
@@ -146,25 +157,120 @@ export class HybridSessionManager {
|
|
|
146
157
|
s.name = newName;
|
|
147
158
|
}
|
|
148
159
|
}
|
|
149
|
-
/**
|
|
160
|
+
/**
|
|
161
|
+
* All sessions in creation order, after checking what is actually running.
|
|
162
|
+
*
|
|
163
|
+
* For anything a person asked for. Discovery is an AppleScript round trip, so
|
|
164
|
+
* this is the wrong call on a path that runs per message — use
|
|
165
|
+
* `knownSessions()` there.
|
|
166
|
+
*/
|
|
150
167
|
listSessions() {
|
|
168
|
+
this.syncFromLive();
|
|
169
|
+
return [...this.sessions];
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* What the registry already knows, without going out to look.
|
|
173
|
+
*
|
|
174
|
+
* The read for hot paths: resolving a name for a push, checking that an id
|
|
175
|
+
* still exists on the way through. Those run per message, and an enumeration
|
|
176
|
+
* per message would put a terminal round trip in the delivery loop. Callers
|
|
177
|
+
* that need certainty rather than speed should fall back to `listSessions()`
|
|
178
|
+
* when this misses — cheap in the common case, correct in the rare one.
|
|
179
|
+
*/
|
|
180
|
+
knownSessions() {
|
|
151
181
|
return [...this.sessions];
|
|
152
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Bring the registry in line with what is actually running.
|
|
185
|
+
*
|
|
186
|
+
* Both read paths call this, which is the point: a caller cannot forget to
|
|
187
|
+
* populate, because populating is not the caller's job. Registration by the
|
|
188
|
+
* gateway and by the command that spawns a tab still works and is still
|
|
189
|
+
* useful — it names a session at the moment it is created, before discovery
|
|
190
|
+
* would have anything to go on — this only guarantees the floor.
|
|
191
|
+
*
|
|
192
|
+
* TWO FAILURES THAT ARE NOT THE SAME FACT. Discovery throwing means we could
|
|
193
|
+
* not look; discovery returning nothing means we looked and there is nothing.
|
|
194
|
+
* Folding them together would trade a false "none" for a false "current" —
|
|
195
|
+
* a list that reads as freshly enumerated while being last-known, which is
|
|
196
|
+
* the same lie as a Funnel reporting itself on while refusing connections.
|
|
197
|
+
*
|
|
198
|
+
* So: a throw keeps the known list AND is surfaced to the reader. An honest
|
|
199
|
+
* empty prunes, because a registry that keeps dead rows after looking is
|
|
200
|
+
* showing sessions that do not exist.
|
|
201
|
+
*/
|
|
202
|
+
syncFromLive() {
|
|
203
|
+
if (!this.discover)
|
|
204
|
+
return;
|
|
205
|
+
let live;
|
|
206
|
+
try {
|
|
207
|
+
live = this.discover();
|
|
208
|
+
this.lastDiscoveryFailed = false;
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
this.lastDiscoveryFailed = true;
|
|
212
|
+
log(`HybridManager: discovery failed, keeping the known list — ${e instanceof Error ? e.message : String(e)}`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const liveIds = new Set(live.map((s) => s.id));
|
|
216
|
+
this.pruneDeadVisualSessions(liveIds);
|
|
217
|
+
const known = new Set(this.sessions.map((s) => s.backendSessionId));
|
|
218
|
+
const seen = new Set();
|
|
219
|
+
const activeBefore = this.activeSession;
|
|
220
|
+
for (const snap of live) {
|
|
221
|
+
const displayName = snap.paiName ?? snap.tabTitle ?? snap.name;
|
|
222
|
+
if (seen.has(displayName))
|
|
223
|
+
continue;
|
|
224
|
+
seen.add(displayName);
|
|
225
|
+
if (!known.has(snap.id))
|
|
226
|
+
this.registerVisualSession(displayName, "", snap.id);
|
|
227
|
+
}
|
|
228
|
+
// registerVisualSession moves the selection to whatever it just added.
|
|
229
|
+
// Discovery is not a selection, so put it back where the user left it —
|
|
230
|
+
// and on the FIRST population there is nothing to put back, which would
|
|
231
|
+
// otherwise leave "active" pointing at whichever tab happened to be
|
|
232
|
+
// enumerated last. Fall back to the session the hub actually considers
|
|
233
|
+
// active, so this list and every other reader mark the same one.
|
|
234
|
+
const restoreTo = activeBefore?.backendSessionId ?? activeItermSessionId;
|
|
235
|
+
if (restoreTo) {
|
|
236
|
+
const i = this.sessions.findIndex((s) => s.backendSessionId === restoreTo);
|
|
237
|
+
if (i >= 0)
|
|
238
|
+
this._activeIndex = i;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
153
241
|
/** Get session by 1-based display index. */
|
|
154
242
|
getByIndex(index) {
|
|
155
243
|
return this.sessions[index - 1];
|
|
156
244
|
}
|
|
157
245
|
// ── Display ──
|
|
158
|
-
/**
|
|
246
|
+
/**
|
|
247
|
+
* Format the unified session list for display.
|
|
248
|
+
*
|
|
249
|
+
* Discovers first, so "No sessions." can only ever mean there are none —
|
|
250
|
+
* not that nobody had populated the registry yet. Before this, the answer
|
|
251
|
+
* depended on whether a PAILot client had happened to connect since the last
|
|
252
|
+
* daemon restart: ask over a channel before that and you were told the
|
|
253
|
+
* machine was empty, in the same words it would use if it were.
|
|
254
|
+
*/
|
|
159
255
|
formatSessionList() {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
256
|
+
this.syncFromLive();
|
|
257
|
+
// Say which of the two it is. A reader cannot otherwise tell a machine with
|
|
258
|
+
// nothing running from one we could not ask.
|
|
259
|
+
if (this.sessions.length === 0) {
|
|
260
|
+
return this.lastDiscoveryFailed
|
|
261
|
+
? "Could not enumerate sessions, and none were known."
|
|
262
|
+
: "No sessions.";
|
|
263
|
+
}
|
|
264
|
+
const rows = this.sessions.map((s, i) => {
|
|
163
265
|
const isActive = i === this._activeIndex;
|
|
164
266
|
const tag = s.kind === "api" ? "[api]" : "[visual]";
|
|
165
267
|
const marker = isActive ? "*" : " ";
|
|
166
268
|
return `${marker}${i + 1}. ${s.name} ${tag}${s.cwd ? ` (${s.cwd})` : ""}`;
|
|
167
|
-
})
|
|
269
|
+
});
|
|
270
|
+
if (this.lastDiscoveryFailed) {
|
|
271
|
+
rows.push("(could not enumerate sessions — showing last known)");
|
|
272
|
+
}
|
|
273
|
+
return rows.join("\n");
|
|
168
274
|
}
|
|
169
275
|
/**
|
|
170
276
|
* Get a status string for the active session.
|
package/dist/core/hybrid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hybrid.js","sourceRoot":"","sources":["../../src/core/hybrid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"hybrid.js","sourceRoot":"","sources":["../../src/core/hybrid.ts"],"names":[],"mappings":"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"}
|
package/dist/core/log.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/core/log.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/core/log.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAa5C"}
|
package/dist/core/log.js
CHANGED
|
@@ -7,7 +7,13 @@ export function setLogPrefix(prefix) {
|
|
|
7
7
|
_prefix = prefix;
|
|
8
8
|
}
|
|
9
9
|
export function log(...args) {
|
|
10
|
-
|
|
10
|
+
// LOCAL time, not UTC. Every other timestamp a person sees here — the audit
|
|
11
|
+
// trail, the tracker, the messages themselves — is local, and this log is
|
|
12
|
+
// read by lining it up against those. An offset that has to be remembered
|
|
13
|
+
// gets forgotten, and the reader concludes the events are unrelated.
|
|
14
|
+
const now = new Date();
|
|
15
|
+
const p = (n, w = 2) => String(n).padStart(w, "0");
|
|
16
|
+
const timestamp = `${p(now.getHours())}:${p(now.getMinutes())}:${p(now.getSeconds())}.${p(now.getMilliseconds(), 3)}`;
|
|
11
17
|
const parts = args.map((a) => typeof a === "string" ? a : JSON.stringify(a));
|
|
12
18
|
process.stderr.write(`[${_prefix} ${timestamp}] ${parts.join(" ")}\n`);
|
|
13
19
|
}
|
package/dist/core/log.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/core/log.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,IAAI,OAAO,GAAG,UAAU,CAAC;AAEzB,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,GAAG,MAAM,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,GAAG,IAAe;IACpC,MAAM,
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/core/log.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,IAAI,OAAO,GAAG,UAAU,CAAC;AAEzB,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,GAAG,MAAM,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,GAAG,IAAe;IACpC,4EAA4E;IAC5E,0EAA0E;IAC1E,0EAA0E;IAC1E,qEAAqE;IACrE,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,SAAS,GACb,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;IACtG,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3B,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAC9C,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,IAAI,SAAS,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACzE,CAAC"}
|
package/dist/core/mime.d.ts
CHANGED
|
@@ -8,4 +8,16 @@ export declare const MIME_MAP: Record<string, string>;
|
|
|
8
8
|
* Falls back to "application/octet-stream" for unknown extensions.
|
|
9
9
|
*/
|
|
10
10
|
export declare function lookupMime(ext: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* The extension a MIME type should be saved under, WITHOUT a leading dot.
|
|
13
|
+
*
|
|
14
|
+
* The reverse direction needs to exist here rather than at each call site. Both
|
|
15
|
+
* places that had their own answer got it wrong in the same way: a short list of
|
|
16
|
+
* types they had thought about, and `bin` for everything else — so a video
|
|
17
|
+
* arrived as `.bin`, unplayable until renamed by hand. A table that already
|
|
18
|
+
* knows every type it accepts should also know what to call it on the way back.
|
|
19
|
+
*
|
|
20
|
+
* Falls back to `bin`, which is honest for a type nothing here recognises.
|
|
21
|
+
*/
|
|
22
|
+
export declare function extForMime(mime: string): string;
|
|
11
23
|
//# sourceMappingURL=mime.d.ts.map
|
package/dist/core/mime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../src/core/mime.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAgC3C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9C"}
|
|
1
|
+
{"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../src/core/mime.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAgC3C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS/C"}
|
package/dist/core/mime.js
CHANGED
|
@@ -43,4 +43,27 @@ export function lookupMime(ext) {
|
|
|
43
43
|
const normalized = ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`;
|
|
44
44
|
return MIME_MAP[normalized] ?? "application/octet-stream";
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* The extension a MIME type should be saved under, WITHOUT a leading dot.
|
|
48
|
+
*
|
|
49
|
+
* The reverse direction needs to exist here rather than at each call site. Both
|
|
50
|
+
* places that had their own answer got it wrong in the same way: a short list of
|
|
51
|
+
* types they had thought about, and `bin` for everything else — so a video
|
|
52
|
+
* arrived as `.bin`, unplayable until renamed by hand. A table that already
|
|
53
|
+
* knows every type it accepts should also know what to call it on the way back.
|
|
54
|
+
*
|
|
55
|
+
* Falls back to `bin`, which is honest for a type nothing here recognises.
|
|
56
|
+
*/
|
|
57
|
+
export function extForMime(mime) {
|
|
58
|
+
const normalized = mime.toLowerCase().split(";")[0].trim();
|
|
59
|
+
for (const [ext, type] of Object.entries(MIME_MAP)) {
|
|
60
|
+
if (type === normalized)
|
|
61
|
+
return ext.slice(1);
|
|
62
|
+
}
|
|
63
|
+
// A type we do not carry, but whose subtype names itself: image/heic -> heic.
|
|
64
|
+
const subtype = normalized.split("/")[1];
|
|
65
|
+
if (subtype && /^[a-z0-9]{2,5}$/.test(subtype))
|
|
66
|
+
return subtype;
|
|
67
|
+
return "bin";
|
|
68
|
+
}
|
|
46
69
|
//# sourceMappingURL=mime.js.map
|
package/dist/core/mime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime.js","sourceRoot":"","sources":["../../src/core/mime.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,QAAQ,GAA2B;IAC9C,YAAY;IACZ,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,oBAAoB;IAC5B,OAAO,EAAE,yEAAyE;IAClF,MAAM,EAAE,0BAA0B;IAClC,OAAO,EAAE,mEAAmE;IAC5E,MAAM,EAAE,+BAA+B;IACvC,OAAO,EAAE,2EAA2E;IACpF,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE,kBAAkB;IACzB,OAAO,EAAE,kBAAkB;IAC3B,SAAS;IACT,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,eAAe;IACvB,QAAQ;IACR,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,kBAAkB;IAC1B,QAAQ;IACR,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;IACrF,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,0BAA0B,CAAC;AAC5D,CAAC"}
|
|
1
|
+
{"version":3,"file":"mime.js","sourceRoot":"","sources":["../../src/core/mime.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,QAAQ,GAA2B;IAC9C,YAAY;IACZ,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,oBAAoB;IAC5B,OAAO,EAAE,yEAAyE;IAClF,MAAM,EAAE,0BAA0B;IAClC,OAAO,EAAE,mEAAmE;IAC5E,MAAM,EAAE,+BAA+B;IACvC,OAAO,EAAE,2EAA2E;IACpF,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE,kBAAkB;IACzB,OAAO,EAAE,kBAAkB;IAC3B,SAAS;IACT,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,eAAe;IACvB,QAAQ;IACR,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,kBAAkB;IAC1B,QAAQ;IACR,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;IACrF,OAAO,QAAQ,CAAC,UAAU,CAAC,IAAI,0BAA0B,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3D,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnD,IAAI,IAAI,KAAK,UAAU;YAAE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,8EAA8E;IAC9E,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,IAAI,OAAO,IAAI,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC/D,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/session-discovery.ts — one answer to "which sessions are there".
|
|
3
|
+
*
|
|
4
|
+
* There were two, and they disagreed. The MCP tool enumerated live iTerm tabs
|
|
5
|
+
* directly; the channel commands read `HybridSessionManager`, an in-memory
|
|
6
|
+
* registry that only the PAILot gateway ever populated. So `aibroker_sessions`
|
|
7
|
+
* reported eight sessions while `/s` over WhatsApp answered "No sessions" —
|
|
8
|
+
* and the daemon's own handler carried the comment
|
|
9
|
+
*
|
|
10
|
+
* manager.listSessions() is always empty (nothing populates the registry)
|
|
11
|
+
*
|
|
12
|
+
* which is the defect written down and worked around rather than fixed.
|
|
13
|
+
*
|
|
14
|
+
* WHY THIS IS WORSE THAN A WRONG NUMBER. "No sessions" is what an empty machine
|
|
15
|
+
* says, so an unpopulated registry is indistinguishable from a true answer. The
|
|
16
|
+
* same shape has bitten this codebase before and been closed deliberately each
|
|
17
|
+
* time: an unreadable audit log that read as an empty one, a Funnel reporting
|
|
18
|
+
* itself on while refusing every connection. A reader cannot tell "none" from
|
|
19
|
+
* "did not look" unless the code refuses to say "none" without looking.
|
|
20
|
+
*
|
|
21
|
+
* So discovery lives here, both readers call it, and nobody has to remember to
|
|
22
|
+
* populate anything first.
|
|
23
|
+
*/
|
|
24
|
+
import { snapshotAllSessions } from "../adapters/iterm/core.js";
|
|
25
|
+
export type LiveSession = ReturnType<typeof snapshotAllSessions>[0];
|
|
26
|
+
/**
|
|
27
|
+
* Live sessions, with their PAI names filled in.
|
|
28
|
+
*
|
|
29
|
+
* `snapshotAllSessions()` has returned `paiName: null` since 0.7.10 — the
|
|
30
|
+
* authoritative source is the persistent store — so a caller that skips this
|
|
31
|
+
* step gets tab titles where it expects names.
|
|
32
|
+
*/
|
|
33
|
+
export declare function discoverLiveSessions(): LiveSession[];
|
|
34
|
+
/**
|
|
35
|
+
* Is this tab something a person would call a session?
|
|
36
|
+
*
|
|
37
|
+
* Deliberately generous: a named tab, anything that says Claude, or anything
|
|
38
|
+
* not sitting at a shell prompt. Being wrong in the inclusive direction shows
|
|
39
|
+
* a row too many, which a human corrects at a glance; being wrong the other way
|
|
40
|
+
* hides a session that exists, which reads as it not existing at all.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isClaudeRelated(snap: LiveSession): boolean;
|
|
43
|
+
//# sourceMappingURL=session-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-discovery.d.ts","sourceRoot":"","sources":["../../src/core/session-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAGhE,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,WAAW,EAAE,CAOpD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAM1D"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/session-discovery.ts — one answer to "which sessions are there".
|
|
3
|
+
*
|
|
4
|
+
* There were two, and they disagreed. The MCP tool enumerated live iTerm tabs
|
|
5
|
+
* directly; the channel commands read `HybridSessionManager`, an in-memory
|
|
6
|
+
* registry that only the PAILot gateway ever populated. So `aibroker_sessions`
|
|
7
|
+
* reported eight sessions while `/s` over WhatsApp answered "No sessions" —
|
|
8
|
+
* and the daemon's own handler carried the comment
|
|
9
|
+
*
|
|
10
|
+
* manager.listSessions() is always empty (nothing populates the registry)
|
|
11
|
+
*
|
|
12
|
+
* which is the defect written down and worked around rather than fixed.
|
|
13
|
+
*
|
|
14
|
+
* WHY THIS IS WORSE THAN A WRONG NUMBER. "No sessions" is what an empty machine
|
|
15
|
+
* says, so an unpopulated registry is indistinguishable from a true answer. The
|
|
16
|
+
* same shape has bitten this codebase before and been closed deliberately each
|
|
17
|
+
* time: an unreadable audit log that read as an empty one, a Funnel reporting
|
|
18
|
+
* itself on while refusing every connection. A reader cannot tell "none" from
|
|
19
|
+
* "did not look" unless the code refuses to say "none" without looking.
|
|
20
|
+
*
|
|
21
|
+
* So discovery lives here, both readers call it, and nobody has to remember to
|
|
22
|
+
* populate anything first.
|
|
23
|
+
*/
|
|
24
|
+
import { snapshotAllSessions } from "../adapters/iterm/core.js";
|
|
25
|
+
import { getAllPersistentSessionNames, lookupPersistentName } from "./persistence.js";
|
|
26
|
+
/**
|
|
27
|
+
* Live sessions, with their PAI names filled in.
|
|
28
|
+
*
|
|
29
|
+
* `snapshotAllSessions()` has returned `paiName: null` since 0.7.10 — the
|
|
30
|
+
* authoritative source is the persistent store — so a caller that skips this
|
|
31
|
+
* step gets tab titles where it expects names.
|
|
32
|
+
*/
|
|
33
|
+
export function discoverLiveSessions() {
|
|
34
|
+
const snaps = snapshotAllSessions();
|
|
35
|
+
const persistentNames = getAllPersistentSessionNames();
|
|
36
|
+
for (const snap of snaps) {
|
|
37
|
+
snap.paiName = lookupPersistentName(persistentNames, snap.id, snap.aibrokerId);
|
|
38
|
+
}
|
|
39
|
+
return snaps;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Is this tab something a person would call a session?
|
|
43
|
+
*
|
|
44
|
+
* Deliberately generous: a named tab, anything that says Claude, or anything
|
|
45
|
+
* not sitting at a shell prompt. Being wrong in the inclusive direction shows
|
|
46
|
+
* a row too many, which a human corrects at a glance; being wrong the other way
|
|
47
|
+
* hides a session that exists, which reads as it not existing at all.
|
|
48
|
+
*/
|
|
49
|
+
export function isClaudeRelated(snap) {
|
|
50
|
+
if (snap.paiName)
|
|
51
|
+
return true;
|
|
52
|
+
const name = (snap.tabTitle ?? snap.name).toLowerCase();
|
|
53
|
+
if (name.includes("claude"))
|
|
54
|
+
return true;
|
|
55
|
+
if (!snap.atPrompt)
|
|
56
|
+
return true;
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=session-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-discovery.js","sourceRoot":"","sources":["../../src/core/session-discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAItF;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,MAAM,eAAe,GAAG,4BAA4B,EAAE,CAAC;IACvD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,oBAAoB,CAAC,eAAe,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,IAAiB;IAC/C,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACxD,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/daemon/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/daemon/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAwDH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA6B3D;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,IAAI,CACzC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,cAAc,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CA88BxB"}
|