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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions VENDORED.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ never a convenience.
## Ledger

Pinned to [faremeter/interchange](https://github.com/faremeter/interchange)
@ `79adc433` (origin/main).
@ `5453d0b0` (origin/main, v0.4.0).

| Vendored path | What it is | Owner | Kill date |
| ------------------------------ | ------------------------------------------------------------------------ | ------ | ---------- |
Expand All @@ -44,8 +44,9 @@ Pinned to [faremeter/interchange](https://github.com/faremeter/interchange)
| `vendor/intx/workflow-deploy` | `@intx/workflow-deploy` at a newer commit than npm has published | maintainers | 2026-11-03 |
| `vendor/intx/workflow-host` | `@intx/workflow-host`, with a local step-grants-collapse delta and a temporary delta omitting an empty inbound `Subject` header instead of passing it through empty (kill condition: upstream tolerates an empty Subject, INTR-577) | maintainers | 2026-11-03 |

npm's published `0.3.0` predates this pin for every row above, so each is
re-vendored at the same commit rather than mixed pins; the root
Every row above is re-vendored at the same commit rather than mixed pins
(npm's `0.4.0` is this commit; rows with no local delta are un-vendoring
candidates); the root
`package.json` `overrides` point each vendored name at `workspace:*` so
their own `@intx/*` dependencies resolve onto the vendored copies too.
Local deltas are restated in each package's own `VENDORED-FROM` file.
Expand Down
10 changes: 5 additions & 5 deletions agents/myra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@
"@corbits/deferred-tools": "workspace:*",
"@corbits/mcp": "github:corbitsdev/corbits-mcp#9df1ab81d2f50d798bca2d2f44eb4759c92fac0e",
"@corbits/memory": "github:corbitsdev/corbits-memory#8be608becc749fa0795a899826277823dca6db3e",
"@intx/tools-mail": "0.3.0",
"@intx/tools-posix": "0.3.0"
"@intx/tools-mail": "0.4.0",
"@intx/tools-posix": "0.4.0"
},
"devDependencies": {
"@types/bun": "catalog:",
"typescript": "catalog:"
},
"peerDependencies": {
"@intx/agent": "0.3.0",
"@intx/types": "0.3.0",
"@intx/workflow": "0.3.0"
"@intx/agent": "0.4.0",
"@intx/types": "0.4.0",
"@intx/workflow": "0.4.0"
},
"corbits": {
"workflow": {
Expand Down
8 changes: 4 additions & 4 deletions apps/hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@
"@corbits/webhooks": "github:corbitsdev/webhooks#3f4f83147fd826d3aec5bcac7ef4b0ae7f75c00d",
"@corbits/workflows": "workspace:*",
"@corbits/xai-provider": "github:corbitsdev/corbits-xai-provider#9f2d4bac40ea075df092fef807404a13c638cf14",
"@intx/authz": "0.3.0",
"@intx/crypto": "0.3.0",
"@intx/authz": "0.4.0",
"@intx/crypto": "0.4.0",
"@intx/db": "workspace:*",
"@intx/hub-api": "workspace:*",
"@intx/hub-common": "0.3.0",
"@intx/hub-common": "0.4.0",
"@intx/hub-sessions": "workspace:*",
"@intx/log": "0.3.0",
"@intx/log": "0.4.0",
"@intx/mime": "workspace:*",
"@intx/types": "workspace:*",
"@intx/workflow": "workspace:*",
Expand Down
20 changes: 10 additions & 10 deletions apps/sidecar/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@workbench/sidecar",
"version": "0.3.0",
"version": "0.4.0",
"private": true,
"description": "Host process running a fleet of hub-orchestrated agent runtimes in one Bun process",
"license": "SEE LICENSE IN ../../LICENSE.md",
Expand All @@ -18,20 +18,20 @@
"@corbits/credential-header": "github:corbitsdev/corbits-credential-header#eee7b7eb79755fd1ddc3eb2b9a57c4e50982e293",
"@corbits/credential-mcp": "github:corbitsdev/corbits-credential-mcp#fb9ec3a6754348cd33c388c70c0830b8844c83de",
"@intx/agent": "workspace:*",
"@intx/authz": "0.3.0",
"@intx/crypto": "0.3.0",
"@intx/authz": "0.4.0",
"@intx/crypto": "0.4.0",
"@intx/harness": "workspace:*",
"@intx/hub-agent": "workspace:*",
"@intx/hub-sessions": "workspace:*",
"@intx/inference": "workspace:*",
"@intx/log": "0.3.0",
"@intx/log": "0.4.0",
"@intx/mail-memory": "workspace:*",
"@intx/pack-transport": "0.3.0",
"@intx/storage-isogit": "0.3.0",
"@intx/tool-packaging": "0.3.0",
"@intx/tools-lsp": "0.3.0",
"@intx/tools-mail": "0.3.0",
"@intx/tools-posix": "0.3.0",
"@intx/pack-transport": "0.4.0",
"@intx/storage-isogit": "0.4.0",
"@intx/tool-packaging": "0.4.0",
"@intx/tools-lsp": "0.4.0",
"@intx/tools-mail": "0.4.0",
"@intx/tools-posix": "0.4.0",
"@intx/types": "workspace:*",
"@intx/workflow": "workspace:*",
"@intx/workflow-deploy": "workspace:*",
Expand Down
73 changes: 66 additions & 7 deletions apps/sidecar/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
// Boundary readers for the sidecar's env-config inputs, centralized so
// validation rules live in one place at the boot edge.
// Boundary readers for the sidecar's env-config inputs.
//
// Centralizing the readers here keeps env-validation rules in one
// place — `SIDECAR_CACHE_MAX_BYTES` must be a positive finite
// number, with a 10 GiB default — even though the sidecar only
// invokes the reader at one site today (the orchestrator boot in
// `apps/sidecar/src/index.ts`). The harness builder receives the
// resolved value through `DefaultHarnessBuilderConfig` rather than
// re-reading env, so the boundary stays at the boot edge.

import { AdapterManifest } from "@intx/inference";
import { hexDecode } from "@intx/types";

const DEFAULT_CACHE_MAX_BYTES = 10 * 1024 * 1024 * 1024;

// Separate from the hub's CREDENTIAL_ENCRYPTION_KEY, so a sidecar
// disk-plus-key compromise can't also decrypt the hub's database.
// The operator key the sidecar seals its at-rest credential material under
// (inference-source apiKeys, and the tool credential material store). A
// SEPARATE key from the hub's `CREDENTIAL_ENCRYPTION_KEY`: the sidecar seals a
// local store on a host it does not control as tightly, so a sidecar
// disk-plus-key compromise must not also decrypt the hub's credential
// database, and the two keys rotate independently. Required at boot: a missing
// key fails loudly here rather than letting the sidecar run and persist secrets
// it cannot protect. 32 bytes, hex -- e.g. `openssl rand -hex 32`, the same
// shape as the hub's key.
export function readCredentialEncryptionKey(): Uint8Array {
const raw = process.env["SIDECAR_CREDENTIAL_ENCRYPTION_KEY"];
if (raw === undefined || raw.trim() === "") {
Expand All @@ -26,7 +40,11 @@ export function readCacheMaxBytes(): number {
return n;
}

// Mirrors the hub's DEFAULT_HUB_MAX_TARBALL_BYTES.
// Mirrors the hub's `DEFAULT_HUB_MAX_TARBALL_BYTES`. The sidecar's
// HTTP-registry fetcher enforces this cap on every upstream registry
// tarball pull. An operator pointing the sidecar at a third-party
// registry whose curated tarballs run larger should raise the cap
// explicitly via `SIDECAR_REGISTRY_MAX_TARBALL_BYTES`.
const DEFAULT_REGISTRY_MAX_TARBALL_BYTES = 10 * 1024 * 1024;

export function readRegistryMaxTarballBytes(): number {
Expand All @@ -39,8 +57,49 @@ export function readRegistryMaxTarballBytes(): number {
return n;
}

// Trusted operator input only: import(specifier) is arbitrary code
// execution, so a specifier must never originate from deploy or tenant data.
// Hub-link reconnect backoff, in milliseconds. Absent or whitespace-only
// yields `undefined`, which leaves the delay to the hub link's
// `DEFAULT_RECONNECT_DELAY_MS`; the constant therefore lives at one site
// instead of being restated here. A present value must be a positive
// integer, and anything else throws, so a typo fails the boot rather than
// silently reverting to the link's default: `Number` rejects the whole
// string rather than reading a leading numeric prefix, so a unit suffix
// like "3000ms" throws instead of arriving as 3000, and `Number.isInteger`
// rejects a fractional value rather than truncating it.
//
// Production never sets this. The deploy-flow test harness sets a short
// value so reconnect-survival tests that do not assert the delay itself
// (they assert the reconnect's recovery semantics, not the backoff
// duration) do not burn 3s of wall clock per dropped link.
//
// Takes the raw string rather than reading `process.env` itself: the
// sidecar's only caller is its module-top-level boot, which cannot be
// imported without booting a sidecar, so the validation rule is reachable
// from a test only as a pure function of its input.
export function parseReconnectDelayMs(raw: string | undefined): number | undefined {
if (raw === undefined || raw.trim() === "") return undefined;
const n = Number(raw);
if (!Number.isInteger(n) || n <= 0) {
throw new Error(
`SIDECAR_RECONNECT_DELAY_MS must be a positive integer (milliseconds), got ${raw}`,
);
}
return n;
}

// Operator-configured custom inference adapter manifest. The value is
// TRUSTED operator input read only from this process's environment;
// `import(specifier)` is arbitrary code execution, so a specifier must
// never originate from deploy or tenant data — the agent deploy tree
// carries only a `provider` key, never a specifier. The shape is
// arktype-validated here (and re-validated at the workflow-child spawn
// boundary as defense in depth), but the value itself is trusted.
//
// Unset or whitespace-only means "no custom adapters", a valid
// configuration — the sidecar then resolves only the statically-linked
// built-ins. A present-but-malformed value fails loud at boot: an
// opaque `JSON.parse` SyntaxError is rethrown naming the env key, and
// the parsed value is asserted against `AdapterManifest`.
export function readAdapterManifest(): AdapterManifest {
const raw = process.env["SIDECAR_ADAPTER_MANIFEST"];
if (raw === undefined || raw.trim() === "") return [];
Expand Down
Loading
Loading