thinkpool-pair 0.7.364 → 0.7.366
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/LICENSE +18 -0
- package/README.md +20 -0
- package/abort-turn-barrier.mjs +1 -23
- package/account.mjs +1 -1437
- package/acp-client.mjs +1 -140
- package/agent-detect.mjs +1 -28
- package/agent-notify.mjs +1 -142
- package/agent-visibility.mjs +1 -67
- package/auth-store.mjs +1 -120
- package/bridge.mjs +1 -6266
- package/byok-detect.mjs +1 -126
- package/claude-command-catalog.mjs +1 -91
- package/claude-session.mjs +1 -1519
- package/code-event-contract.mjs +1 -118
- package/codex-app-server.mjs +1 -340
- package/codex-commands.mjs +1 -83
- package/codex-event-mapper.mjs +1 -232
- package/codex-images.mjs +1 -69
- package/codex-mcp-http.mjs +1 -131
- package/codex-session.mjs +1 -1295
- package/command-catalog.mjs +1 -118
- package/command-guidance.mjs +1 -8
- package/context-contract.mjs +1 -95
- package/context-windows.mjs +1 -107
- package/cross-terminal.mjs +1 -789
- package/cumulative-event-relay.mjs +1 -53
- package/design-edit.mjs +1 -424
- package/design-source-contract.mjs +1 -4
- package/direct-pair-room.mjs +1 -57
- package/dispatch-lease.mjs +1 -37
- package/dispatch-permission-cleanup.mjs +1 -86
- package/edit-diff.mjs +1 -136
- package/error-recovery.mjs +1 -50
- package/event-bounds.mjs +1 -121
- package/event-delivery-queue.mjs +1 -60
- package/event-id.mjs +1 -549
- package/evidence-citations.mjs +1 -50
- package/evidence-compact.mjs +1 -11
- package/flow-assembly.mjs +1 -196
- package/flow-budget.mjs +1 -84
- package/flow-conductor.mjs +1 -259
- package/flow-context-store.mjs +1 -387
- package/flow-host-revert.mjs +1 -42
- package/flow-models.mjs +1 -139
- package/flow-preview.mjs +1 -148
- package/flow-receipt.mjs +1 -122
- package/flow-redispatch.mjs +1 -72
- package/flow-review-gate.mjs +1 -402
- package/flow-review-reflect.mjs +1 -115
- package/flow-review.mjs +1 -152
- package/flow-scope-evidence.mjs +1 -117
- package/flow-skill-registry.mjs +1 -140
- package/flow-task-graph.mjs +1 -562
- package/flow-worktree.mjs +1 -71
- package/git-diff-report.mjs +1 -121
- package/hermes-delegation-guard.mjs +1 -14
- package/hermes-event-mapper.mjs +1 -194
- package/hermes-isolation.mjs +1 -61
- package/hermes-model-cache.mjs +1 -54
- package/hermes-policy.mjs +1 -92
- package/hermes-probe.mjs +1 -57
- package/hermes-session.mjs +1 -683
- package/hermes-setup.mjs +1 -167
- package/host-memory.mjs +1 -116
- package/interrupted-resume.mjs +1 -95
- package/keep-awake.mjs +1 -148
- package/key-shape.mjs +1 -49
- package/lane-continuation.mjs +1 -83
- package/lane-lifecycle.mjs +1 -189
- package/lane-worktree.mjs +1 -77
- package/launcher.mjs +1 -354
- package/mcp-flight-recorder.mjs +1 -79
- package/mockup-delivery.mjs +1 -57
- package/model-prices.mjs +1 -113
- package/package.json +13 -4
- package/pair-bus.mjs +1 -98
- package/pair-control-authority.mjs +1 -89
- package/past-work-search.mjs +1 -105
- package/plan-meters.mjs +1 -144
- package/presence.mjs +1 -191
- package/privacy-report.mjs +1 -108
- package/provider-resilience.mjs +1 -356
- package/provider.mjs +1 -133
- package/providers.mjs +1 -491
- package/publish-guard.mjs +2 -0
- package/publish-manifest.json +129 -0
- package/question-response.mjs +1 -58
- package/reap-terminal.mjs +1 -68
- package/recap.mjs +1 -297
- package/replay-transport.mjs +1 -64
- package/repo-search.mjs +1 -190
- package/review-check.mjs +1 -182
- package/runtime-contract.mjs +1 -93
- package/runtime-registry.mjs +1 -64
- package/runtime-session.mjs +1 -20
- package/scheduled-run-admission.mjs +1 -364
- package/scheduled-runs.mjs +1 -268
- package/sdk-admission.mjs +1 -9
- package/sdk-smoke.mjs +1 -61
- package/serve-consent.mjs +1 -118
- package/serve-dir.mjs +1 -40
- package/service.mjs +1 -877
- package/session-store.mjs +1 -484
- package/side-lane.mjs +1 -63
- package/supabase-key.mjs +1 -176
- package/supervisor-ready.mjs +1 -57
- package/switch-provider.mjs +1 -84
- package/terminal-name.mjs +1 -359
- package/terminal-row-reconcile.mjs +1 -70
- package/thinkpool-prompt-contracts.mjs +1 -85
- package/thinkpool-room-prompt.mjs +1 -187
- package/transcript-sanitize.mjs +1 -332
- package/turn-stall.mjs +1 -61
- package/update-gate.mjs +1 -53
- package/viewport.mjs +1 -810
- package/worker-completion.mjs +1 -57
package/hermes-policy.mjs
CHANGED
|
@@ -1,92 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// contract: Python receives only this JSON and fails closed for anything else.
|
|
3
|
-
import { PEER_MCP_TOOLS, PEER_READ_MCP_TOOLS } from './cross-terminal.mjs'
|
|
4
|
-
|
|
5
|
-
export const HERMES_POLICY_VERSION = 1
|
|
6
|
-
export const HERMES_QUESTION_MCP_TOOL = 'request_user_input'
|
|
7
|
-
export const MCP_FLIGHT_RECORDER_TOOL = 'read_mcp_flight_recorder'
|
|
8
|
-
|
|
9
|
-
export const CODING_TOOLS = Object.freeze([
|
|
10
|
-
'web_search', 'web_extract', 'terminal', 'process', 'read_file', 'write_file',
|
|
11
|
-
'patch', 'search_files', 'vision_analyze', 'skills_list', 'skill_view',
|
|
12
|
-
'skill_manage', 'browser_navigate', 'browser_snapshot', 'browser_click',
|
|
13
|
-
'browser_type', 'browser_scroll', 'browser_back', 'browser_press',
|
|
14
|
-
'browser_get_images', 'browser_vision', 'browser_console', 'browser_cdp',
|
|
15
|
-
'browser_dialog', 'todo', 'memory', 'execute_code',
|
|
16
|
-
])
|
|
17
|
-
|
|
18
|
-
const READ_ONLY_TOOLS = Object.freeze(['read_file', 'search_files'])
|
|
19
|
-
// Hermes providers advertise capabilities conditionally (for example browser
|
|
20
|
-
// CDP/dialog support depends on the installed browser integration). These are
|
|
21
|
-
// the only builtins an ordinary or builder lane must have to do useful coding
|
|
22
|
-
// work; the complete coding allowlist above remains permitted when available.
|
|
23
|
-
export const ESSENTIAL_CODING_TOOLS = Object.freeze([
|
|
24
|
-
'terminal', 'process', 'read_file', 'write_file', 'patch', 'search_files',
|
|
25
|
-
])
|
|
26
|
-
const ROLE_REQUIRED = Object.freeze({
|
|
27
|
-
ordinary: [...PEER_MCP_TOOLS, HERMES_QUESTION_MCP_TOOL, MCP_FLIGHT_RECORDER_TOOL, 'spawn_terminal', 'close_terminal'],
|
|
28
|
-
plan: [],
|
|
29
|
-
conductor: ['submit_flow_plan'],
|
|
30
|
-
builder: ['mark_flow_done'],
|
|
31
|
-
reviewer: ['submit_flow_review', 'run_review_check', 'read_review_file'],
|
|
32
|
-
'manual-review': ['run_review_check', 'read_review_file'],
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
export function hermesRoleFor({ flowRole, sliceType } = {}) {
|
|
36
|
-
if (flowRole === 'conductor') return 'conductor'
|
|
37
|
-
if (flowRole === 'reviewer') return 'reviewer'
|
|
38
|
-
if (sliceType === 'review') return 'manual-review'
|
|
39
|
-
if (flowRole === 'builder') return 'builder'
|
|
40
|
-
return 'ordinary'
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function hermesPolicyForRole(role, { mcpTools } = {}) {
|
|
44
|
-
if (!Object.hasOwn(ROLE_REQUIRED, role)) throw new Error(`Unknown Hermes role policy: ${role}`)
|
|
45
|
-
const supplied = Array.isArray(mcpTools) ? mcpTools.map(String).filter(Boolean) : ROLE_REQUIRED[role]
|
|
46
|
-
const required = ROLE_REQUIRED[role]
|
|
47
|
-
// Ordinary worker leaves deliberately lack spawn/close. Main-lane proof is
|
|
48
|
-
// enforced by requiredMcpTools at dispatch; keep this schema usable for a
|
|
49
|
-
// non-delegating ordinary child without widening it.
|
|
50
|
-
const minimum = role === 'ordinary' ? [...PEER_MCP_TOOLS, HERMES_QUESTION_MCP_TOOL, MCP_FLIGHT_RECORDER_TOOL] : required
|
|
51
|
-
for (const tool of minimum) if (!supplied.includes(tool)) throw new Error(`Hermes ${role} policy is missing required ThinkPool tool ${tool}`)
|
|
52
|
-
const restricted = role === 'plan' || role === 'conductor' || role === 'reviewer' || role === 'manual-review'
|
|
53
|
-
const builtinTools = restricted ? READ_ONLY_TOOLS : CODING_TOOLS
|
|
54
|
-
const requiredBuiltinTools = restricted ? READ_ONLY_TOOLS : ESSENTIAL_CODING_TOOLS
|
|
55
|
-
return Object.freeze({
|
|
56
|
-
version: HERMES_POLICY_VERSION,
|
|
57
|
-
role,
|
|
58
|
-
// `builtinTools` is the role's complete approved surface, not a promise
|
|
59
|
-
// that an availability-gated upstream provider implements every tool.
|
|
60
|
-
builtinTools,
|
|
61
|
-
requiredBuiltinTools,
|
|
62
|
-
mcpServer: 'thinkpool',
|
|
63
|
-
mcpTools: [...new Set(supplied)].sort(),
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function hermesRequiredMcpTools(role, { canSpawnWorkers = false } = {}) {
|
|
68
|
-
if (role === 'ordinary') return [...PEER_MCP_TOOLS, HERMES_QUESTION_MCP_TOOL, MCP_FLIGHT_RECORDER_TOOL, ...(canSpawnWorkers ? ['spawn_terminal', 'close_terminal'] : [])]
|
|
69
|
-
return [...ROLE_REQUIRED[role]]
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const HERMES_PLAN_SAFE_MCP_TOOLS = PEER_READ_MCP_TOOLS
|
|
73
|
-
|
|
74
|
-
// Keep the bridge's local /tools proof on the same canonical inventory as the
|
|
75
|
-
// process-local Python bootstrap. `allBuiltinTools` lets that proof reject a
|
|
76
|
-
// known built-in which is not part of this role, rather than merely looking
|
|
77
|
-
// for a couple of required MCP names.
|
|
78
|
-
export function hermesExactInventory(role, options = {}) {
|
|
79
|
-
const policy = hermesPolicyForRole(role, options)
|
|
80
|
-
return Object.freeze({
|
|
81
|
-
builtinTools: [...policy.builtinTools].sort(),
|
|
82
|
-
requiredBuiltinTools: [...policy.requiredBuiltinTools].sort(),
|
|
83
|
-
mcpTools: [...policy.mcpTools].sort(),
|
|
84
|
-
// The two excluded upstream capabilities must be checked as forbidden too;
|
|
85
|
-
// they are intentionally absent from CODING_TOOLS, not unknown to policy.
|
|
86
|
-
allBuiltinTools: [...CODING_TOOLS, 'delegate_task', 'session_search'].sort(),
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function hermesPolicyEnv(role, options = {}) {
|
|
91
|
-
return JSON.stringify(hermesPolicyForRole(role, options))
|
|
92
|
-
}
|
|
1
|
+
import{PEER_MCP_TOOLS as e,PEER_READ_MCP_TOOLS as r}from"./cross-terminal.mjs";export const HERMES_POLICY_VERSION=1;export const HERMES_QUESTION_MCP_TOOL="request_user_input";export const MCP_FLIGHT_RECORDER_TOOL="read_mcp_flight_recorder";export const CODING_TOOLS=Object.freeze(["web_search","web_extract","terminal","process","read_file","write_file","patch","search_files","vision_analyze","skills_list","skill_view","skill_manage","browser_navigate","browser_snapshot","browser_click","browser_type","browser_scroll","browser_back","browser_press","browser_get_images","browser_vision","browser_console","browser_cdp","browser_dialog","todo","memory","execute_code"]);const o=Object.freeze(["read_file","search_files"]);export const ESSENTIAL_CODING_TOOLS=Object.freeze(["terminal","process","read_file","write_file","patch","search_files"]);const s=Object.freeze({ordinary:[...e,"request_user_input",MCP_FLIGHT_RECORDER_TOOL,"spawn_terminal","close_terminal"],plan:[],conductor:["submit_flow_plan"],builder:["mark_flow_done"],reviewer:["submit_flow_review","run_review_check","read_review_file"],"manual-review":["run_review_check","read_review_file"]});export function hermesRoleFor({flowRole:e,sliceType:r}={}){return"conductor"===e?"conductor":"reviewer"===e?"reviewer":"review"===r?"manual-review":"builder"===e?"builder":"ordinary"}export function hermesPolicyForRole(r,{mcpTools:i}={}){if(!Object.hasOwn(s,r))throw new Error(`Unknown Hermes role policy: ${r}`);const t=Array.isArray(i)?i.map(String).filter(Boolean):s[r],n=s[r],l="ordinary"===r?[...e,"request_user_input",MCP_FLIGHT_RECORDER_TOOL]:n;for(const e of l)if(!t.includes(e))throw new Error(`Hermes ${r} policy is missing required ThinkPool tool ${e}`);const _="plan"===r||"conductor"===r||"reviewer"===r||"manual-review"===r,c=_?o:CODING_TOOLS,a=_?o:ESSENTIAL_CODING_TOOLS;return Object.freeze({version:1,role:r,builtinTools:c,requiredBuiltinTools:a,mcpServer:"thinkpool",mcpTools:[...new Set(t)].sort()})}export function hermesRequiredMcpTools(r,{canSpawnWorkers:o=!1}={}){return"ordinary"===r?[...e,"request_user_input",MCP_FLIGHT_RECORDER_TOOL,...o?["spawn_terminal","close_terminal"]:[]]:[...s[r]]}export const HERMES_PLAN_SAFE_MCP_TOOLS=r;export function hermesExactInventory(e,r={}){const o=hermesPolicyForRole(e,r);return Object.freeze({builtinTools:[...o.builtinTools].sort(),requiredBuiltinTools:[...o.requiredBuiltinTools].sort(),mcpTools:[...o.mcpTools].sort(),allBuiltinTools:[...CODING_TOOLS,"delegate_task","session_search"].sort()})}export function hermesPolicyEnv(e,r={}){return JSON.stringify(hermesPolicyForRole(e,r))}
|
package/hermes-probe.mjs
CHANGED
|
@@ -1,57 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
|
|
5
|
-
const clean = (value) => String(value || '').replace(/[\r\n]+/g, ' ').trim()
|
|
6
|
-
|
|
7
|
-
const INSTALL = /Install directory:\s*(.+?)(?:\r?\n|$)/i
|
|
8
|
-
const PROFILE = /Config:\s*(.+?)(?:\r?\n|$)/i
|
|
9
|
-
const NOUS_PROVIDER = /Model:\s*\{[^\r\n]*['"]provider['"]\s*:\s*['"]nous['"][^\r\n]*\}/i
|
|
10
|
-
|
|
11
|
-
// Resolve the installed venv and isolated profile once, then launch ACP through
|
|
12
|
-
// bridge-owned code. `thinkpool` is only queried for inventory; it is never the
|
|
13
|
-
// executable that serves an ACP lane.
|
|
14
|
-
export function resolveHermesAcpRuntime({ command = 'thinkpool', execFile = execFileSync, exists = fs.existsSync, bootstrap = new URL('./hermes-acp-bootstrap.py', import.meta.url) } = {}) {
|
|
15
|
-
try {
|
|
16
|
-
const versionOutput = execFile(command, ['--version'], { encoding: 'utf8', timeout: 10_000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
17
|
-
const install = clean(String(versionOutput).match(INSTALL)?.[1])
|
|
18
|
-
if (!install || !path.isAbsolute(install)) throw new Error('Hermes did not report an absolute install directory')
|
|
19
|
-
const python = path.join(install, 'venv', 'bin', 'python')
|
|
20
|
-
const bootstrapPath = bootstrap instanceof URL ? bootstrap.pathname : String(bootstrap)
|
|
21
|
-
if (!exists(python) || !exists(bootstrapPath)) throw new Error('Hermes venv Python or ThinkPool ACP bootstrap is missing')
|
|
22
|
-
// `thinkpool` is the dedicated profile wrapper on supported installs. Its
|
|
23
|
-
// config output is evidence, not the ACP launch path.
|
|
24
|
-
const configOutput = execFile(command, ['config', 'show'], { encoding: 'utf8', timeout: 10_000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
25
|
-
if (!NOUS_PROVIDER.test(String(configOutput))) throw new Error('ThinkPool Hermes profile is not pinned to Nous Portal')
|
|
26
|
-
const config = clean(String(configOutput).match(PROFILE)?.[1])
|
|
27
|
-
const profile = config ? path.dirname(config) : ''
|
|
28
|
-
if (!profile || !path.isAbsolute(profile) || path.basename(profile) !== 'thinkpool') throw new Error('Hermes did not report the isolated thinkpool profile')
|
|
29
|
-
return { python, bootstrap: bootstrapPath, profile, install }
|
|
30
|
-
} catch (error) {
|
|
31
|
-
return { error: clean(error?.stderr || error?.message || error) || 'could not resolve Hermes ACP runtime' }
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function probeHermesRuntime({ command = 'thinkpool', prefixArgs = [], execFile = execFileSync, env = process.env, strictBootstrap = false, exists = fs.existsSync } = {}) {
|
|
36
|
-
try {
|
|
37
|
-
const versionOutput = execFile(command, [...prefixArgs, '--version'], { encoding: 'utf8', env, timeout: 10_000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
38
|
-
const version = clean(versionOutput).match(/Hermes Agent v([^\s]+)/i)?.[1] || null
|
|
39
|
-
const configOutput = execFile(command, [...prefixArgs, 'config', 'show'], { encoding: 'utf8', env, timeout: 10_000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
40
|
-
if (!NOUS_PROVIDER.test(String(configOutput))) return { available: false, version, reason: 'ThinkPool Hermes profile is not pinned to Nous Portal' }
|
|
41
|
-
execFile(command, [...prefixArgs, 'acp', '--check'], { encoding: 'utf8', env, timeout: 15_000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
42
|
-
const hooksOutput = execFile(command, [...prefixArgs, 'hooks', 'list'], { encoding: 'utf8', env, timeout: 10_000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
43
|
-
const doctorOutput = execFile(command, [...prefixArgs, 'hooks', 'doctor'], { encoding: 'utf8', env, timeout: 10_000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
44
|
-
const guardOutput = execFile(command, [...prefixArgs, 'hooks', 'test', 'pre_tool_call', '--for-tool', 'delegate_task'], { encoding: 'utf8', env, timeout: 10_000, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
45
|
-
const doctorHealthy = /All shell hooks look healthy/i.test(doctorOutput)
|
|
46
|
-
&& !/(?:issue\(s\) found|modified since approval|⚠)/i.test(doctorOutput)
|
|
47
|
-
const delegationBlocked = /(?:action|decision)["']?\s*[:=]\s*["']block/i.test(guardOutput)
|
|
48
|
-
&& /ThinkPool blocks hidden Hermes delegation/i.test(guardOutput)
|
|
49
|
-
&& /pre_tool_call[\s\S]*delegate_task[\s\S]*(?:✓ allowed|allowed)/i.test(hooksOutput)
|
|
50
|
-
if (!doctorHealthy || !delegationBlocked) return { available: false, version, reason: 'delegate_task guard is not healthy, unchanged, allowlisted, and structurally blocking in the dedicated Hermes profile' }
|
|
51
|
-
const runtime = strictBootstrap ? resolveHermesAcpRuntime({ command, execFile, exists }) : null
|
|
52
|
-
if (runtime?.error) return { available: false, version, reason: `Hermes ACP bootstrap unavailable: ${runtime.error}` }
|
|
53
|
-
return { available: true, version, acpProtocol: 1, delegationBlocked: true, ...(runtime || {}) }
|
|
54
|
-
} catch (error) {
|
|
55
|
-
return { available: false, version: null, reason: clean(error?.stderr || error?.message || error) }
|
|
56
|
-
}
|
|
57
|
-
}
|
|
1
|
+
import{execFileSync as e}from"node:child_process";import o from"node:fs";import t from"node:path";const i=e=>String(e||"").replace(/[\r\n]+/g," ").trim(),n=/Install directory:\s*(.+?)(?:\r?\n|$)/i,r=/Config:\s*(.+?)(?:\r?\n|$)/i,s=/Model:\s*\{[^\r\n]*['"]provider['"]\s*:\s*['"]nous['"][^\r\n]*\}/i;export function resolveHermesAcpRuntime({command:l="thinkpool",execFile:a=e,exists:p=o.existsSync,bootstrap:c=new URL("./hermes-acp-bootstrap.py",import.meta.url)}={}){try{const e=a(l,["--version"],{encoding:"utf8",timeout:1e4,stdio:["ignore","pipe","pipe"]}),o=i(String(e).match(n)?.[1]);if(!o||!t.isAbsolute(o))throw new Error("Hermes did not report an absolute install directory");const d=t.join(o,"venv","bin","python"),m=c instanceof URL?c.pathname:String(c);if(!p(d)||!p(m))throw new Error("Hermes venv Python or ThinkPool ACP bootstrap is missing");const u=a(l,["config","show"],{encoding:"utf8",timeout:1e4,stdio:["ignore","pipe","pipe"]});if(!s.test(String(u)))throw new Error("ThinkPool Hermes profile is not pinned to Nous Portal");const h=i(String(u).match(r)?.[1]),g=h?t.dirname(h):"";if(!g||!t.isAbsolute(g)||"thinkpool"!==t.basename(g))throw new Error("Hermes did not report the isolated thinkpool profile");return{python:d,bootstrap:m,profile:g,install:o}}catch(e){return{error:i(e?.stderr||e?.message||e)||"could not resolve Hermes ACP runtime"}}}export function probeHermesRuntime({command:t="thinkpool",prefixArgs:n=[],execFile:r=e,env:l=process.env,strictBootstrap:a=!1,exists:p=o.existsSync}={}){try{const e=r(t,[...n,"--version"],{encoding:"utf8",env:l,timeout:1e4,stdio:["ignore","pipe","pipe"]}),o=i(e).match(/Hermes Agent v([^\s]+)/i)?.[1]||null,c=r(t,[...n,"config","show"],{encoding:"utf8",env:l,timeout:1e4,stdio:["ignore","pipe","pipe"]});if(!s.test(String(c)))return{available:!1,version:o,reason:"ThinkPool Hermes profile is not pinned to Nous Portal"};r(t,[...n,"acp","--check"],{encoding:"utf8",env:l,timeout:15e3,stdio:["ignore","pipe","pipe"]});const d=r(t,[...n,"hooks","list"],{encoding:"utf8",env:l,timeout:1e4,stdio:["ignore","pipe","pipe"]}),m=r(t,[...n,"hooks","doctor"],{encoding:"utf8",env:l,timeout:1e4,stdio:["ignore","pipe","pipe"]}),u=r(t,[...n,"hooks","test","pre_tool_call","--for-tool","delegate_task"],{encoding:"utf8",env:l,timeout:1e4,stdio:["ignore","pipe","pipe"]}),h=/All shell hooks look healthy/i.test(m)&&!/(?:issue\(s\) found|modified since approval|⚠)/i.test(m),g=/(?:action|decision)["']?\s*[:=]\s*["']block/i.test(u)&&/ThinkPool blocks hidden Hermes delegation/i.test(u)&&/pre_tool_call[\s\S]*delegate_task[\s\S]*(?:✓ allowed|allowed)/i.test(d);if(!h||!g)return{available:!1,version:o,reason:"delegate_task guard is not healthy, unchanged, allowlisted, and structurally blocking in the dedicated Hermes profile"};const f=a?resolveHermesAcpRuntime({command:t,execFile:r,exists:p}):null;return f?.error?{available:!1,version:o,reason:`Hermes ACP bootstrap unavailable: ${f.error}`}:{available:!0,version:o,acpProtocol:1,delegationBlocked:!0,...f||{}}}catch(e){return{available:!1,version:null,reason:i(e?.stderr||e?.message||e)}}}
|