thinkpool-pair 0.7.345 → 0.7.347

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/bridge.mjs CHANGED
@@ -55,7 +55,7 @@ import { readCodexDefaultModel, readCodexModels, codexConfigForMode, codexThread
55
55
  import { codexAccountUsageLine, codexCreditsReportLine, codexLimitReportLine } from './codex-commands.mjs'
56
56
  import { withMcpSessionFactory } from './codex-mcp-http.mjs'
57
57
  import { startStructuredSession } from './runtime-session.mjs'
58
- import { fallbackTerminalName } from './terminal-name.mjs'
58
+ import { cleanTerminalName, modelTerminalNameInput } from './terminal-name.mjs'
59
59
  import { defaultStructuredMode, normalizeStructuredEffort, shouldDeferStructuredRuntime, structuredModeForSlice, structuredModeLocked, structuredModesForLane, structuredRuntimeForCommand, structuredRuntimeMetadata, structuredRuntimeSupportsMode } from './runtime-registry.mjs'
60
60
  import { commandCatalogForRuntime, commandHelpLine, reconcileCommandCatalog } from './command-catalog.mjs'
61
61
  import { gitDiffReport } from './git-diff-report.mjs'
@@ -1479,16 +1479,48 @@ const applyAutoTerminalName = (id, candidate) => {
1479
1479
  return true
1480
1480
  }
1481
1481
 
1482
- // First real task only. Naming is deterministic and entirely local: no provider
1483
- // receives the task, and every runtime follows the same zero-token path.
1482
+ const requestManagedTerminalName = async (id, task, retry = true) => {
1483
+ if (!codeAuthToken || !id || !task) return
1484
+ const controller = new AbortController()
1485
+ const timeout = setTimeout(() => controller.abort(), 4500)
1486
+ try {
1487
+ const response = await fetch(`${WEB_BASE}/api/code-terminal-name`, {
1488
+ method: 'POST',
1489
+ headers: {
1490
+ Authorization: `Bearer ${codeAuthToken}`,
1491
+ 'Content-Type': 'application/json',
1492
+ },
1493
+ body: JSON.stringify({ code: room, terminalId: id, task }),
1494
+ signal: controller.signal,
1495
+ })
1496
+ clearTimeout(timeout)
1497
+ const data = await response.json().catch(() => ({}))
1498
+ // Bridge-created lanes can receive their task before the browser has
1499
+ // inserted the terminal row. Retry that non-billable race once; every
1500
+ // provider failure leaves the terminal on its ordinary default label.
1501
+ if (retry && response.status === 404 && data?.code === 'terminal_pending') {
1502
+ const timer = setTimeout(() => { void requestManagedTerminalName(id, task, false) }, 1500)
1503
+ timer.unref?.()
1504
+ return
1505
+ }
1506
+ if (!response.ok) return
1507
+ const candidate = cleanTerminalName(data?.name)
1508
+ if (candidate) applyAutoTerminalName(id, candidate)
1509
+ } catch {
1510
+ clearTimeout(timeout)
1511
+ }
1512
+ }
1513
+
1514
+ // First real task only. Naming is managed-model-only: keep the terminal's
1515
+ // ordinary default label until Groq returns instead of showing a local guess.
1484
1516
  const autoNameTerminal = (id, text) => {
1485
1517
  const entry = sessions.get(id)
1486
1518
  if (!entry || termNames[id] || manualNameTouched.has(id) || autoNameAttempts.has(id)) return
1487
1519
  if (entry.log?.some((event) => event?.kind === 'you')) return
1488
- const fallback = fallbackTerminalName(text)
1489
- if (!fallback) return
1520
+ const task = modelTerminalNameInput(text)
1521
+ if (!task) return
1490
1522
  autoNameAttempts.add(id)
1491
- applyAutoTerminalName(id, fallback)
1523
+ void requestManagedTerminalName(id, task)
1492
1524
  }
1493
1525
 
1494
1526
  // (cross-person grantee yield removed 2026-07-06 — owner-only serving now; the owner's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.345",
3
+ "version": "0.7.347",
4
4
  "description": "Connect Claude Code, Codex, or Hermes on your computer to a Thinkpool Code room.",
5
5
  "type": "module",
6
6
  "bin": {
package/terminal-name.mjs CHANGED
@@ -315,3 +315,29 @@ export function fallbackTerminalName(text) {
315
315
  .slice(0, 1600)
316
316
  return taskTitle(body)
317
317
  }
318
+
319
+ const MODEL_INPUT_MAX_BYTES = 1600
320
+
321
+ const truncateUtf8 = (value, maxBytes = MODEL_INPUT_MAX_BYTES) => {
322
+ let result = ''
323
+ let bytes = 0
324
+ for (const char of String(value || '')) {
325
+ const size = Buffer.byteLength(char, 'utf8')
326
+ if (bytes + size > maxBytes) break
327
+ result += char
328
+ bytes += size
329
+ }
330
+ return result.trim()
331
+ }
332
+
333
+ // The managed namer never receives the raw room prompt. Reuse the local
334
+ // extractor's authoritative-task selection, secret redaction, and host-reference
335
+ // stripping, then add a byte ceiling so multilingual text stays cost-bounded.
336
+ export function modelTerminalNameInput(text) {
337
+ const body = withoutHostReferences(withoutSecrets(extractTerminalTask(text) || ''))
338
+ .replace(/[`*_>#()[\]{}]/g, ' ')
339
+ .replace(/\s+/g, ' ')
340
+ .trim()
341
+ if (!body || containsSecret(body)) return null
342
+ return truncateUtf8(body)
343
+ }