Conversation
jaiph run spawns the workflow leader detached and previously stopped it with process.kill(-pid, signal) — a POSIX-only negative-PID group kill. On win32 that throws, the child.kill() fallback terminated only the leader, and the agent backends / script children it spawned were orphaned. Add src/runtime/kernel/portability.ts exporting killProcessTree(pid, signal) as the single sanctioned home for group kills. On POSIX it preserves prior behavior (negative-PID group kill with a per-process ESRCH fallback); on win32 it force-kills the whole tree with `taskkill /pid <pid> /T /F` (spawned, not shelled) via an injectable seam, degrading to a per-process kill if taskkill cannot launch. Because taskkill /F is already forceful, the SIGTERM->SIGKILL escalation is a documented no-op on win32. Repoint every group-kill call site at the helper: run teardown (lifecycle.ts), the prompt watchdog (prompt.ts), and the Docker run-timeout kill (docker.ts). New unit tests stub process.platform to cover both branches (negative-PID kill on POSIX plus ESRCH fallback; taskkill /T argv on win32) and prove win32 never calls process.kill with a negative PID; a src/-wide lint test asserts no production file outside portability.ts matches `process.kill(-`. Docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Script steps previously ran by spawning the emitted file directly, relying on the `#!/usr/bin/env <lang>` shebang and the 0o755 exec bit. Windows honors neither, and `noexec` mounts on Linux strip the exec bit, both breaking script execution. The runtime already knows the interpreter since it writes the shebang itself, so `executeScript` now reads the emitted script's shebang, resolves the interpreter through the new `resolveInterpreterFromShebang` (`src/parse/script-bash.ts`), and spawns `<interpreter> <scriptPath> <args...>` explicitly. The shebang line is still written into every emitted script so they remain directly executable by hand on POSIX, but the runtime no longer depends on it or on the exec bit being honored. A spawn ENOENT from a missing interpreter now surfaces as a diagnosable Jaiph error naming the interpreter instead of a raw `spawn <name> ENOENT`. New unit tests assert the resolved interpreter and argv on an injectable `_scriptSpawn` seam, prove a script with the exec bit stripped (0o644) still runs on POSIX, and prove a missing-interpreter shebang produces the diagnosable error. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Inline workflow shell lines (executeShLine) and CLI hook commands used a
hardcoded spawn("sh", ["-c", ...]), which fails on win32 where there is no
sh on the default PATH. Add resolveShell() to the portability module as the
single seam both call sites go through: on POSIX it returns bare sh; on
win32 it locates Git for Windows' bundled sh.exe, first on PATH and then in
the standard install layouts (<Git>/bin/sh.exe, <Git>/usr/bin/sh.exe) under
each known root, throwing a diagnosable E_NO_POSIX_SHELL error naming Git
for Windows if none is found. Resolution is memoized per process. Inline
lines are never translated to cmd/PowerShell, so POSIX shell semantics are
unchanged. Unit tests stub process.platform and the PATH/existence lookup
across all branches, and a src/-wide lint test asserts no spawn("sh") call
site remains outside portability.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Close three remaining POSIX assumptions that broke a host-only Windows runtime. prepareClaudeEnv now falls back to os.homedir() when neither execEnv.HOME nor process.env.HOME is set, so USERPROFILE-only environments resolve the .claude config dir; an explicit execEnv.HOME still wins. resolveDockerConfig forces host-only mode on win32 with a one-line notice (same UX as JAIPH_UNSAFE=true), so the CLI never probes docker and never hard-fails on a missing daemon, and JAIPH_DOCKER_ENABLED=true cannot override it. A new canUseAnsi() helper in the portability module centralizes the isTTY + NO_COLOR gate; every color/erase emission site routes through it so the policy lives in one place. Adds unit tests for each and a src-wide lint test asserting no production file outside portability.ts gates directly on isTTY && NO_COLOR. Docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a Windows x64 target to the release workflow so every release ships a fifth standalone binary alongside darwin/linux x arm64/x64. A new bun-windows-x64 matrix entry (with an ext field so the compiled outfile and uploaded artifact carry the .exe suffix) produces jaiph-windows-x64.exe; Bun has no windows-arm64 target so Windows is x64 only. The .exe is included in SHA256SUMS generation and in both the stable and nightly gh release upload lists. A new sanity-windows job runs on windows-latest, downloads the .exe artifact, and runs jaiph-windows-x64.exe --version through a version gate; the publish job now needs [build, sanity-windows], so a Windows version mismatch fails the whole release. The linux-x64 gate's inline comparison is extracted into a shared, testable scripts/release-version-check.sh that both gates delegate to. Update the release asset naming contract in docs/contributing.md and docs/architecture.md, and add integration/release-workflow.test.ts asserting the five-binary matrix, SHA256SUMS and upload-list coverage, the shared gate behavior, and naming-contract/installer parity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add docs/install.ps1 as the Windows counterpart to the POSIX docs/install (run with irm https://jaiph.org/install.ps1 | iex). It downloads jaiph-windows-x64.exe and SHA256SUMS from the pinned release ref (default the current stable tag, overridable via JAIPH_REPO_REF / first arg plus JAIPH_RELEASE_BASE_URL for local file:// dirs used by tests), verifies the SHA-256 with Get-FileHash and installs nothing on mismatch, then installs to %LOCALAPPDATA%\jaiph\bin\jaiph.exe (overridable via JAIPH_BIN_DIR), adds it to the user PATH if absent, and prints the same try-it hints as the bash installer. Non-x64 / ARM Windows exits non-zero with a documented unsupported-platform message. The bash installer now rejects Windows-like uname values and points at the PowerShell one-liner, and prepare_release.jh rewrites the pinned ref in both installers in lockstep. CI gains an installer-powershell job on windows-latest that cross-compiles the real .exe and runs e2e/tests/installer_powershell.ps1 (checksum mismatch, unsupported arch, happy-path install with no Node/npm/Bun on PATH); the Docker publish job now needs it. Host-portable guards live in integration/installer-powershell.test.ts. Docs updated (setup.md, index.html, architecture.md, contributing.md, README). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The landing page hero install card now offers a Windows PowerShell path alongside the POSIX curl | bash one-liners. A new .os-switch sub-toggle (macOS/Linux vs Windows) sits above the run-sample / init-project / just-install tabs, and each panel wraps its content in .os-variant blocks. On load, attachOsSwitch() (docs/assets/js/main.js) auto-selects the Windows variant for Windows visitors via isWindowsPlatform(), which prefers navigator.userAgentData?.platform and falls back to navigator.platform. macOS/Linux visitors keep today's POSIX default with no layout shift; manual switching stays available and is remembered card-wide. The 'Just install' Windows variant exposes the copy-able irm https://jaiph.org/install.ps1 | iex line; the run-sample and init-project tabs (no PowerShell pipe equivalent) show the install one-liner plus a short 'then run:' jaiph command instead of a bash-only default. The static-render constraint holds: the POSIX variant carries is-active in the markup and CSS hides inactive variants, so with JS disabled all bash one-liners render and no panel is blank. New Playwright cases in the docs suite assert the Windows default, the unchanged macOS/Linux default, manual platform + tab switching, the clipboard contents of the Windows copy button, and the JS-disabled static render. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Config blocks now resolve `${identifier}` and bare-identifier sugar at runtime so workflows can parameterize agent settings like default_model.
Co-authored-by: Cursor <cursoragent@cursor.com>
…lementation)
Spiked MVP of `jaiph mcp <file.jh>` plus design doc and queue tasks.
PARTIAL — do not treat as landed: verification gaps are inventoried in
QUEUE.md ("MCP 1-4/8") and must be closed by the queue task before this
feature is considered done.
Implemented:
- src/cli/mcp/tools.ts — tool derivation (exports narrowing, route-target
exclusion, default→file-slug rename, comment
descriptions, string schemas); 10 unit tests
- src/cli/mcp/server.ts — newline-delimited JSON-RPC 2.0 stdio server
(initialize/ping/tools/list/tools/call,
list_changed); 16 unit tests
- src/cli/mcp/call.ts — per-call runner spawn, event capture, result
composition (return_value.txt → logs → note)
- src/cli/commands/mcp.ts — command wiring, hot reload (watchFile), host
execution like `jaiph run --raw`
- src/cli/index.ts, usage — `mcp` subcommand + `--mcp` alias
- runtime: runRoot(name, args) generalizes runDefault; runner dispatches any
symbol; workflow-launch.ts fix — buildRunModuleLaunch hardcoded "default"
and dropped the requested workflow symbol
- design/2026-07-14-mcp-server.md — full design; QUEUE.md — grouped task with
per-piece verification state + ENV --env passthrough task
Verified: tsc + build clean; 26/26 mcp unit tests; live stdio session
(handshake, list, calls incl. -32602 and isError legs, concurrency).
NOT verified (see QUEUE.md acceptance):
- `jaiph run` direction of the workflow-launch fix (touches every run)
- hot reload path (never exercised)
- runRoot/launch argv have no dedicated unit tests
- full `npm test` suite not run since these changes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Land the verification and gap-closing work for the spiked `jaiph mcp` MVP whose implementation was committed earlier as WIP. Adds the missing automated coverage and user-facing docs; no runtime behaviour changes. Tests: pin the launch argv (the workflow symbol must reach the runner verbatim, guarding the fixed hardcoded-"default" bug, plus the empty fallback); unit tests for runtime `runRoot` (non-default workflow runs as root with positional param binding and return_value.txt on success, unknown workflow returns 1 with no return value, runDefault delegates unchanged); and an end-to-end scripted stdio session covering initialize, tools/list with comment-derived descriptions, tools/call return values, missing-arg -32602, workflow-failure isError, compile diagnostics to stderr, --help/--mcp alias dispatch, hot reload (list_changed) and broken-edit tolerance, and a `jaiph run` regression. Docs: new docs/mcp.md how-to, a `jaiph mcp` reference section in docs/cli.md, README and docs nav entries, CHANGELOG, and QUEUE cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce a repeatable --env flag that lets a workflow receive a specific host variable outside the fail-closed Docker env allowlist, with the flag itself standing in as per-key consent. Supports both --env KEY=VALUE (first = splits, value may contain =, empty allowed) and bare --env KEY (forwards the host value, aborting with E_ENV_MISSING before spawning if unset). Names must match [A-Za-z_][A-Za-z0-9_]* or abort with E_ENV_INVALID, and sandbox-control / runtime-managed keys are refused with E_ENV_RESERVED. Semantics are uniform across every execution mode: in host modes the pairs are applied to the runner env after resolveRuntimeEnv/applySandboxFlags; in a Docker sandbox they are threaded through DockerSpawnOptions.extraEnv into buildDockerArgs as explicit -e KEY=VALUE args that bypass isEnvAllowed, each key emitted exactly once with the --env value winning. On jaiph mcp the pairs are resolved once at startup and applied to every tool call for the server's lifetime. Values cross verbatim with no path remapping. Updates usage strings, docs (cli, env-vars, sandboxing), and adds parse, buildDockerArgs, resolve-env, MCP, and e2e coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Complete the MCP how-to and cross-references for serving a .jh file's workflows as MCP tools over stdio. Add JSON client-config examples (Claude Desktop, Cursor) alongside the claude mcp add command in docs/mcp.md, and correct the default exposure rule to match the tree. Add an MCP server feature note to the landing page (docs/index.html) and reflect it in the CHANGELOG. Close two test gaps that back documented behaviour: a non-object JSON message is an invalid request (-32600) with null id, and toolNameFromFile truncates the derived slug to 128 characters. Drop the completed task from QUEUE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add black-box e2e coverage of `jaiph mcp` through the real binary entrypoint (e2e/tests/139_mcp_server_session.sh), wired into `npm run test:e2e`. The script drives `jaiph mcp <fixture.jh>` as a child process over stdio with a scripted JSON-RPC session — initialize, tools/list, a successful tools/call round-tripping a param through the workflow return, and a failing tools/call — then closes stdin. It asserts every stdout line is valid JSON-RPC 2.0 (no banner or progress leakage), the successful tool result text equals the workflow's return value, the failing workflow yields isError:true rather than a protocol error, and the server exits 0 on stdin close. A regression leg asserts `jaiph run` on a default workflow still exits 0 and prints its return value, pinning the shared workflow-launch.ts launch path in both the named-symbol and default directions after the hardcoded-`default` fix. Update CHANGELOG.md and drop the completed task from QUEUE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Long-running `jaiph mcp` tool calls can now stream progress to the client and be cancelled mid-run. When a `tools/call` carries a `params._meta.progressToken`, each of the run's STEP_START/STEP_END events is translated into a `notifications/progress` with a monotonically increasing counter; notifications stop the moment the call's response is sent, and calls without a progressToken emit none. A `notifications/cancelled` for an in-flight request id terminates that call's child process tree via the new `cancelRunProcess` in lifecycle.ts (SIGINT then an unref'd force-kill timer escalating to SIGKILL), sends no response for the cancelled id, and keeps the server serving. Step events and the child terminator are surfaced through a new per-call `McpCallContext` (`onStep` / `onCancelHandle`), wired by `attachOutputCollector` off the existing `parseStepEvent` stderr stream. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs-lint caught the only failure in the full suite after the MCP queue
landed: cli.md linked mcp.md#stream-progress-and-cancel-a-long-call but the
heading is numbered ("## 7. Stream progress and cancel a long call"), so the
anchor needs the 7- prefix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…el to prompts only. In-file agent.model no longer maps into JAIPH_AGENT_MODEL — it resolves per prompt and is passed as --model to the backend CLI. JAIPH_AGENT_MODEL remains a shell-only run-wide override. Parser, runtime, tests, docs, and in-repo .jh modules updated. Co-authored-by: Cursor <cursoragent@cursor.com>
…onfigs. install-from-local.sh builds runtime/Dockerfile and tags ghcr.io/jaiphlang/jaiph-runtime:<version> plus :nightly so sandbox runs match the checkout without JAIPH_DOCKER_IMAGE. Standalone CLI resolves the default image tag from embedded VERSION when package.json is absent. Update .jaiph workflows with per-step agent.model and claude backend defaults; queue 0.11.0 release task. Co-authored-by: Cursor <cursoragent@cursor.com>
Add a #dev-ready task to render prompt steps as `prompt <backend> <model> "…"` in the live run tree, using the already-resolved model from prompt invocation. Co-authored-by: Cursor <cursoragent@cursor.com>
Add a #dev-ready task requiring bare identifier, quoted, and bare ${ref}
forms to be equivalent everywhere the language accepts orchestration strings.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add a #dev-ready task to fix orphaned containers after SIGINT and extend e2e beyond sandbox-dir cleanup to assert the container exits. Co-authored-by: Cursor <cursoragent@cursor.com>
…s scope. Document a #dev-ready UX task for unsafe host-only consent, clearer workspace-vs-whole-machine warnings, and the inplace typo fix. Co-authored-by: Cursor <cursoragent@cursor.com>
Accept bare ${name} interpolation refs everywhere Jaiph string RHS parsers
already allow bare identifiers or quoted strings. ensure_ci_passes unsets
inherited JAIPH_* variables before npm run test:ci so agent workflow env
does not leak into integration tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
Cut v0.11.0 from the Unreleased changelog block. Bump package.json and package-lock.json to 0.11.0, refresh the pinned installer ref v0.10.0 -> v0.11.0 in both docs/install and docs/install.ps1, and stamp CHANGELOG.md: rename Unreleased -> 0.11.0 with a new Summary section (MCP server + Docker parity, --env passthrough, config interpolation + agent.model breaking changes, Windows portability/distro) above the existing All changes bullets, leaving a fresh empty Unreleased section at the top. Update remaining user-facing "current release" literals from 0.10.0 to 0.11.0 across docs/index.html, README.md, docs/setup.md, docs/env-vars.md, docs/cli.md, and docs/contributing.md, and drop the completed release task from QUEUE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each NodeWorkflowRuntime run now clears the process-local mock response queue so identical JAIPH_MOCK_RESPONSES_JSON does not exhaust shared mocks and fall through to real backends with long retry backoff. resolveRuntimeEnv sets JAIPH_AGENT_MODEL to "" when unset so scripts under set -u do not fail. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace implementation-heavy bullets with a short, human-facing feature summary for MCP, --env, config/model changes, and Windows support. Co-authored-by: Cursor <cursoragent@cursor.com>
Nightly prereleases are branch-driven only; the redundant git tag conflicted with refs/heads/nightly and broke plain git push. Co-authored-by: Cursor <cursoragent@cursor.com>
Prompt steps in the live run tree and non-TTY step labels now render the
effective model as a bare token between the backend and the quoted
preview: `▸ prompt claude sonnet "Classify this task…"` on start and
`✓ prompt claude sonnet (5s)` on completion. The model is the value
already passed to the backend for that invocation (whatever
`resolveModel` returns with a non-empty string); the token is omitted
when the backend auto-selects, falling back to the prior two-token
`prompt <backend> "…"` form. A custom `agent.command` still shows the
command basename with the model appended after it.
The resolved model is threaded to the display layer as a new `model`
field on the `STEP_START`/`STEP_END` events and parsed into
`StepEvent.model`, so the display formatters render the three-part label
without re-reading `PROMPT_START`. Existing truncation rules (24-char
preview, 96-char line cap) are unchanged. This is CLI/run-tree
presentation only — the `.jh` language is untouched and
`config { agent.model = … }` remains the authoring surface.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each line written to run_summary.jsonl now carries a `prevHash` field computed from the SHA-256 of the previous line's raw payload, forming a tamper-evident hash chain. A misbehaving agent that rewrites or truncates its own audit trail is detectable by walking the chain. Known credential env-var values (ANTHROPIC_API_KEY and the Docker/backend allowlist) are redacted from persisted prompt bodies and reconstructed command lines before any artifact write. New unit tests cover chain verification (tamper detection) and redaction of fixture secrets. Architecture and artifacts docs describe the chain format and the `jaiph verify-run` recipe. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses ASI-09 (LOW) from the 2026-07-20 security review. The install scripts now verify a cosign detached signature over SHA256SUMS before trusting any checksum, failing closed when the signature file is absent or invalid. runtime/Dockerfile replaces all curl-pipe-to-shell toolchain installs with pinned version URLs plus inline SHA-256 verification, ensuring no unverified remote script is executed during image builds. CI gains a release-workflow integration test covering the signature round-trip, the e2e installer test asserts the fail-closed path, and docs/contributing.md + docs/setup.md document the cosign trust model and verification steps for end users. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Imported-module metadata can no longer override execution-config keys (agent.command, agent.backend, and their JAIPH_AGENT_COMMAND/BACKEND equivalents) via applyMetadataScope. Only the entry module's config may set those keys for a run. The existing *_LOCKED env gates are preserved; defaults are now safe without requiring callers to pre-lock. Adds tests (unit + e2e) covering the trust boundary: an imported module that sets agent.command is silently ignored while the entry module's setting is still honoured. Docs updated to note the execution-config trust boundary for imported libraries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously ENV_ALLOW_PREFIXES forwarded entire ANTHROPIC_*/CURSOR_*/CLAUDE_*/OPENAI_* host env families into the container, exposing unrelated secrets to sandboxed code. Replace prefix forwarding for credentials with an enumerated BACKEND_CREDENTIAL_KEYS map and scope isEnvAllowed to the resolved agent backend(s) for the run; JAIPH_* run-control keys keep prefix forwarding since the runtime itself needs them. --env remains the documented escape hatch for anything else. Updates preflight-credentials to check against the resolved backend and adds unit tests locking the forwarded key set per backend.
…dening Overlay mode starts as root with SYS_ADMIN, SETUID/SETGID, CHOWN, DAC_READ_SEARCH, and (on Linux) apparmor=unconfined to mount fuse-overlayfs, then drops privileges before workflow code runs. That is a larger kernel attack surface than copy mode for the same isolation guarantee. After re-evaluating, overlay stays the default on fuse hosts because copy pays a full workspace clone per run while overlay starts in O(1); the tradeoff is now spelled out in docs/sandboxing.md and docs/sandbox-run.md, including when to force JAIPH_DOCKER_NO_OVERLAY=1. The apparmor=unconfined exception is called out as an explicit tracked follow-up (new QUEUE.md item for a tailored AppArmor profile), and new posture-lock tests in src/runtime/docker.test.ts pin the exact cap set, security-opt set, and per-mode start UID so future changes can't widen the posture silently. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
Add a deterministic git.push(branch) step so the credentialed push is performed by trusted runtime code rather than the LLM agent. With an explicit branch it force-targets HEAD:refs/heads/<branch> so the push cannot be retargeted; gh_ci_passes.jh now has the agent commit locally only and pushes via run git.push(branch). Queue: reword the credential task to Move 1 only (scrub secrets from prompt subprocess env, .jaiph out of scope) and add a standalone trusted_envs feature spec (host-snapshot resolution, trusted-run-only, entry-file-only module lock). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Write placeholder SHA256SUMS.minisig files in the PowerShell installer e2e test so it proceeds past the signature-download step to the checksum verification the test actually targets. - Select the last (not first) ```text block in the docs tutorial task-6 test, since the tutorial now shows an extra stderr-only prompt block before the real post-run output.
Docker-gated e2e tests self-check `docker info` and run regardless of matrix.label, so the "host" job still exercises Docker-sandboxed legs. Overlay must be disabled for any Ubuntu GHA runner, not just the "docker" label.
The prior docs parity update edited docs/jaiph-skill.md without rerunning tools/embed-assets.js, leaving the checked-in JAIPH_SKILL_MD_BASE64 blob stale and failing the embedded-assets drift check in CI.
…exclude) Preserve JAIPH_DOCKER_NO_OVERLAY through e2e env scrub so CI's no-overlay setting actually reaches tests, and exclude the configured runs root from sandbox workspace clones so GNU cp does not recurse into itself when JAIPH_RUNS_DIR is nested inside the workspace. Co-authored-by: Cursor <cursoragent@cursor.com>
Regression for the GNU cp self-copy failure when copy-mode allocates .sandbox-* under a workspace-relative runs root. Co-authored-by: Cursor <cursoragent@cursor.com>
Previously an --env-forwarded secret (e.g. `jaiph run --env GITHUB_TOKEN .jaiph/gh_ci_passes.jh`) leaked into the LLM agent's environment. The prompt backend inherited the full workflow env verbatim: runBackend defaulted childEnv to execEnv (scope.env — process.env plus everything merged from --env), and prepareClaudeEnv only augmented the env rather than stripping it, so the claude subprocess (spawned with bypassPermissions and fed untrusted content) received GITHUB_TOKEN and every other --env secret. Under the trust model only deterministic author-written `run` steps need credentials; `prompt` hands control to the model, so no prompt step legitimately needs an --env-injected secret and the scrub is applied unconditionally. Every prompt backend is now spawned through the new scrubPromptEnv(execEnv, backend), forwarding only the base environment a CLI needs (PATH, HOME, locale, TLS/proxies, CLAUDE_CONFIG_DIR, XDG/Windows basics), JAIPH_* control keys, and the agent's own backend credential keys — dropping everything else fail-closed. The existing isEnvAllowed / BACKEND_CREDENTIAL_KEYS / ENV_ALLOW_* / RUN_WORKFLOW_ENV allowlist was lifted from src/runtime/docker.ts into a new kernel module src/runtime/kernel/env-allowlist.ts (re-exported from docker.ts), so one fail-closed policy now governs prompt subprocesses in all sandbox modes, host mode included, where previously there was no allowlist at all. Tests cover scrubPromptEnv, the env handed to spawned prompt backends (host mode for cursor/claude and Docker copy mode), and a regression that a trusted `run` script step still receives the full --env value. Docs updated in docs/sandboxing.md and docs/env-vars.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GitHub rejects 0-byte release assets with HTTP 400 Bad Content-Length. Only attach a real minisign signature, and drop any stale one when unsigned. Co-authored-by: Cursor <cursoragent@cursor.com>
Two dev-ready tasks from the fs-isolation redesign. First: replace the fuse-overlay/copy duality with a single host-side snapshot mechanism (point-in-time, CoW-first via clonefile/reflink, zero added caps, snapshot under .jaiph/runs/<run>/sandbox with a tmpfs mask over the run mount), pinning host-change resistance as the contract and enumerating an exhaustive, grep-verified deletion inventory for the fuse path. Second: restrict snapshot content to what git sees (ls-files + .git wholesale) so gitignored secrets never enter the sandbox and artifact dirs stop dominating copy cost; non-git workspaces keep copy-everything. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Workflows can now list required host env keys in config instead of relying on --env; values reach trusted run steps only and never prompt subprocesses. Also switch engineer, qa, and security_review workflows from fable to opus. Co-authored-by: Cursor <cursoragent@cursor.com>
The default Docker mode now gives the container a writable point-in-time snapshot of the workspace, cloned host-side at run start (copy-on-write where the filesystem supports it) and bind-mounted read-write. Host edits during a run stay invisible to the container, container workspace writes are discarded at exit, and the live host workspace is never mounted at all. The snapshot lives at <run dir>/sandbox, is masked from the container's own /jaiph/run view by a tmpfs, and is purged on exit unless JAIPH_DOCKER_KEEP_SANDBOX is set. SandboxMode collapses to "snapshot" | "inplace"; selectSandboxMode is now just JAIPH_INPLACE-or-snapshot with no /dev/fuse probing. This removes fuse-overlayfs entirely along with its elevated setup posture (root start, SYS_ADMIN/SETUID/SETGID/CHOWN/DAC_READ_SEARCH cap-adds, apparmor=unconfined, setpriv drop), the JAIPH_DOCKER_NO_OVERLAY escape hatch, JAIPH_HOST_UID/GID, E_DOCKER_OVERLAY, runtime/overlay-run.sh and its embedded asset, and the fuse-overlayfs/fuse3 image packages. Snapshot and inplace now share one minimal posture (cap-drop ALL, no cap-adds, no-new-privileges, no device, no apparmor opt, host uid:gid on Linux), pinned by posture-lock tests. The run banner reads (Docker sandbox, snapshot). Docs, CI matrix, and e2e are rewritten to the new model, with a new snapshot-isolation e2e test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dbox The default Docker snapshot no longer copies the whole workspace. For a git workspace, cloneWorkspaceForSandbox now derives the snapshot's file set from git — `git ls-files -z --cached --others --exclude-standard` (tracked plus untracked-but-not-ignored) plus the `.git/` directory wholesale — so gitignored paths are absent from the container, not empty: a `.env`, an `.npmrc` token, a built `dist/`, and a `node_modules/` tree are never copied and never scanned. git is the sole ignore oracle (no reimplemented matcher), the copy walk prunes at directory granularity, and the content is identical across every platform and copy mechanism (APFS clonefile, reflink, plain copy). Submodule gitlinks are copied wholesale as opaque subtrees; tracked-but-deleted worktree paths are skipped; the runs-root exclusion still applies. A non-git workspace (no `.git` root, or `git ls-files` fails) falls back to copying everything. There is no config escape hatch, so a workflow that builds or tests installs its dependencies inside the container, exactly as a fresh CI checkout would. Applies to the Docker snapshot path only; `--inplace` and host modes are untouched. Adds unit coverage in docker.test.ts (mechanism-independent content, nested `.gitignore` `!` negation matches git, `.git/` present and functional, non-git fallback, deleted-file tolerance) and e2e 74e_docker_git_snapshot_content.sh; documents the model in docs/sandboxing.md, architecture.md, and sandbox-run.md.
…elease. Ship jaiph.pub as the canonical signing key, default both installers to its RW line, ignore jaiph.key, and extend the release-prep workflow with a prompt step that stamps CHANGELOG.md before bumping version. Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Jakub Dzikowski <jakub.t.dzikowski@gmail.com>
…image Add design/2026-07-23-serve-http-api.md: `jaiph serve <file.jh>` exposes workflows as an HTTP API with a generated OpenAPI 3.1 document and Swagger UI, reusing the MCP call layer and the durable run journal for SSE run inspection. Queue five dev-ready tasks: serve core, run inspection endpoints, OTLP trace export, Sentry error reporting, and the runtime image as a standalone docker/k8s runner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merge post-0.10.0 notes into a single 0.11.0 section, keep registry:build working when jaiphlang/registry is unavailable, and fix prepare_release fenced-script closers. Co-authored-by: Cursor <cursoragent@cursor.com>
GitHub-hosted ubuntu-latest runners need root for apt; the inline apt-get fallback failed with permission denied and blocked SHA256SUMS.minisig signing. Co-authored-by: Cursor <cursoragent@cursor.com>
Harden CI signing for passwordless and encrypted keys, sync jaiph.pub and installer defaults to the new key, and trim release-signing docs.
Drop passphrase handling for passwordless CI keys and reject the public key or incomplete secrets before minisign hits base64 decode errors.
minisign -W still writes an "encrypted secret key" header; pipe an empty password instead of rejecting the key as passphrase-protected. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the long per-feature paragraphs with concise release highlights covering MCP, secrets, config, Windows, sandbox, security, and language changes. Co-authored-by: Cursor <cursoragent@cursor.com>
Satisfies the release-workflow test that requires rotation guidance in the Release signing section. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
jaiph mcpto expose your workflows as tools in Claude Code, Cursor, and other MCP apps — with live progress and the ability to cancel long runs.--env GITHUB_TOKEN(and similar) forwards a host variable into Docker-backed runs when the default allowlist would block it; declare recurring keys in-file withtrusted_envs.config { }values can reference variables and workflow parameters. Breaking: renameagent.default_modeltoagent.model; it applies per prompt step, not as a global env var..exe, and get the same workflows without WSL or Docker onwin32..env,node_modules/) never enter the container.runsteps, never prompt agents;jaiph mcpisolates the workspace by default, matchingjaiph run.else ifchains,matchpattern alternation ("a" | "b"), andlogwarnfor yellow warnings in the run tree.