thinkpool-pair 0.7.225 → 0.7.227
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/account.mjs +4 -4
- package/bridge.mjs +6 -6
- package/event-bounds.mjs +1 -1
- package/package.json +1 -1
- package/viewport.mjs +6 -2
package/account.mjs
CHANGED
|
@@ -778,7 +778,7 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
778
778
|
if (!tc || roomPartner.get(target) !== partnerUid) return reply({ error: `Room ${target} is not a room this device shares with you.` })
|
|
779
779
|
const reqId = randomUUID()
|
|
780
780
|
pairPending.set(reqId, { reply: (mm) => reply({ ok: mm.ok, ref: mm.ref, error: mm.error }), askerRoom: null })
|
|
781
|
-
try { tc.send({ t: 'pair-post-local', reqId, fromRoom: payload.fromRoom, fromHost: payload.fromHost, text: payload.text, terminal: payload.terminal }) }
|
|
781
|
+
try { tc.send({ t: 'pair-post-local', reqId, fromRoom: payload.fromRoom, fromHost: payload.fromHost, fromTerminalName: payload.fromTerminalName, text: payload.text, terminal: payload.terminal }) }
|
|
782
782
|
catch { pairPending.delete(reqId); reply({ error: `Could not reach room ${target}.` }) }
|
|
783
783
|
}
|
|
784
784
|
// Join the per-pair bus for `partnerUid` (idempotent). The channel is PRIVATE so
|
|
@@ -801,7 +801,7 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
801
801
|
// Forward a local child's cross-room request to the bus (Tier 2 peek / Tier 3 post)
|
|
802
802
|
// when the target room is NOT served on this machine. Used inside the child handler.
|
|
803
803
|
const busPeek = (targetRoom, terminal, lines) => busRequestFirst('peer-peek-req', { targetRoom, terminal, lines }, CROSSROOM_BUS.busTimeoutMs)
|
|
804
|
-
const busPost = (targetRoom, terminal, text, fromRoom) => busRequestFirst('peer-post-req', { targetRoom, terminal, text, fromRoom, fromHost: machine }, 180_000)
|
|
804
|
+
const busPost = (targetRoom, terminal, text, fromRoom, fromTerminalName) => busRequestFirst('peer-post-req', { targetRoom, terminal, text, fromRoom, fromHost: machine, fromTerminalName }, 180_000)
|
|
805
805
|
|
|
806
806
|
// Re-entrancy guard: bind-room fires tick() AND a 15s interval fires tick(); tick
|
|
807
807
|
// awaits a code_sessions query before children.set(), so two overlapping runs can
|
|
@@ -953,13 +953,13 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
953
953
|
if (tc) { // LOCAL target — the target child raises its own receipt card
|
|
954
954
|
const reqId = randomUUID()
|
|
955
955
|
pairPending.set(reqId, { reply: (mm) => reply({ ok: mm.ok, ref: mm.ref, error: mm.error }), askerRoom: room })
|
|
956
|
-
try { tc.send({ t: 'pair-post-local', reqId, fromRoom: room, fromHost: machine, text: m.text, terminal: m.terminal }) }
|
|
956
|
+
try { tc.send({ t: 'pair-post-local', reqId, fromRoom: room, fromHost: machine, fromTerminalName: m.fromTerminalName, text: m.text, terminal: m.terminal }) }
|
|
957
957
|
catch { pairPending.delete(reqId); reply({ error: `Could not reach session ${target}.` }) }
|
|
958
958
|
return
|
|
959
959
|
}
|
|
960
960
|
// REMOTE target — over the pair bus.
|
|
961
961
|
if (!pairChannels.size) return reply({ error: `Room ${target} is not on this machine, and no pair bus is open to reach it.` })
|
|
962
|
-
const res = await busPost(target, m.terminal, m.text, room)
|
|
962
|
+
const res = await busPost(target, m.terminal, m.text, room, m.fromTerminalName)
|
|
963
963
|
if (!res) return reply({ error: `Room ${target} did not respond to the hand-off in time.` })
|
|
964
964
|
reply({ ok: res.ok, ref: res.ref, error: res.error })
|
|
965
965
|
})()
|
package/bridge.mjs
CHANGED
|
@@ -1123,7 +1123,7 @@ function raiseCard(entry, req) {
|
|
|
1123
1123
|
// the target agent lane, raise the receipt card, and on approval inject the turn one
|
|
1124
1124
|
// room-hop deep (so the reached agent can read/peek but cannot post onward to a third
|
|
1125
1125
|
// room — crossRoomPostDecision blocks roomHop≥1). Returns { ok, ref } or { error }.
|
|
1126
|
-
async function receiveCrossRoomPost({ fromRoom, fromHost, text, terminal }) {
|
|
1126
|
+
async function receiveCrossRoomPost({ fromRoom, fromHost, fromTerminalName, text, terminal }) {
|
|
1127
1127
|
if (process.env.TP_PAIRBUS_OFF === '1') return { error: 'Cross-room posting is turned off in this room.' }
|
|
1128
1128
|
const body = String(text || '').trim()
|
|
1129
1129
|
if (!body) return { error: 'Empty cross-room post ignored.' }
|
|
@@ -1145,13 +1145,13 @@ async function receiveCrossRoomPost({ fromRoom, fromHost, text, terminal }) {
|
|
|
1145
1145
|
if (!te?.session) return { error: `That terminal in room ${room} is no longer live.` }
|
|
1146
1146
|
const fromLabel = `${fromRoom}${fromHost ? ` on ${fromHost}` : ''}`
|
|
1147
1147
|
let decision = 'deny'
|
|
1148
|
-
try { decision = await raiseCard(te, { id: randomUUID(), toolName: 'mcp__thinkpool__post_to_session', input: { session: fromRoom, host: fromHost, text: body, incoming: true }, risk: 'high' }) } catch { decision = 'deny' }
|
|
1148
|
+
try { decision = await raiseCard(te, { id: randomUUID(), toolName: 'mcp__thinkpool__post_to_session', input: { session: fromRoom, host: fromHost, terminalName: fromTerminalName, text: body, incoming: true }, risk: 'high' }) } catch { decision = 'deny' }
|
|
1149
1149
|
if (decision !== 'allow') return { error: `The people in room ${room} declined the incoming task from ${fromLabel}.` }
|
|
1150
1150
|
// Injected turn: one room-hop deep, fresh in-room budgets (the loop-breaker reset
|
|
1151
1151
|
// point is code-turn — a real human turn — which this is NOT, so the deeper hop sticks).
|
|
1152
1152
|
te.roomHop = 1; te.hop = (te.hop || 0) + 1; te.peekCount = 0; te.postCount = 0; te.pairPeekCount = 0; te.crossRoomPostCount = 0
|
|
1153
1153
|
const msg = `[From: room ${fromLabel} — relayed via the ThinkPool cross-room Ensemble, approved by a person in this room]\n${body}`
|
|
1154
|
-
const evt = { kind: 'you', text: msg, by: `room ${fromRoom}`, crosspost: true }
|
|
1154
|
+
const evt = { kind: 'you', text: msg, by: `room ${fromRoom}`, crosspost: true, relaySourceName: String(fromTerminalName || '').trim().slice(0, 80) || undefined }
|
|
1155
1155
|
stampEvent(evt); pushLog(te, evt); bcast('code-event', { term: targetId, evt })
|
|
1156
1156
|
try { te.session.sendTurn(msg) } catch { return { error: `Could not deliver to room ${room} — the lane may have just closed.` } }
|
|
1157
1157
|
return { ok: true, ref: String(targetId).slice(0, 8) }
|
|
@@ -2047,7 +2047,7 @@ function openStructured({ id, runtime = 'claude', model, effort, resume, log, co
|
|
|
2047
2047
|
entry.crossRoomPostCount = (entry.crossRoomPostCount || 0) + 1
|
|
2048
2048
|
// Generous timeout: the TARGET room's person must approve a receipt card,
|
|
2049
2049
|
// which can take a while. Fails soft (never hangs the turn) past the window.
|
|
2050
|
-
const res = await pairRequest('pair-post-req', { targetRoom: target, terminal: args?.terminal, text, fromRoom: room }, 180_000)
|
|
2050
|
+
const res = await pairRequest('pair-post-req', { targetRoom: target, terminal: args?.terminal, text, fromRoom: room, fromTerminalName: termNames[id] || undefined }, 180_000)
|
|
2051
2051
|
if (res?.error) return okText(res.error)
|
|
2052
2052
|
return okText(res?.ok ? `Delivered to room ${target}${res.ref ? ` (lane ${res.ref})` : ''} — it will respond in its own room; check back with read_session.` : `Room ${target} did not accept the message.`)
|
|
2053
2053
|
},
|
|
@@ -2087,7 +2087,7 @@ function openStructured({ id, runtime = 'claude', model, effort, resume, log, co
|
|
|
2087
2087
|
te.postCount = 0
|
|
2088
2088
|
// Echo the injected prompt into the TARGET lane so both people see what
|
|
2089
2089
|
// arrived (rides the existing code-event 'you' path — no new topic).
|
|
2090
|
-
const evt = { kind: 'you', text: msg, by: `terminal ${fromRef} (agent)`, crosspost: true }
|
|
2090
|
+
const evt = { kind: 'you', text: msg, by: `terminal ${fromRef} (agent)`, crosspost: true, relaySourceName: termNames[id] || undefined }
|
|
2091
2091
|
stampEvent(evt)
|
|
2092
2092
|
pushLog(te, evt)
|
|
2093
2093
|
bcast('code-event', { term: target.id, evt })
|
|
@@ -2177,7 +2177,7 @@ function openStructured({ id, runtime = 'claude', model, effort, resume, log, co
|
|
|
2177
2177
|
// to any lane still resets it to hop 0 (code-turn). Mirrors Tier C injection.
|
|
2178
2178
|
ne.hop = (entry.hop || 0) + 1; ne.peekCount = 0; ne.postCount = 0; ne.spawnTimes = []
|
|
2179
2179
|
const msg = `[Task from terminal ${fromRef}'s agent — relayed via ThinkPool cross-terminal; you are a fresh lane it opened for you]\n${args.task}`
|
|
2180
|
-
const evt = { kind: 'you', text: msg, by: `terminal ${fromRef} (agent)`, crosspost: true }
|
|
2180
|
+
const evt = { kind: 'you', text: msg, by: `terminal ${fromRef} (agent)`, crosspost: true, relaySourceName: termNames[id] || undefined }
|
|
2181
2181
|
stampEvent(evt); pushLog(ne, evt); bcast('code-event', { term: newId, evt })
|
|
2182
2182
|
try { ne.session.sendTurn(msg) } catch { return okText(`Opened lane ${newRef}${args?.name ? ` ("${args.name}")` : ''}, but it may still be starting — could not hand off the task. Try post_to_terminal shortly.`) }
|
|
2183
2183
|
return okText(`Opened agent lane ${newRef}${args?.name ? ` ("${args.name}")` : ''} and handed it the task. It runs in its own lane — check back with read_terminal, then close_terminal when done.`)
|
package/event-bounds.mjs
CHANGED
|
@@ -83,7 +83,7 @@ export function boundStructuredEvent(event, cap = STRUCTURED_EVENT_CAP) {
|
|
|
83
83
|
|
|
84
84
|
// Hard fallback: retain ordering/dedup identity and recognisable tool blocks.
|
|
85
85
|
const identity = {}
|
|
86
|
-
for (const key of ['kind', 'seq', 'cid', 'ts', 'term', 'toolUseId', 'isError']) {
|
|
86
|
+
for (const key of ['kind', 'seq', 'cid', 'ts', 'term', 'toolUseId', 'isError', 'by', 'crosspost', 'relaySourceName']) {
|
|
87
87
|
if (out[key] != null) identity[key] = out[key]
|
|
88
88
|
}
|
|
89
89
|
if (Array.isArray(out.blocks)) identity.blocks = out.blocks.slice(0, 4).map(block => compactBlock(block, 6000))
|
package/package.json
CHANGED
package/viewport.mjs
CHANGED
|
@@ -350,9 +350,13 @@ export class ViewportManager {
|
|
|
350
350
|
await fsp.writeFile(file, result.png)
|
|
351
351
|
captures[name] = { ...result, file }
|
|
352
352
|
}
|
|
353
|
-
|
|
353
|
+
// A preview root may be a multi-file build. Its index.html is not a portable
|
|
354
|
+
// artifact: root-relative chunks cannot reliably boot when that text is later
|
|
355
|
+
// injected into a sandboxed srcDoc (Windows Edge exposed the permanent loader).
|
|
356
|
+
// preview_capture is therefore screenshot-only evidence. Editable mockups use
|
|
357
|
+
// render.sh's explicit trusted `source` contract instead.
|
|
354
358
|
const manifest = {
|
|
355
|
-
slug, title: String(title || 'Viewport capture').slice(0, 120),
|
|
359
|
+
slug, title: String(title || 'Viewport capture').slice(0, 120),
|
|
356
360
|
desktop: captures.desktop?.file || '', mobile: captures.mobile?.file || '', ts: Date.now(),
|
|
357
361
|
}
|
|
358
362
|
const manifestPath = path.join(this.outbox, `${slug}.json`)
|