Skip to content

feat(process): persistent-process tier SDK surface (JS/TS)#14

Merged
joshuajbouw merged 4 commits into
mainfrom
feat/process-persistent-sdk
Jun 7, 2026
Merged

feat(process): persistent-process tier SDK surface (JS/TS)#14
joshuajbouw merged 4 commits into
mainfrom
feat/process-persistent-sdk

Conversation

@joshuajbouw

Copy link
Copy Markdown
Contributor

Summary

Mirrors the Rust SDK and the host astrid:[email protected] persistent-process tier (host: astrid-runtime/astrid#867; Rust SDK: astrid-runtime/sdk-rust#53; contract: astrid-runtime/wit#12), so JS/TS capsule authors can spawn a background child that outlives the pooled, stateless instance that started it — unlike process.BackgroundProcessHandle, whose kernel resource is reaped on instance reset.

Changes

  • contracts submodule → the merged astrid:[email protected] persistent tier (wit commit 1a06cf4, the feat(contracts): regenerate bundle with hook-event-request #12 squash). Deliberately the feat(contracts): regenerate bundle with hook-event-request #12-only commit (predates fix(build): resolve SDK runtime + host WIT for registry installs (0.1.1) #11), so the astrid:contracts events bundle (astrid-contracts.wit / generated contracts.ts) is untouched (sync-contracts-wit.sh --check stays green) and unrelated open SDK PRs are unaffected. On main, astrid-build reads the host WIT live from contracts/host/ (CANONICAL_WIT_DIR), so the bump is all ComponentizeJS needs.
  • wit-imports.d.ts (hand-written host ABI types): persistent types + free-function declarations on astrid:process/[email protected]spawnPersistent, attach, listProcesses, status, statusMany, readLogs, readSince, writeStdin, closeStdin, signal, wait, stop, releaseProcess, watch, unwatch; ProcessInfo, ProcessPhase, LogStream, LogCursor, LogChunk, OverflowPolicy, ResourceLimits; ErrorCode gains no-such-process / registry-full / persist-unsupported; ProcessSignal gains stop / cont; SpawnRequest gains the 8 persistent fields.
  • process.ts ergonomic layer: spawnPersistent(cmd, args, options) + the persistent SpawnPersistentOptions knobs; PersistentProcess class (status / readLogs (drain) / readSince (non-draining cursor → byte-faithful LogChunkResult; logCursorStart()) / writeStdin / closeStdin / signal / wait (bounded) / stop (consumes — SIGTERM→grace→SIGKILL) / release); module fns attach(id) (id-wrapper reattach — works without the host's deferred attach resource fn), listProcesses, statusMany; ergonomic types. buildSpawnRequest updated for the new SpawnRequest fields (required).
  • index.ts: top-level re-exports for the new public types.

Not exposed (host-deferred)

watch / unwatch (open publish-authority RFC question — poll via status + bounded wait) and resource-limit enforcement (limits is plumbed through but the host doesn't enforce it yet).

Test Plan

  • npm run build (tsc) green across the workspace
  • End-to-end: the examples/test-capsule componentizes against the persistent WIT — ComponentizeJS produced a 12.96 MB WASM with 169 host imports, proving the new host functions wire through the JS build path
  • scripts/sync-contracts-wit.sh --check passes (events bundle untouched)
  • Git diff scoped to 5 files (contracts pointer + 3 SDK sources + CHANGELOG); no contracts.ts drift

Mirror the Rust SDK + host astrid:[email protected] persistent tier. Bump contracts to the #12 commit (1a06cf4, process-package-only — events bundle / contracts.ts untouched, so the open hook PR is unaffected); ComponentizeJS reads the persistent host WIT live from contracts/host/.

wit-imports.d.ts: persistent types + free-fn declarations. process.ts: spawnPersistent() + persistent knobs, PersistentProcess class (status/readLogs/readSince/writeStdin/closeStdin/signal/wait/stop/release), process.{attach,listProcesses,statusMany}, logCursorStart(), types. ProcessSignal gains stop/cont. attach is an id-wrapper (reattach without the host's deferred attach fn). Verified: tsc clean; example capsule componentizes (169 host imports).

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a persistent-process tier to the SDK, introducing spawnPersistent, PersistentProcess, and associated management functions to support background processes that survive instance resets. The review feedback points out potential RangeError crashes when converting non-finite numbers (like Infinity or NaN) to BigInt in several configuration and timing parameters. To address this, the reviewer suggests implementing a safe conversion helper toSafeBigInt and adding input validation to the wait method.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/astrid-sdk/src/process.ts Outdated
Comment thread packages/astrid-sdk/src/process.ts
Comment thread packages/astrid-sdk/src/process.ts
Comment thread packages/astrid-sdk/src/process.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new persistent-process tier to the JS/TS SDK process module, mirroring the host astrid:[email protected] persistent tier and the Rust SDK so capsule authors can manage background processes that outlive pooled instance resets.

Changes:

  • Extended the host ABI type surface (wit-imports.d.ts) with persistent-process types and id-keyed free functions.
  • Added an ergonomic persistent API layer in process.ts (spawnPersistent, PersistentProcess, attach, listProcesses, statusMany, cursor-based log reads).
  • Re-exported new process-related types from the SDK entrypoint and documented the feature in the changelog.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 9 comments.

File Description
packages/astrid-sdk/src/wit-imports.d.ts Adds persistent-tier types and free-function host imports for id-keyed process operations.
packages/astrid-sdk/src/process.ts Implements the JS/TS ergonomic persistent-process layer and request shaping for new WIT fields.
packages/astrid-sdk/src/index.ts Re-exports newly introduced process types for ergonomic consumption.
CHANGELOG.md Documents the new persistent-process tier API surface and behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/astrid-sdk/src/process.ts
Comment thread packages/astrid-sdk/src/process.ts
Comment thread packages/astrid-sdk/src/process.ts
Comment thread packages/astrid-sdk/src/process.ts
Comment thread packages/astrid-sdk/src/process.ts
Comment thread packages/astrid-sdk/src/process.ts
Comment thread packages/astrid-sdk/src/process.ts Outdated
Comment thread packages/astrid-sdk/src/process.ts
Comment thread packages/astrid-sdk/src/process.ts
…16)

## What

Adds `capabilities.enumerate()` to `@unicity-astrid/sdk` — the ergonomic
wrapper for `astrid:sys/host.enumerate-capabilities`
(astrid-runtime/wit#13, merged), mirroring the Rust SDK
`capabilities::enumerate`.

It is the infallible list dual of `capabilities.check`: returns the
calling capsule's own held capability names — the categories declared in
its `[capabilities]` manifest block (`host_process`, `net_connect`,
`fs_read`, …), not the scoped arguments within them. Argument-free,
returns `string[]` with no error path (an empty array is the valid "no
capabilities" answer).

Hand-adds the ambient `enumerateCapabilities(): string[]` host
declaration in `wit-imports.d.ts` and bumps the `contracts` submodule to
the merged `9742f80` so `astrid-build`'s `CANONICAL_WIT_DIR`
(`contracts/host`) declares the import for downstream componentization.
The `astrid:contracts` events bundle is unchanged.

## Stacking

Stacked on `feat/process-persistent-sdk` (#14) — base is that branch, so
the diff shows only the enumerate delta. Rebase onto `main` and retarget
once #14 lands.

## Verify

`tsc -b` typecheck green; `capabilities.enumerate` is exported via
`export * as capabilities`.
… RangeError

BigInt(Infinity) / BigInt(NaN) throws a cryptic RangeError. A capsule author
passing Infinity to mean no limit hit that on the persistent-process knobs.
Adds a toSafeBigInt helper that maps non-finite input to undefined (= use the
host default/ceiling, which already clamps) for the soft knobs — TTLs, resource
limits, and stop grace. wait(timeoutMs) is bounded by design, so it validates
finiteness and throws a clear SysError.api instead, mirroring net.toHostTimeout.
Regenerates astrid-contracts.wit (and the generated contracts.ts) via
scripts/sync-contracts-wit.sh so sync-contracts-wit.sh --check passes. The
mirror was missing astrid:hook's hook-event-request record present in
contracts/interfaces at the pinned 9742f80. Only the bundle is touched — the
contracts submodule pin is untouched.
@joshuajbouw joshuajbouw merged commit 124d426 into main Jun 7, 2026
2 checks passed
@joshuajbouw joshuajbouw deleted the feat/process-persistent-sdk branch June 7, 2026 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants