From 4fd97d3fe2262ed60eb547751176caef875c0168 Mon Sep 17 00:00:00 2001 From: Rhein1 / Agoragentic <140637602+rhein1@users.noreply.github.com> Date: Mon, 28 Sep 2026 19:30:54 -0400 Subject: [PATCH 01/12] feat(openshell): add pinned offline Agent OS integration scaffold Add strict role policy planning, immutable digests, unverified observation shaping, hard-off adapter, 93 offline tests, qualification handoff, and CI. No runtime, provider, payment, publication, or production activation. --- .github/workflows/openshell-scaffold.yml | 39 ++++ agent-os/openshell/CODEX_HANDOFF.md | 59 ++++++ agent-os/openshell/README.md | 75 ++++++++ agent-os/openshell/fixture.json | 11 ++ agent-os/openshell/preview.mjs | 36 ++++ agent-os/openshell/scaffold.mjs | 224 +++++++++++++++++++++++ agent-os/openshell/scaffold.test.mjs | 205 +++++++++++++++++++++ agent-os/openshell/upstream.lock.json | 16 ++ 8 files changed, 665 insertions(+) create mode 100644 .github/workflows/openshell-scaffold.yml create mode 100644 agent-os/openshell/CODEX_HANDOFF.md create mode 100644 agent-os/openshell/README.md create mode 100644 agent-os/openshell/fixture.json create mode 100644 agent-os/openshell/preview.mjs create mode 100644 agent-os/openshell/scaffold.mjs create mode 100644 agent-os/openshell/scaffold.test.mjs create mode 100644 agent-os/openshell/upstream.lock.json diff --git a/.github/workflows/openshell-scaffold.yml b/.github/workflows/openshell-scaffold.yml new file mode 100644 index 00000000..0cb869ff --- /dev/null +++ b/.github/workflows/openshell-scaffold.yml @@ -0,0 +1,39 @@ +name: OpenShell offline scaffold + +on: + pull_request: + paths: + - 'agent-os/openshell/**' + - '.github/workflows/openshell-scaffold.yml' + push: + branches: [main] + paths: + - 'agent-os/openshell/**' + - '.github/workflows/openshell-scaffold.yml' + +permissions: + contents: read + +jobs: + offline-contract: + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + node: ['20', '22'] + steps: + - uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-node@v7.0.0 + with: + node-version: ${{ matrix.node }} + - name: Validate offline contracts; do not install OpenShell + run: | + node --check agent-os/openshell/scaffold.mjs + node --check agent-os/openshell/preview.mjs + node --test agent-os/openshell/scaffold.test.mjs + node agent-os/openshell/preview.mjs agent-os/openshell/fixture.json + node agent-os/openshell/preview.mjs --readiness diff --git a/agent-os/openshell/CODEX_HANDOFF.md b/agent-os/openshell/CODEX_HANDOFF.md new file mode 100644 index 00000000..1c0e694e --- /dev/null +++ b/agent-os/openshell/CODEX_HANDOFF.md @@ -0,0 +1,59 @@ +# OpenShell augmentation and integration handoff + +## Goal and present state + +Integrate OpenShell below Agoragentic's existing control plane, preserving the agent-operated Interchange and all owner, financial, identity, trust, and evidence boundaries. The compiler, offline CLI, observation shaper, tests, and source pin are implemented. A live provider adapter is **not** implemented or qualified. No production migration is authorized by this document. + +## Work sequence + +### 1. Source and build qualification + +Re-read repository instructions and the pinned upstream contracts. Resolve the v0.1.2 release to the recorded commit; verify the SDK package and gateway build rather than trusting a version string. Install only in an owner-approved disposable lab. Verify the generated SDK types accept `createSpec` and verify server-side normalization has not weakened its policy. Upgrade the pin only in a reviewed change that updates the lock and conformance fixtures together. + +Build or select a digest-pinned minimal worker image with the `sandbox` user/group, explicitly required paths and binaries, no credentials, no host-runtime socket, and no production configuration. The example digest is fictional. Validate hard-required Landlock on the actual host. Failure must stop setup, not silently downgrade to best effort. + +Acceptance: recorded package/image/gateway integrity, generated type/schema checks, policy acceptance, and a reproducible host/image definition. These are not yet containment proof. + +### 2. Trusted host adapter + +Implement the adapter in the existing platform provider abstraction, not in an agent-importable SDK shim. Keep source activation default-off. Use explicit workspace-scoped OpenShell operations. `OpenShellClient.connect()` is lazy: a successful construction is not a health check. Require a real `health()` result and verified gateway identity/auth configuration. + +Bind authorization outside the sandbox to the exact owner, tenant/workspace, run, immutable sandbox ID, compiled plan/policy digest, image digest, command, quota, and expiry. Recheck the financial freeze and relevant mandate/consent controls. A caller-supplied `approved: true`, environment flag, self-hash, fake SDK client, or policy advisor proposal is not authorization. Do not accept agent-supplied method names, deployment contracts, policy overrides, `rawSpec`, provider lists, or arbitrary environment variables. + +Use the pinned SDK's `sandbox.create`, `waitReady`, `getConfig`, `exec`/`execStream`, `delete`, and identity-aware `waitDeleted` interfaces only after rechecking their actual types. Confirm effective static and network policy before any worker executes. Keep request data and authority/control-plane clients in separate trust domains. + +Acceptance: local tests show every provider side effect is preceded by the real host gate; denial results in zero provider calls. Runtime status is never inferred from generated plans or mocks. + +### 3. Bounded lab canary and failure cleanup + +Obtain separate owner approval for lab resources; no payment/custody enablement follows. Start with the fixed `/bin/true` canary, then a reviewed read-only worker. Enforce CPU, memory, storage, wall-clock, and output limits in the actual host/runtime, not just metadata. + +Test an approved read succeeds and unapproved hosts/methods/paths, private IPs, cloud metadata, redirects/rebinding, unauthorized files, credential reads, provider attachment, policy widening, and cross-workspace access fail. Use synthetic secrets only. Capture policy identity, sandbox identity, image/build identity, terminal status, and independently observed denials without raw sensitive logs. A network error alone is not proof that a policy denial occurred. + +Test create failure, timeout, cancellation, stream closure without terminal exit, gateway failure, policy drift, owner revocation, interrupted cleanup, and same-name sandbox replacement. Ambiguous create/exec responses are not safe to retry automatically. Track sandbox IDs durably so an orphan can be reconciled. `accepted` deletion stays pending; `unspecified`/unknown stays unknown. Abort of a client RPC is not proof the worker stopped. Verify termination/cleanup separately. + +Acceptance: authenticated, identity-bound live evidence for success, denials, and cleanup. Keep production and live-traffic qualification false until independently reviewed. + +### 4. Role integration, in order + +Cartographer first: supply only sanitized public-source inputs from the existing owner-approved source configuration. Preserve discovery provenance and quotas; no new trust, listing, peer-key, payment, or wallet mutation. Candidates discovered by an agent are not automatically approved egress destinations. + +Emissary second: isolate drafting/inspection. Send contact proposals to the existing host consent/mandate path; do not move sender credentials, key-pinning authority, or transport approval into the worker. Retain first-contact limits and independent owner gates. + +Steward third: consume minimal evidence references and propose bounded maintenance. Do not let it approve its own policy changes, reconfigure the gateway, grant credentials, or merge/deploy itself. Put approval and policy application in separate host components. + +Harness Core/ECF: connect through existing proposal/evidence contracts. Mark imported artifacts as local/unverified until a trusted evidence verifier promotes them. Never treat local plan hashes as portable authority. Risk Fork can evaluate candidate changes, but it is not a substitute for OpenShell containment or the owner's mandate. + +### 5. OpenShell-specific augmentation, after baseline qualification + +Evaluate supervisor middleware for pre-action checks and gateway interceptors for control-plane admission. First inspect the pinned upstream contracts and failure behavior. Keep policy admission fail-closed; do not assume extension existence supplies authenticated Agoragentic authority. Policy advisor/prover output can explain proposed access changes but must remain a recommendation until the independent host approval path authorizes the exact delta. Formal policy reasoning is not business-consent or settlement verification. + +Credential-backed inference/provider routes require a separate approved threat model and endpoint-scoped credential binding. Do not export signer/custody material. Any paid inference or external resource cost requires its own authorized budget. No generic provider attachment or arbitrary network widening is allowed. + +Acceptance: isolated integration tests for unavailable/bypassed middleware, expired/replayed approvals, widened policy, and non-mutating denied requests. No production claims based only on source inspection. + +## Completion report required from the implementation agent + +Report changed files, source/SDK/image/gateway pins, actual test commands and outcomes, evidence provenance, enabled versus default-off paths, outstanding gates, rollback/cleanup status, and the final reviewed commit. Separate local tests, mocked contract tests, live canaries, and production verification. Do not claim subagent review unless real subagent execution was available. + +Rollback before activation is simply leaving the provider unselected. After an approved lab activation, stop new dispatch, revoke the lab grant, terminate only the recorded sandbox IDs, verify cleanup, and preserve redacted evidence. Never delete another workspace's or a same-name replacement's resource. diff --git a/agent-os/openshell/README.md b/agent-os/openshell/README.md new file mode 100644 index 00000000..ff8a14e3 --- /dev/null +++ b/agent-os/openshell/README.md @@ -0,0 +1,75 @@ +# OpenShell integration scaffold + +**Source-only, offline, default-off. No OpenShell installation, gateway connection, sandbox provisioning, agent execution, package publication, or production activation is performed.** This is a preparation surface under the existing Agent OS examples, not a new supported integration or canonical tool ID. + +Agoragentic owns owner mandates, action approvals, identity, budgets, receipts, and reconciliation. OpenShell is a candidate execution-isolation layer underneath those controls. A sandbox policy must not grant payment, federation, publication, or trust authority. + +## Run the implemented scaffold + +Node.js 20+; no package installation or credentials required. From the repository root: + +```sh +node --test agent-os/openshell/scaffold.test.mjs +node agent-os/openshell/preview.mjs agent-os/openshell/fixture.json +node agent-os/openshell/preview.mjs --readiness +``` + +The fixture uses a **fictional image digest and example hostname**. It demonstrates the format, not an image that can be pulled or a live discovery target. The readiness command describes this scaffold only; it does not inspect a host. `activationSupported` remains false. There is no `apply` command or enabling environment variable. + +## What is implemented + +- A deterministic policy/plan compiler with strict input validation, bounded input, immutable output, and SHA-256 plan/policy digests. +- Separate Cartographer, Emissary, and Steward profiles. Cartographer can propose exact-host/exact-path HTTPS GET/HEAD egress. Emissary and Steward remain network-denied. All profiles are proposal-only. +- A TypeScript SDK `createSpec` candidate with create-time filesystem, process, hard-required Landlock, explicit L7 enforcement, no provider attachment, no environment injection, and no exposed service ports. +- An adapter preparation interface whose invocation path always refuses activation. +- Offline observation shaping that requires a terminal integer exit code, binds an expected sandbox ID, distinguishes pending deletion, and never upgrades caller reports to verified receipts. +- A pinned upstream source record and a staged integration/qualification handoff. + +```js +import { OpenShellScaffoldAdapter } from './scaffold.mjs'; +const adapter = new OpenShellScaffoldAdapter(); +const plan = adapter.prepare(request); // pure local compilation +// await adapter.invoke(...); // always refuses; no live driver is bound +``` + +## Role boundaries + +| Role | Current proposal | Later host integration, not implemented here | +|---|---|---| +| Cartographer | Explicit public read candidates; no default egress | Observe approved sources, retain provenance, emit discovery candidates without promoting trust | +| Emissary | Offline drafting, no egress or credentials | Submit bounded contact proposals to the existing consent/mandate-controlled host path | +| Steward | Offline evidence review, no egress or mutation | Recommend maintenance and policy changes; owner gates remain outside the sandbox | + +This preserves agent-operated growth and maintenance without letting the agents approve their own authority expansion. These profiles do not contain or start the actual role agents. The only candidate create-time command is `/bin/true`; replacing it with a role worker is a separate reviewed change. + +## Upstream contract and important details + +Reviewed target: **OpenShell v0.1.2**, commit `6648bd0c290efbc41ba131ee9831ee45cd431f94`. See [upstream.lock.json](upstream.lock.json) for file/blob provenance and review scope. No upstream source is vendored. Runtime compatibility remains untested. + +Primary sources at that immutable revision: + +- [TypeScript SDK interface and distribution](https://github.com/NVIDIA/OpenShell/blob/6648bd0c290efbc41ba131ee9831ee45cd431f94/sdk/typescript/README.md) +- [SDK create/exec/deletion types](https://github.com/NVIDIA/OpenShell/blob/6648bd0c290efbc41ba131ee9831ee45cd431f94/sdk/typescript/src/client.ts) +- [Policy protobuf contract](https://github.com/NVIDIA/OpenShell/blob/6648bd0c290efbc41ba131ee9831ee45cd431f94/proto/sandbox.proto) + +`createSpec` uses **camelCase SDK message-init fields**, not CLI YAML. The policy protobuf specifies audit as the unspecified inspected-network enforcement mode; this compiler sets `enforcement: 1` explicitly. `tls: 0` is automatic TLS handling, not TLS bypass. The proposal has no `allowedIps`, uninspected-credential bypass, provider attachment, middleware registration, or `rawSpec` escape hatch. + +OpenShell filesystem/process/Landlock policy must be supplied at creation; sandbox-scoped policy updates cannot introduce static fields later. The proposed image must have the non-root `sandbox` account, `/bin/true`, `/usr/bin/curl`, required runtime paths, `/sandbox/input`, and `/sandbox/output`. None of those image properties are verified by a digest-shaped string. + +The SDK is documented as distributed through GitHub Packages at this release. Do not assume an identically named public npm package is the reviewed build. A later approved setup must authenticate outside the sandbox, pin SDK and gateway versions together, and record package/image integrity. Do not run an unpinned remote installation script as part of this scaffold. + +## Evidence and safety limits + +A local `planDigest` or `policyDigest` is **not** an OpenShell protobuf policy hash, signature, owner approval, attestation, settlement receipt, or proof of work. The digests bind the scaffold's canonical local JSON. Plan validation detects changes against this compiler; it does not authenticate who proposed the plan. + +Observation summaries accept only the small documented projection (`kind`, `sandboxId`, and `exitCode` or `outcome`). Do not feed raw SDK objects, stdout, stderr, prompts, environment variables, credentials, or customer data into them. Output remains `caller_reported_unverified` even when an exit code is zero. A deletion result of `accepted` is pending, not cleanup success. Missing or replaced sandbox identity must not be papered over by inserting an expected ID into a response that did not contain it. + +Hostname screening is syntactic only. It does not resolve DNS or establish SSRF protection, consent, endpoint semantics, redirect safety, or trust. GET/HEAD does not guarantee a remote application is side-effect-free. The host must validate destinations at connection time, control redirects, and test metadata/private-network denial. No real request is made here. + +The runtime deadline is a **host requirement, not an implemented timer**. All runtime-verification flags stay false. CPU/memory/storage quotas, lifecycle cancellation, tenant isolation, gateway identity/authentication, effective policy confirmation, and independent containment evidence remain qualification work. No Docker socket, host credentials, gateway admin token, signer, wallet key, or production database belongs in the worker image or mounts. + +## Integration ownership and next work + +See [CODEX_HANDOFF.md](CODEX_HANDOFF.md). Keep the reusable compiler here; host authority wiring belongs in the platform, while Harness Core should consume only a supported local evidence/proposal interface. Do not create another Harness Core implementation in this repository's migration-pointer directory. Risk Fork/ECF checks are additional gates, not reasons to skip sandbox or owner controls. + +No changes to the canonical integration index, integration counts, paid execution, custody state, trust vocabulary, public readiness claims, or existing runtime selection are made by this scaffold. diff --git a/agent-os/openshell/fixture.json b/agent-os/openshell/fixture.json new file mode 100644 index 00000000..cbbed856 --- /dev/null +++ b/agent-os/openshell/fixture.json @@ -0,0 +1,11 @@ +{ + "schema": "agoragentic.openshell.request.v1", + "role": "cartographer", + "runId": "offline-demo", + "workspace": "agoragentic-lab", + "image": "registry.example.com/agoragentic/worker@sha256:1111111111111111111111111111111111111111111111111111111111111111", + "readEndpoints": [ + { "host": "docs.example.com", "paths": ["/robots.txt", "/.well-known/agent-card.json"] } + ], + "maxRuntimeSeconds": 60 +} diff --git a/agent-os/openshell/preview.mjs b/agent-os/openshell/preview.mjs new file mode 100644 index 00000000..0cefda40 --- /dev/null +++ b/agent-os/openshell/preview.mjs @@ -0,0 +1,36 @@ +import { openSync, readSync, fstatSync, closeSync } from 'node:fs'; +import { buildOpenShellPlan, openShellReadiness } from './scaffold.mjs'; + +// Offline only. No installer, SDK import, subprocess, network, or apply command. +try { + const args = process.argv.slice(2); + if (args.length === 1 && args[0] === '--readiness') { + console.log(JSON.stringify(openShellReadiness(), null, 2)); + } else { + if (args.length !== 1 || args[0].startsWith('-')) throw new Error('usage: node preview.mjs REQUEST.json | --readiness'); + const fd = openSync(args[0], 'r'); + let text; + try { + if (!fstatSync(fd).isFile()) throw new Error('request_must_be_regular_file'); + const bytes = Buffer.alloc(65537); + let size = 0; + while (size < bytes.length) { + const count = readSync(fd, bytes, size, bytes.length - size, null); + if (!count) break; + size += count; + } + if (size > 65536) throw new Error('request_too_large'); + text = bytes.subarray(0, size).toString('utf8'); + } finally { closeSync(fd); } + let request; + try { request = JSON.parse(text); } catch { throw new Error('invalid_request_json'); } + console.log(JSON.stringify(buildOpenShellPlan(request), null, 2)); + } +} catch (error) { + // Do not echo request contents or filesystem paths from native errors. + const message = error.code && !error.code.startsWith('E') ? error.code + : ['invalid_request_json', 'request_too_large', 'request_must_be_regular_file'].includes(error.message) ? error.message + : 'preview_failed: use one request JSON path or --readiness'; + console.error(message); + process.exitCode = 2; +} diff --git a/agent-os/openshell/scaffold.mjs b/agent-os/openshell/scaffold.mjs new file mode 100644 index 00000000..c2afd0a7 --- /dev/null +++ b/agent-os/openshell/scaffold.mjs @@ -0,0 +1,224 @@ +import { createHash } from 'node:crypto'; + +// Source-contract target, NOT a claim of installed or runtime-tested compatibility. +export const UPSTREAM = Object.freeze({ + release: 'v0.1.2', + commit: '6648bd0c290efbc41ba131ee9831ee45cd431f94', + sdk: '@nvidia/openshell-sdk', +}); +export const ROLES = Object.freeze(['cartographer', 'emissary', 'steward']); +const REQUEST_SCHEMA = 'agoragentic.openshell.request.v1'; +const PLAN_SCHEMA = 'agoragentic.openshell.plan.v1'; +const FORBIDDEN_KEYS = new Set(['__proto__', 'prototype', 'constructor']); + +function fail(code) { + const error = new Error(code); + error.code = code; + throw error; +} + +// Do not invoke getters/toJSON, accept class instances, or copy dangerous keys. +function dataOnly(value, depth = 0, seen = new Set(), budget = { nodes: 0 }) { + if (++budget.nodes > 4096 || depth > 16) fail('input_too_complex'); + if (value === null || typeof value === 'boolean') return value; + if (typeof value === 'string') { + if (value.length > 4096) fail('input_string_too_long'); + return value; + } + if (typeof value === 'number' && Number.isFinite(value)) return value; + if (!value || typeof value !== 'object') fail('json_data_required'); + if (seen.has(value)) fail('cyclic_input'); + const array = Array.isArray(value); + if (!array && ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail('plain_object_required'); + if (Object.getOwnPropertySymbols(value).length) fail('symbol_keys_denied'); + seen.add(value); + const output = array ? [] : {}; + for (const key of Object.getOwnPropertyNames(value)) { + if (array && key === 'length') continue; + if (FORBIDDEN_KEYS.has(key)) fail('unsafe_key'); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (!descriptor.enumerable || !Object.hasOwn(descriptor, 'value')) fail('accessor_or_hidden_field_denied'); + if (array && !/^(0|[1-9][0-9]*)$/.test(key)) fail('array_property_denied'); + output[key] = dataOnly(descriptor.value, depth + 1, seen, budget); + } + if (array && (value.length > 4096 || Object.keys(output).length !== value.length)) fail('sparse_array_denied'); + seen.delete(value); + return output; +} + +function fields(value, allowed, required = []) { + if (!value || Array.isArray(value) || typeof value !== 'object') fail('object_required'); + if (Object.keys(value).some((key) => !allowed.includes(key))) fail('unknown_field'); + if (required.some((key) => !Object.hasOwn(value, key))) fail('missing_field'); +} + +function slug(value, max = 63) { + if (typeof value !== 'string' || value.length > max || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value)) fail('invalid_identifier'); + return value; +} + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`; + if (value && typeof value === 'object') { + return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`; + } + return JSON.stringify(value); +} + +function digest(value) { + return `sha256:${createHash('sha256').update(canonical(value)).digest('hex')}`; +} + +function freeze(value) { + if (value && typeof value === 'object') { + Object.values(value).forEach(freeze); + Object.freeze(value); + } + return value; +} + +function endpoint(value) { + fields(value, ['host', 'paths'], ['host', 'paths']); + const { host, paths } = value; + // Syntactic screening only: DNS resolution/rebinding checks belong to the host. + if (typeof host !== 'string' || host.length > 253 || host !== host.toLowerCase() + || !/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/.test(host) + || /(?:^|\.)(?:localhost|local|internal|lan|home|onion)$/.test(host)) fail('invalid_public_hostname'); + if (!Array.isArray(paths) || paths.length < 1 || paths.length > 16) fail('invalid_paths'); + for (const path of paths) { + if (typeof path !== 'string' || path.length > 256 || !/^\/[A-Za-z0-9._~/-]*$/.test(path) + || path.includes('//') || path.split('/').some((part) => part === '.' || part === '..')) fail('invalid_exact_path'); + } + return { host, paths: [...new Set(paths)].sort() }; +} + +/** Compile an offline proposal. It is not an approval, executor, or sandbox. */ +export function buildOpenShellPlan(input) { + const request = dataOnly(input); + fields(request, ['schema', 'role', 'runId', 'workspace', 'image', 'readEndpoints', 'maxRuntimeSeconds'], + ['schema', 'role', 'runId', 'workspace', 'image']); + if (request.schema !== REQUEST_SCHEMA) fail('unsupported_request_schema'); + if (!ROLES.includes(request.role)) fail('unsupported_role'); + slug(request.runId, 24); + slug(request.workspace); + if (typeof request.image !== 'string' || request.image.length > 300 + || !/^[a-z0-9]+(?:[._-][a-z0-9]+)*(?:\/[a-z0-9]+(?:[._-][a-z0-9]+)*)+@sha256:[a-f0-9]{64}$/.test(request.image)) fail('digest_pinned_image_required'); + const seconds = Object.hasOwn(request, 'maxRuntimeSeconds') ? request.maxRuntimeSeconds : 60; + if (!Number.isInteger(seconds) || seconds < 1 || seconds > 300) fail('invalid_runtime_limit'); + const endpoints = Object.hasOwn(request, 'readEndpoints') ? request.readEndpoints : []; + if (!Array.isArray(endpoints) || endpoints.length > 16) fail('invalid_endpoints'); + if (request.role !== 'cartographer' && endpoints.length) fail('role_egress_denied'); + const normalized = endpoints.map(endpoint).sort((a, b) => a.host < b.host ? -1 : a.host > b.host ? 1 : 0); + if (new Set(normalized.map((item) => item.host)).size !== normalized.length) fail('duplicate_host'); + const normalizedRequest = { ...request, readEndpoints: normalized, maxRuntimeSeconds: seconds }; + const networkPolicies = {}; + normalized.forEach((item, index) => { + const name = `read_${index}`; + networkPolicies[name] = { + name, + binaries: [{ path: '/usr/bin/curl' }], + endpoints: [{ + host: item.host, port: 443, protocol: 'rest', + tls: 0, enforcement: 1, + rules: item.paths.flatMap((path) => ['GET', 'HEAD'].map((method) => ({ allow: { method, path } }))), + }], + }; + }); + const policy = { + version: 1, + filesystem: { + includeWorkdir: false, + readOnly: ['/usr', '/bin', '/lib', '/lib64', '/etc/ssl/certs', '/etc/hosts', '/etc/resolv.conf', '/sandbox/input'], + readWrite: ['/sandbox/output', '/tmp'], + }, + landlock: { compatibility: 'hard_requirement' }, + process: { runAsUser: 'sandbox', runAsGroup: 'sandbox' }, + networkPolicies, + }; + const plan = { + schema: PLAN_SCHEMA, + request: normalizedRequest, + upstream: { ...UPSTREAM }, + status: 'offline_proposal', + authority: { execution: false, provisioning: false, payment: false, trustMutation: false, policyApproval: false }, + // camelCase is the TypeScript SDK message-init shape, NOT CLI YAML. + createSpec: { + name: `ag-${request.role}-${request.runId}`, + workspace: request.workspace, + image: request.image, + command: ['/bin/true'], + tty: false, + gpu: false, + environment: {}, + providers: [], + serviceExposures: [], + policy, + }, + hostRequirements: { + maxRuntimeSeconds: seconds, + deadlineEnforced: false, + imageVerified: false, + gatewayVerified: false, + dnsAndRedirectContainmentVerified: false, + runtimePolicyVerified: false, + ownerApprovalRequired: true, + credentialBoundary: 'control_plane_only', + }, + policyDigest: digest(policy), + evidenceClass: 'local_plan_not_runtime_proof', + }; + return freeze({ ...plan, planDigest: digest(plan) }); +} + +/** Reject altered or extended plans, including self-rehashed policy overrides. */ +export function validateOpenShellPlan(input) { + const plan = dataOnly(input); + const rebuilt = buildOpenShellPlan(plan.request); + if (canonical(plan) !== canonical(rebuilt)) fail('plan_mismatch'); + return rebuilt; +} + +/** Offline SDK-result shaping. Caller reports NEVER become verified receipts. */ +export function summarizeOpenShellObservation(planInput, expectedSandboxId, input) { + const plan = validateOpenShellPlan(planInput); + if (typeof expectedSandboxId !== 'string' || !/^[A-Za-z0-9-]{1,80}$/.test(expectedSandboxId)) fail('invalid_sandbox_id'); + const observation = dataOnly(input); + fields(observation, ['kind', 'sandboxId', 'exitCode', 'outcome'], ['kind', 'sandboxId']); + if (observation.sandboxId !== expectedSandboxId) fail('sandbox_identity_mismatch'); + let reportedStatus; + if (observation.kind === 'exec') { + if (Object.hasOwn(observation, 'outcome')) fail('unexpected_outcome'); + if (!Number.isInteger(observation.exitCode) || observation.exitCode < 0 || observation.exitCode > 255) fail('terminal_exit_code_required'); + reportedStatus = observation.exitCode === 0 ? 'reported_success' : 'reported_failure'; + } else if (observation.kind === 'delete') { + if (Object.hasOwn(observation, 'exitCode')) fail('unexpected_exit_code'); + const states = { completed: 'reported_completed', accepted: 'reported_pending', already_absent: 'reported_absent_unverified', unspecified: 'unknown', unknown: 'unknown' }; + if (!Object.hasOwn(states, observation.outcome)) fail('unknown_deletion_outcome'); + reportedStatus = states[observation.outcome]; + } else fail('unsupported_observation'); + return freeze({ + schema: 'agoragentic.openshell.observation.v1', + runId: plan.request.runId, + planDigest: plan.planDigest, + sandboxId: expectedSandboxId, + kind: observation.kind, + reportedStatus, + evidenceClass: 'caller_reported_unverified', + verified: false, + settlementProof: false, + executionAuthority: false, + }); +} + +export function openShellReadiness() { + return freeze({ + status: 'scaffold_only', upstream: { ...UPSTREAM }, + activationSupported: false, liveTrafficProtected: false, + blockers: ['qualified_host_adapter_missing', 'image_and_gateway_not_verified', 'live_containment_not_tested', 'owner_activation_not_approved'], + }); +} + +export class OpenShellScaffoldAdapter { + prepare(request) { return buildOpenShellPlan(request); } + async invoke() { fail('openshell_activation_not_implemented'); } +} diff --git a/agent-os/openshell/scaffold.test.mjs b/agent-os/openshell/scaffold.test.mjs new file mode 100644 index 00000000..40176ca7 --- /dev/null +++ b/agent-os/openshell/scaffold.test.mjs @@ -0,0 +1,205 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync, mkdtempSync, writeFileSync, rmSync } from 'node:fs'; +import { execFileSync, spawnSync } from 'node:child_process'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { UPSTREAM, ROLES, buildOpenShellPlan, validateOpenShellPlan, summarizeOpenShellObservation, openShellReadiness, OpenShellScaffoldAdapter } from './scaffold.mjs'; + +const fixture = JSON.parse(readFileSync(new URL('./fixture.json', import.meta.url), 'utf8')); +const fresh = () => structuredClone(fixture); +const plan = () => buildOpenShellPlan(fresh()); +const rejects = (input, code) => assert.throws(() => buildOpenShellPlan(input), { code }); + +for (const role of ROLES) { + test(`${role}: offline, authority-free, immutable plan`, () => { + const input = { ...fresh(), role, readEndpoints: [] }; + const result = buildOpenShellPlan(input); + assert.equal(result.status, 'offline_proposal'); + assert.ok(Object.values(result.authority).every((value) => value === false)); + assert.deepEqual(result.createSpec.command, ['/bin/true']); + assert.deepEqual(result.createSpec.providers, []); + assert.deepEqual(result.createSpec.environment, {}); + assert.deepEqual(result.createSpec.serviceExposures, []); + assert.equal(result.hostRequirements.deadlineEnforced, false); + assert.equal(result.hostRequirements.ownerApprovalRequired, true); + assert.throws(() => { result.createSpec.policy.filesystem.readWrite.push('/'); }, TypeError); + assert.deepEqual(validateOpenShellPlan(JSON.parse(JSON.stringify(result))), result); + input.readEndpoints.push({ host: 'changed.example.com', paths: ['/'] }); + assert.equal(result.request.readEndpoints.length, 0); + }); +} + +test('cartographer emits exact GET/HEAD rules with explicit enforcement, no credential/IP escape hatch', () => { + const policy = plan().createSpec.policy; + assert.equal(policy.landlock.compatibility, 'hard_requirement'); + assert.equal(policy.filesystem.includeWorkdir, false); + assert.deepEqual(policy.filesystem.readWrite, ['/sandbox/output', '/tmp']); + assert.deepEqual(policy.process, { runAsUser: 'sandbox', runAsGroup: 'sandbox' }); + const rule = policy.networkPolicies.read_0; + assert.deepEqual(rule.binaries, [{ path: '/usr/bin/curl' }]); + const item = rule.endpoints[0]; + assert.equal(item.enforcement, 1); + assert.equal(item.protocol, 'rest'); + assert.equal(item.tls, 0); + assert.equal(item.port, 443); + assert.ok(item.rules.every(({ allow }) => ['GET', 'HEAD'].includes(allow.method))); + assert.deepEqual(Object.keys(item).sort(), ['host', 'port', 'protocol', 'tls', 'enforcement', 'rules'].sort()); +}); + +test('deterministic normalization and digest binds all policy and host-limit inputs', () => { + const a = fresh(); + a.readEndpoints.push({ host: 'a.example.com', paths: ['/z', '/a', '/a'] }); + const b = fresh(); + b.readEndpoints.unshift({ host: 'a.example.com', paths: ['/a', '/z'] }); + assert.deepEqual(buildOpenShellPlan(a), buildOpenShellPlan(b)); + const c = { ...b, maxRuntimeSeconds: 61 }; + assert.notEqual(buildOpenShellPlan(c).planDigest, buildOpenShellPlan(b).planDigest); + assert.equal(buildOpenShellPlan(c).policyDigest, buildOpenShellPlan(b).policyDigest); +}); + +test('optional omitted fields use bounded defaults, not permissive policy', () => { + const input = fresh(); + delete input.readEndpoints; + delete input.maxRuntimeSeconds; + const result = buildOpenShellPlan(input); + assert.deepEqual(result.createSpec.policy.networkPolicies, {}); + assert.equal(result.hostRequirements.maxRuntimeSeconds, 60); +}); + +for (const host of ['127.0.0.1', '169.254.169.254', '::1', '[::1]', '2130706433', '0177.0.0.1', '0x7f000001', 'localhost', 'foo.localhost', 'metadata.internal', 'foo.local', 'foo.lan', 'foo.onion', '*.example.com', '**.example.com', 'https://example.com', 'user@example.com', 'example.com:443', 'example.com.', 'EXAMPLE.com', 'exämple.com', 'example.com\n', 'a..example.com']) { + test(`reject hostname ${JSON.stringify(host)}`, () => { + rejects({ ...fresh(), readEndpoints: [{ host, paths: ['/'] }] }, 'invalid_public_hostname'); + }); +} +for (const path of ['/*', '/**', '//evil', '/x//y', '/../x', '/a/./b', '/a/../b', '/%2e%2e/x', '/x%2fy', '/x?token=secret', '/x#fragment', '/x\\y', '/x\n', 'relative', 7, null]) { + test(`reject path ${JSON.stringify(path)}`, () => { + rejects({ ...fresh(), readEndpoints: [{ host: 'docs.example.com', paths: [path] }] }, 'invalid_exact_path'); + }); +} +for (const value of [0, -1, 301, 1.5, '60', null, true]) { + test(`reject runtime limit ${JSON.stringify(value)}`, () => rejects({ ...fresh(), maxRuntimeSeconds: value }, 'invalid_runtime_limit')); +} +for (const field of ['enabled', 'approved', 'providers', 'environment', 'rawSpec', 'policy', 'command', 'gateway', 'token', 'filesystem']) { + test(`deny unrecognized authority/override field ${field}`, () => rejects({ ...fresh(), [field]: true }, 'unknown_field')); +} +for (const role of ['emissary', 'steward']) { + test(`${role} cannot inherit cartographer egress`, () => rejects({ ...fresh(), role }, 'role_egress_denied')); +} +for (const image of ['ubuntu:latest', 'ubuntu', 'repo/name@sha256:abc', 'repo/name@sha256:' + 'A'.repeat(64), 'repo/name@sha256:' + '1'.repeat(64) + '\n']) { + test(`reject unpinned/noncanonical image ${JSON.stringify(image)}`, () => rejects({ ...fresh(), image }, 'digest_pinned_image_required')); +} + +test('reject duplicate hosts, excessive endpoints, null endpoints and empty path lists', () => { + rejects({ ...fresh(), readEndpoints: [fixture.readEndpoints[0], fixture.readEndpoints[0]] }, 'duplicate_host'); + rejects({ ...fresh(), readEndpoints: Array(17).fill(fixture.readEndpoints[0]) }, 'invalid_endpoints'); + rejects({ ...fresh(), readEndpoints: null }, 'invalid_endpoints'); + rejects({ ...fresh(), readEndpoints: [{ host: 'example.com', paths: [] }] }, 'invalid_paths'); + rejects({ ...fresh(), readEndpoints: [{ host: 'example.com', paths: ['/'], allowedIps: ['10.0.0.1'] }] }, 'unknown_field'); +}); + +test('reject invalid schema, missing fields, role and identifiers', () => { + rejects({ ...fresh(), schema: 'unknown' }, 'unsupported_request_schema'); + const input = fresh(); delete input.image; + rejects(input, 'missing_field'); + rejects({ ...fresh(), role: 'admin' }, 'unsupported_role'); + for (const runId of ['../x', '', 'a'.repeat(25), 'a\n', '--option']) rejects({ ...fresh(), runId }, 'invalid_identifier'); +}); + +test('reject non-data objects, accessors without invoking them, cyclic and hostile keys', () => { + rejects(new Date(), 'plain_object_required'); + let touched = false; + const input = fresh(); + Object.defineProperty(input, 'token', { enumerable: true, get() { touched = true; return 'secret'; } }); + rejects(input, 'accessor_or_hidden_field_denied'); + assert.equal(touched, false); + const cyclic = fresh(); cyclic.extra = cyclic; + rejects(cyclic, 'cyclic_input'); + rejects(JSON.parse('{"__proto__":{"polluted":true}}'), 'unsafe_key'); + rejects({ ...fresh(), extra: () => true }, 'json_data_required'); + rejects({ ...fresh(), extra: Infinity }, 'json_data_required'); + rejects({ ...fresh(), readEndpoints: Array(2) }, 'sparse_array_denied'); + assert.equal({}.polluted, undefined); +}); + +test('reject edited, extended, and authority-bearing serialized plans', () => { + for (const change of [ + (p) => { p.authority.execution = true; }, + (p) => { p.createSpec.policy.networkPolicies.read_0.endpoints[0].enforcement = 0; }, + (p) => { p.createSpec.rawSpec = { policy: {} }; }, + (p) => { p.extra = true; }, + (p) => { p.planDigest = 'sha256:' + '0'.repeat(64); }, + ]) { + const changed = structuredClone(plan()); change(changed); + assert.throws(() => validateOpenShellPlan(changed), { code: 'plan_mismatch' }); + } +}); + +for (const [exitCode, expected] of [[0, 'reported_success'], [1, 'reported_failure'], [137, 'reported_failure']]) { + test(`execution report ${exitCode} remains unverified`, () => { + const report = summarizeOpenShellObservation(plan(), 'sandbox-123', { kind: 'exec', sandboxId: 'sandbox-123', exitCode }); + assert.equal(report.reportedStatus, expected); + assert.equal(report.verified, false); + assert.equal(report.settlementProof, false); + assert.equal(report.executionAuthority, false); + assert.equal(report.evidenceClass, 'caller_reported_unverified'); + }); +} +for (const exitCode of [undefined, null, '', '0', -1, 256, 0.5, false]) { + test(`missing/invalid terminal exit ${String(exitCode)} is not success`, () => { + const report = { kind: 'exec', sandboxId: 'sandbox-123' }; + if (exitCode !== undefined) report.exitCode = exitCode; + assert.throws(() => summarizeOpenShellObservation(plan(), 'sandbox-123', report), { code: 'terminal_exit_code_required' }); + }); +} +for (const [outcome, status] of [['completed', 'reported_completed'], ['accepted', 'reported_pending'], ['already_absent', 'reported_absent_unverified'], ['unspecified', 'unknown'], ['unknown', 'unknown']]) { + test(`deletion ${outcome} preserves pending/uncertainty`, () => { + const result = summarizeOpenShellObservation(plan(), 'original-id', { kind: 'delete', sandboxId: 'original-id', outcome }); + assert.equal(result.reportedStatus, status); + assert.equal(result.verified, false); + }); +} + +test('observations cannot change identity, carry raw logs, or mix terminal types', () => { + assert.throws(() => summarizeOpenShellObservation(plan(), 'original', { kind: 'exec', sandboxId: 'replacement', exitCode: 0 }), { code: 'sandbox_identity_mismatch' }); + assert.throws(() => summarizeOpenShellObservation(plan(), 'original', { kind: 'exec', sandboxId: 'original', exitCode: 0, stdout: 'secret' }), { code: 'unknown_field' }); + assert.throws(() => summarizeOpenShellObservation(plan(), 'original', { kind: 'delete', sandboxId: 'original', outcome: 'future' }), { code: 'unknown_deletion_outcome' }); + assert.throws(() => summarizeOpenShellObservation(plan(), 'original', { kind: 'delete', sandboxId: 'original', outcome: 'completed', exitCode: 0 }), { code: 'unexpected_exit_code' }); +}); + +test('adapter is hard-off regardless of flags, booleans or supplied client', async () => { + let calls = 0; + const adapter = new OpenShellScaffoldAdapter({ enabled: true, client: { create() { calls++; } } }); + assert.equal(adapter.prepare(fresh()).status, 'offline_proposal'); + await assert.rejects(adapter.invoke('create_sandbox', { enabled: true, approved: true, plan: plan() }), { code: 'openshell_activation_not_implemented' }); + assert.equal(calls, 0); + assert.equal(openShellReadiness().activationSupported, false); + assert.equal(openShellReadiness().liveTrafficProtected, false); +}); + +test('upstream pin matches checked-in lock; runtime proof is not claimed', () => { + const lock = JSON.parse(readFileSync(new URL('./upstream.lock.json', import.meta.url), 'utf8')); + for (const key of ['release', 'commit', 'sdk']) assert.equal(UPSTREAM[key], lock[key]); + assert.equal(lock.runtimeTested, false); +}); + +test('CLI is offline, reads fixture, denies apply, limits input, and does not echo malformed secrets', () => { + const cli = fileURLToPath(new URL('./preview.mjs', import.meta.url)); + const file = fileURLToPath(new URL('./fixture.json', import.meta.url)); + const result = JSON.parse(execFileSync(process.execPath, [cli, file], { encoding: 'utf8' })); + assert.deepEqual(result, plan()); + const ready = JSON.parse(execFileSync(process.execPath, [cli, '--readiness'], { encoding: 'utf8' })); + assert.equal(ready.activationSupported, false); + assert.equal(spawnSync(process.execPath, [cli, '--apply'], { encoding: 'utf8' }).status, 2); + const dir = mkdtempSync(join(tmpdir(), 'openshell-scaffold-')); + try { + const bad = join(dir, 'bad.json'); + writeFileSync(bad, '{"secret":"do-not-echo-this"'); + const failed = spawnSync(process.execPath, [cli, bad], { encoding: 'utf8' }); + assert.equal(failed.status, 2); + assert.equal(failed.stderr.includes('do-not-echo-this'), false); + writeFileSync(bad, 'x'.repeat(65537)); + assert.match(spawnSync(process.execPath, [cli, bad], { encoding: 'utf8' }).stderr, /request_too_large/); + } finally { rmSync(dir, { recursive: true, force: true }); } +}); diff --git a/agent-os/openshell/upstream.lock.json b/agent-os/openshell/upstream.lock.json new file mode 100644 index 00000000..b0c383cb --- /dev/null +++ b/agent-os/openshell/upstream.lock.json @@ -0,0 +1,16 @@ +{ + "schema": "agoragentic.openshell.upstream-lock.v1", + "reviewedAt": "2026-09-28", + "repository": "https://github.com/NVIDIA/OpenShell", + "release": "v0.1.2", + "commit": "6648bd0c290efbc41ba131ee9831ee45cd431f94", + "sdk": "@nvidia/openshell-sdk", + "sdkDistribution": "GitHub Packages at the reviewed release; no package installation performed", + "license": "Apache-2.0", + "runtimeTested": false, + "sourceFiles": [ + { "path": "sdk/typescript/README.md", "gitBlob": "99c4fac9d4d9dfe0904f6fb2510e22154e5b206f" }, + { "path": "sdk/typescript/src/client.ts", "gitBlob": "93ad8ba6d163f409e522a7e9bde53f13eafa9338", "reviewedLines": "1-280" }, + { "path": "proto/sandbox.proto", "gitBlob": "1d837b7f0e1d4e0e9d82763252f214804027c5b6", "reviewedLines": "1-260,300-430" } + ] +} From 8be37db95e824da1c00e6028010d26b3c52f7794 Mon Sep 17 00:00:00 2001 From: Rhein1 / Agoragentic <140637602+rhein1@users.noreply.github.com> Date: Mon, 28 Sep 2026 19:32:54 -0400 Subject: [PATCH 02/12] docs(agent-os): link offline OpenShell preparation and qualification handoff --- agent-os/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/agent-os/README.md b/agent-os/README.md index 90fd5c72..a8de132a 100644 --- a/agent-os/README.md +++ b/agent-os/README.md @@ -143,3 +143,13 @@ Use this to track recurring spend, success rate, receipt linkage, and budget pre - Agent OS quickstart: https://agoragentic.com/guides/agent-os-quickstart/ - API reference: https://agoragentic.com/docs.html - Discovery check: https://agoragentic.com/api/discovery/check + +## OpenShell preparation scaffold + +The [OpenShell scaffold](openshell/README.md) compiles offline sandbox-policy proposals for Cartographer, Emissary, and Steward against a pinned upstream source contract. It includes local tests and a [Codex integration handoff](openshell/CODEX_HANDOFF.md), but does not install OpenShell, connect a gateway, provision a sandbox, run an agent, or grant execution/payment authority. No live compatibility or production protection is claimed. + +```sh +node --test agent-os/openshell/scaffold.test.mjs +node agent-os/openshell/preview.mjs agent-os/openshell/fixture.json +node agent-os/openshell/preview.mjs --readiness +``` From 015443b7af2b55e3a1711672c3b699dd879ecaf4 Mon Sep 17 00:00:00 2001 From: Rhein1 / Agoragentic <140637602+rhein1@users.noreply.github.com> Date: Wed, 30 Sep 2026 09:39:42 -0400 Subject: [PATCH 03/12] fix(mcp): clear transitive dependency audit advisories --- mcp/package-lock.json | 12 ++++++------ mcp/package.json | 2 +- risk-fork-hosted-mcp/dist/runtime/index.mjs | 4 ++-- risk-fork-hosted-mcp/integrity-manifest.json | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mcp/package-lock.json b/mcp/package-lock.json index 1cef2831..bb5eaaba 100644 --- a/mcp/package-lock.json +++ b/mcp/package-lock.json @@ -1041,9 +1041,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -1243,9 +1243,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", - "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "dev": true, "license": "MIT", "engines": { diff --git a/mcp/package.json b/mcp/package.json index c36ee9e5..6d6048af 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -64,7 +64,7 @@ }, "overrides": { "@hono/node-server": "2.0.11", - "fast-uri": "3.1.7", + "fast-uri": "3.1.8", "qs": "6.16.0" }, "engines": { diff --git a/risk-fork-hosted-mcp/dist/runtime/index.mjs b/risk-fork-hosted-mcp/dist/runtime/index.mjs index 04ae0296..b9528971 100644 --- a/risk-fork-hosted-mcp/dist/runtime/index.mjs +++ b/risk-fork-hosted-mcp/dist/runtime/index.mjs @@ -116,7 +116,7 @@ var require_package = __commonJS({ }, overrides: { "@hono/node-server": "2.0.11", - "fast-uri": "3.1.7", + "fast-uri": "3.1.8", qs: "6.16.0" }, engines: { @@ -74494,7 +74494,7 @@ function createE2BAuthorityFreeSourceVerifier(options = {}) { } // risk-fork-hosted-mcp/src/index.mjs -var REVIEWED_SOURCE_INTEGRITY = true ? "sha256:b376639a54275b44e3bb845c7d26aa63b7119f0ced68ebc6a7ca75e54f4a787f" : null; +var REVIEWED_SOURCE_INTEGRITY = true ? "sha256:7cf5b428998d43726f192359130d6c5d250507ab0cace2cc63fff273955b6739" : null; var HOSTED_MCP_BUNDLE_METADATA = Object.freeze({ package_name: "@agoragentic/risk-fork-hosted-mcp", package_version: "0.1.0-alpha.0", diff --git a/risk-fork-hosted-mcp/integrity-manifest.json b/risk-fork-hosted-mcp/integrity-manifest.json index ae74bf50..2c9f49cb 100644 --- a/risk-fork-hosted-mcp/integrity-manifest.json +++ b/risk-fork-hosted-mcp/integrity-manifest.json @@ -9,7 +9,7 @@ "schema": "agoragentic.risk-fork-hosted-mcp.reviewed-sources.v2", "normalization": "utf8_crlf_to_lf_lone_cr_preserved", "files": 66, - "sha256": "sha256:b376639a54275b44e3bb845c7d26aa63b7119f0ced68ebc6a7ca75e54f4a787f" + "sha256": "sha256:7cf5b428998d43726f192359130d6c5d250507ab0cace2cc63fff273955b6739" }, "reviewed_sources": [ { @@ -25,7 +25,7 @@ { "path": "mcp/package.json", "bytes": 3195, - "sha256": "sha256:bb551da95f41912a91b331e809f6195652b4d8bccd5881e94843d84b9f73c8d0" + "sha256": "sha256:f1810726d850cee5c84d2902e2ce108596e780d1cba573845f294d2cd59c85a1" }, { "path": "risk-fork/LICENSE", @@ -520,7 +520,7 @@ "path": "mcp/package.json", "source": "reviewed_source", "bytes": 3195, - "sha256": "sha256:bb551da95f41912a91b331e809f6195652b4d8bccd5881e94843d84b9f73c8d0" + "sha256": "sha256:f1810726d850cee5c84d2902e2ce108596e780d1cba573845f294d2cd59c85a1" }, { "path": "risk-fork-hosted-mcp/node_modules/@modelcontextprotocol/core/LICENSE", @@ -2629,7 +2629,7 @@ "artifact": { "path": "dist/runtime/index.mjs", "bytes": 3111081, - "sha256": "sha256:181a3ecf5788ee443595d4d7192c4c9bff50829e02ea6c52e9b842a2a74d6609" + "sha256": "sha256:c5e9814e13b84d5489527adeaac27e4d47363acec706cef3bcbe17f53e06dcc6" }, "packaged_assets": [ { From 59bc5d9692c7582df83a66f9b2103148610135ba Mon Sep 17 00:00:00 2001 From: rhein1 Date: Wed, 30 Sep 2026 10:07:23 -0400 Subject: [PATCH 04/12] fix(ci): refresh n8n and Claude SDK vulnerable transitive locks --- claude-agent-sdk/package-lock.json | 12 ++++++------ n8n/package-lock.json | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/claude-agent-sdk/package-lock.json b/claude-agent-sdk/package-lock.json index 7f11903f..8d7dd1e2 100644 --- a/claude-agent-sdk/package-lock.json +++ b/claude-agent-sdk/package-lock.json @@ -690,9 +690,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -893,9 +893,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", - "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "dev": true, "license": "MIT", "engines": { diff --git a/n8n/package-lock.json b/n8n/package-lock.json index 28771282..c7c6fde4 100644 --- a/n8n/package-lock.json +++ b/n8n/package-lock.json @@ -227,9 +227,9 @@ "license": "MIT" }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -305,9 +305,9 @@ "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -1686,9 +1686,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2352,9 +2352,9 @@ "license": "MIT" }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { From a14df17b5071efd678e0efed414743586e4f4c00 Mon Sep 17 00:00:00 2001 From: rhein1 Date: Wed, 30 Sep 2026 10:10:16 -0400 Subject: [PATCH 05/12] feat(openshell): reconcile offline lifecycle and ambiguous cleanup observations --- .github/workflows/openshell-scaffold.yml | 2 + agent-os/README.md | 3 +- agent-os/openshell/CODEX_HANDOFF.md | 6 +- agent-os/openshell/README.md | 22 +++ agent-os/openshell/host-adapter.mjs | 172 +++++++++++++++++++++++ agent-os/openshell/host-adapter.test.mjs | 137 ++++++++++++++++++ 6 files changed, 338 insertions(+), 4 deletions(-) create mode 100644 agent-os/openshell/host-adapter.mjs create mode 100644 agent-os/openshell/host-adapter.test.mjs diff --git a/.github/workflows/openshell-scaffold.yml b/.github/workflows/openshell-scaffold.yml index 0cb869ff..4c6199c8 100644 --- a/.github/workflows/openshell-scaffold.yml +++ b/.github/workflows/openshell-scaffold.yml @@ -34,6 +34,8 @@ jobs: run: | node --check agent-os/openshell/scaffold.mjs node --check agent-os/openshell/preview.mjs + node --check agent-os/openshell/host-adapter.mjs node --test agent-os/openshell/scaffold.test.mjs + node --test agent-os/openshell/host-adapter.test.mjs node agent-os/openshell/preview.mjs agent-os/openshell/fixture.json node agent-os/openshell/preview.mjs --readiness diff --git a/agent-os/README.md b/agent-os/README.md index a8de132a..f5fbb4b0 100644 --- a/agent-os/README.md +++ b/agent-os/README.md @@ -146,10 +146,11 @@ Use this to track recurring spend, success rate, receipt linkage, and budget pre ## OpenShell preparation scaffold -The [OpenShell scaffold](openshell/README.md) compiles offline sandbox-policy proposals for Cartographer, Emissary, and Steward against a pinned upstream source contract. It includes local tests and a [Codex integration handoff](openshell/CODEX_HANDOFF.md), but does not install OpenShell, connect a gateway, provision a sandbox, run an agent, or grant execution/payment authority. No live compatibility or production protection is claimed. +The [OpenShell scaffold](openshell/README.md) compiles offline sandbox-policy proposals for Cartographer, Emissary, and Steward against a pinned upstream source contract. It includes lifecycle observation reconciliation, hard-off live invocation, synthetic failure tests, and a [Codex integration handoff](openshell/CODEX_HANDOFF.md), but does not install OpenShell, connect a gateway, provision a sandbox, run an agent, or grant execution/payment authority. No live compatibility or production protection is claimed. ```sh node --test agent-os/openshell/scaffold.test.mjs +node --test agent-os/openshell/host-adapter.test.mjs node agent-os/openshell/preview.mjs agent-os/openshell/fixture.json node agent-os/openshell/preview.mjs --readiness ``` diff --git a/agent-os/openshell/CODEX_HANDOFF.md b/agent-os/openshell/CODEX_HANDOFF.md index 1c0e694e..d71760e4 100644 --- a/agent-os/openshell/CODEX_HANDOFF.md +++ b/agent-os/openshell/CODEX_HANDOFF.md @@ -2,7 +2,7 @@ ## Goal and present state -Integrate OpenShell below Agoragentic's existing control plane, preserving the agent-operated Interchange and all owner, financial, identity, trust, and evidence boundaries. The compiler, offline CLI, observation shaper, tests, and source pin are implemented. A live provider adapter is **not** implemented or qualified. No production migration is authorized by this document. +Integrate OpenShell below Agoragentic's existing control plane, preserving the agent-operated Interchange and all owner, financial, identity, trust, and evidence boundaries. The compiler, offline CLI, observation shaper, offline lifecycle reconciler, synthetic failure tests, and source pin are implemented. A live provider adapter is **not** implemented or qualified. No production migration is authorized by this document. ## Work sequence @@ -16,13 +16,13 @@ Acceptance: recorded package/image/gateway integrity, generated type/schema chec ### 2. Trusted host adapter -Implement the adapter in the existing platform provider abstraction, not in an agent-importable SDK shim. Keep source activation default-off. Use explicit workspace-scoped OpenShell operations. `OpenShellClient.connect()` is lazy: a successful construction is not a health check. Require a real `health()` result and verified gateway identity/auth configuration. +The integrations repository contains offline lifecycle reconciliation in `host-adapter.mjs`; it is not a live provider, SDK shim, or authority boundary. It validates bounded journal events and preserves unresolved identity, execution, and cleanup states without provider, process, or network effects. A future implementation in the existing platform provider abstraction must first bind to the real server-owned mandate, consent, identity, budget, and provisioning approval boundary; no caller-supplied object may stand in for that authority. `OpenShellClient.connect()` is lazy: a successful construction is not a health check. Require a real `health()` result and verified gateway identity/auth configuration. Bind authorization outside the sandbox to the exact owner, tenant/workspace, run, immutable sandbox ID, compiled plan/policy digest, image digest, command, quota, and expiry. Recheck the financial freeze and relevant mandate/consent controls. A caller-supplied `approved: true`, environment flag, self-hash, fake SDK client, or policy advisor proposal is not authorization. Do not accept agent-supplied method names, deployment contracts, policy overrides, `rawSpec`, provider lists, or arbitrary environment variables. Use the pinned SDK's `sandbox.create`, `waitReady`, `getConfig`, `exec`/`execStream`, `delete`, and identity-aware `waitDeleted` interfaces only after rechecking their actual types. Confirm effective static and network policy before any worker executes. Keep request data and authority/control-plane clients in separate trust domains. -Acceptance: local tests show every provider side effect is preceded by the real host gate; denial results in zero provider calls. Runtime status is never inferred from generated plans or mocks. +Acceptance for this source tranche: local tests exercise complete and interrupted journals, ambiguous create and late identity, cancellation/revocation/timeouts, missing terminal codes, wrong sandbox identity, config mismatch, pending/unknown deletion, invalid ordering, replay, and bounded strict input. Reconciliation never synthesizes success, authorizes retries, or converts caller observations into verified evidence. Live invocation remains hard-off. This does not establish a real host gate, gateway health, or runtime containment. ### 3. Bounded lab canary and failure cleanup diff --git a/agent-os/openshell/README.md b/agent-os/openshell/README.md index ff8a14e3..116b9f38 100644 --- a/agent-os/openshell/README.md +++ b/agent-os/openshell/README.md @@ -10,6 +10,7 @@ Node.js 20+; no package installation or credentials required. From the repositor ```sh node --test agent-os/openshell/scaffold.test.mjs +node --test agent-os/openshell/host-adapter.test.mjs node agent-os/openshell/preview.mjs agent-os/openshell/fixture.json node agent-os/openshell/preview.mjs --readiness ``` @@ -22,6 +23,8 @@ The fixture uses a **fictional image digest and example hostname**. It demonstra - Separate Cartographer, Emissary, and Steward profiles. Cartographer can propose exact-host/exact-path HTTPS GET/HEAD egress. Emissary and Steward remain network-denied. All profiles are proposal-only. - A TypeScript SDK `createSpec` candidate with create-time filesystem, process, hard-required Landlock, explicit L7 enforcement, no provider attachment, no environment injection, and no exposed service ports. - An adapter preparation interface whose invocation path always refuses activation. +- Offline lifecycle reconciliation with strict event ordering, plan and immutable sandbox identity binding, late identity capture after ambiguous creation, terminal exit-code requirements, and unresolved cleanup tracking. Cancellation, deadlines, and revocation do not imply worker termination. No event initiates an operation or authorizes a retry. +- Hard-off live invocation and readiness surfaces that identify the missing real Agent OS authority boundary. Journal output remains caller-reported and does not prove gateway health, policy enforcement, or runtime containment. - Offline observation shaping that requires a terminal integer exit code, binds an expected sandbox ID, distinguishes pending deletion, and never upgrades caller reports to verified receipts. - A pinned upstream source record and a staged integration/qualification handoff. @@ -32,6 +35,25 @@ const plan = adapter.prepare(request); // pure local compilation // await adapter.invoke(...); // always refuses; no live driver is bound ``` +The lifecycle reconciler accepts a bounded JSON journal, with contiguous `seq` numbers starting at 1. It has no authorization callback or provider client injection point. The existing Agent OS control plane must supply and authenticate any future authority boundary. No worker starts and no success is synthesized from an empty journal. + +```js +import { reconcileOpenShellLifecycle } from './host-adapter.mjs'; +const result = reconcileOpenShellLifecycle(plan, JSON.stringify({ + schema: 'agoragentic.openshell.lifecycle-journal.v1', + planDigest: plan.planDigest, + events: [ + { seq: 1, type: 'create_requested' }, + { seq: 2, type: 'create_unknown' }, + ], +})); +// result.reconciliationRequired === true; result.repeatCreateAllowed === false +``` + +Events cover `create_requested`, `create_unknown`, `create_rejected`, `created`, `ready`, `config_observed`, `exec_started`, `exec_unknown`, `exec_terminal`, `cancelled`, `timed_out`, `revoked`, `delete_requested`, and `deletion_observed`. Identity-bearing events require the original `sandboxId`. Config observations carry `image` and the local `policyDigest`; matching those fields is only a reported local projection match, never verification of the effective protobuf policy. Terminal observations carry `exitCode`; deletion observations carry the existing observation shaper's `outcome`. Unknown fields and invalid sequences fail closed. See the tests for complete and interrupted journal examples. + +The journal digest binds local content, not its author or provenance. Even a completed reported deletion leaves `cleanupVerified` and `terminationVerified` false. `reconciliationRequired: false` means this journal reports no outstanding resource, not that independent cleanup has been proved. Durable journal storage, authenticated host observations, effective-policy verification, real timers, and RPC handling remain the future host's responsibilities. + ## Role boundaries | Role | Current proposal | Later host integration, not implemented here | diff --git a/agent-os/openshell/host-adapter.mjs b/agent-os/openshell/host-adapter.mjs new file mode 100644 index 00000000..848a58da --- /dev/null +++ b/agent-os/openshell/host-adapter.mjs @@ -0,0 +1,172 @@ +import { createHash } from 'node:crypto'; +import { validateOpenShellPlan, summarizeOpenShellObservation } from './scaffold.mjs'; + +function fail(code) { + const error = new Error(code); + error.code = code; + throw error; +} + +function fields(value, required) { + if (!value || typeof value !== 'object' || Array.isArray(value) + || Object.keys(value).length !== required.length + || required.some((key) => !Object.hasOwn(value, key))) fail('invalid_journal_fields'); +} + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`; + if (value && typeof value === 'object') { + return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`; + } + return JSON.stringify(value); +} + +function freeze(value) { + if (value && typeof value === 'object') { + Object.values(value).forEach(freeze); + Object.freeze(value); + } + return value; +} + +const EVENT_FIELDS = Object.freeze({ + create_requested: [], create_unknown: [], create_rejected: [], + created: ['sandboxId'], ready: ['sandboxId'], + config_observed: ['sandboxId', 'image', 'policyDigest'], + exec_started: ['sandboxId'], exec_unknown: ['sandboxId'], + exec_terminal: ['sandboxId', 'exitCode'], + cancelled: [], timed_out: [], revoked: [], + delete_requested: ['sandboxId'], deletion_observed: ['sandboxId', 'outcome'], +}); + +/** + * Replay a bounded, untrusted observation journal. No gateway, authority, + * callback or worker is involved. Sequence validation helps a future host + * reconcile failures; it does not authenticate the observations or the plan. + * JSON text is intentional: this entry point never evaluates object accessors. + */ +export function reconcileOpenShellLifecycle(planInput, journalText) { + const plan = validateOpenShellPlan(planInput); + if (typeof journalText !== 'string' || Buffer.byteLength(journalText, 'utf8') > 65536) fail('bounded_journal_json_required'); + let journal; + try { journal = JSON.parse(journalText); } catch { fail('invalid_journal_json'); } + fields(journal, ['schema', 'planDigest', 'events']); + if (journal.schema !== 'agoragentic.openshell.lifecycle-journal.v1') fail('unsupported_journal_schema'); + if (journal.planDigest !== plan.planDigest) fail('journal_plan_mismatch'); + if (!Array.isArray(journal.events) || journal.events.length > 128) fail('invalid_journal_events'); + + let creation = 'not_requested'; + let sandboxId = null; + let ready = false; + let config = 'not_observed'; + let execution = 'not_started'; + let terminal = null; + let cleanup = 'not_requested'; + let deletion = null; + const interruptions = []; + const requireState = (condition) => { if (!condition) fail('invalid_lifecycle_sequence'); }; + + for (const [index, event] of journal.events.entries()) { + if (!event || !Object.hasOwn(EVENT_FIELDS, event.type)) fail('unsupported_lifecycle_event'); + fields(event, ['seq', 'type', ...EVENT_FIELDS[event.type]]); + if (event.seq !== index + 1) fail('non_contiguous_journal'); + requireState(cleanup !== 'reported_completed' && creation !== 'reported_rejected'); + if (Object.hasOwn(event, 'sandboxId')) { + if (typeof event.sandboxId !== 'string' || !/^[A-Za-z0-9-]{1,80}$/.test(event.sandboxId)) fail('invalid_sandbox_id'); + if (sandboxId !== null && event.sandboxId !== sandboxId) fail('sandbox_identity_mismatch'); + } + const uninterrupted = interruptions.length === 0 && cleanup === 'not_requested'; + switch (event.type) { + case 'create_requested': + requireState(creation === 'not_requested'); + creation = 'pending'; + break; + case 'create_unknown': + requireState(creation === 'pending'); + creation = 'unknown'; + interruptions.push('create_unknown'); + break; + case 'create_rejected': + requireState(['pending', 'unknown'].includes(creation)); + creation = 'reported_rejected'; + break; + case 'created': + // After ambiguous create or cancellation, a late ID is retained for + // cleanup. A second create is never inferred or authorized. + requireState(['pending', 'unknown'].includes(creation)); + sandboxId = event.sandboxId; + creation = 'reported_created'; + break; + case 'ready': + requireState(sandboxId !== null && !ready && uninterrupted); + ready = true; + break; + case 'config_observed': + requireState(ready && config === 'not_observed' && uninterrupted); + if (typeof event.image !== 'string' || typeof event.policyDigest !== 'string') fail('invalid_config_observation'); + config = event.image === plan.request.image && event.policyDigest === plan.policyDigest + ? 'reported_local_projection_match' : 'reported_mismatch'; + if (config === 'reported_mismatch') interruptions.push('config_mismatch'); + break; + case 'exec_started': + requireState(ready && config === 'reported_local_projection_match' && execution === 'not_started' && uninterrupted); + execution = 'running'; + break; + case 'exec_unknown': + requireState(execution === 'running'); + execution = 'unknown'; + interruptions.push('exec_unknown'); + break; + case 'exec_terminal': + requireState(['running', 'unknown'].includes(execution)); + terminal = summarizeOpenShellObservation(plan, sandboxId, { kind: 'exec', sandboxId: event.sandboxId, exitCode: event.exitCode }); + execution = terminal.reportedStatus; + break; + case 'cancelled': + case 'timed_out': + case 'revoked': + requireState(creation !== 'not_requested' && !interruptions.includes(event.type)); + interruptions.push(event.type); + // RPC cancellation/deadline/revocation alone cannot prove termination. + if (execution === 'running') execution = 'unknown'; + break; + case 'delete_requested': + requireState(sandboxId !== null && cleanup === 'not_requested'); + cleanup = 'requested'; + if (execution === 'running') execution = 'unknown'; + break; + case 'deletion_observed': + requireState(cleanup !== 'not_requested'); + deletion = summarizeOpenShellObservation(plan, sandboxId, { kind: 'delete', sandboxId: event.sandboxId, outcome: event.outcome }); + cleanup = deletion.reportedStatus; + break; + default: fail('unsupported_lifecycle_event'); + } + } + + const cleanupOutstanding = sandboxId !== null && cleanup !== 'reported_completed'; + const identityOutstanding = ['pending', 'unknown'].includes(creation); + return freeze({ + schema: 'agoragentic.openshell.lifecycle-reconciliation.v1', + runId: plan.request.runId, workspace: plan.request.workspace, planDigest: plan.planDigest, + journalDigest: `sha256:${createHash('sha256').update(canonical(journal)).digest('hex')}`, + eventCount: journal.events.length, sandboxId, creation, ready, config, execution, + terminal, cleanup, deletion, interruptions, + reconciliationRequired: identityOutstanding || cleanupOutstanding, + terminationVerified: false, cleanupVerified: false, + evidenceClass: 'caller_reported_unverified', + executionAuthority: false, repeatCreateAllowed: false, repeatExecAllowed: false, + providerCalls: false, runtimeContainmentVerified: false, liveTrafficProtected: false, + }); +} + +export function openShellHostReadiness() { + return freeze({ + status: 'offline_lifecycle_reconciliation', activationSupported: false, + providerCalls: false, processStarted: false, networkUsed: false, credentialsUsed: false, + blockers: ['real_agent_os_authority_boundary_not_bound', 'qualified_gateway_and_image_missing', + 'live_containment_not_tested', 'owner_activation_not_approved'], + }); +} + +export async function invokeOpenShell() { fail('openshell_live_adapter_not_implemented'); } diff --git a/agent-os/openshell/host-adapter.test.mjs b/agent-os/openshell/host-adapter.test.mjs new file mode 100644 index 00000000..d5e560ab --- /dev/null +++ b/agent-os/openshell/host-adapter.test.mjs @@ -0,0 +1,137 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { buildOpenShellPlan } from './scaffold.mjs'; +import { reconcileOpenShellLifecycle, invokeOpenShell, openShellHostReadiness } from './host-adapter.mjs'; + +const fixture = JSON.parse(readFileSync(new URL('./fixture.json', import.meta.url), 'utf8')); +const plan = buildOpenShellPlan(fixture); +const id = 'original-id'; +const e = (type, extra = {}) => ({ type, ...extra }); +const created = [e('create_requested'), e('created', { sandboxId: id })]; +const configured = [...created, e('ready', { sandboxId: id }), e('config_observed', { + sandboxId: id, image: plan.request.image, policyDigest: plan.policyDigest, +})]; +const running = [...configured, e('exec_started', { sandboxId: id })]; +const terminal = [...running, e('exec_terminal', { sandboxId: id, exitCode: 0 })]; +const journal = (events) => ({ schema: 'agoragentic.openshell.lifecycle-journal.v1', planDigest: plan.planDigest, + events: events.map((event, index) => ({ seq: index + 1, ...event })) }); +const replay = (events) => reconcileOpenShellLifecycle(plan, JSON.stringify(journal(events))); + +test('complete observed lifecycle binds identity and plan but never creates verified evidence', () => { + const events = [...terminal, e('delete_requested', { sandboxId: id }), + e('deletion_observed', { sandboxId: id, outcome: 'accepted' }), + e('deletion_observed', { sandboxId: id, outcome: 'completed' })]; + const result = replay(events); + assert.equal(result.cleanup, 'reported_completed'); + assert.equal(result.execution, 'reported_success'); + assert.equal(result.reconciliationRequired, false); + for (const field of ['terminationVerified', 'cleanupVerified', 'executionAuthority', 'providerCalls', 'runtimeContainmentVerified']) { + assert.equal(result[field], false); + } + assert.equal(result.evidenceClass, 'caller_reported_unverified'); + assert.equal(result.sandboxId, id); + assert.equal(result.planDigest, plan.planDigest); + assert.deepEqual(result, replay(events)); + assert.ok(Object.isFrozen(result.interruptions)); + assert.ok(Object.isFrozen(result.terminal)); +}); + +test('empty journal invents no successful execution or cleanup', () => { + const result = replay([]); + assert.equal(result.sandboxId, null); + assert.equal(result.execution, 'not_started'); + assert.equal(result.terminal, null); + assert.equal(result.cleanup, 'not_requested'); +}); + +test('ambiguous create prevents retry and late identity remains available for cleanup', () => { + const uncertain = [e('create_requested'), e('create_unknown')]; + assert.equal(replay(uncertain).reconciliationRequired, true); + assert.equal(replay(uncertain).repeatCreateAllowed, false); + assert.throws(() => replay([...uncertain, e('create_requested')]), { code: 'invalid_lifecycle_sequence' }); + assert.throws(() => replay([...uncertain, e('created', { sandboxId: id }), e('ready', { sandboxId: id })]), { code: 'invalid_lifecycle_sequence' }); + const late = replay([...uncertain, e('cancelled'), e('created', { sandboxId: id }), e('delete_requested', { sandboxId: id })]); + assert.equal(late.sandboxId, id); + assert.equal(late.cleanup, 'requested'); + assert.equal(late.reconciliationRequired, true); +}); + +for (const type of ['cancelled', 'timed_out', 'revoked', 'exec_unknown']) { + test(`${type} cannot imply worker termination or authorize another exec`, () => { + const stop = e(type, type === 'exec_unknown' ? { sandboxId: id } : {}); + const result = replay([...running, stop]); + assert.equal(result.execution, 'unknown'); + assert.equal(result.terminationVerified, false); + assert.equal(result.reconciliationRequired, true); + assert.throws(() => replay([...running, stop, e('exec_started', { sandboxId: id })]), { code: 'invalid_lifecycle_sequence' }); + assert.equal(replay([...running, stop, e('exec_terminal', { sandboxId: id, exitCode: 137 })]).execution, 'reported_failure'); + }); +} + +for (const outcome of ['accepted', 'already_absent', 'unspecified', 'unknown']) { + test(`deletion ${outcome} remains unresolved`, () => { + const result = replay([...terminal, e('delete_requested', { sandboxId: id }), e('deletion_observed', { sandboxId: id, outcome })]); + assert.equal(result.reconciliationRequired, true); + assert.equal(result.cleanupVerified, false); + assert.notEqual(result.cleanup, 'reported_completed'); + }); +} + +test('same-name replacement identity cannot be used for cleanup or terminal status', () => { + assert.throws(() => replay([...terminal, e('delete_requested', { sandboxId: 'replacement' })]), { code: 'sandbox_identity_mismatch' }); + assert.throws(() => replay([...running, e('exec_terminal', { sandboxId: 'replacement', exitCode: 0 })]), { code: 'sandbox_identity_mismatch' }); +}); + +test('policy or image mismatch blocks subsequent execution but permits original-ID cleanup', () => { + for (const key of ['image', 'policyDigest']) { + const events = structuredClone(configured); + events.at(-1)[key] = 'different'; + assert.equal(replay(events).config, 'reported_mismatch'); + assert.throws(() => replay([...events, e('exec_started', { sandboxId: id })]), { code: 'invalid_lifecycle_sequence' }); + assert.equal(replay([...events, e('delete_requested', { sandboxId: id })]).cleanup, 'requested'); + } +}); + +test('out-of-order, duplicate and post-cleanup operations are rejected', () => { + const invalid = [ + [e('ready', { sandboxId: id })], + [...created, e('exec_started', { sandboxId: id })], + [...configured, e('exec_terminal', { sandboxId: id, exitCode: 0 })], + [...created, e('created', { sandboxId: id })], + [...terminal, e('exec_terminal', { sandboxId: id, exitCode: 0 })], + [...created, e('deletion_observed', { sandboxId: id, outcome: 'completed' })], + [...created, e('delete_requested', { sandboxId: id }), e('delete_requested', { sandboxId: id })], + [...created, e('delete_requested', { sandboxId: id }), e('deletion_observed', { sandboxId: id, outcome: 'completed' }), e('ready', { sandboxId: id })], + [e('create_requested'), e('create_rejected'), e('create_requested')], + ]; + for (const events of invalid) assert.throws(() => replay(events), { code: 'invalid_lifecycle_sequence' }); +}); + +test('missing, boolean, negative and nonterminal exit codes cannot count as success', () => { + for (const exitCode of [null, true, -1, 256, '0']) { + assert.throws(() => replay([...running, e('exec_terminal', { sandboxId: id, exitCode })]), { code: 'terminal_exit_code_required' }); + } + assert.throws(() => replay([...running, e('exec_terminal', { sandboxId: id })]), { code: 'invalid_journal_fields' }); +}); + +test('journal schema, lineage, count and sequence are strict', () => { + const data = journal(created); + for (const mutation of [ + { ...data, approved: true }, { ...data, schema: 'other' }, { ...data, planDigest: 'wrong' }, + { ...data, events: [{ ...data.events[0], seq: 2 }] }, + { ...data, events: [{ ...data.events[0], method: 'create' }] }, + { ...data, events: Array(129).fill(data.events[0]) }, + ]) assert.throws(() => reconcileOpenShellLifecycle(plan, JSON.stringify(mutation))); + assert.throws(() => reconcileOpenShellLifecycle(plan, 'x'.repeat(65537)), { code: 'bounded_journal_json_required' }); + assert.throws(() => reconcileOpenShellLifecycle(plan, '{'), { code: 'invalid_journal_json' }); + let invoked = false; + assert.throws(() => reconcileOpenShellLifecycle(plan, { get events() { invoked = true; return []; } }), { code: 'bounded_journal_json_required' }); + assert.equal(invoked, false); +}); + +test('live invocation refuses even with caller-provided approval or fake client', async () => { + await assert.rejects(invokeOpenShell({ approved: true, client: { create() { throw Error('called'); } } }), { code: 'openshell_live_adapter_not_implemented' }); + assert.equal(openShellHostReadiness().activationSupported, false); + assert.equal(openShellHostReadiness().providerCalls, false); +}); From 1db6e0892f307e615240a7e837a9a5f435befb47 Mon Sep 17 00:00:00 2001 From: Rhein1 / Agoragentic <140637602+rhein1@users.noreply.github.com> Date: Wed, 30 Sep 2026 09:39:42 -0400 Subject: [PATCH 06/12] fix(mcp): clear transitive dependency audit advisories --- mcp/package-lock.json | 12 ++++++------ mcp/package.json | 2 +- risk-fork-hosted-mcp/dist/runtime/index.mjs | 4 ++-- risk-fork-hosted-mcp/integrity-manifest.json | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mcp/package-lock.json b/mcp/package-lock.json index 1cef2831..bb5eaaba 100644 --- a/mcp/package-lock.json +++ b/mcp/package-lock.json @@ -1041,9 +1041,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -1243,9 +1243,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", - "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "dev": true, "license": "MIT", "engines": { diff --git a/mcp/package.json b/mcp/package.json index c36ee9e5..6d6048af 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -64,7 +64,7 @@ }, "overrides": { "@hono/node-server": "2.0.11", - "fast-uri": "3.1.7", + "fast-uri": "3.1.8", "qs": "6.16.0" }, "engines": { diff --git a/risk-fork-hosted-mcp/dist/runtime/index.mjs b/risk-fork-hosted-mcp/dist/runtime/index.mjs index 04ae0296..b9528971 100644 --- a/risk-fork-hosted-mcp/dist/runtime/index.mjs +++ b/risk-fork-hosted-mcp/dist/runtime/index.mjs @@ -116,7 +116,7 @@ var require_package = __commonJS({ }, overrides: { "@hono/node-server": "2.0.11", - "fast-uri": "3.1.7", + "fast-uri": "3.1.8", qs: "6.16.0" }, engines: { @@ -74494,7 +74494,7 @@ function createE2BAuthorityFreeSourceVerifier(options = {}) { } // risk-fork-hosted-mcp/src/index.mjs -var REVIEWED_SOURCE_INTEGRITY = true ? "sha256:b376639a54275b44e3bb845c7d26aa63b7119f0ced68ebc6a7ca75e54f4a787f" : null; +var REVIEWED_SOURCE_INTEGRITY = true ? "sha256:7cf5b428998d43726f192359130d6c5d250507ab0cace2cc63fff273955b6739" : null; var HOSTED_MCP_BUNDLE_METADATA = Object.freeze({ package_name: "@agoragentic/risk-fork-hosted-mcp", package_version: "0.1.0-alpha.0", diff --git a/risk-fork-hosted-mcp/integrity-manifest.json b/risk-fork-hosted-mcp/integrity-manifest.json index ae74bf50..2c9f49cb 100644 --- a/risk-fork-hosted-mcp/integrity-manifest.json +++ b/risk-fork-hosted-mcp/integrity-manifest.json @@ -9,7 +9,7 @@ "schema": "agoragentic.risk-fork-hosted-mcp.reviewed-sources.v2", "normalization": "utf8_crlf_to_lf_lone_cr_preserved", "files": 66, - "sha256": "sha256:b376639a54275b44e3bb845c7d26aa63b7119f0ced68ebc6a7ca75e54f4a787f" + "sha256": "sha256:7cf5b428998d43726f192359130d6c5d250507ab0cace2cc63fff273955b6739" }, "reviewed_sources": [ { @@ -25,7 +25,7 @@ { "path": "mcp/package.json", "bytes": 3195, - "sha256": "sha256:bb551da95f41912a91b331e809f6195652b4d8bccd5881e94843d84b9f73c8d0" + "sha256": "sha256:f1810726d850cee5c84d2902e2ce108596e780d1cba573845f294d2cd59c85a1" }, { "path": "risk-fork/LICENSE", @@ -520,7 +520,7 @@ "path": "mcp/package.json", "source": "reviewed_source", "bytes": 3195, - "sha256": "sha256:bb551da95f41912a91b331e809f6195652b4d8bccd5881e94843d84b9f73c8d0" + "sha256": "sha256:f1810726d850cee5c84d2902e2ce108596e780d1cba573845f294d2cd59c85a1" }, { "path": "risk-fork-hosted-mcp/node_modules/@modelcontextprotocol/core/LICENSE", @@ -2629,7 +2629,7 @@ "artifact": { "path": "dist/runtime/index.mjs", "bytes": 3111081, - "sha256": "sha256:181a3ecf5788ee443595d4d7192c4c9bff50829e02ea6c52e9b842a2a74d6609" + "sha256": "sha256:c5e9814e13b84d5489527adeaac27e4d47363acec706cef3bcbe17f53e06dcc6" }, "packaged_assets": [ { From e4e72bc43dda9505998d46cb0acbdca4ed7debe2 Mon Sep 17 00:00:00 2001 From: rhein1 Date: Wed, 30 Sep 2026 10:07:23 -0400 Subject: [PATCH 07/12] fix(ci): refresh n8n and Claude SDK vulnerable transitive locks --- claude-agent-sdk/package-lock.json | 12 ++++++------ n8n/package-lock.json | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/claude-agent-sdk/package-lock.json b/claude-agent-sdk/package-lock.json index 7f11903f..8d7dd1e2 100644 --- a/claude-agent-sdk/package-lock.json +++ b/claude-agent-sdk/package-lock.json @@ -690,9 +690,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -893,9 +893,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", - "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "dev": true, "license": "MIT", "engines": { diff --git a/n8n/package-lock.json b/n8n/package-lock.json index 28771282..c7c6fde4 100644 --- a/n8n/package-lock.json +++ b/n8n/package-lock.json @@ -227,9 +227,9 @@ "license": "MIT" }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -305,9 +305,9 @@ "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -1686,9 +1686,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2352,9 +2352,9 @@ "license": "MIT" }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { From a26fcfb2f78151ee065cfbf4f1180ca293ae4d94 Mon Sep 17 00:00:00 2001 From: rhein1 Date: Thu, 1 Oct 2026 19:04:14 -0400 Subject: [PATCH 08/12] fix(n8n): qualify audit-clean development workflow fixture --- n8n/TOOLCHAIN_AUDIT.md | 35 +- n8n/package-lock.json | 832 ++------------------------------------ n8n/package.json | 4 +- n8n/test/release.test.cjs | 4 +- 4 files changed, 59 insertions(+), 816 deletions(-) diff --git a/n8n/TOOLCHAIN_AUDIT.md b/n8n/TOOLCHAIN_AUDIT.md index 7c57b54d..6bb90c36 100644 --- a/n8n/TOOLCHAIN_AUDIT.md +++ b/n8n/TOOLCHAIN_AUDIT.md @@ -2,7 +2,7 @@ Original toolchain audit: 2026-09-13 (supersedes 2026-09-01) -Dependency-fixture refresh: 2026-09-25. Rechecked the manifest and lockfile, +Dependency-fixture refresh: 2026-09-30. Rechecked the manifest and lockfile, peer ranges, clean install, tests, lint, build, dependency audits, and package contents. Historical CLI compatibility and Creator Portal acceptance were not revalidated. @@ -17,8 +17,8 @@ revalidated. - Linter: `eslint@9.29.0` - Formatter: `prettier@3.9.6` - Compiler: `typescript@5.9.2` -- Development host fixture: `n8n-workflow@2.39.1` -- Exact workflow peer fixture: `zod@3.25.76` +- Development host fixture: `n8n-workflow@2.28.4` +- Exact workflow peer fixture: `zod@3.25.67` - Minimum consumer Node.js: 20.19 - Locked development and publishing Node.js: 24 - Install mode: committed lockfile plus `npm ci` @@ -30,6 +30,17 @@ publishing workflow. ## Coordinated compatibility decision +The 2.39.1 fixture and available 2.41.1 stable / 2.42.1 beta releases pin +`axios@1.18.0` exactly. Seven newly reported Axios advisories require 1.20.0; +substituting a lock entry cannot satisfy that pin, and overrides remain prohibited. +Use the latest pre-Axios 2.28 patch fixture, 2.28.4, with its exact Zod peer +3.25.67. Its complete development graph audits clean. Clean installation, tests, +lint, build, runtime imports and the offline error boundary pass. All eleven +generated code, declaration and static-asset files are byte-identical to the +2.39.1 build. This changes the development fixture only: package version, +runtime peer, Node consumer floor, node source and audit policy are unchanged. +Requalify a newer fixture once its published dependency graph is patched. + Every available `@n8n/node-cli` line checked from 0.40.0 through the current stable and beta releases pulls an AI/template-only dependency branch through `@n8n/ai-node-sdk`. That branch pins `@langchain/classic@1.0.27` and @@ -84,17 +95,16 @@ The rejected dependency majors remain incoherent with this source candidate: The release-contract test pins the complete supported set so a future isolated major bump cannot look valid after changing only one manifest line. -`n8n-workflow@2.39.1` is the pinned development fixture, not a runtime -constraint. Its exact peer is satisfied by `zod@3.25.76`. The published peer +`n8n-workflow@2.28.4` is the pinned development fixture, not a runtime +constraint. Its exact peer is satisfied by `zod@3.25.67`. The published peer remains `n8n-workflow: "*"`, so host n8n versions are not narrowed. These fixtures keep clean-install build tests deterministic and are not shipped in the package tarball. -The pinned workflow fixture's development graph includes `isolated-vm@7`, -whose declared engine is Node.js 24 or newer. The published community node does -not ship that fixture, so the consumer floor remains Node.js 20.19; repository -validation and trusted publishing use Node.js 24 to satisfy the complete locked -development graph without ignoring engine metadata. +The pinned workflow fixture's development graph includes `isolated-vm@6.2.0`. +The published community node does not ship that fixture, so the consumer floor +remains Node.js 20.19; repository validation and trusted publishing continue to +use Node.js 24 without ignoring engine metadata. ## Security boundary @@ -110,9 +120,8 @@ found 0 vulnerabilities The CLI/AI/LangChain branch and `uuid@10.0.0` are absent from the lockfile. The only resolved `uuid` is patched `uuid@11.1.1` beneath the pinned workflow -fixture. The 2.39.1 development graph resolves `@n8n/utils@1.47.0` and patched -`nanoid@3.3.18`, and adds `quickjs-emscripten@0.32.0` with its `@jitl` QuickJS -WASM packages. These lock entries are development-only. `npm run audit:dev` +fixture. The vulnerable Axios dependency branch is absent from this fixture. +These lock entries are development-only. `npm run audit:dev` rejects any advisory at any severity; there is no development allowlist. The prior `brace-expansion`, `ip-address`, and `undici` findings remain resolved diff --git a/n8n/package-lock.json b/n8n/package-lock.json index c7c6fde4..14af7cb9 100644 --- a/n8n/package-lock.json +++ b/n8n/package-lock.json @@ -15,11 +15,11 @@ "eslint-import-resolver-typescript": "4.4.5", "eslint-plugin-import-x": "4.17.1", "eslint-plugin-n8n-nodes-base": "2.0.0", - "n8n-workflow": "2.39.1", + "n8n-workflow": "2.28.4", "prettier": "3.9.6", "typescript": "5.9.2", "typescript-eslint": "8.70.0", - "zod": "3.25.76" + "zod": "3.25.67" }, "engines": { "node": ">=20.19.0" @@ -28,103 +28,6 @@ "n8n-workflow": "*" } }, - "node_modules/@apm-js-collab/code-transformer": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.18.1.tgz", - "integrity": "sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/estree": "^1.0.8", - "astring": "^1.9.0", - "esquery": "^1.7.0", - "meriyah": "^6.1.4", - "semifies": "^1.0.0", - "source-map": "^0.6.0" - }, - "bin": { - "code-transformer": "cli.js" - } - }, - "node_modules/@apm-js-collab/code-transformer-bundler-plugins": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.7.4.tgz", - "integrity": "sha512-nAfOeZPSUAQvJa1iFT/5oCrTm5YQhMMrfCNthNnaXHZiOQhu1KGuLoIx7HtbAi3wfwaBYLaICPIeenIaEwcXIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@apm-js-collab/code-transformer": "^0.18.1", - "es-module-lexer": "^2.1.0", - "magic-string": "^0.30.21", - "module-details-from-path": "^1.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@apm-js-collab/tracing-hooks": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.13.0.tgz", - "integrity": "sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@apm-js-collab/code-transformer": "^0.18.0", - "debug": "^4.4.1", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@codemirror/autocomplete": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", - "integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@codemirror/language": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz", - "integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.5.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "node_modules/@codemirror/state": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz", - "integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@marijn/find-cluster-break": "^1.0.0" - } - }, - "node_modules/@codemirror/view": { - "version": "6.43.9", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.9.tgz", - "integrity": "sha512-sTuUzTpPMFebRhg6dawChoKKgndIwfjmJgKVxBefPElcU2NwQ6AFroupk0SFqEerQyZOGRfDNnSN8Dw/lMAsXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.7.0", - "crelt": "^1.0.6", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, "node_modules/@emnapi/core": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", @@ -441,105 +344,10 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jitl/quickjs-ffi-types": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-ffi-types/-/quickjs-ffi-types-0.32.0.tgz", - "integrity": "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jitl/quickjs-wasmfile-debug-asyncify": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-debug-asyncify/-/quickjs-wasmfile-debug-asyncify-0.32.0.tgz", - "integrity": "sha512-EX8zbXwGqCgAE764M+qvkHtyXDi/FUoMBea0JnES7vCM3P7a2+EOZOjGv85wtZ2sJhI1oJ+nekmqpOODFDY+hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, - "node_modules/@jitl/quickjs-wasmfile-debug-sync": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-debug-sync/-/quickjs-wasmfile-debug-sync-0.32.0.tgz", - "integrity": "sha512-LeYWrPGC1uNCTBWvibo3ZLJj0CSVNYUXvJpXMCmuQ5Sap2cCACc3uvGvYV4homHHBAzfw5akoTqMMS4YFRtw+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, - "node_modules/@jitl/quickjs-wasmfile-release-asyncify": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-release-asyncify/-/quickjs-wasmfile-release-asyncify-0.32.0.tgz", - "integrity": "sha512-3oSwPfja12ICz4aIblB58cuY8JlEq5Txt8Cut4VLo+LH47QN+mzCnSgnbB03hWzg1LBcc+VyyI9UOag7a1NF+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, - "node_modules/@jitl/quickjs-wasmfile-release-sync": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-release-sync/-/quickjs-wasmfile-release-sync-0.32.0.tgz", - "integrity": "sha512-BKNDI/TPBfGlLNGYpLrhcDGXmIk4xHm4MRAisOBnOzpXVn9HZWsfmMAc9WMBrAHjvvds6HOikKeaOBKdPdpVrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@lezer/common": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", - "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@lezer/highlight": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", - "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.3.0" - } - }, - "node_modules/@lezer/lr": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", - "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@marijn/find-cluster-break": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.3.tgz", - "integrity": "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@n8n/constants": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@n8n/constants/-/constants-0.38.0.tgz", - "integrity": "sha512-xcAHwgKl17ZQS1RFAwjit95dr/AL53WlLqmAcUIFi+A9cTr494aD00RBO/egACTycOY1ZWTst2D1B7jlHpESkg==", - "dev": true, - "license": "SEE LICENSE IN LICENSE.md" - }, "node_modules/@n8n/errors": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@n8n/errors/-/errors-0.13.0.tgz", - "integrity": "sha512-hUZ+ePKp2BzJL40OK1+wTVefJkKLy+mjiBT6KsejkXi1HboYoqHruFqeQZivm5PHhX3CZsHa9ZJy2LGHZrpKCQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@n8n/errors/-/errors-0.10.0.tgz", + "integrity": "sha512-Qle4gCsx7qtDRMdie3YkjuXPuo369PP32cR9Svb+INwcjLMxNzmnbJ66jFnOmLn0ntwBccUw1IecJ6axwyTgBQ==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -563,48 +371,40 @@ } }, "node_modules/@n8n/expression-runtime": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/@n8n/expression-runtime/-/expression-runtime-0.30.0.tgz", - "integrity": "sha512-mC8yM8+eh0uuN7ZVwvERf/CVUN9hOiaAKG+QtKzfXrFaYSt5AwNXktTsmAdWsLEfH1HjBBXaMc3eMF4IaifPoA==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@n8n/expression-runtime/-/expression-runtime-0.19.1.tgz", + "integrity": "sha512-tv86UUD+uBqORBOH70BQaRRXYfatPlygvhk80a4dO2SEubXzIFQxshMb1oA7loEj9ZMeYQesUotvgN57JbPQgA==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@n8n/errors": "0.13.0", - "@n8n/tournament": "1.11.0", - "isolated-vm": "^7.0.0", + "@n8n/errors": "0.10.0", + "@n8n/tournament": "1.4.0", + "isolated-vm": "^6.1.2", "jmespath": "0.16.0", "js-base64": "3.7.8", "jssha": "3.3.1", "lodash": "4.18.1", "luxon": "3.7.2", "md5": "2.3.0", - "quickjs-emscripten": "^0.32.0", "title-case": "3.0.3", "transliteration": "2.3.5", - "zod": "3.25.76" + "zod": "3.25.67" } }, "node_modules/@n8n/tournament": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@n8n/tournament/-/tournament-1.11.0.tgz", - "integrity": "sha512-cwu7t9UTTwAPDhGRjY03dRiWeR01wavkj2yWBEN/Rs0og17SvJqfgJ4z7Wgsiha8+iwg+mZ/l+Fv4uEhC5F3FQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@n8n/tournament/-/tournament-1.4.0.tgz", + "integrity": "sha512-llDdmIj3Kdfq2vX/NpLBOUU4kLjhITtIMeJmOSTApAOeAArj1D45ey4D7CFaYcYsPBw6DgF0vxifyskPDlgekw==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "ast-types": "^0.16.1", "esprima-next": "^5.8.4", "recast": "^0.22.0" - } - }, - "node_modules/@n8n/utils": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/@n8n/utils/-/utils-1.47.0.tgz", - "integrity": "sha512-/wE1a/EEZWqFdJkeEqMp971hDvI2noDSVwQDm1AHFv5oW23h5kVTeG2SySVMWcGZYgnc5bl6Mvcrt35vaSFasg==", - "dev": true, - "license": "SEE LICENSE IN LICENSE.md", - "dependencies": { - "@n8n/constants": "0.38.0", - "nanoid": "3.3.18" + }, + "engines": { + "node": ">=20.15", + "pnpm": ">=9.5" } }, "node_modules/@napi-rs/wasm-runtime": { @@ -626,247 +426,6 @@ "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", - "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz", - "integrity": "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz", - "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.220.0.tgz", - "integrity": "sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.220.0", - "import-in-the-middle": "^3.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz", - "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.10.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz", - "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.10.0", - "@opentelemetry/resources": "2.10.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz", - "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.10.0", - "@opentelemetry/resources": "2.10.0", - "@opentelemetry/sdk-trace": "2.10.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", - "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@sentry/conventions": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz", - "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@sentry/core": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.70.0.tgz", - "integrity": "sha512-ozhCTDqg89oB4XmWfAwuHshABpvT7AkRpaPnogopPfMAaI61G1t8EKCJ4W7aum8JSBonlfyjPCyW5oYZFm0KvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.16.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.70.0.tgz", - "integrity": "sha512-SPOOVxmKTVIEtqvOKkQT163e/pOwucjS7OPsCHyRs8sFR4nfBNu0EThplyqnvqd5BWBMTPH6WTBQfo+QWHV+HA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.9.1", - "@opentelemetry/instrumentation": "^0.220.0", - "@opentelemetry/sdk-trace-base": "^2.9.0", - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.70.0", - "@sentry/node-core": "10.70.0", - "@sentry/opentelemetry": "10.70.0", - "@sentry/server-utils": "10.70.0", - "import-in-the-middle": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node-core": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.70.0.tgz", - "integrity": "sha512-oPOEVVNxv5WHtckx2i06Wi9FLWyvOg/1DUeX732jZ4iqT2nupINaMH4nF4f4kSvUThFnxkFSRQxwqOxgzMKhKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.70.0", - "@sentry/opentelemetry": "10.70.0", - "import-in-the-middle": "^3.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", - "@opentelemetry/instrumentation": ">=0.57.1 <1", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@opentelemetry/core": { - "optional": true - }, - "@opentelemetry/exporter-trace-otlp-http": { - "optional": true - }, - "@opentelemetry/instrumentation": { - "optional": true - }, - "@opentelemetry/sdk-trace-base": { - "optional": true - } - } - }, - "node_modules/@sentry/opentelemetry": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.70.0.tgz", - "integrity": "sha512-UNV/2tqypcUK6FDzerAsFJn1Km/c4VZCYkUZDNbnV5S0cwAq2BYKMo4M5vovaLDBQlxA+Wk9ovbxi5wYjjl9fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.70.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" - } - }, - "node_modules/@sentry/server-utils": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.70.0.tgz", - "integrity": "sha512-rzegZjMFFgCp3o+N8+XU13rfSvz4B+f8rU0ijBGrQcHdMNyfsFDTu1UTm262JofmrV2u+s+D0u0vFTnqtOGkbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.3", - "@apm-js-collab/tracing-hooks": "^0.13.0", - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.70.0", - "meriyah": "^6.1.4" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@tybys/wasm-util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", @@ -1545,16 +1104,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", @@ -1582,16 +1131,6 @@ "node": ">=4" } }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "dev": true, - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1615,56 +1154,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axios": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", - "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/axios/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/axios/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/axios/node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -1675,16 +1164,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/brace-expansion": { "version": "5.0.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", @@ -1698,16 +1177,6 @@ "node": "20 || >=22" } }, - "node_modules/buildcheck": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", - "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -1819,13 +1288,6 @@ "node": "*" } }, - "node_modules/cjs-module-lexer": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.1.tgz", - "integrity": "sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q==", - "dev": true, - "license": "MIT" - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1891,28 +1353,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cpu-features": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", - "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "buildcheck": "~0.0.6", - "nan": "^2.19.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/crelt": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", - "integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2051,13 +1491,6 @@ "node": ">= 0.4" } }, - "node_modules/es-module-lexer": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", - "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", - "dev": true, - "license": "MIT" - }, "node_modules/es-object-atoms": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", @@ -2559,27 +1992,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -2826,21 +2238,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-in-the-middle": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.3.tgz", - "integrity": "sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "cjs-module-lexer": "^2.2.0", - "es-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3028,9 +2425,9 @@ "license": "ISC" }, "node_modules/isolated-vm": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/isolated-vm/-/isolated-vm-7.0.1.tgz", - "integrity": "sha512-nBCQ1hXEaYKYJ79wwfhaF44ITOJC30Hw0wPEsFeNsdD3hdeKK7FW1SGMQYBUA+qasvkqdOazI+iBuq0lcbN/aw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/isolated-vm/-/isolated-vm-6.2.0.tgz", + "integrity": "sha512-UuSlxSHWt2QuJ5WvBhzlIJx2VVZN/a44SqBbEZFKNdvuSyhOvhmyDo8SQ+njVbhnh/njoL/aW0bUTiFYlpweGQ==", "dev": true, "hasInstallScript": true, "license": "ISC", @@ -3038,7 +2435,7 @@ "node-gyp-build": "^4.8.4" }, "engines": { - "node": ">=24.0.0" + "node": ">=22.0.0" } }, "node_modules/jmespath": { @@ -3088,13 +2485,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3203,16 +2593,6 @@ "node": ">=12" } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3235,16 +2615,6 @@ "is-buffer": "~1.1.6" } }, - "node_modules/meriyah": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.1.4.tgz", - "integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3284,13 +2654,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/module-details-from-path": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", - "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", - "dev": true, - "license": "MIT" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3299,42 +2662,34 @@ "license": "MIT" }, "node_modules/n8n-workflow": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-2.39.1.tgz", - "integrity": "sha512-nN6Wctmwr8O/e1mxmjXA58PbAtbmHQf0tYFHExRW4ZvmgwpowJ/VZBxEaCia2HwtzippmdfCj8ckTQbQnibvRw==", + "version": "2.28.4", + "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-2.28.4.tgz", + "integrity": "sha512-wNWCEvk5eBxnp5p2y/6FpMsA5t6XZVBm8HIV/u1XiUX/p91UtdBvQW0f8tTr4B79G1aeKl/l8cSroit/WFuK6A==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@codemirror/autocomplete": "6.20.0", - "@n8n/constants": "0.38.0", - "@n8n/errors": "0.13.0", - "@n8n/expression-runtime": "0.30.0", - "@n8n/tournament": "1.11.0", - "@n8n/utils": "1.47.0", - "@sentry/core": "10.70.0", - "@sentry/node": "10.70.0", + "@n8n/errors": "0.10.0", + "@n8n/expression-runtime": "0.19.1", + "@n8n/tournament": "1.4.0", "ast-types": "0.16.1", - "axios": "1.18.0", "callsites": "3.1.0", "esprima-next": "5.8.4", "form-data": "4.0.6", "jmespath": "0.16.0", "js-base64": "3.7.8", - "json-schema": "0.4.0", "jsonrepair": "3.13.2", "jssha": "3.3.1", "lodash": "4.18.1", "luxon": "3.7.2", "md5": "2.3.0", "recast": "0.22.0", - "ssh2": "1.15.0", "title-case": "3.0.3", "transliteration": "2.3.5", "uuid": "11.1.1", "xml2js": "0.6.2" }, "peerDependencies": { - "zod": "3.25.76" + "zod": "3.25.67" } }, "node_modules/n8n-workflow/node_modules/uuid": { @@ -3351,33 +2706,6 @@ "uuid": "dist/esm/bin/uuid" } }, - "node_modules/nan": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", - "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/napi-postinstall": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", @@ -3622,33 +2950,6 @@ "node": ">=6" } }, - "node_modules/quickjs-emscripten": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/quickjs-emscripten/-/quickjs-emscripten-0.32.0.tgz", - "integrity": "sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-wasmfile-debug-asyncify": "0.32.0", - "@jitl/quickjs-wasmfile-debug-sync": "0.32.0", - "@jitl/quickjs-wasmfile-release-asyncify": "0.32.0", - "@jitl/quickjs-wasmfile-release-sync": "0.32.0", - "quickjs-emscripten-core": "0.32.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/quickjs-emscripten-core": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/quickjs-emscripten-core/-/quickjs-emscripten-core-0.32.0.tgz", - "integrity": "sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, "node_modules/recast": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/recast/-/recast-0.22.0.tgz", @@ -3689,20 +2990,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3741,13 +3028,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, "node_modules/sax": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", @@ -3758,13 +3038,6 @@ "node": ">=11.0.0" } }, - "node_modules/semifies": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semifies/-/semifies-1.0.0.tgz", - "integrity": "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -3841,24 +3114,6 @@ "node": ">=0.10.0" } }, - "node_modules/ssh2": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz", - "integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "asn1": "^0.2.6", - "bcrypt-pbkdf": "^1.0.2" - }, - "engines": { - "node": ">=10.16.0" - }, - "optionalDependencies": { - "cpu-features": "~0.0.9", - "nan": "^2.18.0" - } - }, "node_modules/stable-hash-x": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", @@ -3910,13 +3165,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/style-mod": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", - "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -4012,13 +3260,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "license": "Unlicense" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4142,13 +3383,6 @@ "which-typed-array": "^1.1.2" } }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", - "dev": true, - "license": "MIT" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4292,9 +3526,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "3.25.67", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", + "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", "dev": true, "license": "MIT", "funding": { diff --git a/n8n/package.json b/n8n/package.json index d2d3b6e9..04e57256 100644 --- a/n8n/package.json +++ b/n8n/package.json @@ -53,11 +53,11 @@ "eslint-import-resolver-typescript": "4.4.5", "eslint-plugin-import-x": "4.17.1", "eslint-plugin-n8n-nodes-base": "2.0.0", - "n8n-workflow": "2.39.1", + "n8n-workflow": "2.28.4", "prettier": "3.9.6", "typescript": "5.9.2", "typescript-eslint": "8.70.0", - "zod": "3.25.76" + "zod": "3.25.67" }, "peerDependencies": { "n8n-workflow": "*" diff --git a/n8n/test/release.test.cjs b/n8n/test/release.test.cjs index 8c6a9cc6..444a9355 100644 --- a/n8n/test/release.test.cjs +++ b/n8n/test/release.test.cjs @@ -19,11 +19,11 @@ test('0.1.4 release metadata is locked to the audited direct toolchain', () => { 'eslint-import-resolver-typescript': '4.4.5', 'eslint-plugin-import-x': '4.17.1', 'eslint-plugin-n8n-nodes-base': '2.0.0', - 'n8n-workflow': '2.39.1', + 'n8n-workflow': '2.28.4', prettier: '3.9.6', typescript: '5.9.2', 'typescript-eslint': '8.70.0', - zod: '3.25.76', + zod: '3.25.67', }; assert.equal(pkg.version, '0.1.4'); From c0679c56deb33035197515acd681786cc5617f86 Mon Sep 17 00:00:00 2001 From: rhein1 Date: Thu, 1 Oct 2026 19:05:48 -0400 Subject: [PATCH 09/12] fix(n8n): avoid Windows audit subprocess path parsing --- n8n/scripts/audit-dev-dependencies.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/n8n/scripts/audit-dev-dependencies.cjs b/n8n/scripts/audit-dev-dependencies.cjs index c3a422b7..23b651a6 100644 --- a/n8n/scripts/audit-dev-dependencies.cjs +++ b/n8n/scripts/audit-dev-dependencies.cjs @@ -29,7 +29,7 @@ function main() { : ['audit', '--json', '--audit-level=low']; const result = spawnSync(command, args, { encoding: 'utf8', - shell: process.platform === 'win32', + shell: !npmCli && process.platform === 'win32', }); if (result.error) throw result.error; if (![0, 1].includes(result.status)) { From a95729e8f944b5cb3b187a16db015ce71f86c11e Mon Sep 17 00:00:00 2001 From: rhein1 Date: Thu, 1 Oct 2026 19:20:40 -0400 Subject: [PATCH 10/12] chore: renew bounded MatrAIx inventory review --- integrations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations.json b/integrations.json index f3cb13fc..c876a69f 100644 --- a/integrations.json +++ b/integrations.json @@ -443,7 +443,7 @@ "reason": "The exact upstream source lock, closed representation contract, no-spend target policy, and deterministic fixture replay pass, but no live MatrAIx provider run or independent external compatibility review has occurred.", "owner": "repository-maintainers", "tracking_ref": "https://github.com/rhein1/agoragentic-integrations/pull/320", - "review_by": "2026-09-30", + "review_by": "2026-10-08", "published": false, "external_compatibility_verified": false, "ready_for_manifest": false, From a1283ee9a0740f9314220d84d5694c4b450911b2 Mon Sep 17 00:00:00 2001 From: rhein1 Date: Thu, 1 Oct 2026 20:43:08 -0400 Subject: [PATCH 11/12] fix(deps): align hosted MCP bundle with reviewed dependency closure --- risk-fork-hosted-mcp/THIRD_PARTY_NOTICES.txt | 2 +- risk-fork-hosted-mcp/dist/runtime/index.mjs | 13 ++--- risk-fork-hosted-mcp/integrity-manifest.json | 16 +++--- risk-fork-hosted-mcp/package-lock.json | 12 ++--- .../test/package-artifact.test.mjs | 50 +++++++++++++++++++ 5 files changed, 72 insertions(+), 21 deletions(-) diff --git a/risk-fork-hosted-mcp/THIRD_PARTY_NOTICES.txt b/risk-fork-hosted-mcp/THIRD_PARTY_NOTICES.txt index a6870c9c..5fd1bd1b 100644 --- a/risk-fork-hosted-mcp/THIRD_PARTY_NOTICES.txt +++ b/risk-fork-hosted-mcp/THIRD_PARTY_NOTICES.txt @@ -548,7 +548,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ======================================================================== -fast-uri@3.1.7 +fast-uri@3.1.8 Declared license: BSD-3-Clause Notice source: risk-fork-hosted-mcp/node_modules/fast-uri/LICENSE Notice source method: standalone_license_file diff --git a/risk-fork-hosted-mcp/dist/runtime/index.mjs b/risk-fork-hosted-mcp/dist/runtime/index.mjs index b9528971..c87447a0 100644 --- a/risk-fork-hosted-mcp/dist/runtime/index.mjs +++ b/risk-fork-hosted-mcp/dist/runtime/index.mjs @@ -48050,13 +48050,14 @@ var require_fast_uri = __commonJS({ if (!malformedIPLiteral) { malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP2); } - if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) { - if (uri.indexOf("%") !== -1) { - if (parsed.host !== void 0 && !malformedIPLiteral) { - const host = isIP2 ? parsed.host : normalizePercentEncoding(parsed.host, true); - parsed.host = reescapeHostDelimiters(host, isIP2); - } + if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) { + let host = isIP2 ? parsed.host : normalizePercentEncoding(parsed.host, true); + if (!isIP2) { + host = normalizePercentEncoding(host.toLowerCase()); } + parsed.host = reescapeHostDelimiters(host, isIP2); + } + if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) { if (parsed.path) { parsed.path = normalizePathEncoding(parsed.path); } diff --git a/risk-fork-hosted-mcp/integrity-manifest.json b/risk-fork-hosted-mcp/integrity-manifest.json index 2c9f49cb..27745032 100644 --- a/risk-fork-hosted-mcp/integrity-manifest.json +++ b/risk-fork-hosted-mcp/integrity-manifest.json @@ -1191,8 +1191,8 @@ { "path": "risk-fork-hosted-mcp/node_modules/fast-uri/index.js", "source": "workspace_dependency", - "bytes": 22061, - "sha256": "sha256:e82d3e55d954483e4927f9740bc3113a24d19ef5a73c7962d8a93c499fc982ff" + "bytes": 22264, + "sha256": "sha256:62876e5094b43ba2ba90374613dab4576fd337063dad20b4b07cc5e3a7e2303b" }, { "path": "risk-fork-hosted-mcp/node_modules/fast-uri/lib/schemes.js", @@ -1210,7 +1210,7 @@ "path": "risk-fork-hosted-mcp/node_modules/fast-uri/package.json", "source": "workspace_dependency", "bytes": 1983, - "sha256": "sha256:e634f3848a2c6c7e1cdc28b119b16e072e10dada8a3ca3f175a2372d0dd83360" + "sha256": "sha256:30cad05aad3d3596513706451dbfc7e0422f9c1d00f2903ecc34580327d54bc7" }, { "path": "risk-fork-hosted-mcp/node_modules/json-schema-traverse/LICENSE", @@ -2380,7 +2380,7 @@ "third_party_notices": { "path": "THIRD_PARTY_NOTICES.txt", "bytes": 56885, - "sha256": "sha256:88c6a3301b747237128435917d181c66aa2568234f40947531a0b0de0d3477fd", + "sha256": "sha256:84761a19dbe9d442127b5dd3d83f35831a756f888589ae6fc5132b31af733855", "sources": [ { "package": "@modelcontextprotocol/core", @@ -2439,7 +2439,7 @@ }, { "package": "fast-uri", - "version": "3.1.7", + "version": "3.1.8", "declared_license": "BSD-3-Clause", "method": "standalone_license_file", "path": "risk-fork-hosted-mcp/node_modules/fast-uri/LICENSE", @@ -2628,8 +2628,8 @@ }, "artifact": { "path": "dist/runtime/index.mjs", - "bytes": 3111081, - "sha256": "sha256:c5e9814e13b84d5489527adeaac27e4d47363acec706cef3bcbe17f53e06dcc6" + "bytes": 3111138, + "sha256": "sha256:db99484dfa89944be8edaf8e5e52c0b0ce6ca620fc5455c509d245ae2ffd3a09" }, "packaged_assets": [ { @@ -2653,7 +2653,7 @@ { "path": "THIRD_PARTY_NOTICES.txt", "bytes": 56885, - "sha256": "sha256:88c6a3301b747237128435917d181c66aa2568234f40947531a0b0de0d3477fd" + "sha256": "sha256:84761a19dbe9d442127b5dd3d83f35831a756f888589ae6fc5132b31af733855" }, { "path": "e2b-context/risk-fork/e2b-template/bin/boot-guard.mjs", diff --git a/risk-fork-hosted-mcp/package-lock.json b/risk-fork-hosted-mcp/package-lock.json index 1d61d55d..e5c7d962 100644 --- a/risk-fork-hosted-mcp/package-lock.json +++ b/risk-fork-hosted-mcp/package-lock.json @@ -1049,9 +1049,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -1252,9 +1252,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz", - "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.3.tgz", + "integrity": "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==", "dev": true, "license": "MIT", "engines": { diff --git a/risk-fork-hosted-mcp/test/package-artifact.test.mjs b/risk-fork-hosted-mcp/test/package-artifact.test.mjs index 3179c584..32228a2c 100644 --- a/risk-fork-hosted-mcp/test/package-artifact.test.mjs +++ b/risk-fork-hosted-mcp/test/package-artifact.test.mjs @@ -44,6 +44,7 @@ const HOSTED_FIXTURE_COPIES = Object.freeze([ ['transaction-assurance/src', 'transaction-assurance/src'], ['risk-fork-hosted-mcp/integrity-manifest.json', 'risk-fork-hosted-mcp/integrity-manifest.json'], ['risk-fork-hosted-mcp/node_modules', 'risk-fork-hosted-mcp/node_modules'], + ['risk-fork-hosted-mcp/package-lock.json', 'risk-fork-hosted-mcp/package-lock.json'], ['risk-fork-hosted-mcp/package.json', 'risk-fork-hosted-mcp/package.json'], ['risk-fork-hosted-mcp/scripts', 'risk-fork-hosted-mcp/scripts'], ['risk-fork-hosted-mcp/src', 'risk-fork-hosted-mcp/src'], @@ -249,6 +250,34 @@ async function assertGeneratedHostedOutputsUnchanged(expectedSnapshot) { } } +async function assertHostedFastUriClosure(root) { + const hostedRoot = path.join(root, 'risk-fork-hosted-mcp'); + const lock = JSON.parse(await readFile(path.join(hostedRoot, 'package-lock.json'), 'utf8')); + const mcpPackage = JSON.parse(await readFile(path.join(root, 'mcp', 'package.json'), 'utf8')); + const advertisedVersion = mcpPackage.overrides?.['fast-uri']; + assert.equal(typeof advertisedVersion, 'string'); + + const lockRecord = lock.packages?.['node_modules/fast-uri']; + assert.equal(lockRecord?.version, advertisedVersion, 'hosted lock must satisfy the MCP fast-uri override'); + const installedPackagePath = path.join(hostedRoot, 'node_modules', 'fast-uri', 'package.json'); + const installedPackageBytes = await readFile(installedPackagePath); + const installedPackage = JSON.parse(installedPackageBytes); + assert.equal(installedPackage.version, lockRecord.version, 'installed fast-uri must satisfy the hosted lock'); + + const manifest = JSON.parse(await readFile(path.join(hostedRoot, 'integrity-manifest.json'), 'utf8')); + const notice = manifest.third_party_notices.sources.find((source) => source.package === 'fast-uri'); + assert.equal(notice?.version, installedPackage.version, 'attested fast-uri notice must match the installed package'); + const inputPath = 'risk-fork-hosted-mcp/node_modules/fast-uri/package.json'; + const input = manifest.inputs.find((record) => record.path === inputPath); + assert.ok(input, 'attested fast-uri package metadata must be an integrity input'); + assert.equal(input.bytes, installedPackageBytes.byteLength); + assert.equal(input.sha256, sha256(installedPackageBytes)); + + const bundle = (await readFile(path.join(hostedRoot, 'dist', 'runtime', 'index.mjs'))).toString('utf8'); + const advertisedEntry = `${JSON.stringify('fast-uri')}: ${JSON.stringify(advertisedVersion)}`; + assert.ok(bundle.includes(advertisedEntry), 'bundle must retain the MCP fast-uri advertisement'); +} + const committedGeneratedOutputSnapshot = await snapshotGeneratedHostedOutputs(packageRoot); test('package contract is private, exact-version, and has no mandatory runtime dependencies', async () => { @@ -539,6 +568,27 @@ test('build is deterministic and records exact source and artifact integrity', a run(process.execPath, ['scripts/verify-integrity.mjs', '--source']); }); +test('hosted lock, bundle advertisement, and attested fast-uri records stay aligned', async () => { + const fixture = await createHostedFixture('risk-fork-hosted-dependency-closure-'); + try { + run(process.execPath, ['scripts/build.mjs'], { cwd: fixture.packageRoot }); + await assertHostedFastUriClosure(fixture.repositoryRoot); + + const lockPath = path.join(fixture.packageRoot, 'package-lock.json'); + const staleLock = JSON.parse(await readFile(lockPath, 'utf8')); + staleLock.packages['node_modules/fast-uri'].version = '3.1.7'; + staleLock.packages['node_modules/fast-uri'].resolved = 'https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz'; + staleLock.packages['node_modules/fast-uri'].integrity = 'sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg=='; + await writeFile(lockPath, `${JSON.stringify(staleLock, null, 2)}\n`, 'utf8'); + await assert.rejects( + assertHostedFastUriClosure(fixture.repositoryRoot), + /hosted lock must satisfy the MCP fast-uri override/, + ); + } finally { + await cleanupTemporary(fixture.temporary); + } +}); + test('standalone license discovery preserves actual casing and fails closed on ambiguity or non-files', async () => { const entry = (name, type = 'file') => ({ name, From 7c9bc9083401ae079821c286709f2e70badfa2bb Mon Sep 17 00:00:00 2001 From: rhein1 Date: Wed, 14 Oct 2026 16:46:43 -0400 Subject: [PATCH 12/12] fix(openshell): reject FIFO preview inputs without blocking --- agent-os/openshell/preview.mjs | 4 ++-- agent-os/openshell/scaffold.test.mjs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/agent-os/openshell/preview.mjs b/agent-os/openshell/preview.mjs index 0cefda40..8413ac57 100644 --- a/agent-os/openshell/preview.mjs +++ b/agent-os/openshell/preview.mjs @@ -1,4 +1,4 @@ -import { openSync, readSync, fstatSync, closeSync } from 'node:fs'; +import { constants, openSync, readSync, fstatSync, closeSync } from 'node:fs'; import { buildOpenShellPlan, openShellReadiness } from './scaffold.mjs'; // Offline only. No installer, SDK import, subprocess, network, or apply command. @@ -8,7 +8,7 @@ try { console.log(JSON.stringify(openShellReadiness(), null, 2)); } else { if (args.length !== 1 || args[0].startsWith('-')) throw new Error('usage: node preview.mjs REQUEST.json | --readiness'); - const fd = openSync(args[0], 'r'); + const fd = openSync(args[0], constants.O_RDONLY | (constants.O_NONBLOCK ?? 0)); let text; try { if (!fstatSync(fd).isFile()) throw new Error('request_must_be_regular_file'); diff --git a/agent-os/openshell/scaffold.test.mjs b/agent-os/openshell/scaffold.test.mjs index 40176ca7..96ca1402 100644 --- a/agent-os/openshell/scaffold.test.mjs +++ b/agent-os/openshell/scaffold.test.mjs @@ -203,3 +203,16 @@ test('CLI is offline, reads fixture, denies apply, limits input, and does not ec assert.match(spawnSync(process.execPath, [cli, bad], { encoding: 'utf8' }).stderr, /request_too_large/); } finally { rmSync(dir, { recursive: true, force: true }); } }); + +test('CLI rejects a FIFO without waiting for a writer', { skip: process.platform === 'win32' }, () => { + const cli = fileURLToPath(new URL('./preview.mjs', import.meta.url)); + const dir = mkdtempSync(join(tmpdir(), 'openshell-fifo-')); + try { + const fifo = join(dir, 'request.fifo'); + execFileSync('mkfifo', [fifo]); + const result = spawnSync(process.execPath, [cli, fifo], { encoding: 'utf8', timeout: 5000 }); + assert.equal(result.error, undefined, 'CLI must reject promptly rather than time out waiting for a writer'); + assert.equal(result.status, 2); + assert.match(result.stderr, /request_must_be_regular_file/); + } finally { rmSync(dir, { recursive: true, force: true }); } +});