thinkpool-pair 0.7.264 → 0.7.265
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 +2 -4
- package/codex-session.mjs +2 -39
- package/package.json +2 -1
- package/question-response.mjs +52 -0
package/bridge.mjs
CHANGED
|
@@ -60,6 +60,7 @@ import { canonicalRoomFilePath, waitForNativeImages } from './codex-images.mjs'
|
|
|
60
60
|
import { createManagedLaneWorktree, removeManagedLaneWorktree } from './lane-worktree.mjs'
|
|
61
61
|
import { commandOnPath } from './agent-detect.mjs'
|
|
62
62
|
import { requiresSdkAdmission, sdkSmokePassed } from './sdk-admission.mjs'
|
|
63
|
+
import { hermesUserInputResponse } from './question-response.mjs'
|
|
63
64
|
|
|
64
65
|
const STRUCTURED_MODES = new Set(['default', 'acceptEdits', 'plan', 'review', 'bypassPermissions'])
|
|
65
66
|
import { FLOW_CONDUCTOR_PROMPT, FLOW_LANE_PROMPT, FLOW_CODEX_CONDUCTOR_PROMPT, FLOW_CODEX_LANE_PROMPT, buildConductorEnv, assembleCrossWaveContext, buildLanePrompt } from './flow-conductor.mjs'
|
|
@@ -2189,10 +2190,7 @@ function openStructured({ id, runtime = 'claude', model, models, effort, resume,
|
|
|
2189
2190
|
answerFormat: 'hermes',
|
|
2190
2191
|
autoResolutionMs: args.autoResolutionMs,
|
|
2191
2192
|
})
|
|
2192
|
-
|
|
2193
|
-
const answers = result && typeof result === 'object' && result.answers && typeof result.answers === 'object' ? result.answers : {}
|
|
2194
|
-
const status = String(decision || '').startsWith('answer:') ? 'answered' : decision === 'timeout' ? 'timed_out' : 'dismissed'
|
|
2195
|
-
return { content: [{ type: 'text', text: JSON.stringify({ status, answers }) }] }
|
|
2193
|
+
return { content: [{ type: 'text', text: JSON.stringify(hermesUserInputResponse(result, questions)) }] }
|
|
2196
2194
|
},
|
|
2197
2195
|
)] : []),
|
|
2198
2196
|
...createViewportTools({ tool, z, manager: entry.viewport }),
|
package/codex-session.mjs
CHANGED
|
@@ -31,6 +31,7 @@ import { buildCodexAppServerArgs, codexAppServerGate, createCodexAppServer } fro
|
|
|
31
31
|
import { CodexEventMapper } from './codex-event-mapper.mjs'
|
|
32
32
|
import { startCodexMcpHttp } from './codex-mcp-http.mjs'
|
|
33
33
|
import { CODEX_THINKPOOL_FIRST_TURN_PREAMBLE, buildThinkPoolTurnGuidance, createRoomContextSelector, usesFullThinkPoolReminder } from './thinkpool-room-prompt.mjs'
|
|
34
|
+
import { questionAnswerResponse } from './question-response.mjs'
|
|
34
35
|
|
|
35
36
|
const DEFAULT_SANDBOX = 'workspace-write'
|
|
36
37
|
const SAFE_SANDBOXES = new Set(['read-only', 'workspace-write', 'danger-full-access'])
|
|
@@ -316,46 +317,8 @@ export function codexApprovalResponse(decision) {
|
|
|
316
317
|
return { decision: decision === 'always' ? 'acceptForSession' : decision === 'allow' ? 'accept' : 'decline' }
|
|
317
318
|
}
|
|
318
319
|
|
|
319
|
-
function legacyQuestionAnswers(decision, questions) {
|
|
320
|
-
if (typeof decision !== 'string' || !decision.startsWith('answer:')) return {}
|
|
321
|
-
const text = decision.slice(7).trim()
|
|
322
|
-
if (!text || !questions.length) return {}
|
|
323
|
-
if (questions.length === 1) {
|
|
324
|
-
const question = questions[0]
|
|
325
|
-
const prefix = `${question.header || 'Q1'}: `
|
|
326
|
-
const value = text.startsWith(prefix) ? text.slice(prefix.length) : text
|
|
327
|
-
return { [String(question.id || 0)]: { answers: [value] } }
|
|
328
|
-
}
|
|
329
|
-
const answers = {}
|
|
330
|
-
questions.forEach((question, index) => {
|
|
331
|
-
const id = String(question.id || index)
|
|
332
|
-
const marker = `${index ? '; ' : ''}${question.header || `Q${index + 1}`}: `
|
|
333
|
-
const start = text.indexOf(marker)
|
|
334
|
-
if (start < 0) return
|
|
335
|
-
const valueStart = start + marker.length
|
|
336
|
-
const nextQuestion = questions[index + 1]
|
|
337
|
-
const nextMarker = nextQuestion ? `; ${nextQuestion.header || `Q${index + 2}`}: ` : null
|
|
338
|
-
const end = nextMarker ? text.indexOf(nextMarker, valueStart) : -1
|
|
339
|
-
const value = text.slice(valueStart, end < 0 ? undefined : end).trim()
|
|
340
|
-
if (value) answers[id] = { answers: [value] }
|
|
341
|
-
})
|
|
342
|
-
return answers
|
|
343
|
-
}
|
|
344
|
-
|
|
345
320
|
export function codexUserInputResponse(result, questions = []) {
|
|
346
|
-
|
|
347
|
-
const answers = {}
|
|
348
|
-
if (supplied && typeof supplied === 'object') {
|
|
349
|
-
const allowed = new Set(questions.map((question, index) => String(question?.id || index)))
|
|
350
|
-
for (const [id, value] of Object.entries(supplied)) {
|
|
351
|
-
if (!allowed.has(String(id)) || !Array.isArray(value?.answers)) continue
|
|
352
|
-
const values = value.answers.map((answer) => String(answer)).filter(Boolean)
|
|
353
|
-
if (values.length) answers[String(id)] = { answers: values }
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
if (Object.keys(answers).length) return { answers }
|
|
357
|
-
const decision = result && typeof result === 'object' ? result.decision : result
|
|
358
|
-
return { answers: legacyQuestionAnswers(decision, questions) }
|
|
321
|
+
return questionAnswerResponse(result, questions)
|
|
359
322
|
}
|
|
360
323
|
|
|
361
324
|
function appServerItemForMapper(item) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinkpool-pair",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.265",
|
|
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": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"context-windows.mjs",
|
|
17
17
|
"claude-session.mjs",
|
|
18
18
|
"codex-session.mjs",
|
|
19
|
+
"question-response.mjs",
|
|
19
20
|
"thinkpool-room-prompt.mjs",
|
|
20
21
|
"thinkpool-prompt-contracts.mjs",
|
|
21
22
|
"thinkpool-capabilities.json",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
function legacyQuestionAnswers (decision, questions) {
|
|
2
|
+
if (typeof decision !== 'string' || !decision.startsWith('answer:')) return {}
|
|
3
|
+
const text = decision.slice(7).trim()
|
|
4
|
+
if (!text || !questions.length) return {}
|
|
5
|
+
if (questions.length === 1) {
|
|
6
|
+
const question = questions[0]
|
|
7
|
+
const prefix = `${question.header || 'Q1'}: `
|
|
8
|
+
const value = text.startsWith(prefix) ? text.slice(prefix.length) : text
|
|
9
|
+
return { [String(question.id || 0)]: { answers: [value] } }
|
|
10
|
+
}
|
|
11
|
+
const answers = {}
|
|
12
|
+
questions.forEach((question, index) => {
|
|
13
|
+
const id = String(question.id || index)
|
|
14
|
+
const marker = `${index ? '; ' : ''}${question.header || `Q${index + 1}`}: `
|
|
15
|
+
const start = text.indexOf(marker)
|
|
16
|
+
if (start < 0) return
|
|
17
|
+
const valueStart = start + marker.length
|
|
18
|
+
const nextQuestion = questions[index + 1]
|
|
19
|
+
const nextMarker = nextQuestion ? `; ${nextQuestion.header || `Q${index + 2}`}: ` : null
|
|
20
|
+
const end = nextMarker ? text.indexOf(nextMarker, valueStart) : -1
|
|
21
|
+
const value = text.slice(valueStart, end < 0 ? undefined : end).trim()
|
|
22
|
+
if (value) answers[id] = { answers: [value] }
|
|
23
|
+
})
|
|
24
|
+
return answers
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Codex and Hermes use different native tool protocols, but the room answer is
|
|
28
|
+
// one contract. Keep filtering, multi-select preservation, and legacy fallback
|
|
29
|
+
// in one pure adapter so the two runtimes cannot drift independently.
|
|
30
|
+
export function questionAnswerResponse (result, questions = []) {
|
|
31
|
+
const supplied = result && typeof result === 'object' ? result.answers : null
|
|
32
|
+
const answers = {}
|
|
33
|
+
if (supplied && typeof supplied === 'object') {
|
|
34
|
+
const allowed = new Set(questions.map((question, index) => String(question?.id || index)))
|
|
35
|
+
for (const [id, value] of Object.entries(supplied)) {
|
|
36
|
+
if (!allowed.has(String(id)) || !Array.isArray(value?.answers)) continue
|
|
37
|
+
const values = value.answers.map((answer) => String(answer)).filter(Boolean)
|
|
38
|
+
if (values.length) answers[String(id)] = { answers: values }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (Object.keys(answers).length) return { answers }
|
|
42
|
+
const decision = result && typeof result === 'object' ? result.decision : result
|
|
43
|
+
return { answers: legacyQuestionAnswers(decision, questions) }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function hermesUserInputResponse (result, questions = []) {
|
|
47
|
+
const decision = result && typeof result === 'object' ? result.decision : result
|
|
48
|
+
return {
|
|
49
|
+
status: String(decision || '').startsWith('answer:') ? 'answered' : decision === 'timeout' ? 'timed_out' : 'dismissed',
|
|
50
|
+
...questionAnswerResponse(result, questions),
|
|
51
|
+
}
|
|
52
|
+
}
|