pantheon-opencode 1.5.0-beta.1 → 1.5.0-beta.10

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 (82) hide show
  1. package/.pantheon/code-mode/manifest.json +21 -0
  2. package/CHANGELOG.md +1026 -0
  3. package/README.md +171 -16
  4. package/README.pt-BR.md +99 -13
  5. package/bin/pantheon-init.mjs +178 -21
  6. package/package.json +23 -14
  7. package/plugin.json +1 -1
  8. package/pyproject.toml +65 -0
  9. package/scripts/code_mode_server.py +431 -55
  10. package/scripts/cost.mjs +30 -14
  11. package/scripts/doctor.mjs +178 -9
  12. package/scripts/install/cli-ui.mjs +17 -11
  13. package/scripts/install/config-migration.mjs +4 -6
  14. package/scripts/install/opencode.mjs +137 -35
  15. package/scripts/install/plugin.mjs +3 -33
  16. package/scripts/install/shared.mjs +43 -2
  17. package/scripts/install/strings.mjs +129 -0
  18. package/scripts/install-mcp.mjs +1 -0
  19. package/scripts/manifest-inventory.mjs +410 -0
  20. package/scripts/mcp_persistence_server.py +537 -128
  21. package/scripts/package-evidence.mjs +242 -0
  22. package/scripts/probe-context-rehydrate.mjs +240 -0
  23. package/scripts/probe-pantheon-cost.mjs +313 -0
  24. package/scripts/release-beta-version.mjs +56 -60
  25. package/scripts/sync-artifacts.mjs +169 -0
  26. package/scripts/sync-tui.mjs +121 -53
  27. package/scripts/test-opencode-v1-v2-sandbox.sh +367 -96
  28. package/scripts/validation-policy.mjs +7 -7
  29. package/scripts/version-check.mjs +113 -108
  30. package/scripts/versioning.mjs +230 -86
  31. package/src/agents/mnemosyne.md +1 -1
  32. package/src/agents/zeus.md +2 -2
  33. package/src/mcp/code_mode_server.py +431 -55
  34. package/src/mcp/mcp_persistence_server.py +537 -128
  35. package/src/pantheon/auto-wake.ts +1 -5
  36. package/src/pantheon/background-job-board.ts +43 -1
  37. package/src/pantheon/compaction-handoff.ts +304 -0
  38. package/src/pantheon/cost-command.ts +345 -62
  39. package/src/pantheon/delegate-manager.ts +390 -8
  40. package/src/pantheon/delegation-finalize.ts +32 -0
  41. package/src/pantheon/delegation.ts +235 -18
  42. package/src/pantheon/file-persistence-salvage.ts +64 -0
  43. package/src/pantheon/file-persistence.ts +194 -45
  44. package/src/pantheon/native-probe.ts +234 -0
  45. package/src/pantheon/native-task-status.ts +139 -0
  46. package/src/pantheon/provider-capabilities.ts +221 -0
  47. package/src/pantheon/provider-stream-tracker.ts +182 -0
  48. package/src/pantheon/shared-board.ts +37 -0
  49. package/src/pantheon/task-result-guard.ts +107 -0
  50. package/src/pantheon/token-opt.ts +146 -12
  51. package/src/pantheon/tool-ceiling.ts +159 -0
  52. package/src/pantheon/v2-events.ts +18 -1
  53. package/src/plugin-v2.ts +2 -14
  54. package/src/plugin.ts +28 -17
  55. package/src/plugins/pantheon-hooks.ts +51 -0
  56. package/src/plugins/tui/dist/server.d.ts +1 -1
  57. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  58. package/src/plugins/tui/dist/server.js +1 -1
  59. package/src/plugins/tui/dist/server.js.map +1 -1
  60. package/src/plugins/tui/dist/tui.d.ts +116 -27
  61. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  62. package/src/plugins/tui/dist/tui.js +414 -150
  63. package/src/plugins/tui/dist/tui.js.map +1 -1
  64. package/src/plugins/tui/dist/tui.tsx +447 -84
  65. package/src/plugins/tui/package-lock.json +2 -2
  66. package/src/plugins/tui/package.json +1 -1
  67. package/src/plugins/tui/src/index.tsx +447 -84
  68. package/.pantheon/code-mode/.gitkeep +0 -0
  69. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-1.json +0 -8
  70. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-10.json +0 -8
  71. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-11.json +0 -8
  72. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-2.json +0 -8
  73. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-3.json +0 -8
  74. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-4.json +0 -8
  75. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-5.json +0 -8
  76. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-6.json +0 -8
  77. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-7.json +0 -8
  78. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-8.json +0 -8
  79. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-9.json +0 -8
  80. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/heartbeat.json +0 -8
  81. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/session.json +0 -11
  82. package/src/pantheon/delegate-task-adapter.ts +0 -444
@@ -4,14 +4,14 @@ export const VALIDATION_STATUS = Object.freeze({
4
4
  WARN: 'WARN',
5
5
  ERROR: 'ERROR',
6
6
  SKIP: 'SKIP',
7
+ AMBIENTAL: 'AMBIENTAL',
8
+ NOT_TESTED: 'NOT_TESTED',
7
9
  })
8
10
 
9
- export function classifyDoctorExit(exitStatus, output = '') {
10
- if (exitStatus === 0) return VALIDATION_STATUS.PASS
11
- if (exitStatus === 1 && !/(?:❌\s*)?[1-9]\d*\s+errors?/i.test(output)) {
12
- return VALIDATION_STATUS.WARN
13
- }
14
- return VALIDATION_STATUS.ERROR
11
+ export function classifyDoctorExit(exitStatus, _output = '') {
12
+ return Number.isInteger(exitStatus) && exitStatus === 0
13
+ ? VALIDATION_STATUS.PASS
14
+ : VALIDATION_STATUS.ERROR
15
15
  }
16
16
 
17
17
  export function classifyTuiExit(exitStatus) {
@@ -19,5 +19,5 @@ export function classifyTuiExit(exitStatus) {
19
19
  }
20
20
 
21
21
  export function validationExitCode(doctorStatus, tuiStatus) {
22
- return doctorStatus === VALIDATION_STATUS.ERROR || tuiStatus === VALIDATION_STATUS.ERROR ? 1 : 0
22
+ return doctorStatus === VALIDATION_STATUS.PASS && tuiStatus === VALIDATION_STATUS.PASS ? 0 : 1
23
23
  }
@@ -1,151 +1,156 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * version-check.mjs — Single source of truth enforcement
4
- *
5
- * package.json is the SOURCE OF TRUTH for the Pantheon version.
6
- * Every other manifest (pyproject.toml, plugin.json, src/plugins/tui/package.json)
7
- * MUST carry the same version. This script:
8
- *
9
- * - compares every manifest against package.json and prints a divergence
10
- * table when they differ (exit code 1),
11
- * - with --fix, rewrites each divergent manifest to the package.json version.
12
- *
13
- * Mirrors the MANIFEST_FILES list in scripts/versioning.mjs so `apply`
14
- * and `check` can never drift apart.
15
- *
16
- * Usage:
17
- * node scripts/version-check.mjs # verify (exit 0 = in sync)
18
- * node scripts/version-check.mjs --fix # sync all manifests
19
- */
2
+ /** Fail-closed version and lockfile validation. */
20
3
 
21
- import { readFileSync, writeFileSync } from 'node:fs'
4
+ import { readFileSync } from 'node:fs'
22
5
  import { join } from 'node:path'
23
- import { fileURLToPath, pathToFileURL } from 'node:url'
6
+ import { pathToFileURL } from 'node:url'
7
+ import {
8
+ LOCK_INVENTORY,
9
+ VERSION_MANIFEST_INVENTORY,
10
+ validateInventory,
11
+ writeInventoryVersion,
12
+ } from './manifest-inventory.mjs'
24
13
 
25
- /** Same set of manifests as versioning.mjs (minus package.json itself). */
26
- export const MANIFESTS = [
27
- { name: 'pyproject.toml', file: 'pyproject.toml', kind: 'toml' },
28
- { name: 'plugin.json', file: 'plugin.json', kind: 'json' },
29
- {
30
- name: 'src/plugins/tui/package.json',
31
- file: join('src', 'plugins', 'tui', 'package.json'),
32
- kind: 'json',
33
- },
34
- ]
35
-
36
- function readTomlVersion(filePath) {
37
- const text = readFileSync(filePath, 'utf-8')
38
- const match = text.match(/^version\s*=\s*"([^"]+)"/m)
39
- if (!match) throw new Error(`no version field in ${filePath}`)
40
- return match[1]
41
- }
14
+ // Kept as a public compatibility API. New code must use MANIFEST_INVENTORY.
15
+ export const MANIFESTS = Object.freeze(
16
+ ['pyproject.toml', 'plugin.json', 'src/plugins/tui/package.json'].map((file) => {
17
+ const entry = VERSION_MANIFEST_INVENTORY.find((item) => item.file === file)
18
+ return { name: file, file, kind: entry.kind }
19
+ }),
20
+ )
42
21
 
43
- function readJsonVersion(filePath) {
44
- return JSON.parse(readFileSync(filePath, 'utf-8')).version
22
+ function readLegacyVersion(root, manifest) {
23
+ const path = join(root, manifest.file)
24
+ const text = readFileSync(path, 'utf8')
25
+ if (manifest.kind === 'toml') {
26
+ const match = text.match(/^version\s*=\s*"([^"]+)"/m)
27
+ if (!match) throw new Error(`${manifest.file} is missing version`)
28
+ return match[1]
29
+ }
30
+ const data = JSON.parse(text)
31
+ if (typeof data.version !== 'string') throw new Error(`${manifest.file} is missing version`)
32
+ return data.version
45
33
  }
46
34
 
47
- function readManifestVersion(filePath, kind) {
48
- try {
49
- return kind === 'toml' ? readTomlVersion(filePath) : readJsonVersion(filePath)
50
- } catch {
51
- return null
52
- }
35
+ function readSourceVersion(root) {
36
+ const data = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
37
+ return typeof data.version === 'string' ? data.version : null
53
38
  }
54
39
 
55
- function writeManifestVersion(filePath, kind, version) {
56
- if (kind === 'toml') {
57
- const text = readFileSync(filePath, 'utf-8')
58
- const updated = text.replace(/^(version\s*=\s*")[^"]+(")/m, `$1${version}$2`)
59
- writeFileSync(filePath, updated)
60
- } else {
61
- const content = JSON.parse(readFileSync(filePath, 'utf-8'))
62
- content.version = version
63
- writeFileSync(filePath, `${JSON.stringify(content, null, 2)}\n`)
64
- }
40
+ function readLockVersion(root, entry) {
41
+ const data = JSON.parse(readFileSync(join(root, entry.file), 'utf8'))
42
+ return typeof data.version === 'string' ? data.version : null
65
43
  }
66
44
 
67
45
  /**
68
- * Compare every manifest version against package.json (source of truth).
69
- *
70
- * @param {string} root - repository root containing package.json + manifests
71
- * @returns {{source: string, manifests: Array<{name: string, version: string|null, ok: boolean}>, ok: boolean}}
72
- * - source: version read from package.json
73
- * - manifests: per-manifest {name, version (null when unreadable), ok}
74
- * - ok: true when every manifest matches the source version
46
+ * Compare versions while retaining the historical MANIFESTS result shape.
47
+ * Invalid/missing inventory entries are represented as null so callers can
48
+ * display a useful table; all CLI mutation paths use validateInventory first.
75
49
  */
76
50
  export function compareVersions(root) {
77
- const source = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8')).version
78
- const manifests = MANIFESTS.map(({ name, file, kind }) => {
79
- const version = readManifestVersion(join(root, file), kind)
80
- return { name, version, ok: version === source }
51
+ let source = null
52
+ const errors = []
53
+ try {
54
+ source = readSourceVersion(root)
55
+ } catch (error) {
56
+ errors.push(`package.json: ${error.message}`)
57
+ }
58
+ const manifests = MANIFESTS.map((manifest) => {
59
+ let version = null
60
+ try {
61
+ version = readLegacyVersion(root, manifest)
62
+ } catch (error) {
63
+ errors.push(`${manifest.file}: ${error.message}`)
64
+ }
65
+ return { name: manifest.name, version, ok: version !== null && version === source }
81
66
  })
82
- return { source, manifests, ok: manifests.every((m) => m.ok) }
67
+ const locks = LOCK_INVENTORY.map((entry) => {
68
+ let version = null
69
+ try {
70
+ version = readLockVersion(root, entry)
71
+ } catch (error) {
72
+ errors.push(`${entry.file}: ${error.message}`)
73
+ }
74
+ return { name: entry.file, version, ok: version !== null && version === source }
75
+ })
76
+ try {
77
+ validateInventory(root)
78
+ } catch (error) {
79
+ errors.push(error.message)
80
+ }
81
+ return {
82
+ source,
83
+ manifests,
84
+ locks,
85
+ errors: [...new Set(errors)],
86
+ ok:
87
+ source !== null &&
88
+ manifests.every((item) => item.ok) &&
89
+ locks.every((item) => item.ok) &&
90
+ errors.length === 0,
91
+ }
83
92
  }
84
93
 
85
94
  /**
86
- * Rewrite every divergent manifest to the package.json version.
87
- *
88
- * @param {string} root - repository root
89
- * @returns {number} count of manifests rewritten
95
+ * Rewrite only version metadata after every inventory entry has passed the
96
+ * structural preflight. A second validation is performed by the CLI.
90
97
  */
91
98
  export function syncToSource(root) {
92
- const { source, manifests } = compareVersions(root)
93
- let changed = 0
94
- for (const manifest of manifests) {
95
- if (manifest.ok) continue
96
- const def = MANIFESTS.find((m) => m.name === manifest.name)
97
- writeManifestVersion(join(root, def.file), def.kind, source)
98
- changed += 1
99
- }
100
- return changed
99
+ const inventory = validateInventory(root, { allowVersionDivergence: true })
100
+ const source = inventory.sourceVersion
101
+ const changed = inventory.versions.filter(({ version }) => version !== source).length
102
+ const lockChanged = inventory.documents.filter(
103
+ ({ entry, data }) => entry.role === 'lock' && data.version !== source,
104
+ ).length
105
+ if (changed || lockChanged) writeInventoryVersion(inventory, source)
106
+ return changed + lockChanged
101
107
  }
102
108
 
103
109
  function printDivergenceTable(result) {
104
110
  const rows = [
105
111
  ['manifest', 'version', 'status'],
106
- ...result.manifests.map((m) => [m.name, m.version ?? 'MISSING', m.ok ? 'ok' : 'DIVERGENT']),
112
+ ...result.manifests.map((item) => [
113
+ item.name,
114
+ item.version ?? 'MISSING',
115
+ item.ok ? 'ok' : 'DIVERGENT',
116
+ ]),
117
+ ...result.locks.map((item) => [
118
+ item.name,
119
+ item.version ?? 'MISSING',
120
+ item.ok ? 'ok' : 'DIVERGENT',
121
+ ]),
107
122
  ]
108
- const widths = rows[0].map((_, i) => Math.max(...rows.map((row) => row[i].length)))
109
- console.log(`Source of truth: package.json → v${result.source}\n`)
110
- for (const row of rows) {
111
- console.log(row.map((cell, i) => cell.padEnd(widths[i])).join(' '))
112
- }
123
+ const widths = rows[0].map((_, index) => Math.max(...rows.map((row) => row[index].length)))
124
+ console.log(`Source of truth: package.json → v${result.source ?? 'INVALID'}\n`)
125
+ for (const row of rows)
126
+ console.log(row.map((cell, index) => cell.padEnd(widths[index])).join(' '))
127
+ for (const error of result.errors) console.error(`version-check: ${error}`)
113
128
  console.log('')
114
129
  }
115
130
 
116
131
  const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href
117
132
 
118
133
  if (isDirectRun) {
119
- // npm scripts run from the package root, so the working directory is the
120
- // repository root to check. Explicit cwd keeps the CLI testable.
121
134
  const root = process.cwd()
122
135
  const fix = process.argv.includes('--fix')
123
- let result
124
- try {
125
- result = compareVersions(root)
126
- } catch (error) {
127
- console.error(`version-check: cannot read package.json — ${error.message}`)
128
- process.exit(1)
129
- }
130
-
136
+ const result = compareVersions(root)
131
137
  if (result.ok) {
132
- console.log(`✓ All manifests match package.json v${result.source}`)
138
+ console.log(`✓ All manifests match package.json v${result.source}; locks are in sync`)
133
139
  process.exit(0)
134
140
  }
135
-
136
141
  printDivergenceTable(result)
137
- if (fix) {
142
+ if (!fix) {
143
+ console.error('version-check: inventory is invalid or out of sync')
144
+ process.exit(1)
145
+ }
146
+ try {
138
147
  const changed = syncToSource(root)
139
148
  const after = compareVersions(root)
140
- if (after.ok) {
141
- console.log(`✓ Synced ${changed} manifest(s) to v${after.source}`)
142
- process.exit(0)
143
- }
144
- console.error(
145
- `version-check: sync incomplete — ${after.manifests.filter((m) => !m.ok).length} manifest(s) still divergent`,
146
- )
149
+ if (!after.ok) throw new Error(after.errors.join('; ') || 'inventory remains divergent')
150
+ console.log(`✓ Synced ${changed} metadata entries to v${after.source}`)
151
+ process.exit(0)
152
+ } catch (error) {
153
+ console.error(`version-check: refusing to write: ${error.message}`)
147
154
  process.exit(1)
148
155
  }
149
- console.error('version-check: manifests out of sync (run with --fix to sync)')
150
- process.exit(1)
151
156
  }