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.
Files changed (116) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +20 -0
  3. package/abort-turn-barrier.mjs +1 -23
  4. package/account.mjs +1 -1437
  5. package/acp-client.mjs +1 -140
  6. package/agent-detect.mjs +1 -28
  7. package/agent-notify.mjs +1 -142
  8. package/agent-visibility.mjs +1 -67
  9. package/auth-store.mjs +1 -120
  10. package/bridge.mjs +1 -6266
  11. package/byok-detect.mjs +1 -126
  12. package/claude-command-catalog.mjs +1 -91
  13. package/claude-session.mjs +1 -1519
  14. package/code-event-contract.mjs +1 -118
  15. package/codex-app-server.mjs +1 -340
  16. package/codex-commands.mjs +1 -83
  17. package/codex-event-mapper.mjs +1 -232
  18. package/codex-images.mjs +1 -69
  19. package/codex-mcp-http.mjs +1 -131
  20. package/codex-session.mjs +1 -1295
  21. package/command-catalog.mjs +1 -118
  22. package/command-guidance.mjs +1 -8
  23. package/context-contract.mjs +1 -95
  24. package/context-windows.mjs +1 -107
  25. package/cross-terminal.mjs +1 -789
  26. package/cumulative-event-relay.mjs +1 -53
  27. package/design-edit.mjs +1 -424
  28. package/design-source-contract.mjs +1 -4
  29. package/direct-pair-room.mjs +1 -57
  30. package/dispatch-lease.mjs +1 -37
  31. package/dispatch-permission-cleanup.mjs +1 -86
  32. package/edit-diff.mjs +1 -136
  33. package/error-recovery.mjs +1 -50
  34. package/event-bounds.mjs +1 -121
  35. package/event-delivery-queue.mjs +1 -60
  36. package/event-id.mjs +1 -549
  37. package/evidence-citations.mjs +1 -50
  38. package/evidence-compact.mjs +1 -11
  39. package/flow-assembly.mjs +1 -196
  40. package/flow-budget.mjs +1 -84
  41. package/flow-conductor.mjs +1 -259
  42. package/flow-context-store.mjs +1 -387
  43. package/flow-host-revert.mjs +1 -42
  44. package/flow-models.mjs +1 -139
  45. package/flow-preview.mjs +1 -148
  46. package/flow-receipt.mjs +1 -122
  47. package/flow-redispatch.mjs +1 -72
  48. package/flow-review-gate.mjs +1 -402
  49. package/flow-review-reflect.mjs +1 -115
  50. package/flow-review.mjs +1 -152
  51. package/flow-scope-evidence.mjs +1 -117
  52. package/flow-skill-registry.mjs +1 -140
  53. package/flow-task-graph.mjs +1 -562
  54. package/flow-worktree.mjs +1 -71
  55. package/git-diff-report.mjs +1 -121
  56. package/hermes-delegation-guard.mjs +1 -14
  57. package/hermes-event-mapper.mjs +1 -194
  58. package/hermes-isolation.mjs +1 -61
  59. package/hermes-model-cache.mjs +1 -54
  60. package/hermes-policy.mjs +1 -92
  61. package/hermes-probe.mjs +1 -57
  62. package/hermes-session.mjs +1 -683
  63. package/hermes-setup.mjs +1 -167
  64. package/host-memory.mjs +1 -116
  65. package/interrupted-resume.mjs +1 -95
  66. package/keep-awake.mjs +1 -148
  67. package/key-shape.mjs +1 -49
  68. package/lane-continuation.mjs +1 -83
  69. package/lane-lifecycle.mjs +1 -189
  70. package/lane-worktree.mjs +1 -77
  71. package/launcher.mjs +1 -354
  72. package/mcp-flight-recorder.mjs +1 -79
  73. package/mockup-delivery.mjs +1 -57
  74. package/model-prices.mjs +1 -113
  75. package/package.json +13 -4
  76. package/pair-bus.mjs +1 -98
  77. package/pair-control-authority.mjs +1 -89
  78. package/past-work-search.mjs +1 -105
  79. package/plan-meters.mjs +1 -144
  80. package/presence.mjs +1 -191
  81. package/privacy-report.mjs +1 -108
  82. package/provider-resilience.mjs +1 -356
  83. package/provider.mjs +1 -133
  84. package/providers.mjs +1 -491
  85. package/publish-guard.mjs +2 -0
  86. package/publish-manifest.json +129 -0
  87. package/question-response.mjs +1 -58
  88. package/reap-terminal.mjs +1 -68
  89. package/recap.mjs +1 -297
  90. package/replay-transport.mjs +1 -64
  91. package/repo-search.mjs +1 -190
  92. package/review-check.mjs +1 -182
  93. package/runtime-contract.mjs +1 -93
  94. package/runtime-registry.mjs +1 -64
  95. package/runtime-session.mjs +1 -20
  96. package/scheduled-run-admission.mjs +1 -364
  97. package/scheduled-runs.mjs +1 -268
  98. package/sdk-admission.mjs +1 -9
  99. package/sdk-smoke.mjs +1 -61
  100. package/serve-consent.mjs +1 -118
  101. package/serve-dir.mjs +1 -40
  102. package/service.mjs +1 -877
  103. package/session-store.mjs +1 -484
  104. package/side-lane.mjs +1 -63
  105. package/supabase-key.mjs +1 -176
  106. package/supervisor-ready.mjs +1 -57
  107. package/switch-provider.mjs +1 -84
  108. package/terminal-name.mjs +1 -359
  109. package/terminal-row-reconcile.mjs +1 -70
  110. package/thinkpool-prompt-contracts.mjs +1 -85
  111. package/thinkpool-room-prompt.mjs +1 -187
  112. package/transcript-sanitize.mjs +1 -332
  113. package/turn-stall.mjs +1 -61
  114. package/update-gate.mjs +1 -53
  115. package/viewport.mjs +1 -810
  116. package/worker-completion.mjs +1 -57
package/byok-detect.mjs CHANGED
@@ -1,126 +1 @@
1
- /* ─────────────────────────────────────────────────────────────
2
- byok-detect — "paste a key → pick a model" for the bridge menu.
3
- ThinkPool Code is structured-only (Anthropic Messages API). That format
4
- has become the de-facto standard for agentic coding: Anthropic, Z.ai/GLM,
5
- DeepSeek, Moonshot/Kimi and MiniMax all serve it natively (each base
6
- below live-probed 2026-07-10: POST <base>/v1/messages → 401 auth error,
7
- i.e. the path exists), and LM Studio / llama.cpp serve it on localhost.
8
- The holdouts are OpenAI + Google — those need a translation gateway
9
- (LiteLLM etc.). NOTE OpenRouter's native /v1/messages serves ANTHROPIC
10
- MODELS ONLY (live-probed 2026-07-09, see src/lib/providers.js) — it is
11
- not a GPT/Gemini path, despite what this file used to claim.
12
- A bare OpenAI/Gemini/Groq key is DETECTED so we can guide the user to
13
- a gateway instead of failing silently.
14
- Spec: docs/specs/2026-06-28-byok-only-two-tier-pivot.md (addendum)
15
- ───────────────────────────────────────────────────────────── */
16
-
17
- // Providers whose endpoint speaks the Anthropic Messages API → usable directly
18
- // in structured mode. `base` is what we set as ANTHROPIC_BASE_URL; `models` is
19
- // the model-list endpoint; `auth` is how the model-list call authenticates.
20
- // A wrong/moved `models` URL degrades gracefully — fetchModels() returns []
21
- // and the menu falls back to a free-text model prompt.
22
- export const ANTHROPIC_COMPATIBLE = {
23
- anthropic: {
24
- name: 'Anthropic',
25
- base: 'https://api.anthropic.com',
26
- models: 'https://api.anthropic.com/v1/models',
27
- auth: 'x-api-key',
28
- },
29
- openrouter: {
30
- name: 'OpenRouter (Anthropic models)',
31
- base: 'https://openrouter.ai/api',
32
- models: 'https://openrouter.ai/api/v1/models',
33
- auth: 'bearer',
34
- },
35
- zai: {
36
- name: 'Z.ai — GLM',
37
- base: 'https://api.z.ai/api/anthropic',
38
- models: 'https://api.z.ai/api/paas/v4/models',
39
- auth: 'bearer',
40
- },
41
- deepseek: {
42
- name: 'DeepSeek',
43
- base: 'https://api.deepseek.com/anthropic',
44
- models: 'https://api.deepseek.com/models',
45
- auth: 'bearer',
46
- },
47
- moonshot: {
48
- name: 'Moonshot — Kimi',
49
- base: 'https://api.moonshot.ai/anthropic',
50
- models: 'https://api.moonshot.ai/v1/models',
51
- auth: 'bearer',
52
- },
53
- minimax: {
54
- name: 'MiniMax',
55
- base: 'https://api.minimax.io/anthropic',
56
- models: 'https://api.minimax.io/v1/models',
57
- auth: 'bearer',
58
- },
59
- }
60
-
61
- // Key-prefix → provider. `compatible: true` means it works directly; false
62
- // means we detected it but it needs a gateway (different wire format).
63
- // Keep the most specific prefixes first (sk-ant- / sk-or- before sk-).
64
- // Undetectable-by-prefix but directly compatible: DeepSeek + Moonshot keys are
65
- // bare `sk-…` (indistinguishable from OpenAI — gatewayHint names the escape),
66
- // Z.ai keys are `<hex>.<secret>` with no stable prefix. Those route via the
67
- // dashboard's provider presets (src/lib/providers.js BRIDGE_PROVIDER_PRESETS)
68
- // or the manual-endpoint path instead.
69
- const PREFIXES = [
70
- ['sk-ant-', 'anthropic', true, 'Anthropic'],
71
- ['sk-or-', 'openrouter', true, 'OpenRouter'],
72
- ['AIza', 'google', false, 'Google Gemini'],
73
- ['gsk_', 'groq', false, 'Groq'],
74
- ['pplx-', 'perplexity', false, 'Perplexity'],
75
- ['xai-', 'xai', false, 'xAI (Grok)'],
76
- ['fw_', 'fireworks', false, 'Fireworks'],
77
- ['sk-', 'openai', false, 'OpenAI'], // generic sk- LAST (after sk-ant-/sk-or-)
78
- ]
79
-
80
- // Detect the provider from a pasted key. Returns { id, name, compatible }.
81
- export function detectProvider(key) {
82
- const k = String(key || '').trim()
83
- if (!k) return { id: 'unknown', name: 'unknown', compatible: false }
84
- for (const [prefix, id, compatible, name] of PREFIXES) {
85
- if (k.startsWith(prefix)) return { id, name, compatible }
86
- }
87
- return { id: 'unknown', name: 'unknown', compatible: false }
88
- }
89
-
90
- // Fetch the model-id list for an Anthropic-compatible provider. Handles both
91
- // OpenAI-shape ({ data: [{ id }] }) and Google-shape ({ models: [{ name }] }).
92
- // Returns [] on any failure (caller falls back to a free-text model prompt).
93
- export async function fetchModels({ modelsUrl, key, auth }) {
94
- const headers = auth === 'x-api-key'
95
- ? { 'x-api-key': key, 'anthropic-version': '2023-06-01' }
96
- : { Authorization: `Bearer ${key}` }
97
- const ctrl = new AbortController()
98
- const timer = setTimeout(() => ctrl.abort(), 12_000)
99
- try {
100
- const r = await fetch(modelsUrl, { headers, signal: ctrl.signal, redirect: 'error' })
101
- if (!r.ok) return []
102
- const data = await r.json()
103
- const raw = Array.isArray(data?.data) ? data.data
104
- : Array.isArray(data?.models) ? data.models
105
- : []
106
- return raw
107
- .map((m) => String(m.id || m.name || '').replace(/^models\//, ''))
108
- .filter(Boolean)
109
- } catch {
110
- return []
111
- } finally {
112
- clearTimeout(timer)
113
- }
114
- }
115
-
116
- // The friendly message shown when a detected key isn't directly usable.
117
- // Honest scope (2026-07-10): OpenRouter's native /v1/messages is Anthropic-
118
- // models-only, so it is NOT the "any model" escape hatch for a GPT/Gemini key —
119
- // a translation gateway is. And a bare sk- key might not be OpenAI at all:
120
- // DeepSeek and Moonshot/Kimi keys share the prefix and DO work directly.
121
- export function gatewayHint(name) {
122
- const skAmbiguity = name === 'OpenAI'
123
- ? ' (If that key is actually DeepSeek or Moonshot/Kimi — they also start with sk- — it works directly: pick that provider in the dashboard’s LLM providers panel instead.)'
124
- : ''
125
- return `That key looks like ${name}. ThinkPool Code runs on the Anthropic Messages API — the format Claude, GLM, DeepSeek, Kimi, MiniMax and local models (LM Studio, llama.cpp) all speak natively — but that provider doesn't, so the key can't be used directly. Route it through your own gateway (e.g. LiteLLM) instead.${skAmbiguity}`
126
- }
1
+ export const ANTHROPIC_COMPATIBLE={anthropic:{name:"Anthropic",base:"https://api.anthropic.com",models:"https://api.anthropic.com/v1/models",auth:"x-api-key"},openrouter:{name:"OpenRouter (Anthropic models)",base:"https://openrouter.ai/api",models:"https://openrouter.ai/api/v1/models",auth:"bearer"},zai:{name:"Z.ai — GLM",base:"https://api.z.ai/api/anthropic",models:"https://api.z.ai/api/paas/v4/models",auth:"bearer"},deepseek:{name:"DeepSeek",base:"https://api.deepseek.com/anthropic",models:"https://api.deepseek.com/models",auth:"bearer"},moonshot:{name:"Moonshot — Kimi",base:"https://api.moonshot.ai/anthropic",models:"https://api.moonshot.ai/v1/models",auth:"bearer"},minimax:{name:"MiniMax",base:"https://api.minimax.io/anthropic",models:"https://api.minimax.io/v1/models",auth:"bearer"}};const e=[["sk-ant-","anthropic",!0,"Anthropic"],["sk-or-","openrouter",!0,"OpenRouter"],["AIza","google",!1,"Google Gemini"],["gsk_","groq",!1,"Groq"],["pplx-","perplexity",!1,"Perplexity"],["xai-","xai",!1,"xAI (Grok)"],["fw_","fireworks",!1,"Fireworks"],["sk-","openai",!1,"OpenAI"]];export function detectProvider(t){const o=String(t||"").trim();if(!o)return{id:"unknown",name:"unknown",compatible:!1};for(const[t,a,i,r]of e)if(o.startsWith(t))return{id:a,name:r,compatible:i};return{id:"unknown",name:"unknown",compatible:!1}}export async function fetchModels({modelsUrl:e,key:t,auth:o}){const a="x-api-key"===o?{"x-api-key":t,"anthropic-version":"2023-06-01"}:{Authorization:`Bearer ${t}`},i=new AbortController,r=setTimeout(()=>i.abort(),12e3);try{const t=await fetch(e,{headers:a,signal:i.signal,redirect:"error"});if(!t.ok)return[];const o=await t.json();return(Array.isArray(o?.data)?o.data:Array.isArray(o?.models)?o.models:[]).map(e=>String(e.id||e.name||"").replace(/^models\//,"")).filter(Boolean)}catch{return[]}finally{clearTimeout(r)}}export function gatewayHint(e){return`That key looks like ${e}. ThinkPool Code runs on the Anthropic Messages API — the format Claude, GLM, DeepSeek, Kimi, MiniMax and local models (LM Studio, llama.cpp) all speak natively — but that provider doesn't, so the key can't be used directly. Route it through your own gateway (e.g. LiteLLM) instead.${"OpenAI"===e?" (If that key is actually DeepSeek or Moonshot/Kimi — they also start with sk- — it works directly: pick that provider in the dashboard’s LLM providers panel instead.)":""}`}
@@ -1,91 +1 @@
1
- // Claude Code's SDK init payload is an *advertisement*, not a headless command
2
- // contract. In particular, 2.1.206 advertises TUI/profile commands (`/agents`,
3
- // `/config`, `/doctor`, …) which are not safe or meaningful in a shared room.
4
- // Keep the room catalog deliberately small and evidence-based:
5
- //
6
- // 1. bridge-owned controls work through the existing room controls;
7
- // 2. `/compact` is the one native lifecycle command proven on the streaming
8
- // Agent SDK path; and
9
- // 3. a discovered slash name is exposed only when the same init event calls it
10
- // an installed Skill. Skills are structured prompt invocations, unlike
11
- // Claude Code's interactive/profile command palette.
12
- //
13
- // This is also an explicit command matrix for the 2.1.206 probe. Everything
14
- // outside the allow path is intentionally omitted; `CLAUDE_COMMAND_EXCLUSIONS`
15
- // records the high-risk/interactive families so a later SDK change cannot turn
16
- // discovery into accidental exposure.
17
-
18
- const cleanName = (value) => {
19
- const name = String(typeof value === 'object' ? value?.name : value || '')
20
- .trim()
21
- .replace(/^\/+/, '')
22
- return /^[A-Za-z0-9][A-Za-z0-9:_-]*$/.test(name) ? name : ''
23
- }
24
-
25
- const stringMeta = (value) => {
26
- if (!value || typeof value !== 'object') return {}
27
- const description = String(value.description || '').trim()
28
- const inputHint = String(value.inputHint || value.input_hint || value.input?.hint || '').trim()
29
- return {
30
- ...(description ? { description } : {}),
31
- ...(inputHint ? { inputHint } : {}),
32
- }
33
- }
34
-
35
- export const CLAUDE_COMMAND_MATRIX = Object.freeze({
36
- '/clear': Object.freeze({ description: 'clear context · confirms', owner: 'bridge-control' }),
37
- '/compact': Object.freeze({ description: 'compact context', owner: 'sdk-lifecycle' }),
38
- '/model': Object.freeze({ description: 'pick model — opens selector', owner: 'bridge-control' }),
39
- '/usage': Object.freeze({ description: 'session usage and provider limits', owner: 'bridge-control' }),
40
- '<installed-skill>': Object.freeze({ description: 'run an installed Claude skill', owner: 'sdk-skill' }),
41
- })
42
-
43
- // Exclusions are grouped by why the room cannot safely implement them. This
44
- // is intentionally a deny-list *and* a default-deny catalog: unknown future
45
- // slash names stay hidden until they have an explicit structured contract.
46
- export const CLAUDE_COMMAND_EXCLUSIONS = Object.freeze({
47
- profileOrIdentity: Object.freeze(['agents', 'color', 'config', 'rename', 'team-onboarding']),
48
- credentialsOrUpdates: Object.freeze(['claude-api', 'mcp', 'update-config']),
49
- diagnosticsOrUpload: Object.freeze(['debug', 'doctor', 'heapdump', 'insights']),
50
- hiddenAgentOrAutomation: Object.freeze(['__remote-workflow', 'batch', 'code-review', 'loop', 'review', 'schedule', 'security-review']),
51
- unsafeInteractive: Object.freeze(['design', 'design-consent', 'design-revoke', 'fast', 'fewer-permission-prompts', 'goal', 'init', 'recap', 'reload-skills', 'run']),
52
- })
53
-
54
- const EXCLUDED = new Set(Object.values(CLAUDE_COMMAND_EXCLUSIONS).flat())
55
- const BRIDGE_CONTROLS = new Set(['clear', 'compact', 'model', 'usage'])
56
-
57
- // Convert Claude's string-only 2.1.206 payload (and a future object-shaped
58
- // payload) into the metadata contract shared by all structured runtimes. An
59
- // argument-bearing future skill retains its description and input hint; no
60
- // argument command is silently advertised as a one-tap action.
61
- export function normalizeClaudeCommandCatalog({ slashCommands, skills } = {}) {
62
- const advertised = Array.isArray(slashCommands) ? slashCommands : []
63
- const installedSkills = new Set((Array.isArray(skills) ? skills : []).map(cleanName).filter(Boolean))
64
- const byName = new Map()
65
-
66
- for (const command of advertised) {
67
- const name = cleanName(command)
68
- if (!name || EXCLUDED.has(name)) continue
69
- const allowed = BRIDGE_CONTROLS.has(name) || installedSkills.has(name)
70
- if (!allowed || byName.has(name)) continue
71
- const canonical = CLAUDE_COMMAND_MATRIX[`/${name}`]
72
- const supplied = stringMeta(command)
73
- byName.set(name, {
74
- name: `/${name}`,
75
- description: supplied.description || canonical?.description || `Run installed Claude skill: /${name}`,
76
- ...(supplied.inputHint ? { inputHint: supplied.inputHint } : {}),
77
- })
78
- }
79
-
80
- // Room controls remain available on older SDK init shapes that omit the raw
81
- // slash list. They are ordered first because they have bridge support.
82
- const controls = ['clear', 'compact', 'model', 'usage'].map((name) => byName.get(name) || ({
83
- name: `/${name}`,
84
- description: CLAUDE_COMMAND_MATRIX[`/${name}`].description,
85
- }))
86
- const skillsOnly = [...byName.entries()]
87
- .filter(([name]) => !BRIDGE_CONTROLS.has(name))
88
- .sort(([a], [b]) => a.localeCompare(b))
89
- .map(([, command]) => command)
90
- return [...controls, ...skillsOnly]
91
- }
1
+ const e=e=>{const t=String("object"==typeof e?e?.name:e||"").trim().replace(/^\/+/,"");return/^[A-Za-z0-9][A-Za-z0-9:_-]*$/.test(t)?t:""},t=e=>{if(!e||"object"!=typeof e)return{};const t=String(e.description||"").trim(),n=String(e.inputHint||e.input_hint||e.input?.hint||"").trim();return{...t?{description:t}:{},...n?{inputHint:n}:{}}};export const CLAUDE_COMMAND_MATRIX=Object.freeze({"/clear":Object.freeze({description:"clear context · confirms",owner:"bridge-control"}),"/compact":Object.freeze({description:"compact context",owner:"sdk-lifecycle"}),"/model":Object.freeze({description:"pick model — opens selector",owner:"bridge-control"}),"/usage":Object.freeze({description:"session usage and provider limits",owner:"bridge-control"}),"<installed-skill>":Object.freeze({description:"run an installed Claude skill",owner:"sdk-skill"})});export const CLAUDE_COMMAND_EXCLUSIONS=Object.freeze({profileOrIdentity:Object.freeze(["agents","color","config","rename","team-onboarding"]),credentialsOrUpdates:Object.freeze(["claude-api","mcp","update-config"]),diagnosticsOrUpload:Object.freeze(["debug","doctor","heapdump","insights"]),hiddenAgentOrAutomation:Object.freeze(["__remote-workflow","batch","code-review","loop","review","schedule","security-review"]),unsafeInteractive:Object.freeze(["design","design-consent","design-revoke","fast","fewer-permission-prompts","goal","init","recap","reload-skills","run"])});const n=new Set(Object.values(CLAUDE_COMMAND_EXCLUSIONS).flat()),r=new Set(["clear","compact","model","usage"]);export function normalizeClaudeCommandCatalog({slashCommands:i,skills:o}={}){const c=Array.isArray(i)?i:[],s=new Set((Array.isArray(o)?o:[]).map(e).filter(Boolean)),a=new Map;for(const i of c){const o=e(i);if(!o||n.has(o))continue;if(!r.has(o)&&!s.has(o)||a.has(o))continue;const c=CLAUDE_COMMAND_MATRIX[`/${o}`],l=t(i);a.set(o,{name:`/${o}`,description:l.description||c?.description||`Run installed Claude skill: /${o}`,...l.inputHint?{inputHint:l.inputHint}:{}})}return[...["clear","compact","model","usage"].map(e=>a.get(e)||{name:`/${e}`,description:CLAUDE_COMMAND_MATRIX[`/${e}`].description}),...[...a.entries()].filter(([e])=>!r.has(e)).sort(([e],[t])=>e.localeCompare(t)).map(([,e])=>e)]}