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.
Files changed (43) hide show
  1. package/README.md +101 -3
  2. package/bin/pantheon-init.mjs +79 -0
  3. package/commands/pantheon-vision.md +67 -0
  4. package/opencode.json +13 -9
  5. package/package.json +12 -5
  6. package/scripts/_pantheon_paths.py +0 -2
  7. package/scripts/doctor.mjs +4 -2
  8. package/scripts/install/health-check.mjs +101 -1
  9. package/scripts/install/model-picker.mjs +118 -0
  10. package/scripts/install/opencode.mjs +102 -19
  11. package/scripts/install/venv.mjs +25 -2
  12. package/scripts/install-mcp.mjs +98 -13
  13. package/scripts/mcp_persistence_server.py +1 -1
  14. package/scripts/secret-scan.mjs +88 -0
  15. package/scripts/uninstall.mjs +3 -3
  16. package/scripts/validate-routing.mjs +15 -0
  17. package/src/agents/hermes.md +2 -2
  18. package/src/agents/mnemosyne.md +1 -1
  19. package/src/agents/zeus.md +1 -1
  20. package/src/mcp/_pantheon_paths.py +0 -2
  21. package/src/mcp/install-mcp.mjs +6 -913
  22. package/src/mcp/mcp_persistence_server.py +2 -1
  23. package/src/mcp/pantheon_vision_server.py +495 -0
  24. package/src/mcp/requirements-vision.txt +3 -0
  25. package/src/pantheon/auto-wake.ts +91 -0
  26. package/src/pantheon/background-job-board.ts +568 -0
  27. package/src/pantheon/file-persistence.ts +95 -0
  28. package/src/pantheon/presets.d.mts +108 -0
  29. package/src/pantheon/presets.mjs +554 -0
  30. package/src/pantheon/vision.ts +1437 -0
  31. package/src/plugin.ts +69 -25
  32. package/src/plugins/tui/package.json +3 -3
  33. package/src/plugins/tui/src/index.tsx +866 -121
  34. package/src/routing.yml +148 -0
  35. package/src/skills/context-compression/SKILL.md +1 -1
  36. package/src/skills/loop-engineering/SKILL.md +1 -1
  37. package/src/skills/orchestration-workflow/SKILL.md +11 -11
  38. package/scripts/scrub_secrets.py +0 -281
  39. package/src/plugins/tui/dist/tui.d.ts +0 -8
  40. package/src/plugins/tui/dist/tui.d.ts.map +0 -1
  41. package/src/plugins/tui/dist/tui.js +0 -609
  42. package/src/plugins/tui/dist/tui.js.map +0 -1
  43. 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
- // Recover on startup — any jobs left in "running" state become "error"
17
- board.recoverRunningJobs().catch((err) =>
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
- * Get the global BackgroundJobBoard singleton.
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
- * All modules (Zeus, auto-wake, etc.) should access the board through this
35
- * function to ensure a single consistent state machine across the plugin.
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
- export function getBackgroundJobBoard(): BackgroundJobBoard {
38
- return board
39
- }
54
+ const plugin: Plugin = async (input: PluginInput) => {
55
+ const vision = createVisionHandler(input)
40
56
 
41
- export default {
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
- return config
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
- } satisfies PluginConfig
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.0.0",
5
- "description": "Pantheon TUI sidebar plugin \u2014 agent status, commands, and system info",
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
+ }