thinkpool-pair 0.7.229 → 0.7.231
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 +8 -12
- package/codex-app-server.mjs +8 -1
- package/codex-session.mjs +7 -1
- package/design-edit.mjs +26 -0
- package/package.json +1 -1
package/bridge.mjs
CHANGED
|
@@ -66,7 +66,7 @@ import { writeLaneArtifact, digestSlice, appendDigest, resumeLane } from './flow
|
|
|
66
66
|
import { createFlowWorktree, worktreeSpec } from './flow-worktree.mjs'
|
|
67
67
|
import { startPreview, stopAllPreviews, previews } from './flow-preview.mjs'
|
|
68
68
|
import { ViewportManager, createViewportTools, sharedViewportBrowser } from './viewport.mjs'
|
|
69
|
-
import { deleteDesignAssetDraft, designPrompt, materializeDesignAsset, resolveDesignSource, resolveManifestDesignSource, restoreDesignSources, syncRealtimeAuth, validateDesignBatchRequest } from './design-edit.mjs'
|
|
69
|
+
import { deleteDesignAssetDraft, designPrompt, designTranscript, materializeDesignAsset, resolveDesignSource, resolveManifestDesignSource, restoreDesignSources, syncRealtimeAuth, validateDesignBatchRequest } from './design-edit.mjs'
|
|
70
70
|
// FL-M9 — per-lane preview servers leak (one per done lane, never stopped until shutdown).
|
|
71
71
|
// Lane previews are keyed `lane:<flowId>:<laneId>`; stop a whole flow's set when it assembles
|
|
72
72
|
// (the assembled preview supersedes them) or when a lane is reverted.
|
|
@@ -1283,19 +1283,13 @@ function pumpDesign(term) {
|
|
|
1283
1283
|
const active = { ...next, resultOk: false, proof: null, timer: null }
|
|
1284
1284
|
designActive.set(term, active)
|
|
1285
1285
|
designStatus({ previewId: next.record.previewId, requestId: next.request.cid, state: 'applying' })
|
|
1286
|
-
//
|
|
1287
|
-
const action = next.request.mode === 'text' ? 'replace text'
|
|
1288
|
-
: next.request.mode === 'move' ? 'move element'
|
|
1289
|
-
: next.request.mode === 'image' ? 'replace image'
|
|
1290
|
-
: next.request.intent
|
|
1291
|
-
const batchCount = next.request.mode === 'batch' ? next.request.edits.length : 0
|
|
1286
|
+
// Complete room-visible instructions, without host paths or locator packets.
|
|
1292
1287
|
const visible = {
|
|
1293
1288
|
kind: 'you',
|
|
1294
|
-
text:
|
|
1295
|
-
? `Design batch · ${batchCount} pooled edit${batchCount === 1 ? '' : 's'}`
|
|
1296
|
-
: `Design edit · ${next.request.target.name || next.request.target.tag}: ${action}`,
|
|
1289
|
+
text: designTranscript(next.request, { restore: !!next.restoreRecord }),
|
|
1297
1290
|
cid: next.request.cid,
|
|
1298
1291
|
by: next.by,
|
|
1292
|
+
design: true,
|
|
1299
1293
|
}
|
|
1300
1294
|
stampEvent(visible); pushLog(lane, visible); bcast('code-event', { term, evt: visible })
|
|
1301
1295
|
try { lane.session.sendTurn(designPrompt({ record: next.record, request: next.request, by: next.by, restore: !!next.restoreRecord, priorRecord: next.restoreRecord })) }
|
|
@@ -4038,7 +4032,8 @@ designChannel
|
|
|
4038
4032
|
materializedDrafts.push(edit.asset.path)
|
|
4039
4033
|
}
|
|
4040
4034
|
await Promise.all(materializedDrafts.map((assetPath) => deleteDesignAssetDraft({ webBase: WEB_BASE, token: codeAuthToken, room, path: assetPath })))
|
|
4041
|
-
|
|
4035
|
+
const by = String(payload?.by || 'A room member').replace(/\0/g, '').trim().slice(0, 120) || 'A room member'
|
|
4036
|
+
queue.push({ ...verdict, by })
|
|
4042
4037
|
const batchCount = verdict.request.mode === 'batch' ? verdict.request.edits.length : 1
|
|
4043
4038
|
designStatus({ previewId, requestId, state: 'queued', message: `${batchCount} edit${batchCount === 1 ? '' : 's'} pooled to the producing lane.` })
|
|
4044
4039
|
pumpDesign(term)
|
|
@@ -4061,7 +4056,8 @@ designChannel
|
|
|
4061
4056
|
designStatus({ previewId, requestId, state: 'failed', message: 'Too many Design edits are queued for this lane.' })
|
|
4062
4057
|
return
|
|
4063
4058
|
}
|
|
4064
|
-
|
|
4059
|
+
const by = String(payload?.by || 'A room member').replace(/\0/g, '').trim().slice(0, 120) || 'A room member'
|
|
4060
|
+
queue.push({ record, restoreRecord: restore.priorRecord, request: { previewId, revision: record.revision, mode: 'restore', intent: 'restore previous verified revision', target: restore.request.target || { selector: 'html', tag: 'artifact', name: `${restore.request.edits?.length || 1} pooled edits` }, cid: requestId }, by })
|
|
4065
4061
|
designStatus({ previewId, requestId, state: 'queued', message: 'Restore queued in the producing lane.' })
|
|
4066
4062
|
pumpDesign(record.term)
|
|
4067
4063
|
})
|
package/codex-app-server.mjs
CHANGED
|
@@ -59,7 +59,14 @@ export function codexAppServerGate({ env = process.env, execFile = execFileSync
|
|
|
59
59
|
export function buildCodexAppServerArgs({ providerConfig, mcpUrl, writableDirs = [] } = {}) {
|
|
60
60
|
const args = ['app-server', '--stdio']
|
|
61
61
|
if (providerConfig) args.push('-c', providerConfig)
|
|
62
|
-
if (mcpUrl)
|
|
62
|
+
if (mcpUrl) {
|
|
63
|
+
args.push('-c', `mcp_servers.thinkpool.url=${JSON.stringify(mcpUrl)}`)
|
|
64
|
+
// The room prompt promises spawn/read/close_terminal as native room tools.
|
|
65
|
+
// Fail startup instead of silently running a tool-less Codex turn when the
|
|
66
|
+
// private loopback MCP handshake is unavailable. Existing MCP approval
|
|
67
|
+
// policy still owns the room permission card for each call.
|
|
68
|
+
args.push('-c', 'mcp_servers.thinkpool.required=true')
|
|
69
|
+
}
|
|
63
70
|
if (writableDirs.length) args.push('-c', `sandbox_workspace_write.writable_roots=${JSON.stringify(writableDirs)}`)
|
|
64
71
|
return args
|
|
65
72
|
}
|
package/codex-session.mjs
CHANGED
|
@@ -248,7 +248,13 @@ export function buildCodexExecArgs({ sessionId, model, effort, sandbox, approval
|
|
|
248
248
|
// accept --add-dir itself. Supply these only when creating the thread.
|
|
249
249
|
if (!sessionId) for (const dir of writableDirs) args.push('--add-dir', dir)
|
|
250
250
|
// Per-lane override only: never mutate the person's global Codex config.
|
|
251
|
-
if (mcpUrl)
|
|
251
|
+
if (mcpUrl) {
|
|
252
|
+
args.push('-c', `mcp_servers.thinkpool.url=${JSON.stringify(mcpUrl)}`)
|
|
253
|
+
// Room tools are part of the ThinkPool execution contract, not optional
|
|
254
|
+
// enrichment. A failed handshake must abort this invocation so Codex
|
|
255
|
+
// cannot continue after spawn_terminal vanished from its callable set.
|
|
256
|
+
args.push('-c', 'mcp_servers.thinkpool.required=true')
|
|
257
|
+
}
|
|
252
258
|
// Both fresh `exec` and `exec resume` accept repeatable --image flags. Only
|
|
253
259
|
// bridge-derived absolute paths reach this layer; relative values are ignored
|
|
254
260
|
// so a future caller cannot silently change the child's path interpretation.
|
package/design-edit.mjs
CHANGED
|
@@ -223,6 +223,32 @@ function designAction(request) {
|
|
|
223
223
|
return `Apply this visual change to only the selected element unless the instruction explicitly requires its immediate context:\n${request.intent}`
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
const designModeLabel = (mode) => ({ text: 'Text', prompt: 'Prompt', move: 'Move', image: 'Image' })[mode] || 'Edit'
|
|
227
|
+
|
|
228
|
+
function visibleDesignAction(request) {
|
|
229
|
+
if (request.mode === 'text') return `Replacement text:\n${clean(request.intent, MAX_TEXT)}`
|
|
230
|
+
if (request.mode === 'prompt') return `Instruction:\n${clean(request.intent, MAX_INTENT)}`
|
|
231
|
+
if (request.mode === 'move') return `Move from position ${request.movement.fromIndex + 1} to position ${request.movement.toIndex + 1}.`
|
|
232
|
+
if (request.mode === 'image') return `Replacement image: ${clean(request.asset?.name || 'uploaded image', 240)}`
|
|
233
|
+
return ''
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Complete, room-safe transcript text for the visible Design dispatch. Keep the
|
|
237
|
+
// user instructions and order; omit host-only paths and locator packets.
|
|
238
|
+
export function designTranscript(request, { restore = false } = {}) {
|
|
239
|
+
if (restore) return 'Thinkpool Design · restore the previous verified revision'
|
|
240
|
+
const edits = request?.mode === 'batch' ? request.edits : [request]
|
|
241
|
+
const valid = Array.isArray(edits) ? edits.filter(Boolean).slice(0, MAX_BATCH_EDITS) : []
|
|
242
|
+
const heading = request?.mode === 'batch'
|
|
243
|
+
? `Thinkpool Design · ${valid.length} pooled edit${valid.length === 1 ? '' : 's'}`
|
|
244
|
+
: 'Thinkpool Design · 1 edit'
|
|
245
|
+
const details = valid.map((edit, index) => {
|
|
246
|
+
const target = clean(edit.target?.name || edit.target?.tag || 'element', 240)
|
|
247
|
+
return `${index + 1}. ${designModeLabel(edit.mode)} · ${target}\n${visibleDesignAction(edit)}`
|
|
248
|
+
})
|
|
249
|
+
return [heading, ...details].join('\n\n')
|
|
250
|
+
}
|
|
251
|
+
|
|
226
252
|
export function designPrompt({ record, request, by, restore = false, priorRecord = null }) {
|
|
227
253
|
const batch = request.mode === 'batch' ? request.edits : null
|
|
228
254
|
const work = restore
|