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

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 (47) hide show
  1. package/README.md +44 -15
  2. package/README.pt-BR.md +39 -12
  3. package/package.json +7 -3
  4. package/plugin.json +1 -1
  5. package/pyproject.toml +57 -0
  6. package/scripts/code_mode_server.py +101 -36
  7. package/scripts/cost.mjs +30 -14
  8. package/scripts/install/opencode.mjs +6 -0
  9. package/scripts/install/plugin.mjs +3 -33
  10. package/scripts/manifest-inventory.mjs +410 -0
  11. package/scripts/mcp_persistence_server.py +518 -128
  12. package/scripts/package-evidence.mjs +242 -0
  13. package/scripts/probe-context-rehydrate.mjs +240 -0
  14. package/scripts/probe-pantheon-cost.mjs +304 -0
  15. package/scripts/release-beta-version.mjs +61 -60
  16. package/scripts/sync-tui.mjs +5 -27
  17. package/scripts/test-opencode-v1-v2-sandbox.sh +367 -96
  18. package/scripts/validation-policy.mjs +7 -7
  19. package/scripts/version-check.mjs +113 -108
  20. package/scripts/versioning.mjs +131 -88
  21. package/src/mcp/code_mode_server.py +101 -36
  22. package/src/mcp/mcp_persistence_server.py +518 -128
  23. package/src/pantheon/compaction-handoff.ts +304 -0
  24. package/src/pantheon/cost-command.ts +49 -32
  25. package/src/pantheon/delegate-manager.ts +285 -0
  26. package/src/pantheon/provider-capabilities.ts +221 -0
  27. package/src/pantheon/provider-stream-tracker.ts +182 -0
  28. package/src/pantheon/token-opt.ts +237 -11
  29. package/src/plugin-v2.ts +2 -14
  30. package/src/plugin.ts +3 -8
  31. package/src/plugins/tui/package-lock.json +2 -2
  32. package/src/plugins/tui/package.json +1 -1
  33. package/.pantheon/code-mode/.gitkeep +0 -0
  34. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-1.json +0 -8
  35. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-10.json +0 -8
  36. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-11.json +0 -8
  37. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-2.json +0 -8
  38. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-3.json +0 -8
  39. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-4.json +0 -8
  40. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-5.json +0 -8
  41. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-6.json +0 -8
  42. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-7.json +0 -8
  43. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-8.json +0 -8
  44. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-9.json +0 -8
  45. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/heartbeat.json +0 -8
  46. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/session.json +0 -11
  47. package/src/pantheon/delegate-task-adapter.ts +0 -444
@@ -41,18 +41,20 @@ MAX_SCRIPT_TIMEOUT: int = 300
41
41
  # ── Env Allowlist ──────────────────────────────────────────────────────────────
42
42
  # Only these variables are passed to script subprocesses. Everything else
43
43
  # (secrets, cloud credentials, etc.) is stripped to limit blast radius.
44
- _CODE_MODE_ENV_ALLOWLIST: frozenset[str] = frozenset({
45
- "PATH",
46
- "HOME",
47
- "LANG",
48
- "PANTHEON_HOME",
49
- "PANTHEON_PROJECT",
50
- "OPENAI_API_KEY",
51
- "OPENAI_BASE_URL",
52
- "EVAL_JUDGE_MODEL",
53
- "SHELL",
54
- "USER",
55
- })
44
+ _CODE_MODE_ENV_ALLOWLIST: frozenset[str] = frozenset(
45
+ {
46
+ "PATH",
47
+ "HOME",
48
+ "LANG",
49
+ "PANTHEON_HOME",
50
+ "PANTHEON_PROJECT",
51
+ "OPENAI_API_KEY",
52
+ "OPENAI_BASE_URL",
53
+ "EVAL_JUDGE_MODEL",
54
+ "SHELL",
55
+ "USER",
56
+ }
57
+ )
56
58
 
57
59
  # Optional extra vars that callers may opt-in to pass through. Populate
58
60
  # this set at startup if needed (e.g. ``CODE_MODE_EXTRA_ENV.add("MY_VAR")``).
@@ -127,6 +129,7 @@ def _detect_prlimit() -> str | None:
127
129
  return None
128
130
  return shutil.which("prlimit")
129
131
 
132
+
130
133
  # ── Scripts Directory Resolution ─────────────────────────────────────────────
131
134
  # Priority:
132
135
  # 1. /.opencode/.pantheon/code-mode/ (project install)
@@ -136,6 +139,49 @@ def _detect_prlimit() -> str | None:
136
139
  # fallback — package.json `files` includes .pantheon/code-mode/**)
137
140
 
138
141
 
142
+ def _is_within(path: Path, root: Path) -> bool:
143
+ """Return whether a resolved *path* is contained by a resolved *root*."""
144
+ try:
145
+ return os.path.commonpath((str(root), str(path))) == str(root)
146
+ except (OSError, ValueError):
147
+ return False
148
+
149
+
150
+ def _has_usable_scripts(scripts_dir: Path) -> bool:
151
+ """Return whether *scripts_dir* contains an executable code-mode target.
152
+
153
+ A directory is only a valid resolver candidate when it contains a regular
154
+ ``.py`` or ``.sh`` file that can pass the same direct-child and path
155
+ containment rules used by script execution. This prevents an empty
156
+ project overlay (or a directory containing only metadata) from masking a
157
+ lower-priority installation.
158
+ """
159
+ try:
160
+ if scripts_dir.is_symlink():
161
+ return False
162
+ resolved_dir = scripts_dir.resolve(strict=True)
163
+ if not resolved_dir.is_dir():
164
+ return False
165
+ for script_path in scripts_dir.iterdir():
166
+ if script_path.name.startswith("."):
167
+ continue
168
+ if script_path.suffix.lower() not in ALLOWED_EXTENSIONS:
169
+ continue
170
+ try:
171
+ resolved_script = script_path.resolve(strict=True)
172
+ except (OSError, RuntimeError):
173
+ continue
174
+ if (
175
+ resolved_script.is_file()
176
+ and resolved_script.parent == resolved_dir
177
+ and _is_within(resolved_script, resolved_dir)
178
+ ):
179
+ return True
180
+ except (OSError, RuntimeError):
181
+ return False
182
+ return False
183
+
184
+
139
185
  def _packaged_scripts_dir() -> Path | None:
140
186
  """Return the code-mode dir shipped inside the installed package, if any.
141
187
 
@@ -144,22 +190,27 @@ def _packaged_scripts_dir() -> Path | None:
144
190
  Returns None when no packaged copy exists.
145
191
  """
146
192
  try:
147
- here = Path(__file__).resolve()
148
- except Exception:
193
+ here = Path(__file__).resolve(strict=True)
194
+ except (OSError, RuntimeError):
149
195
  return None
150
196
  for parent in [here.parent, *here.parents]:
151
197
  try:
152
198
  candidate = parent / ".pantheon" / "code-mode"
153
199
  except Exception:
154
200
  continue
155
- try:
156
- if candidate.is_dir():
157
- return candidate
158
- except OSError:
159
- continue
201
+ if _has_usable_scripts(candidate):
202
+ return candidate
160
203
  return None
161
204
 
162
205
 
206
+ def _resolve_scripts_dir(candidates: list[Path]) -> Path | None:
207
+ """Select the first usable candidate, then try the packaged fallback."""
208
+ for candidate in candidates:
209
+ if _has_usable_scripts(candidate):
210
+ return candidate
211
+ return _packaged_scripts_dir()
212
+
213
+
163
214
  _PANTHEON_HOME: Path = pantheon_home()
164
215
  _SCRIPTS_DIR_CANDIDATES: list[Path] = []
165
216
  _proj = pantheon_project()
@@ -168,15 +219,9 @@ if _proj is not None:
168
219
  _SCRIPTS_DIR_CANDIDATES.append(_proj / ".pantheon" / "code-mode")
169
220
  _SCRIPTS_DIR_CANDIDATES.append(_PANTHEON_HOME / ".pantheon" / "code-mode")
170
221
 
171
- SCRIPTS_DIR: Path = _PANTHEON_HOME / ".pantheon" / "code-mode" # default
172
- for _candidate in _SCRIPTS_DIR_CANDIDATES:
173
- if _candidate.is_dir():
174
- SCRIPTS_DIR = _candidate
175
- break
176
- else:
177
- _packaged = _packaged_scripts_dir()
178
- if _packaged is not None:
179
- SCRIPTS_DIR = _packaged
222
+ SCRIPTS_DIR: Path = _resolve_scripts_dir(_SCRIPTS_DIR_CANDIDATES) or (
223
+ _PANTHEON_HOME / ".pantheon" / "code-mode"
224
+ )
180
225
 
181
226
  # ── FastMCP App ───────────────────────────────────────────────────────────────
182
227
  mcp = FastMCP(
@@ -208,11 +253,21 @@ def _validate_script_name(script_name: str) -> Path:
208
253
  allowed = ", ".join(sorted(ALLOWED_EXTENSIONS))
209
254
  raise ValueError(f"Extension '{ext}' not allowed. Allowed: {allowed}")
210
255
 
211
- script_path = (SCRIPTS_DIR / name).resolve()
212
- if not str(script_path).startswith(str(SCRIPTS_DIR.resolve())):
256
+ if SCRIPTS_DIR.is_symlink():
257
+ raise ValueError(f"Invalid script name: '{script_name}'")
258
+
259
+ try:
260
+ scripts_root = SCRIPTS_DIR.resolve(strict=True)
261
+ script_path = (SCRIPTS_DIR / name).resolve(strict=True)
262
+ except (FileNotFoundError, OSError, RuntimeError):
263
+ raise ValueError(f"Script '{script_name}' not found") from None
264
+
265
+ if (
266
+ not _is_within(script_path, scripts_root)
267
+ or script_path.parent != scripts_root
268
+ or not script_path.is_file()
269
+ ):
213
270
  raise ValueError(f"Invalid script name: '{script_name}'")
214
- if not script_path.exists():
215
- raise ValueError(f"Script '{script_name}' not found")
216
271
 
217
272
  return script_path
218
273
 
@@ -286,7 +341,9 @@ def _parse_frontmatter(script_path: Path) -> dict[str, Any]:
286
341
  break
287
342
  if not stripped.startswith("#"):
288
343
  return {} # non-comment line inside the block → not frontmatter
289
- body_lines.append(stripped[1:].lstrip() if stripped.startswith("# ") else stripped[1:])
344
+ body_lines.append(
345
+ stripped[1:].lstrip() if stripped.startswith("# ") else stripped[1:]
346
+ )
290
347
  else:
291
348
  return {} # no closing delimiter
292
349
 
@@ -378,7 +435,9 @@ async def list_code_mode_scripts() -> str:
378
435
 
379
436
  @mcp.resource(
380
437
  "pantheon://code-mode/scripts/{script_name}",
381
- description="Content of a code-mode script by name, with parsed frontmatter metadata",
438
+ description=(
439
+ "Content of a code-mode script by name, with parsed frontmatter metadata"
440
+ ),
382
441
  )
383
442
  async def get_code_mode_script(script_name: str) -> str:
384
443
  """Return the source content of a code-mode script plus its metadata."""
@@ -486,8 +545,14 @@ async def execute_code_script(
486
545
  await proc.wait()
487
546
  duration_ms = int((time.monotonic() - started) * 1000)
488
547
  return _build_result(
489
- "", "", -1, timed_out=True, duration_ms=duration_ms,
490
- metadata=metadata, json_output=json_output, timeout_s=timeout_s,
548
+ "",
549
+ "",
550
+ -1,
551
+ timed_out=True,
552
+ duration_ms=duration_ms,
553
+ metadata=metadata,
554
+ json_output=json_output,
555
+ timeout_s=timeout_s,
491
556
  )
492
557
  except FileNotFoundError:
493
558
  return _build_result(