pantheon-opencode 1.2.0 → 1.2.1
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/README.md +101 -3
- package/bin/pantheon-init.mjs +79 -0
- package/commands/pantheon-vision.md +67 -0
- package/opencode.json +13 -9
- package/package.json +12 -5
- package/scripts/_pantheon_paths.py +0 -2
- package/scripts/doctor.mjs +4 -2
- package/scripts/install/health-check.mjs +101 -1
- package/scripts/install/model-picker.mjs +118 -0
- package/scripts/install/opencode.mjs +102 -19
- package/scripts/install/venv.mjs +25 -2
- package/scripts/install-mcp.mjs +98 -13
- package/scripts/mcp_persistence_server.py +1 -1
- package/scripts/secret-scan.mjs +88 -0
- package/scripts/uninstall.mjs +3 -3
- package/scripts/validate-routing.mjs +15 -0
- package/src/agents/hermes.md +2 -2
- package/src/agents/mnemosyne.md +1 -1
- package/src/agents/zeus.md +1 -1
- package/src/mcp/_pantheon_paths.py +0 -2
- package/src/mcp/install-mcp.mjs +6 -913
- package/src/mcp/mcp_persistence_server.py +2 -1
- package/src/mcp/pantheon_vision_server.py +495 -0
- package/src/mcp/requirements-vision.txt +3 -0
- package/src/pantheon/auto-wake.ts +91 -0
- package/src/pantheon/background-job-board.ts +568 -0
- package/src/pantheon/file-persistence.ts +95 -0
- package/src/pantheon/presets.d.mts +108 -0
- package/src/pantheon/presets.mjs +554 -0
- package/src/pantheon/vision.ts +1437 -0
- package/src/plugin.ts +69 -25
- package/src/plugins/tui/package.json +3 -3
- package/src/plugins/tui/src/index.tsx +866 -121
- package/src/routing.yml +148 -0
- package/src/skills/context-compression/SKILL.md +1 -1
- package/src/skills/loop-engineering/SKILL.md +1 -1
- package/src/skills/orchestration-workflow/SKILL.md +11 -11
- package/scripts/scrub_secrets.py +0 -281
- package/src/plugins/tui/dist/tui.d.ts +0 -8
- package/src/plugins/tui/dist/tui.d.ts.map +0 -1
- package/src/plugins/tui/dist/tui.js +0 -609
- package/src/plugins/tui/dist/tui.js.map +0 -1
- package/src/plugins/tui/dist/tui.tsx +0 -444
package/src/plugin.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { Plugin, PluginInput } from '@opencode-ai/plugin'
|
|
1
2
|
import type { PluginConfig } from 'opencode'
|
|
2
3
|
import { BackgroundJobBoard } from './pantheon/background-job-board.ts'
|
|
3
4
|
import { FilePersistenceAdapter } from './pantheon/file-persistence.ts'
|
|
5
|
+
import { applyActivePresetToConfig } from './pantheon/presets.mjs'
|
|
6
|
+
import { activePresetCandidates, createVisionHandler } from './pantheon/vision.ts'
|
|
4
7
|
|
|
5
8
|
// ─── Background Job Board Singleton ────────────────────────────────────
|
|
6
9
|
|
|
@@ -8,17 +11,11 @@ const board = new BackgroundJobBoard({
|
|
|
8
11
|
maxConcurrentPerAgent: 3,
|
|
9
12
|
signalDir: '.pantheon/deepwork/board-signals',
|
|
10
13
|
})
|
|
11
|
-
|
|
12
|
-
// Wire up file-based persistence and recover orphaned jobs from crashes
|
|
13
14
|
const persistence = new FilePersistenceAdapter('.pantheon/board/state.json')
|
|
14
15
|
board.setPersistence(persistence)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
console.error('[Pantheon Plugin] Failed to recover running jobs:', err),
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
// Log board state changes to console for observability
|
|
16
|
+
board
|
|
17
|
+
.recoverRunningJobs()
|
|
18
|
+
.catch((err) => console.error('[Pantheon Plugin] Failed to recover running jobs:', err))
|
|
22
19
|
board.onTerminal((taskID: string) => {
|
|
23
20
|
const job = board.get(taskID)
|
|
24
21
|
if (job) {
|
|
@@ -29,29 +26,76 @@ board.onTerminal((taskID: string) => {
|
|
|
29
26
|
})
|
|
30
27
|
|
|
31
28
|
/**
|
|
32
|
-
*
|
|
29
|
+
* Pantheon plugin for OpenCode. Pasted images are intercepted via the
|
|
30
|
+
* `chat.message` hook (proven to fire in opencode 1.18.11). When a provider
|
|
31
|
+
* key is available — env PANTHEON_OPENCODE_API_KEY / OPENCODE_API_KEY, or the
|
|
32
|
+
* opencode auth store for `opencode auth login` users — the image is
|
|
33
|
+
* described NATIVELY by the multimodal model via the opencode Zen
|
|
34
|
+
* OpenAI-compatible endpoint, and replaced with the text description — no MCP
|
|
35
|
+
* tool required. Without a key the legacy pattern applies: the image is
|
|
36
|
+
* replaced with a text instruction telling the model to call a vision MCP tool
|
|
37
|
+
* (default `pantheon_vision_vision_describe`). Either way the image never reaches
|
|
38
|
+
* the main provider, so text-only models cannot fail with an `image_url` error.
|
|
33
39
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
40
|
+
* The canonical `pantheon-vision` MCP owns the standalone describe/OCR/analyze
|
|
41
|
+
* tools. The installed OpenCode plugin API exposes no stable pre-provider
|
|
42
|
+
* message hook (1.18.11 only exposes the experimental history transform), so
|
|
43
|
+
* that transform is retained as the runtime-compatible fallback. If a stable
|
|
44
|
+
* provider-bound hook is added, register the same sanitizer there instead of
|
|
45
|
+
* assuming this experimental hook is guaranteed to run.
|
|
46
|
+
*
|
|
47
|
+
* IMPORTANT (OpenCode 1.18.11 legacy loader): this module must export EXACTLY
|
|
48
|
+
* ONE function-valued export — the default plugin. The legacy loader does
|
|
49
|
+
* `Object.values(mod)` and invokes every function export as a plugin factory;
|
|
50
|
+
* any named function export (e.g. a re-exported helper like
|
|
51
|
+
* generateInjectionPrompt) is called with a PluginInput object and can throw.
|
|
52
|
+
* Helpers live in src/pantheon/vision.ts and are imported from there directly.
|
|
36
53
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
54
|
+
const plugin: Plugin = async (input: PluginInput) => {
|
|
55
|
+
const vision = createVisionHandler(input)
|
|
40
56
|
|
|
41
|
-
|
|
42
|
-
name: 'pantheon',
|
|
43
|
-
version: '5.0.0',
|
|
44
|
-
description: 'Pantheon multi-agent orchestration platform',
|
|
45
|
-
|
|
46
|
-
hooks: {
|
|
57
|
+
return {
|
|
47
58
|
config: async (config: PluginConfig) => {
|
|
48
59
|
config.agentsPath = config.agentsPath ?? []
|
|
49
60
|
config.agentsPath.push(new URL('./agents', import.meta.url).pathname)
|
|
50
|
-
|
|
51
61
|
config.skillsPaths = config.skillsPaths ?? []
|
|
52
62
|
config.skillsPaths.push(new URL('./skills', import.meta.url).pathname)
|
|
53
63
|
|
|
54
|
-
|
|
64
|
+
// Apply the active model preset (`init --preset` / `set-tier` write
|
|
65
|
+
// .pantheon/active-preset.json): resolve it with the SAME candidate
|
|
66
|
+
// order the vision handler uses (project > XDG > HOME) and mutate the
|
|
67
|
+
// agent models / reasoning effort / fallback models + provider configs.
|
|
68
|
+
// Fail-safe: without an active preset the config is untouched; a missing
|
|
69
|
+
// provider key is logged and skipped — the hook must never break startup
|
|
70
|
+
// (set-tier already fail-fast validates keys at write time). Vision
|
|
71
|
+
// rotation stays in vision.ts, which resolves its model independently.
|
|
72
|
+
try {
|
|
73
|
+
const resolved = applyActivePresetToConfig(config, {
|
|
74
|
+
candidates: activePresetCandidates(),
|
|
75
|
+
})
|
|
76
|
+
if (resolved) {
|
|
77
|
+
console.log(
|
|
78
|
+
`[Pantheon Plugin] Applied model preset: ${resolved.name} (${resolved.source})`,
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
// Fully STATIC warning — the thrown error object is tainted because
|
|
83
|
+
// presets.mjs constructs it with the provider's apiKeyEnv name (e.g.
|
|
84
|
+
// PANTHEON_OPENCODE_API_KEY), and CodeQL's clear-text logging dataflow
|
|
85
|
+
// flags ANY interpolation derived from it — including err.name and
|
|
86
|
+
// err.message. The env var name is not itself sensitive to log, but
|
|
87
|
+
// the scanner cannot distinguish it, so nothing derived from the error
|
|
88
|
+
// may appear here. Diagnostics (provider id + env var) are already
|
|
89
|
+
// emitted by applyActivePresetToConfig's own logger before it throws.
|
|
90
|
+
// The catch binding is intentionally omitted so no tainted value can
|
|
91
|
+
// ever reach this log line (CodeQL alert #11).
|
|
92
|
+
console.warn('[plugin] preset application failed (see logs for details)')
|
|
93
|
+
}
|
|
55
94
|
},
|
|
56
|
-
|
|
57
|
-
|
|
95
|
+
'chat.message': vision.chatMessage,
|
|
96
|
+
'experimental.chat.messages.transform': vision.messagesTransform,
|
|
97
|
+
event: vision.event,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default plugin
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pantheon-tui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "Pantheon TUI
|
|
4
|
+
"version": "1.2.0",
|
|
5
|
+
"description": "Pantheon TUI plugin \u2014 sidebar (agents, sessions, commands, config, memory, active preset) + AI subscription usage gauges incl. OpenCode Go/Zen dollar limits (vendored, MIT)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
8
8
|
"./tui": "./dist/tui.tsx"
|
|
@@ -32,4 +32,4 @@
|
|
|
32
32
|
"@opentui/solid": ">=0.4.5",
|
|
33
33
|
"solid-js": ">=1.9.0"
|
|
34
34
|
}
|
|
35
|
-
}
|
|
35
|
+
}
|