pantheon-opencode 1.4.2 → 1.5.0-beta.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 (98) hide show
  1. package/.pantheon/code-mode/.gitkeep +0 -0
  2. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-1.json +8 -0
  3. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-10.json +8 -0
  4. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-11.json +8 -0
  5. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-2.json +8 -0
  6. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-3.json +8 -0
  7. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-4.json +8 -0
  8. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-5.json +8 -0
  9. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-6.json +8 -0
  10. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-7.json +8 -0
  11. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-8.json +8 -0
  12. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-9.json +8 -0
  13. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/heartbeat.json +8 -0
  14. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/session.json +11 -0
  15. package/.pantheon/code-mode/README.md +93 -0
  16. package/.pantheon/code-mode/checkpoint-session.py +7 -0
  17. package/.pantheon/code-mode/checkpoint-session.sh +11 -0
  18. package/.pantheon/code-mode/checkpoint_session.py +481 -0
  19. package/.pantheon/code-mode/compress-inline.py +450 -0
  20. package/.pantheon/code-mode/eval-llm-judge.py +255 -0
  21. package/.pantheon/code-mode/eval-monte-carlo.py +368 -0
  22. package/.pantheon/code-mode/eval-run.py +197 -0
  23. package/.pantheon/code-mode/eval-static.py +321 -0
  24. package/.pantheon/code-mode/example-sync.sh +5 -0
  25. package/.pantheon/code-mode/session-end-save.py +186 -0
  26. package/.pantheon/code-mode/session-end-save.sh +18 -0
  27. package/.pantheon/code-mode/test-allowed.sh +5 -0
  28. package/.pantheon/code-mode/test-auto-continue-checkpoint.sh +179 -0
  29. package/.pantheon/code-mode/test-env.sh +7 -0
  30. package/.pantheon/code-mode/test-prlimit.sh +9 -0
  31. package/.pantheon/code-mode/test-timeout.sh +6 -0
  32. package/README.md +158 -1189
  33. package/README.pt-BR.md +222 -0
  34. package/bin/pantheon-init.mjs +30 -11
  35. package/opencode.json +1 -0
  36. package/package.json +14 -3
  37. package/plugin.json +15 -0
  38. package/scripts/_pantheon_paths.py +48 -7
  39. package/scripts/ci-validate-yaml.py +7 -2
  40. package/scripts/code_mode_server.py +33 -2
  41. package/scripts/doctor.mjs +135 -127
  42. package/scripts/generate-preset-docs.mjs +127 -66
  43. package/scripts/generate-prompts.sh +2 -2
  44. package/scripts/install/cli-ui.mjs +8 -1
  45. package/scripts/install/config-migration.mjs +401 -0
  46. package/scripts/install/health-check.mjs +3 -3
  47. package/scripts/install/model-picker.mjs +72 -23
  48. package/scripts/install/opencode-version.mjs +47 -0
  49. package/scripts/install/opencode.mjs +261 -88
  50. package/scripts/install/plugin.mjs +112 -42
  51. package/scripts/install/shared.mjs +13 -158
  52. package/scripts/install-mcp.mjs +44 -232
  53. package/scripts/mcp_persistence_server.py +344 -15
  54. package/scripts/mcp_resources_server.py +144 -40
  55. package/scripts/memory_mcp_server.py +2 -2
  56. package/scripts/recover-zenodo-deposition.mjs +146 -0
  57. package/scripts/sync-tui.mjs +32 -7
  58. package/scripts/test-opencode-v1-v2-sandbox.sh +591 -0
  59. package/scripts/uninstall.mjs +298 -954
  60. package/scripts/validate-zenodo-release.mjs +154 -0
  61. package/scripts/zenodo-artifact.mjs +21 -0
  62. package/scripts/zenodo-release-state.mjs +53 -0
  63. package/src/mcp/_pantheon_paths.py +48 -7
  64. package/src/mcp/code_mode_server.py +33 -2
  65. package/src/mcp/eval_store.py +1 -1
  66. package/src/mcp/mcp_persistence_server.py +344 -15
  67. package/src/mcp/mcp_resources_server.py +144 -40
  68. package/src/mcp/pantheon_vision_server.py +1 -1
  69. package/src/mcp/toon_codec.py +491 -0
  70. package/src/pantheon/background-job-board.ts +67 -1
  71. package/src/pantheon/cost-command.ts +6 -2
  72. package/src/pantheon/delegate-manager.ts +384 -0
  73. package/src/pantheon/delegate-task-adapter.ts +444 -0
  74. package/src/pantheon/delegation-notify.ts +35 -20
  75. package/src/pantheon/model-command.ts +86 -32
  76. package/src/pantheon/token-opt.ts +555 -0
  77. package/src/pantheon/toon-codec.ts +481 -0
  78. package/src/pantheon/tui-version-check.ts +1 -3
  79. package/src/pantheon/v2-bridge.ts +129 -0
  80. package/src/pantheon/v2-events.ts +131 -0
  81. package/src/pantheon/v2-hooks.ts +287 -0
  82. package/src/pantheon/v2-tool-definitions.ts +331 -0
  83. package/src/plugin-v2/index.ts +21 -0
  84. package/src/plugin-v2.ts +836 -0
  85. package/src/plugin.ts +57 -10
  86. package/src/plugins/tui/dist/server.d.ts +1 -1
  87. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  88. package/src/plugins/tui/dist/server.js +1 -1
  89. package/src/plugins/tui/dist/server.js.map +1 -1
  90. package/src/plugins/tui/dist/tui.d.ts +10 -7
  91. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  92. package/src/plugins/tui/dist/tui.js +11 -8
  93. package/src/plugins/tui/dist/tui.js.map +1 -1
  94. package/src/plugins/tui/dist/tui.tsx +22 -16
  95. package/src/plugins/tui/package-lock.json +51 -51
  96. package/src/plugins/tui/package.json +7 -7
  97. package/src/plugins/tui/src/index.tsx +22 -16
  98. package/src/skills/README.md +0 -2
@@ -65,10 +65,7 @@ const modelArgs = {
65
65
  .optional()
66
66
  .describe('Operation: status/show, set, or reset. No args runs wizard.'),
67
67
  agent: z.string().optional().describe('Agent name (one of 14 canonical agents).'),
68
- model: z
69
- .string()
70
- .optional()
71
- .describe('Model in provider/model-id format; used by set.'),
68
+ model: z.string().optional().describe('Model in provider/model-id format; used by set.'),
72
69
  effort: z
73
70
  .enum(['low', 'medium', 'high'])
74
71
  .optional()
@@ -357,10 +354,7 @@ function presetPathForScope(options: ModelCommandOptions, scope: ModelScope): st
357
354
  return globalCandidates[0] ?? candidates[0]!
358
355
  }
359
356
 
360
- async function readActivePresetRaw(
361
- path: string,
362
- label: string,
363
- ): Promise<ReadFileResult> {
357
+ async function readActivePresetRaw(path: string, label: string): Promise<ReadFileResult> {
364
358
  const file = await readRegularFile(path, label)
365
359
  if (file === undefined) return { exists: false, valid: true, data: null }
366
360
  try {
@@ -368,7 +362,13 @@ async function readActivePresetRaw(
368
362
  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
369
363
  return { exists: true, valid: false, data: null, content: file.content, mode: file.mode }
370
364
  }
371
- return { exists: true, valid: true, data: parsed as Record<string, unknown>, content: file.content, mode: file.mode }
365
+ return {
366
+ exists: true,
367
+ valid: true,
368
+ data: parsed as Record<string, unknown>,
369
+ content: file.content,
370
+ mode: file.mode,
371
+ }
372
372
  } catch {
373
373
  return { exists: true, valid: false, data: null, content: file.content, mode: file.mode }
374
374
  }
@@ -444,9 +444,21 @@ async function writeActivePresetAtomically(
444
444
  if (current.content === undefined) {
445
445
  throw new ModelCommandError('configuration content could not be read; file preserved')
446
446
  }
447
- await atomicReplace(backupPath, current.content, backupMode, 'configuration backup', backup !== undefined)
447
+ await atomicReplace(
448
+ backupPath,
449
+ current.content,
450
+ backupMode,
451
+ 'configuration backup',
452
+ backup !== undefined,
453
+ )
448
454
  }
449
- await atomicReplace(path, `${JSON.stringify(data, null, 2)}\n`, originalMode, 'configuration file', current.exists)
455
+ await atomicReplace(
456
+ path,
457
+ `${JSON.stringify(data, null, 2)}\n`,
458
+ originalMode,
459
+ 'configuration file',
460
+ current.exists,
461
+ )
450
462
  }
451
463
 
452
464
  function ensureActivePresetShape(
@@ -460,11 +472,21 @@ function ensureActivePresetShape(
460
472
  }
461
473
  if (typeof out.source !== 'string') out.source = 'cli'
462
474
  out.updated_at = new Date().toISOString()
463
- if (out.overrides === undefined || out.overrides === null || typeof out.overrides !== 'object' || Array.isArray(out.overrides)) {
475
+ if (
476
+ out.overrides === undefined ||
477
+ out.overrides === null ||
478
+ typeof out.overrides !== 'object' ||
479
+ Array.isArray(out.overrides)
480
+ ) {
464
481
  out.overrides = {}
465
482
  }
466
483
  const overrides = out.overrides as Record<string, unknown>
467
- if (overrides.agents === undefined || overrides.agents === null || typeof overrides.agents !== 'object' || Array.isArray(overrides.agents)) {
484
+ if (
485
+ overrides.agents === undefined ||
486
+ overrides.agents === null ||
487
+ typeof overrides.agents !== 'object' ||
488
+ Array.isArray(overrides.agents)
489
+ ) {
468
490
  overrides.agents = {}
469
491
  }
470
492
  return out
@@ -509,14 +531,25 @@ async function updateActivePreset(
509
531
  } catch {
510
532
  // ignore
511
533
  }
512
- } else if (current.data && typeof current.data === 'object' && (current.data as Record<string, unknown>).preset) {
534
+ } else if (
535
+ current.data &&
536
+ typeof current.data === 'object' &&
537
+ (current.data as Record<string, unknown>).preset
538
+ ) {
513
539
  const existing = (current.data as Record<string, unknown>).preset as string
514
- if (typeof existing === 'string' && existing.trim() !== '' && SAFE_PRESET_NAME.test(existing.trim())) {
540
+ if (
541
+ typeof existing === 'string' &&
542
+ existing.trim() !== '' &&
543
+ SAFE_PRESET_NAME.test(existing.trim())
544
+ ) {
515
545
  fallbackPreset = existing.trim()
516
546
  }
517
547
  }
518
548
 
519
- const shaped = ensureActivePresetShape(current.data as Record<string, unknown> | null, fallbackPreset)
549
+ const shaped = ensureActivePresetShape(
550
+ current.data as Record<string, unknown> | null,
551
+ fallbackPreset,
552
+ )
520
553
  const overrides = shaped.overrides as Record<string, unknown>
521
554
  const agents = overrides.agents as Record<string, unknown>
522
555
 
@@ -551,7 +584,10 @@ async function updateActivePreset(
551
584
 
552
585
  // If clamped, append warning
553
586
  if (variantInfo.clamped) {
554
- visionWarning = [visionWarning, `effort clamped to ${variantInfo.variant} for model ${model}`]
587
+ visionWarning = [
588
+ visionWarning,
589
+ `effort clamped to ${variantInfo.variant} for model ${model}`,
590
+ ]
555
591
  .filter(Boolean)
556
592
  .join('; ')
557
593
  }
@@ -584,10 +620,6 @@ async function updateActivePreset(
584
620
 
585
621
  // ─── Status ─────────────────────────────────────────────────────────────────
586
622
 
587
- function silentLogger(): ModelCommandLogger {
588
- return { warn: () => {}, log: () => {} }
589
- }
590
-
591
623
  async function resolvePerAgentStatus(options: ModelCommandOptions): Promise<{
592
624
  presetName: string | null
593
625
  presetSource: string | null
@@ -624,12 +656,20 @@ async function resolvePerAgentStatus(options: ModelCommandOptions): Promise<{
624
656
  let vision: boolean | undefined
625
657
 
626
658
  if (resolved) {
627
- const merged = (resolved.agents as Record<string, { model?: string; reasoning_effort?: string; variant?: string }>)?.[key]
659
+ const merged = (
660
+ resolved.agents as Record<
661
+ string,
662
+ { model?: string; reasoning_effort?: string; variant?: string }
663
+ >
664
+ )?.[key]
628
665
  if (merged?.model) {
629
666
  model = merged.model
630
- effort = (merged as { reasoning_effort?: string }).reasoning_effort ?? (merged as { variant?: string }).variant
667
+ effort =
668
+ (merged as { reasoning_effort?: string }).reasoning_effort ??
669
+ (merged as { variant?: string }).variant
631
670
  // Determine origin: file overrides > file preset > env
632
- const fileOverrides = (resolved.overrides as { agents?: Record<string, unknown> } | null)?.agents
671
+ const fileOverrides = (resolved.overrides as { agents?: Record<string, unknown> } | null)
672
+ ?.agents
633
673
  if (resolved.source === 'file' && fileOverrides && Object.hasOwn(fileOverrides, key)) {
634
674
  origin = 'override'
635
675
  } else if (resolved.source === 'env') {
@@ -665,7 +705,8 @@ async function renderStatus(options: ModelCommandOptions): Promise<string> {
665
705
  const lines: string[] = []
666
706
  lines.push('Pantheon model configuration per-agent (restart OpenCode after changes):')
667
707
  if (presetName) {
668
- const originLabel = presetSource === 'env' ? 'env' : presetSource === 'file' ? 'preset' : presetSource
708
+ const originLabel =
709
+ presetSource === 'env' ? 'env' : presetSource === 'file' ? 'preset' : presetSource
669
710
  lines.push(`active preset: ${presetName} (${presetSource ?? 'unknown'})`)
670
711
  // For test compatibility, also show origin mapping explicitly
671
712
  void originLabel
@@ -700,7 +741,8 @@ export function buildWizardQuestions(): unknown[] {
700
741
  },
701
742
  {
702
743
  header: 'Modelo',
703
- question: 'Informe o modelo em provider/model-id (ex: openai/gpt-5.6 ou opencode-go/kimi-k2.7-code)',
744
+ question:
745
+ 'Informe o modelo em provider/model-id (ex: openai/gpt-5.6 ou opencode-go/kimi-k2.7-code)',
704
746
  kind: 'text',
705
747
  },
706
748
  {
@@ -717,8 +759,14 @@ export function buildWizardQuestions(): unknown[] {
717
759
  header: 'Escopo',
718
760
  question: 'Onde salvar o override?',
719
761
  options: [
720
- { label: 'project', description: './.pantheon/active-preset.json (escopo do projeto, seguro)' },
721
- { label: 'global', description: '~/.config/opencode/.pantheon/active-preset.json (global)' },
762
+ {
763
+ label: 'project',
764
+ description: './.pantheon/active-preset.json (escopo do projeto, seguro)',
765
+ },
766
+ {
767
+ label: 'global',
768
+ description: '~/.config/opencode/.pantheon/active-preset.json (global)',
769
+ },
722
770
  ],
723
771
  multiSelect: false,
724
772
  },
@@ -741,11 +789,15 @@ async function promptViaReadline(
741
789
  answers.model = modelAns
742
790
 
743
791
  const effortQ = questions[2]!
744
- const effortAns = (await rl.question(`${effortQ.question} [low/medium/high] : `)).trim().toLowerCase()
792
+ const effortAns = (await rl.question(`${effortQ.question} [low/medium/high] : `))
793
+ .trim()
794
+ .toLowerCase()
745
795
  answers.effort = effortAns || 'medium'
746
796
 
747
797
  const scopeQ = questions[3]!
748
- const scopeAns = (await rl.question(`${scopeQ.question} [project/global] : `)).trim().toLowerCase()
798
+ const scopeAns = (await rl.question(`${scopeQ.question} [project/global] : `))
799
+ .trim()
800
+ .toLowerCase()
749
801
  answers.scope = scopeAns || 'project'
750
802
 
751
803
  return answers
@@ -779,7 +831,8 @@ async function runWizard(options: ModelCommandOptions): Promise<string> {
779
831
  const agent = typeof answers.agent === 'string' ? answers.agent.trim() : ''
780
832
  const model = typeof answers.model === 'string' ? answers.model.trim() : ''
781
833
  const effort = typeof answers.effort === 'string' ? answers.effort.trim().toLowerCase() : ''
782
- const scopeRaw = typeof answers.scope === 'string' ? answers.scope.trim().toLowerCase() : 'project'
834
+ const scopeRaw =
835
+ typeof answers.scope === 'string' ? answers.scope.trim().toLowerCase() : 'project'
783
836
  const scope: ModelScope = scopeRaw === 'global' ? 'global' : 'project'
784
837
 
785
838
  if (!agent) return 'pantheon_model wizard failed: agent is required'
@@ -830,7 +883,8 @@ export function createModelCommand(options: ModelCommandOptions = {}): ModelTool
830
883
  return await runWizard(options)
831
884
  }
832
885
 
833
- const action: ModelAction = (args.action ?? (hasAgentModelEffort ? 'set' : 'status')) as ModelAction
886
+ const action: ModelAction = (args.action ??
887
+ (hasAgentModelEffort ? 'set' : 'status')) as ModelAction
834
888
 
835
889
  if (action === 'status' || action === 'show') return await renderStatus(options)
836
890