Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22.14.0'
node-version: '26.5.0'
cache: 'pnpm'

- name: Install deps
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22.14.0'
node-version: '26.5.0'
cache: 'pnpm'

- name: Install deps
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: '22.14.0'
node-version: '26.5.0'
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/verify-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22.14.0'
node-version: '26.5.0'
registry-url: 'https://registry.npmjs.org'

- name: Resolve version
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Workforce installed-layout local-preview repair

- Date: 2026-07-16
- Commit: `a27067422bdc6b4fa8cca9e27bb74fa9ce72d5d6`

## Scope

Repaired the local-preview worker root/permission strategy in `@agentworkforce/runtime` so installed-package execution stages under the invocation workspace instead of deriving a pseudo-workspace from `@agentworkforce/runtime/dist`. Added premature-worker stderr diagnostics with redaction, and added regression coverage that imports a copied runtime package from a temp consumer `node_modules` layout.

## Files changed

- `packages/runtime/src/local-preview.ts`
- `packages/runtime/src/local-preview.test.ts`

## Verification

Passed:

1. `mise exec node@26.3.1 -- pnpm --filter @agentworkforce/runtime test`
2. `mise exec node@26.3.1 -- pnpm --dir packages/cli exec tsc -p tsconfig.json`
3. `mise exec node@26.3.1 -- node --test dist/invoke-command.test.js` (run from `packages/cli`)
4. `mise exec node@26.3.1 -- pnpm build`
5. Installed-package smoke:
- packed `@agentworkforce/runtime`
- installed the tarball into a temp consumer project
- ran `executeLocalRun()` from the installed package against a simple local-preview bundle
- observed `{"exitCode":0,"status":"succeeded"}`

## Notes

- The permission allowlist now stages under `process.cwd()/.workforce` and grants only:
- the staged bundle/stage roots,
- the runtime package root/runtime dist root,
- dependency roots implied by the runtime package layout (consumer/global `node_modules`, monorepo `packages` + root `node_modules` when applicable).
- Premature worker exits now include redacted child stderr so `ERR_ACCESS_DENIED` failures are actionable.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Workforce composable runtime closure notes

- Extended `agentworkforce invoke` to support exactly one of `--fixture`, `--schedule`, or `--case`, plus `--reads`, `--model`, and rerun-only `--watch`.
- Routed local invoke through `RunRequestV1` / `RunRecordV2` using `packages/runtime/src/local-preview.ts`.
- Enforced preview isolation before bundle import by:
- binding Relayfile preview transport before import and restoring it in `finally`
- sanitizing process env to strip ambient credentials
- installing a policy-controlled fetch bridge
- denying authored raw imports of `node:http`, `node:https`, `node:net`, `node:tls`, `node:dgram`, and `node:child_process`
- Added deterministic preview coverage proving:
- preview transport intercepts ambient Slack helper writes
- allowed live GET reads can pass while denied POST writes do not reach the sentinel server
- authored raw `node:https` import fails closed before handler execution
- Added versioned YAML case parsing for checked-in case shape, multi-turn state carry-forward, HN-oriented assertions, and HTTP fixtures.
- Added replay bundle export support to `runs export --bundle` and local replay ingestion that preserves replay mode, state-source fidelity labels, and source run provenance when present.
- Extended integrations JSON/detail output with `registrationHealth`.
- Added a parent-process watch rerun loop using local dependency tracking plus watched fixture/case/seed files.

Verification

- `pnpm --filter @agentworkforce/runtime test`
- `pnpm --filter @agentworkforce/deploy test`
- `pnpm --filter @agentworkforce/cli test`

Follow-up after `ee57866`

- Local preview now fails closed unless the parent runtime is a supported patched Node `>=26.3.1` with `--permission`, `--allow-fs-read`, and `--allow-net` support. The refusal message includes the detected Node version.
- Worker argv still omits `--allow-net`, so direct network access inside the worker remains denied even on the supported parent runtime.
- Added credential-shape redaction independent of input key names, applied before values enter worker env / invoke payloads and again on local preview logs and run records.
- Added bounded preview worker readiness / overall / fetch IPC timeouts, deterministic parent-fetch failure responses, AbortController-backed parent fetch cancellation, SIGTERM→SIGKILL teardown, and a timeout test that asserts no orphan `local-preview-worker-*` staging dirs remain.
- Replay ingestion now accepts Cloud's wrapped replay bundle shape (`{schemaVersion, manifest, files}`), unwraps `files["event.json"].content`, preserves run/event provenance from the manifest bundle, and keeps unavailable-vs-historical state fidelity instead of assuming a synthetic top-level event payload.
- Relevant CI / publish / verify / deploy-e2e workflows now pin Node `26.5.0` so invoke safety is exercised on the supported runtime rather than relying on a test-only local setup.

Verification (Node 26.5)

- `mise exec node@26.5.0 -- pnpm --filter @agentworkforce/runtime test`
- `mise exec node@26.5.0 -- pnpm --filter @agentworkforce/deploy test`
- `mise exec node@26.5.0 -- pnpm --filter @agentworkforce/cli test`
- `mise exec node@26.5.0 -- pnpm -r build`
4 changes: 3 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
"dependencies": {
"@agent-relay/cloud": "^10.1.0",
"@agentworkforce/deploy": "workspace:*",
"@agentworkforce/events": "workspace:*",
"@agentworkforce/local-surface": "workspace:*",
"@agentworkforce/persona-kit": "workspace:*",
"@agentworkforce/runtime": "workspace:*",
"@agentworkforce/workload-router": "workspace:*",
"@relayburn/sdk": "^2.5.2",
"@relayfile/local-mount": "^0.10.23",
"ora": "^9.4.0"
"ora": "^9.4.0",
"yaml": "^2.9.0"
},
"repository": {
"type": "git",
Expand Down
34 changes: 19 additions & 15 deletions packages/cli/src/cli-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,8 @@ Commands:
1-based configurable position.
integrations [provider] [--all] [--json]
Discover workspace integrations, connection status, and
known trigger events.
known trigger events. JSON output includes registration
health when the cloud status API provides it.
trigger <agent-name-or-id> [flags]
Manually fire an active deployed persona for testing.
The selector accepts agent id, compact agent id,
Expand Down Expand Up @@ -315,29 +316,32 @@ Commands:
mirroring is not supported, so this targets cron/timer-
only or webhook-shape-only personas. See
\`agentworkforce local-surface --help\` for flags.
invoke <persona-path> --fixture <file> [flags]
Simulate an invocation: run the persona's handler
against fixture event envelope(s) with every external
side effect recorded, NOT executed. Emits a
Cloud-compatible run record (origin "local_dry_run")
to stdout. Distinct from \`deploy --dry-run\`, which
validates without invoking the handler.
invoke <persona-path> (--fixture <file> | --schedule <name> | --case <file>) [flags]
Run a local preview invocation through the composable
runtime path. Effects are previewed locally and raw
output is a RunRecord JSON document.
Flags:
--fixture <file> JSON envelope, JSON array, or
NDJSON of raw gateway envelopes
--fixture <file> JSON event/replay fixture, JSON
array, or NDJSON
--schedule <name> invoke a declared schedule
--case <file> run a YAML case with assertions
--reads <mode> fixtures | live
--model <mode> stub | fixture | live
--watch rerun the same selected source
--output <file> write the run record to a file
--input KEY=value override a declared persona input
--seed PATH=file seed the simulated filesystem
--workspace <id> workspace id for the simulated ctx
--seed PATH=file seed preview file state
--workspace <id> workspace id for synthesized events
--scaffold <type> emit a fixture skeleton for an
event type instead of running
(no persona needed)
runs export <runId> [flags]
Export the gateway envelope cloud delivered to a run
as a replayable invoke fixture. Flags:
Export the cloud replay fixture or replay bundle for a
run. Flags:
--agent <selector> agentId/deployedName owning the
run (otherwise all are checked)
--fixture <file> write the fixture to a file
--fixture <file> write the legacy fixture to a file
--bundle <file> write the replay bundle to a file
deployments list List deployed cloud agents in the active workspace.
deployments logs Show structured logs for a deployed cloud agent.
destroy <persona-or-agent-id> [flags]
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/integrations-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ const fixture: IntegrationsDocument = {
adapterSlug: 'github',
inCloudCatalog: true,
connected: true,
registrationHealth: { workspace: { registered: true, healthy: true } },
connections: [
{
connectionId: 'conn-github',
scope: 'workspace',
serviceAccountName: null,
status: 'ready'
status: 'ready',
registrationHealth: { registered: true, healthy: true }
}
],
triggers: ['issues.opened', 'pull_request.opened', 'pull_request.closed'],
Expand Down Expand Up @@ -169,6 +171,7 @@ test('runIntegrationsCommand renders single-provider details and snippet', async
assert.equal(process.exitCode, 0);
assert.match(io.stdout.text, /Triggers:\n issues\.opened/);
assert.match(io.stdout.text, /connectionId|conn-github/);
assert.match(io.stdout.text, /registrationHealth/);
assert.match(io.stdout.text, /"integrations": \{ "github": \{\} \}/);
assert.match(io.stdout.text, /triggers: \{ "github": \[\{ "on": "issues\.opened" \}\] \}/);
} finally {
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/src/integrations-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,14 @@ export function formatSingleProvider(row: IntegrationRow | undefined): string {
lines.push(` serviceAccountName: ${connection.serviceAccountName}`);
}
lines.push(` status: ${connection.status}`);
if (connection.registrationHealth) {
lines.push(` registrationHealth: ${JSON.stringify(connection.registrationHealth)}`);
}
}
}
if (row.registrationHealth) {
lines.push('', 'Registration health:', ` ${JSON.stringify(row.registrationHealth)}`);
}
const firstTrigger = row.triggers[0] ?? 'event.name';
lines.push(
'',
Expand Down
Loading
Loading