Both files carry @ts-nocheck, so there's no type-inference benefit
here (that was vector.ts's problem, fixed separately). This is purely
about making the runtime JSON Schema serializer uniform across all 23
tools: the MCP SDK routes Zod v3 schemas through the vendored
zod-to-json-schema and Zod v4 schemas through Zod's own native
toJSONSchema, and the two disagree on `additionalProperties` for
object schemas. Converting vector.ts alone left the surface split 11
"open" (v4, additionalProperties absent) / 12 "closed" (v3,
additionalProperties: false).
Plain import swaps — neither file uses z.record, so no signature
changes were needed. Both files use z.<type>().default(...) on several
fields (agent-network.ts: limit; self-improvement.ts:
proposalOutputIndex, priority, focus); verified via the SDK's own
zod-compat safeParseAsync/objectFromShape helpers that omitting each
of those fields still resolves to its default under zod/v4, exactly as
under v3. Also verified live: a missing required argument is still
rejected (schema-validation error, not a handler crash) on one tool
from each of the three files.
Confirmed post-conversion: all 23 tools report additionalProperties
consistently (0 false / 23 absent — uniform), typecheck stays clean
(these files are excluded from it), and build + unit tests pass.
The SDK's ZodRawShapeCompat degrades on Zod 3 schemas, producing TS2589
and a knock-on TS2769. Importing zod/v4 (already shipped inside zod
3.25.76) clears all 8 type errors with no dependency change. Tool
names and count (23) are unchanged.
This does change the advertised JSON Schema, though. The SDK routes
Zod v3 schemas through the vendored zod-to-json-schema converter, but
Zod v4 schemas through Zod's own native toJSONSchema (see
server/zod-json-schema-compat.js). The two disagree on object schemas:
v4's converter does not emit `additionalProperties: false` where v3's
did. Confirmed live: vector_get_balance (now v4) reports
additionalProperties undefined, while vector_get_agent_profile (still
v3 at this commit) reports additionalProperties: false. Runtime
enforcement is unaffected in both cases — a missing required argument
is still rejected, and extra keys were already stripped under v3 — so
this is a wire-schema difference only, not a validation gap. The two
sibling files (agent-network.ts, self-improvement.ts) still import
plain "zod" (v3) at this commit, so the 23-tool surface is currently
split 11 "open" / 12 "closed" on additionalProperties; a follow-up
commit converts them to zod/v4 to make the surface uniform again and
adds a full-schema snapshot test so this class of regression cannot
land silently in the future.
CI runs test:smoke on every PR but the README only listed test:unit and
test:integration, so a contributor following the README wouldn't know to
run it locally.
This doc ships into a public repo (PR 1's own remit). Strip process
notes that read as an internal artifact rather than architecture:
the Decisions-table row naming who implements/reviews each PR, the
two "needs a funded testnet wallet from Filip" notes (also invites
"so it's untested?" from outside readers), and the instruction to
hold a line on PR #2, which already merged. Renumbered the Open
items list after dropping the wallet entry. The named-reviewer phrase
in section 10 falls in the same category and is genericized for the
same reason, though it wasn't separately called out.
Section 13 (residual risks) and the §12 typecheck findings are
untouched — those are load-bearing technical content, not planning
chatter.
- permissions: contents: read — this workflow only builds and tests,
it never writes to the repo; matches the convention deploy.yml
already sets.
- timeout-minutes: 15 on the job so a hung step doesn't burn runner
time indefinitely.
- concurrency group keyed on the ref so a new push cancels the
in-flight run for the same branch/PR instead of queuing behind it.
Action versions are left unpinned (still @v4) — SHA-pinning only this
workflow while deploy.yml stays on @v4 would be an inconsistency to
clean up in its own pass, not here.
Every PR in the non-custodial-split series is supposed to preserve
`tools/list` returning 23 tools with identical schemas, but nothing
enforced it — PRs 2, 6, 7 and 8 will restructure and rename tool
families, and a snapshot only means something if it's taken before
that starts. Boot the built server, call listTools(), and assert both
the count and the exact sorted name list against a checked-in
baseline captured from a live run on this branch. Failures report
which names were added/removed rather than a bare array-diff.
Reuses startServer()/stopServer() from test/setup.ts; does not call
getMnemonic(), so it needs no wallet. Wired into CI as a new step
after the unit tests. Also raises the server-start timeout in
setup.ts from 15s to 60s — the Cardano WASM import makes cold starts
slow enough that 15s was flaky on the smoke test's first CI run.
test:smoke is intentionally separate from test:unit (which does not
build) since startServer() spawns build/index.js.
The Testing section still told contributors to write a real mnemonic
to mnemonic.txt and run `npm test`, then claimed the result was full
agent-lifecycle coverage against Vector testnet. Since test:unit is
now the default `test` script (five wallet-free CBOR assertions), that
instruction produced a false green and silently skipped the coverage
it advertised. Document test:unit and test:integration separately and
drop the stale VECTOR_MNEMONIC env-var alternative, which setup.ts
never reads.
The repo is public and the referenced fixes are still several PRs out.
Removes the internal audit pointer, the current auth-state confirmation,
the submission failure-mode detail, and the dependency CVE specifics.
Architecture, rationale, and PR sequence are unchanged.
PR #2 shipped 'never stored server-side', which is true but materially
incomplete - the mnemonic is a tool-call parameter, so it transits the MCP
client, the model provider, and hosted process memory. Replaced with an
accurate notice pointing at the architecture doc.
Renames the five tools to match the module's public name (Self-Improvement
Module), and renames the source file and registration function in kind.
Consumers relying on the old vector_governance_* names must update.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The SLOT_CONFIG_NETWORK.Mainnet zeroTime was hardcoded to the testnet
system start (1752057484000), causing RATE:slot_mismatch failures on
mainnet where the actual system start is 1756485600000. Now queries
Ogmios queryNetwork/startTime on first use and caches the result.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous proposal_spend and proposal_mint reference script UTxOs were
consumed. Redeployed all 5 governance reference scripts to unspendable
script-hash-derived addresses via redeploy_ref_scripts.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Repoint REGISTRY_SCRIPT_CBOR + REGISTRY_POLICY_ID from
5dd5118943d5aa7329696181252a6565a27dbf2c6de92b02a6aae361 (v1) to
be1a0a2912da180757ed3cd61b56bb8eab0188c19dc3c0e3912d2c01 (v2),
deployed to Vector mainnet via TX
3c53fa482245a82f88a39e90191a90cce3b6f09f6e28613bc53d35a0c3ba8587.
v2 enforces indefinite-length CBOR (D8 79 9F ... FF) for the
OutputReference inside the asset-name derivation, matching Aiken's
builtin.serialise_data() on Conway. Lucid's Data.to(new Constr(...))
emits definite-length arrays (D8 79 82 ...), so deriveNftAssetName is
hand-rolled from cborUint + cborBytes primitives. Verified byte-for-byte
against pycardano OutputReference.to_cbor() and agent-sdk-py
plutus_serialise_data across 5 test vectors (see new
test/derive-asset-name.test.ts).
governance.ts: AGENT_REGISTRY_POLICY env-fallback updated to v2.
Datum/redeemer schemas are unchanged between v1 and v2 — no other
tx-building changes required.
Follow-up: agent-sdk-ts at /home/david/code/agent-sdk-ts still ships
v1 constants and the pre-Conway Lucid asset-name derivation; needs the
same migration before consumers can use v2 from that SDK.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the internal Lock TX hash from submit_proposal output and label the
Proposal TX explicitly so agents use the correct hash as proposalTxHash.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Plutus validity_range.lower_bound is POSIX time in ms, not a slot number.
The validator's get_slot trace misleadingly names it 'current_slot' but
it's actually POSIX ms. Activity datum and proposal datum timestamps
must use POSIX ms to match the tx validity range.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use Lucid's own conversion function to ensure the POSIX→slot→POSIX
roundtrip produces the exact same slot in the tx validity range
as the spendSlot used in the activity datum.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Validator requires activity.last_proposal_slot == tx_validity_lower_bound.
Derive POSIX time from the same slot used in the datum to guarantee
they convert to the same slot after Lucid's POSIX→slot roundtrip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full redeployment with AGENT_REGISTRY_HASH=5dd51189... (matching the
MCP agent-network registry). All validator hashes, reference scripts,
and infrastructure UTxO refs updated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The registry NFT is locked at the registry script address with
the 10 AP3X deposit, not in the agent's wallet. Use getUtxoByUnit
to find it on-chain regardless of address.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The on-chain proposal_spend validator checks did_valid by looking for
the agent's registry NFT in reference inputs. Added lookup + .readFrom()
for the NFT UTxO in Step 2 of proposal submission.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SLOT_CONFIG_NETWORK.Mainnet was using Cardano mainnet genesis (2020),
causing POSIX→slot conversion to produce slots far beyond the Vector
testnet's era boundaries. Set to Vector testnet genesis 2025-07-09.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lucid-Evolution .validFrom/.validTo expect POSIX time in milliseconds.
The code was passing raw slot numbers, which Lucid tried to convert
back causing unsigned integer wraparound and PastHorizon errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Missing method caused slot=0, then spendSlot=-60, which wraps to
a huge unsigned int triggering PastHorizon errors from Ogmios.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ogmios v6+ returns script references as { language: "plutus:v3", cbor: "..." }
but the parser only handled the legacy { "plutus:v3": "..." } format.
This caused scriptRef to always be null, breaking reference script
resolution for governance proposal submission.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previous batch deployment consumed the spend ref as a coin input.
Redeployed in isolation and verified on-chain at index #0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The proposal_spend reference script UTxO was accidentally consumed,
causing MISSING_SCRIPT errors on proposal submission. Updated to the
redeployed UTxO ref and added validation that detects consumed/missing
reference scripts with actionable error messages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes transaction build failures by passing localUPLCEval: false
to all three governance lock operations (proposal, critique, endorsement).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the lock-only proposal submission with the full validated flow:
- Step 1: Lock ProposalDatum at proposal_spend address
- Step 2: Spend locked UTxO + mint prop_ token + mint pact_ activity token
using CIP-33 reference scripts and governance reference inputs
This makes proposals visible on the Foundation dashboard (which filters
out tokenless proposals). The flow mirrors the Python SDK's
validated_submit_proposal.
Adds:
- Reference script UTxO env vars (CIP-33) with deployment.json defaults
- Infrastructure UTxO env vars (GovernanceParams, Oracle, CrossRefs)
- parseUtxoRef() and waitForTx() helpers
- Activity datum creation (first-time proposer, count=1)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Protocol parameter fields (minFeeA, minFeeB, maxTxSize, maxValSize)
lacked fallback defaults, causing "Cannot convert undefined to a BigInt"
when Lucid-Evolution processed them. Added safe defaults sourced from
Vector public testnet and updated deposit values to match. Made
lovelaceToAda() defensive against undefined/null. Added diagnostic
logging to getWalletInfo and getProtocolParameters error paths.
Converted Dockerfile to multi-stage build and added docker-compose.yml
with inline Vector testnet environment config.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>