Skip to content

feat(adk): @pcc/adk skeleton with a pinned agent package and a clean external install check (R4 D1) - #411

Open
LamaSu wants to merge 4 commits into
masterfrom
feat/adk-package-harness
Open

LamaSu wants to merge 4 commits into
masterfrom
feat/adk-package-harness

Conversation

@LamaSu

@LamaSu LamaSu commented Sep 24, 2026

Copy link
Copy Markdown
Owner

What

The first cut of R4 D1: packages/adk (@pcc/adk, working name), plus a clean external install check that proves the public kit installs and imports outside this workspace, with no npm publish.

It is interface work, per the reconciliation note (returns/pcc-adk.md §0.5 D1) and board row R4. The package is "private": true until the operator decides npm publishing and the public name (queue 36, D1 and D2). Nothing publishes.

The package

  • Depends on the public @pcc/spec and @pcc/kernel-sdk only. It re-exports kernel-sdk, which is the digital-kernel backend (R2).
  • Pinned agent package, with no hand-written tool JSON (#2392).
    • scripts/generate-agent-pin.mjs writes src/generated/agent-pin.ts from apps/dashboard/public/agent-package.json. The pin holds the schema, version (2.19.1), tool count (253) and the sha256 of the exact bytes, plus each tool's {method, path, required}.
    • A unit test fails when the pin is stale. So an agent-package change must regenerate the pin in the same PR (pnpm --filter @pcc/adk generate). FYI aeo.
  • resolveToolRequest(name, input, { baseUrl }) turns a tool call into {method, url, body?}.
    • It is pure and never adds credentials, so the caller decides where a key goes.
    • It refuses endpoints that are not gateway paths. The live agent package still lists pcc_generate_ui at http://localhost:3200/api/generate, the N50/N11 class; R43 removes it.
    • It refuses ., .. and empty path parameters, which URL normalization would turn into a different route.
    • It refuses a base URL that is not plain http(s).
    • Path parameters are URL-encoded, and query keys are sorted.
  • checkAgentPackage(text) compares a live /agent-package.json with the pin: sha256, version and tool count.

Clean external install: pnpm --filter @pcc/adk check:clean-install

  1. Build, then pnpm pack, spec, kernel-sdk and adk. Pack rewrites workspace:*.
  2. Boundary gate on every tarball. It fails on any of these:
    • a workspace: left in the package;
    • an @pcc/* dependency that is outside the packed set or private in the workspace;
    • a chain client (viem, ethers, wagmi, …);
    • a key or env file in the payload.
  3. Install. A consumer project outside the workspace depends on the adk tarball, with pnpm.overrides pointing at the other two tarballs. It installs offline from the local pnpm store, so there is no network access.
  4. Use. A plain Node ESM import, then a TypeScript consumer type-check against the installed declarations.

Result: passes. It prints "agent package 2.19.1 (253 tools)", then "a TypeScript consumer type-checks against the installed declarations". Negative checks:

  • with viem added to adk, it fails at the gate (depends on chain client viem);
  • with a private @pcc/gateway dependency, it fails at pack.

It is not wired into CI. A ci.yml step needs operator approval; I'll propose one once this shape is agreed.

Lockfile

pnpm-lock.yaml gains only the packages/adk importer (16 lines), and a frozen offline install accepts it. A plain pnpm install had also re-resolved unrelated zod peers for viem and wagmi. I dropped that churn deliberately.

Tests

  • adk: 10 passed.
  • tsc --noEmit clean.
  • The clean-install check passes (see above).

Next (not in this PR)

🤖 Generated with Claude Code

https://claude.ai/code/session_01PkeiQMnVePeBGU9svhREFy

…external install check (R4 D1)

The first cut of the one public ADK package (working name; it stays
"private": true until the operator decides npm publishing and the public
name, queue 36 D1/D2). It depends only on the public @pcc/spec and
@pcc/kernel-sdk, and re-exports kernel-sdk (the digital-kernel backend, R2).

Pinned agent package, no hand-written tool JSON (#2392):
- scripts/generate-agent-pin.mjs writes src/generated/agent-pin.ts from
  apps/dashboard/public/agent-package.json: schema, version, tool count,
  the sha256 of the exact bytes, and each tool's method/path/required.
  A test fails when the pin is stale, so a package change regenerates it.
- resolveToolRequest(name, input, { baseUrl }) builds the gateway request.
  It is pure and never adds credentials. It refuses endpoints that are not
  gateway paths (the agent package still lists pcc_generate_ui at
  http://localhost:3200, the N50 class), "." or ".." path parameters, and
  non-http(s) base URLs; path parameters are URL-encoded.
- checkAgentPackage(text) compares a live /agent-package.json with the pin.

Clean external install (scripts/clean-install-check.mjs): build and pnpm
pack spec, kernel-sdk and adk; a boundary gate on every tarball (no
workspace:, no @pcc/* outside the packed set or private, no chain client,
no key or env file); a consumer project outside the workspace installed
OFFLINE from the local store via pnpm.overrides; a Node ESM import and a
TypeScript consumer type-check against the installed declarations. It
passes; with viem added to adk it fails at the gate, and with a private
@pcc dependency it fails at pack. Not wired into CI (a ci.yml change needs
the operator).

pnpm-lock.yaml gains only the packages/adk importer (16 lines); a frozen
offline install accepts it. adk tests 10/10; tsc clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PkeiQMnVePeBGU9svhREFy
AGENT_TOOLS is a plain object literal, so a name inherited from Object
("constructor", "toString", "__proto__", "hasOwnProperty", "valueOf") found a
function or Object.prototype instead of nothing. resolveToolRequest then threw
"TypeError: Cannot read properties of undefined (reading 'startsWith')" rather
than AdkToolError("unknown_tool"), so a caller that maps AdkToolError codes
got an untyped crash.

The lookup now requires an own property. The new test fails before this change
and passes after it. adk tests pass 11/11; tsc is clean; the pin is current;
the clean external install check passes.

agent: pcc-adk (4f6668ed)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LamaSu and others added 2 commits October 2, 2026 19:23
…ON only, validate endpoints (verdict 101)

F1. The generated AGENT_PACKAGE_PIN and AGENT_TOOLS are now deeply frozen
(src/deep-freeze.ts), and resolveToolRequest and checkAgentPackage never read
them after load: they use private frozen copies taken when agent-package.ts
loads (a Map of endpoints and the pin's three fields).

F2/F3. The ADK surface is an explicit list instead of a wildcard re-export of
@pcc/kernel-sdk: buildManifest, plus the agent-package exports. The kernel-sdk
registration client and job handler are no longer part of the kit. A test
pins the exact export list, so a new export is a reviewed change.

F4. resolveToolRequest snapshots its input once, as plain JSON: plain objects
(Object.prototype or null prototype) and arrays only, own enumerable data
properties only, string keys, finite numbers, no holes, no cycles, bounded
depth; undefined object fields are dropped as JSON.stringify drops them. The
required check, the path parameters, the query and the body all read that one
snapshot, and the body is written from it without consulting toJSON.

F5. The generator refuses a package it cannot pin faithfully: a reserved,
malformed or duplicate tool name, an unknown method, a malformed required
list, or a path that is neither a well-formed gateway path nor an http(s)
URL. A gateway path is segments of plain path characters or whole {name}
placeholders, with no empty or dot segment, so no query, fragment,
backslash, percent-escape or dot segment can reach a URL parser. The
resolver applies the same rules at call time (the tests run one corpus
through both) and refuses a URL that a parser would not send as built.

Every new test fails at f8c86a2 (13 of 15 at first run) and passes now.
Mutation checks: each guard removed alone fails a test, except the dot-segment
check and the URL round-trip check, which each catch dot segments on their
own (removing both fails). adk tests 29/29; tsc clean; the pin is current.

agent: pcc-adk (4f6668ed)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…graph, and the payload (verdict 101 F6)

The gate's rules move to scripts/boundary-policy.mjs: pure functions with no
side effects on import, unit-tested in src/__tests__/boundary-policy.test.ts,
so the tests exercise exactly what the check enforces.

- Dependency specs are resolved to the registry package they install: an
  npm: alias is checked by its target as well as its name, and a spec that
  is not a registry range (file:, link:, git, URL, workspace:, catalog:) or
  a bundled dependency is refused. The chain-client list now covers the
  common families by prefix.
- After the offline install, every package in the consumer's pnpm store is
  checked by the name in its own package.json, so a transitive chain client
  or a foreign @pcc/* package fails. The walk must find all three packed
  packages, or the check fails.
- The payload is an allowlist: package.json, README, LICENSE, and dist/
  build output (.js, .d.ts, maps, json) whose src/ source exists. A stray or
  stale file fails, whatever its name. Each payload file's text is scanned
  for secret shapes (private key blocks, PCC, AWS, GitHub, Stripe and Slack
  tokens, secret-valued key fields).
- The check deletes and rebuilds the three packages' dist/ first, so stale
  build output is not packed at all.
- The import step requires the kit's real surface and fails if it exports a
  registration client or a kernel handler.

The README now says what the gate checks, and that its lists catch known
names and shapes only.

Reproduced at f8c86a2 by running the old gate's own loop and regex on
crafted inputs (npm:viem and npm:@pcc/private-package aliases passed;
dist/operator.key, id_ecdsa, credentials.json, .p12 and .PEM files passed),
and by packing a stale dist/operator.key into the adk tarball. Now: adk tests
39/39, tsc clean, the pin current, and the full clean-install check passes
(6 installed packages). With a planted fake key in a source file, it fails on
the packed dist output.

agent: pcc-adk (4f6668ed)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
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