Skip to content

M6 Phase D — run acp-adapter under node --import tsx (FDQ-70) - #30

Closed
Mayakovsky wants to merge 1 commit into
mainfrom
m6-phase-d-tsx-runtime
Closed

M6 Phase D — run acp-adapter under node --import tsx (FDQ-70)#30
Mayakovsky wants to merge 1 commit into
mainfrom
m6-phase-d-tsx-runtime

Conversation

@Mayakovsky

Copy link
Copy Markdown
Owner

M6 Phase D — FDQ-70: run the ACP adapter under node --import tsx

Discovered live in the Phase D cutover window. @virtuals-protocol/acp-node-v2@0.0.4 is "type":"module" with extensionless relative imports (dist/index.js: export * from "./acpAgent", …). bun (the ElizaOS runtime) tolerates these; strict Node ESM rejects them (Cannot find module '.../dist/acpAgent'). The adapter was designed as a standalone node process, so it could not load the SDK under plain node.

Fix — run under the tsx loader, which resolves extensionless ESM:

  • infra/systemd/grey-acp-adapter.serviceExecStart=/usr/bin/node --import tsx adapters/acp-adapter/dist/main.js.
  • adapters/acp-adapter/package.jsontsx moves from devDependencies to dependencies (now a runtime requirement).

(bun isn't on the grey user's PATH; node --experimental-specifier-resolution=node was removed in Node ≥17 — tsx is the clean path, and it was already an adapter dep-of-record.)

Validated live: the running adapter uses this ExecStart and completed proof job #70220 end-to-end (accept → shared handler → submit → nudge → complete → escrow to 0xa966…). Build isolation intact — typecheck + build green, tier-1 offline smoke green (SDK still out of the tsc type graph via the variable-specifier dynamic import).

⚠️ Deploy note: the box's /etc/systemd/system/grey-acp-adapter.service already carries this fix (applied in-window; survives reboot). Do not re-copy the pre-fix committed unit over it until this merges. Once merged, infra/ matches the box.

Alternative for later hardening (not this PR): a build step that bundles/rewrites the SDK's extensionless imports so the adapter can run under bare node.

🤖 Generated with Claude Code

The @virtuals-protocol/acp-node-v2 SDK is ESM with extensionless relative imports
(bun-authored: dist/index.js does `export * from "./acpAgent"`). bun tolerates these;
strict Node ESM rejects them ("Cannot find module .../dist/acpAgent"). The adapter,
designed as a standalone `node` process, therefore could not load the SDK. The tsx
loader resolves extensionless ESM, so the unit runs under `node --import tsx`.

- infra/systemd/grey-acp-adapter.service: ExecStart → `/usr/bin/node --import tsx …`.
- adapters/acp-adapter/package.json: tsx devDependency → dependency (runtime requirement).

Validated live in the Phase D cutover window (job #70220 accepted → delivered →
completed under this runtime). Build isolation intact: typecheck + build green, tier-1
offline smoke green (SDK still out of the tsc type graph via the variable-specifier
dynamic import).

NOTE: the running box already carries this ExecStart (window fix). Once merged, the
committed unit matches the box — until then, do not re-copy the old unit over it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Mayakovsky

Copy link
Copy Markdown
Owner Author

Superseded by #31. #30 shipped a production node --import tsx loader as a workaround; Forces ruled to fix the root cause instead. #31 (branched fresh off main) replaces it with a committed pnpm patch that appends .js to the SDK's 52 extensionless relative imports — plain Node at runtime, tsx stays a devDependency — and folds in the FDQ-70b setBudget idempotency guard. Closing without merge.

@Mayakovsky Mayakovsky closed this Jul 24, 2026
@Mayakovsky
Mayakovsky deleted the m6-phase-d-tsx-runtime branch August 11, 2026 20:07
Mayakovsky added a commit that referenced this pull request Aug 12, 2026
…ENT_INSTANCE (BION-DIRECTIVE-38)

Once deploy() runs, BASE_MECH_AGENT_INSTANCE is the sole (threshold=1)
signer of the service's real Safe multisig, and every delivered mech
response requires deliverToMarketplace, gated onlyOperator (operator
= the multisig, read live from ServiceRegistry). Nothing in this
codebase could produce that call. This builds the capability without
turning it on.

- agentInstanceSigner.ts: isolated hot-key loader (mirrors grey-sweeper's
  wallet.ts pattern), new invariant #30 confines the key to this one file.
- mechAbi.ts / safeAbi.ts: real deliverToMarketplace + Safe v1.3.0
  execTransaction ABIs, traced from OlasMech.sol/Mech.sol/GnosisSafe.sol
  source (valory-xyz/ai-registry-mech, safe-global/safe-contracts@v1.3.0),
  not guessed.
- safeDeliveryClient.ts: builds deliverToMarketplace calldata, reads the
  multisig's real getTransactionHash (ground truth, not hand-rolled
  EIP-712), signs via the plain-ECDSA (non-eth_sign) branch
  checkNSignatures expects, wraps in execTransaction with a real
  estimated safeTxGas so a failed inner call surfaces as success:false
  instead of reverting the whole tx.
- mechAdapter.ts: new deliverSigned() step, same observeOnly-gated
  shape as every other registration step. Not wired to any automatic
  trigger — no task-intake -> deliver flow exists yet.

Real multisig (0x5587...5872) confirmed live: Safe v1.3.0, sole owner
= BASE_MECH_AGENT_INSTANCE, threshold=1. Storage layout (owners
mapping slot 2, mech's maxDeliveryRate slot 1) empirically verified
against real chain state, not derived from source order alone.

Fork-proven (GREY_MECH_ANVIL=1, safeDeliveryClient.anvil.test.ts)
against a real Base mainnet fork: owner substituted via storage
override (real key never leaves Forces), a real pending request
created via the real MechMarketplace.request(), delivered end-to-end
through a real signed execTransaction, independently verified via
numTotalDeliveries incrementing. Grey's real mech's maxDeliveryRate
is an unusually large real value (~1.14e77 wei, confirmed on two
independent RPCs) that also hit a genuine anvil/revm tooling wall for
value transfers above ~1e35 wei (bisected, not assumed) -- worked
around via the same storage-override technique for the test run only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant