thinkpool-pair 0.7.37 → 0.7.39
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 +8 -1
- package/claude-session.mjs +23 -0
- package/package.json +1 -1
package/account.mjs
CHANGED
|
@@ -260,13 +260,20 @@ export async function runAccount(SUPABASE_URL, SUPABASE_ANON) {
|
|
|
260
260
|
const tick = async () => {
|
|
261
261
|
let rooms = []
|
|
262
262
|
try {
|
|
263
|
-
|
|
263
|
+
// Owner-only discovery (Contract C-CODE-2): a host bridges ONLY the sessions
|
|
264
|
+
// it CREATED. RLS on code_sessions returns owned OR participant rooms, so
|
|
265
|
+
// without this owner filter the supervisor auto-serves rooms you merely JOINED
|
|
266
|
+
// as a guest — spawning your machine into another owner's session that already
|
|
267
|
+
// runs its own bridge. The web watches tpacct:<session-owner>, so a
|
|
268
|
+
// participant's bridge is duplication the UI never even looks at.
|
|
269
|
+
const { data } = await sb.from('code_sessions').select('code,name,last_active_at,owner_id').eq('owner_id', session.user.id).order('last_active_at', { ascending: false }).limit(20)
|
|
264
270
|
rooms = data || []
|
|
265
271
|
} catch { /* transient — keep existing children, retry next tick */ }
|
|
266
272
|
const dirs = loadDirs()
|
|
267
273
|
for (const r of rooms) {
|
|
268
274
|
const room = r.code
|
|
269
275
|
if (!room || children.has(room)) continue
|
|
276
|
+
if (r.owner_id && r.owner_id !== session.user.id) continue // never serve a room we don't own (defense-in-depth behind the .eq above)
|
|
270
277
|
const dir = dirs[room] || DEFAULT_DIR // auto-serve unbound sessions in the launch dir
|
|
271
278
|
if (!fs.existsSync(dir)) {
|
|
272
279
|
if (!warned.has(room)) { warned.add(room); process.stderr.write(`\n ◇ ${room}: bound directory is gone (${dir}) — re-bind it.\n`) }
|
package/claude-session.mjs
CHANGED
|
@@ -123,6 +123,14 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
|
|
|
123
123
|
const input = makeInputStream()
|
|
124
124
|
let sessionId = resume || null
|
|
125
125
|
let closed = false
|
|
126
|
+
// Cold-start measurement (2026-06-21, spec 2026-06-21-code-session-deploy-stability):
|
|
127
|
+
// stamp when the open begins so we can log spawn→ready (the SDK's first `init`
|
|
128
|
+
// system message — when the terminal becomes usable). New room terminals "take a
|
|
129
|
+
// long time"; settingSources:['user','project','local'] boots every host MCP
|
|
130
|
+
// server + SessionStart hook + plugin before init. This is the A-side baseline;
|
|
131
|
+
// open a terminal with TP_MCP_STRICT=1 (below) for the B-side to isolate MCP's share.
|
|
132
|
+
const spawnT0 = Date.now()
|
|
133
|
+
let readyLogged = false
|
|
126
134
|
let q = null // the live Query — control requests (interrupt /
|
|
127
135
|
// setPermissionMode) route through it once streaming.
|
|
128
136
|
// The session is CREATED in the caller's chosen mode (not hard-coded default):
|
|
@@ -302,6 +310,13 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
|
|
|
302
310
|
// the loop; only the coarse thinking_tokens system message is surfaced.
|
|
303
311
|
includePartialMessages: true,
|
|
304
312
|
}
|
|
313
|
+
// Measurement lever (default OFF — no behavior change). TP_MCP_STRICT=1 makes the
|
|
314
|
+
// room session skip booting the host's filesystem-configured MCP servers
|
|
315
|
+
// (strictMcpConfig + empty mcpServers map), isolating the MCP share of cold-start
|
|
316
|
+
// in the spawn→ready log below. settingSources still loads slash commands /
|
|
317
|
+
// CLAUDE.md / agents either way — strict only governs MCP server sourcing. Flip
|
|
318
|
+
// this to the default (or curate a slim allowlist) once the numbers justify it.
|
|
319
|
+
if (process.env.TP_MCP_STRICT === '1') { opts.strictMcpConfig = true; opts.mcpServers = {} }
|
|
305
320
|
if (cwd) opts.cwd = cwd
|
|
306
321
|
if (model) opts.model = model
|
|
307
322
|
if (resume) opts.resume = resume
|
|
@@ -343,6 +358,14 @@ export function startClaudeSession({ cwd, model, resume, env, mode: initialMode
|
|
|
343
358
|
break
|
|
344
359
|
}
|
|
345
360
|
if (m.session_id) sessionId = m.session_id
|
|
361
|
+
// Cold-start timing — the init system message is when the terminal is
|
|
362
|
+
// usable, so spawn→here IS the latency the user waits on. One line per
|
|
363
|
+
// open. Compare a normal open vs one with TP_MCP_STRICT=1 to read off how
|
|
364
|
+
// many ms the host MCP servers cost. (resume opens also pay sanitizeSession.)
|
|
365
|
+
if (!readyLogged) {
|
|
366
|
+
readyLogged = true
|
|
367
|
+
process.stderr.write(`\n ◆ session ready in ${Date.now() - spawnT0}ms — MCP ${process.env.TP_MCP_STRICT === '1' ? 'OFF' : 'on'}${resume ? ', resume' : ', fresh'}.\n`)
|
|
368
|
+
}
|
|
346
369
|
// m.slash_commands (init message) — the commands this session really
|
|
347
370
|
// supports: built-ins + the host's custom .claude/commands. Surfaced
|
|
348
371
|
// so the room composer's autocomplete lists what ACTUALLY exists.
|