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.
- package/README.md +44 -15
- package/README.pt-BR.md +39 -12
- package/package.json +7 -3
- package/plugin.json +1 -1
- package/pyproject.toml +57 -0
- package/scripts/code_mode_server.py +101 -36
- package/scripts/cost.mjs +30 -14
- package/scripts/install/opencode.mjs +6 -0
- package/scripts/install/plugin.mjs +3 -33
- package/scripts/manifest-inventory.mjs +410 -0
- package/scripts/mcp_persistence_server.py +518 -128
- package/scripts/package-evidence.mjs +242 -0
- package/scripts/probe-context-rehydrate.mjs +240 -0
- package/scripts/probe-pantheon-cost.mjs +304 -0
- package/scripts/release-beta-version.mjs +61 -60
- package/scripts/sync-tui.mjs +5 -27
- package/scripts/test-opencode-v1-v2-sandbox.sh +367 -96
- package/scripts/validation-policy.mjs +7 -7
- package/scripts/version-check.mjs +113 -108
- package/scripts/versioning.mjs +131 -88
- package/src/mcp/code_mode_server.py +101 -36
- package/src/mcp/mcp_persistence_server.py +518 -128
- package/src/pantheon/compaction-handoff.ts +304 -0
- package/src/pantheon/cost-command.ts +49 -32
- package/src/pantheon/delegate-manager.ts +285 -0
- package/src/pantheon/provider-capabilities.ts +221 -0
- package/src/pantheon/provider-stream-tracker.ts +182 -0
- package/src/pantheon/token-opt.ts +237 -11
- package/src/plugin-v2.ts +2 -14
- package/src/plugin.ts +3 -8
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +1 -1
- package/.pantheon/code-mode/.gitkeep +0 -0
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-1.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-10.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-11.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-2.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-3.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-4.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-5.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-6.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-7.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-8.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-9.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/heartbeat.json +0 -8
- package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/session.json +0 -11
- 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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
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
|
-
|
|
156
|
-
|
|
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 =
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
212
|
-
|
|
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(
|
|
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=
|
|
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
|
-
"",
|
|
490
|
-
|
|
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(
|