diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 1ef199ac2..f4a63c1ba 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -169,3 +169,9 @@ Node.js 22's built-in `fetch()` ignores `HTTPS_PROXY`. The sandbox uses `proxy-b | Old image served despite `:latest` push | AKS node cache | Use `imagePullPolicy: Always` or restart pods | | "Invalid character" in base64 | `x25519:`/`ed25519:` key prefix | Apply vendored base64Decode fix | | Node.js 22 fetch ignores HTTPS_PROXY | Built-in fetch doesn't use proxy env | Load `proxy-bootstrap.js` via `NODE_OPTIONS` | + +## Implementation Quality + +- **No mocks or stubs in production code.** Always provide real, working implementations. If a dependency is unavailable, build the real integration or defer the feature — never ship a mock. +- **No TODO/FIXME/HACK comments as placeholders.** If something needs to be done, do it now or track it as a GitHub issue. Code with TODO comments will not be merged. +- **No placeholder or skeleton implementations.** Every function, class, and module must be fully implemented and tested. Empty methods, `throw new Error("not implemented")`, or `// TODO` stubs are not acceptable. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1185c074f..fb40f34cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,49 @@ jobs: - run: npm run build - run: npm test + mesh-plugin-build: + name: Mesh Plugin Build & Test + runs-on: ubuntu-latest + defaults: + run: + working-directory: mesh-plugin + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "22" + - run: npm install + - run: cd ../vendor/agentmesh-sdk && npm install --ignore-scripts + - run: npm run typecheck + - run: npm run build + - run: npm test + + python-sidecar: + name: Python Lint & Test (AGT Sidecar) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - name: Check for AGT sidecar code + id: check + run: | + if [ -f agt-sidecar/pyproject.toml ] || [ -f agt-sidecar/requirements.txt ]; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + echo "::notice::AGT sidecar not yet present — skipping lint & test" + fi + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + if: steps.check.outputs.exists == 'true' + with: + python-version: "3.12" + - name: Install and test + if: steps.check.outputs.exists == 'true' + run: | + cd agt-sidecar + pip install -e ".[dev]" + python -m ruff check . + python -m pytest + bicep-validate: name: Bicep Validation runs-on: ubuntu-latest diff --git a/mesh-plugin/src/agentmesh-sdk.d.ts b/mesh-plugin/src/agentmesh-sdk.d.ts new file mode 100644 index 000000000..a61c7bfe9 --- /dev/null +++ b/mesh-plugin/src/agentmesh-sdk.d.ts @@ -0,0 +1,71 @@ +// Type declarations for vendored @agentmesh/sdk (no published @types package). +declare module "@agentmesh/sdk" { + export interface IdentityData { + amid: string; + signing_public_key: string; + signing_private_key: string; + exchange_public_key: string; + exchange_private_key: string; + created_at: string; + framework?: string; + framework_version?: string; + [key: string]: unknown; + } + + export class Identity { + readonly amid: string; + readonly signingPublicKey: CryptoKey; + readonly signingPublicKeyRaw: Uint8Array; + readonly signingPrivateKey: CryptoKey; + readonly exchangePublicKey: CryptoKey; + readonly exchangePublicKeyRaw: Uint8Array; + readonly exchangePrivateKey: CryptoKey; + readonly createdAt: Date; + + static generate(): Promise; + static load(storage: unknown, path: string): Promise; + static fromData(data: IdentityData): Promise; + save(storage: unknown, path: string): Promise; + toData(): Promise; + get signingPublicKeyB64(): string; + } + + export class MemoryStorage { + constructor(); + get(key: string): Promise; + set(key: string, value: string): Promise; + delete(key: string): Promise; + } + + export class AgentMeshClient { + static fromIdentity( + identity: Identity, + opts?: Record, + ): AgentMeshClient; + connect(opts?: Record): Promise; + disconnect(): Promise; + send(to: string, payload: unknown): Promise; + onMessage(handler: (from: string, payload: unknown) => void): void; + onKnock( + handler: ( + from: string, + intent: unknown, + ) => Promise<{ accept: boolean }>, + ): void; + addPlaintextPeer(amid: string): void; + removePlaintextPeer(amid: string): void; + getPlaintextPeers(): string[]; + search( + capability: string, + opts?: Record, + ): Promise; + get isConnected(): boolean; + get amid(): string; + } + + export class RegistryClient { + constructor(opts?: Record); + } + + export const VERSION: string; +} diff --git a/mesh-plugin/src/connection.ts b/mesh-plugin/src/connection.ts index c83a0d960..2365df586 100644 --- a/mesh-plugin/src/connection.ts +++ b/mesh-plugin/src/connection.ts @@ -102,9 +102,11 @@ export interface FileTransferAck { // --------------------------------------------------------------------------- // Connection class +import { IMeshTransport } from "./transport-interface.js"; + // --------------------------------------------------------------------------- -export class MeshConnection { +export class MeshConnection implements IMeshTransport { private config: ConnectionConfig; private inbox: InboxMessage[] = []; private maxInboxSize: number; diff --git a/mesh-plugin/src/transport-interface.ts b/mesh-plugin/src/transport-interface.ts new file mode 100644 index 000000000..408ed1cbb --- /dev/null +++ b/mesh-plugin/src/transport-interface.ts @@ -0,0 +1,30 @@ +/** + * AGT Migration — Transport abstraction interface. + * + * Extracted from MeshConnection's public surface so the AGT adapter + * (Phase 2) can implement the same contract without changing callers. + * + * Phase 1: MeshConnection implements IMeshTransport (grounded). + * Phase 2: AgtTransport implements IMeshTransport (AGT SDK-backed). + * Phase 3: Swap implementation, remove vendor. + */ + +export interface IMeshTransport { + // ── Connection lifecycle ───────────────────────────────────── + connect(): Promise; + disconnect(): Promise; + readonly isConnected: boolean; + + // ── Messaging ──────────────────────────────────────────────── + send(toAmid: string, payload: unknown): Promise; + + // ── Plaintext peers (Rust controller compat) ───────────────── + addPlaintextPeer(amid: string): void; + removePlaintextPeer(amid: string): void; + getPlaintextPeers(): string[]; + + // ── Discovery ──────────────────────────────────────────────── + discover(opts?: { capability?: string; limit?: number }): Promise< + Array<{ amid: string; displayName?: string; capabilities?: string[] }> + >; +}