thinkpool-pair 0.7.218 → 0.7.219

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
@@ -65,7 +65,7 @@ import { writeLaneArtifact, digestSlice, appendDigest, resumeLane } from './flow
65
65
  import { createFlowWorktree, worktreeSpec } from './flow-worktree.mjs'
66
66
  import { startPreview, stopAllPreviews, previews } from './flow-preview.mjs'
67
67
  import { ViewportManager, createViewportTools, sharedViewportBrowser } from './viewport.mjs'
68
- import { designPrompt, resolveDesignSource, syncRealtimeAuth, validateDesignRequest } from './design-edit.mjs'
68
+ import { designPrompt, resolveDesignSource, restoreDesignSources, syncRealtimeAuth, validateDesignRequest } from './design-edit.mjs'
69
69
  // FL-M9 — per-lane preview servers leak (one per done lane, never stopped until shutdown).
70
70
  // Lane previews are keyed `lane:<flowId>:<laneId>`; stop a whole flow's set when it assembles
71
71
  // (the assembled preview supersedes them) or when a lane is reverted.
@@ -1862,6 +1862,9 @@ function openStructured({ id, runtime = 'claude', model, effort, resume, log, co
1862
1862
  // they land on this session even after other terminals open/close.
1863
1863
  const mockupOutbox = ownerOutbox('sessions', id)
1864
1864
  entry.mockupWatcher = watchOutbox(mockupOutbox, () => id, true)
1865
+ const restoredDesigns = restoreDesignSources(mockupOutbox, cwd || process.cwd(), id)
1866
+ for (const record of restoredDesigns) designArtifacts.set(record.previewId, record)
1867
+ if (restoredDesigns.length) process.stderr.write(`\n ◆ restored ${restoredDesigns.length} Thinkpool Design artifact${restoredDesigns.length === 1 ? '' : 's'} (${id.slice(0, 8)}).\n`)
1865
1868
  // Visual QA runs in the bridge process, outside either agent runtime's sandbox.
1866
1869
  // It remains scoped to this lane's cwd and private mockup outbox.
1867
1870
  entry.viewport = new ViewportManager({ workspaceRoot: cwd || process.cwd(), ownerId: id, outbox: mockupOutbox })
package/design-edit.mjs CHANGED
@@ -48,6 +48,44 @@ export function resolveDesignSource(file, workspaceRoot) {
48
48
  return { previewId, revision, sourcePath, workspaceRoot: root, source }
49
49
  }
50
50
 
51
+ // Design provenance is intentionally host-only, but it must survive a bridge
52
+ // restart. Rebuild the in-memory registry from manifests already written by the
53
+ // trusted lane render workflow; do not re-upload or re-broadcast old cards.
54
+ export function restoreDesignSources(box, workspaceRoot, term, limit = 100) {
55
+ if (!box || !workspaceRoot || !term) return []
56
+ let files
57
+ try {
58
+ files = fs.readdirSync(box, { withFileTypes: true })
59
+ .filter((entry) => entry.isFile() && entry.name.endsWith('.json') && !entry.name.endsWith('.receipt.json'))
60
+ .map((entry) => {
61
+ const file = path.join(box, entry.name)
62
+ return { file, mtimeMs: fs.statSync(file).mtimeMs }
63
+ })
64
+ .sort((a, b) => b.mtimeMs - a.mtimeMs)
65
+ .slice(0, Math.max(0, limit))
66
+ } catch { return [] }
67
+ const restored = []
68
+ for (const { file } of files) {
69
+ try {
70
+ const manifest = JSON.parse(fs.readFileSync(file, 'utf8'))
71
+ if (!manifest?.source) continue
72
+ const record = resolveDesignSource(manifest.source, workspaceRoot)
73
+ if (!record) continue
74
+ Object.assign(record, {
75
+ term,
76
+ slug: String(manifest.slug || ''),
77
+ title: String(manifest.title || manifest.slug || ''),
78
+ desktop: manifest.desktop || null,
79
+ mobile: manifest.mobile || null,
80
+ })
81
+ const backupPath = path.join(box, '.design-revisions', `${record.revision}.html`)
82
+ if (fs.existsSync(backupPath)) record.backupPath = backupPath
83
+ restored.push(record)
84
+ } catch { /* stale or malformed manifests are ignored */ }
85
+ }
86
+ return restored
87
+ }
88
+
51
89
  function safeTarget(value) {
52
90
  if (!value || typeof value !== 'object') return null
53
91
  const rect = value.rect && typeof value.rect === 'object' ? {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.218",
3
+ "version": "0.7.219",
4
4
  "description": "Share a local coding-agent CLI (Claude Code, Codex, Gemini, Aider, …) into a ThinkPool Code room, live.",
5
5
  "type": "module",
6
6
  "bin": {