diff --git a/EXAMPLES.md b/EXAMPLES.md index f2d47e2..c1309af 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,13 +1,25 @@ # CommandLayer SDK Examples -Canonical examples for the CommandLayer SDK repo. These examples keep the Commons v1.1.0 story receipt-first: `receipt` is signed, `runtime_metadata` is optional and unsigned, and the `x402` object is protocol metadata rather than a commercial SDK surface. +Canonical examples for the CommandLayer SDK repo. These examples keep the Commons v1.1.0 story aligned with the active request and receipt contracts: requests are flat top-level protocol objects, `receipt` is signed, `runtime_metadata` is optional and unsigned, and the `x402` object only appears inside receipts as protocol metadata. All examples in this file target: - Protocol-Commons v1.1.0, +- flat request payloads shaped exactly like `schemas/v1.1.0/commons//.request.schema.json`, - canonical signed receipts returned as `response.receipt`, and - optional execution context returned as `response.runtime_metadata`. -## 1. Canonical response envelope +## 1. Canonical summarize request + +```json +{ + "verb": "summarize", + "version": "1.1.0", + "input": "CommandLayer defines semantic agent verbs.", + "mode": "brief" +} +``` + +## 2. Canonical response envelope ```json { @@ -15,7 +27,7 @@ All examples in this file target: "status": "success", "x402": { "verb": "summarize", - "version": "1.1.0", + "version": "1.1.0" }, "result": { "summary": "..." @@ -39,7 +51,7 @@ All examples in this file target: } ``` -## 2. TypeScript examples +## 3. TypeScript examples ### Create client @@ -47,7 +59,6 @@ All examples in this file target: import { createClient } from "@commandlayer/sdk"; const client = createClient({ - actor: "examples-ts", runtime: "https://runtime.commandlayer.org" }); ``` @@ -56,8 +67,8 @@ const client = createClient({ ```ts const response = await client.summarize({ - content: "CommandLayer defines semantic agent verbs.", - style: "bullet_points" + input: "CommandLayer defines semantic agent verbs.", + mode: "brief" }); console.log(response.receipt.result?.summary); @@ -67,8 +78,8 @@ console.log(response.receipt.result?.summary); ```ts const response = await client.analyze({ - content: "Invoice total: $1200", - goal: "detect finance intent" + input: "Invoice total: $1200", + mode: "deep" }); ``` @@ -76,7 +87,8 @@ const response = await client.analyze({ ```ts const response = await client.classify({ - content: "Contact support@example.com" + input: "Contact support@example.com", + mode: "single" }); ``` @@ -84,8 +96,8 @@ const response = await client.classify({ ```ts const response = await client.clean({ - content: " test@example.com ", - operations: ["trim", "redact_emails"] + input: " test@example.com ", + mode: "sanitize" }); ``` @@ -93,9 +105,8 @@ const response = await client.clean({ ```ts const response = await client.convert({ - content: '{"a":1}', - from: "json", - to: "csv" + input: '{"a":1}', + mode: "structured" }); ``` @@ -103,9 +114,8 @@ const response = await client.convert({ ```ts const response = await client.describe({ - subject: "receipt verification", - audience: "general", - detail: "medium" + input: "receipt verification", + mode: "detailed" }); ``` @@ -113,9 +123,8 @@ const response = await client.describe({ ```ts const response = await client.explain({ - subject: "receipt verification", - audience: "novice", - style: "step-by-step" + input: "receipt verification", + mode: "stepwise" }); ``` @@ -123,8 +132,8 @@ const response = await client.explain({ ```ts const response = await client.format({ - content: "a: 1\nb: 2", - to: "table" + input: "a: 1\nb: 2", + mode: "markdown" }); ``` @@ -132,8 +141,7 @@ const response = await client.format({ ```ts const response = await client.parse({ - content: '{ "a": 1 }', - contentType: "json", + input: '{ "a": 1 }', mode: "strict" }); ``` @@ -142,154 +150,7 @@ const response = await client.parse({ ```ts const response = await client.fetch({ - source: "https://example.com", - include_metadata: true -}); -``` - -## 3. Python examples - -```python -from commandlayer import create_client - -client = create_client(actor="examples-py") - -summary = client.summarize(content="CommandLayer defines semantic agent verbs.", style="bullet_points") -analysis = client.analyze(content="Invoice total: $1200", goal="detect finance intent") -classification = client.classify(content="Contact support@example.com") -cleaned = client.clean(content=" test@example.com ", operations=["trim", "redact_emails"]) -converted = client.convert(content='{"a":1}', from_format="json", to_format="csv") -description = client.describe(subject="receipt verification") -explanation = client.explain(subject="receipt verification", style="step-by-step") -formatted = client.format(content="a: 1\nb: 2", to="table") -parsed = client.parse(content='{ "a": 1 }', content_type="json", mode="strict") -fetched = client.fetch(source="https://example.com", include_metadata=True) -``` - -## 4. Verification examples - -### TypeScript, explicit key - -```ts -import { verifyReceipt } from "@commandlayer/sdk"; - -const result = await verifyReceipt(response.receipt, { - publicKey: "ed25519:BASE64_PUBLIC_KEY" -}); -``` - -### Python, explicit key - -```python -from commandlayer import verify_receipt - -result = verify_receipt(response["receipt"], public_key="ed25519:BASE64_PUBLIC_KEY") -``` - -### ENS-backed verification - -```ts -const result = await verifyReceipt(response.receipt, { - ens: { - name: "summarizeagent.eth", - rpcUrl: process.env.MAINNET_RPC_URL! - } -}); -``` - -```python -result = verify_receipt( - response["receipt"], - ens={"name": "summarizeagent.eth", "rpcUrl": "https://mainnet.infura.io/v3/YOUR_KEY"}, -) -``` - -## 5. CLI examples - -### Summarize - -```bash -commandlayer summarize \ - --content "CommandLayer defines semantic verbs." \ - --style bullet_points \ - --json -``` - -### Analyze - -```bash -commandlayer analyze \ - --content "Invoice total: $500" \ - --goal "detect finance intent" \ - --json -``` - -### Verify a saved receipt - -```bash -commandlayer verify \ - --file receipt.json \ - --public-key "ed25519:BASE64_PUBLIC_KEY" -``` - -## 6. Runtime override - -### TypeScript - -```ts -const client = createClient({ - actor: "override-example", - runtime: "https://staging-runtime.commandlayer.org" + input: "https://example.com", + mode: "html" }); ``` - -### Python - -```python -client = create_client( - actor="override-example", - runtime="https://staging-runtime.commandlayer.org", -) -``` - -## 7. Persist the canonical receipt - -```ts -import { writeFile } from "node:fs/promises"; - -await writeFile("receipt.json", JSON.stringify(response.receipt, null, 2)); -``` - -```python -import json -from pathlib import Path - -Path("receipt.json").write_text(json.dumps(response["receipt"], indent=2), encoding="utf-8") -``` - -## 8. Error handling - -### TypeScript - -```ts -import { CommandLayerError } from "@commandlayer/sdk"; - -try { - await client.summarize({ content: "" }); -} catch (error) { - if (error instanceof CommandLayerError) { - console.error(error.statusCode, error.message, error.details); - } -} -``` - -### Python - -```python -from commandlayer import CommandLayerError - -try: - client.summarize(content="") -except CommandLayerError as error: - print(error.status_code, error, error.details) -``` diff --git a/QUICKSTART.md b/QUICKSTART.md index b1829ee..a2acd94 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -31,11 +31,11 @@ npm install -g @commandlayer/sdk ```ts import { createClient } from "@commandlayer/sdk"; -const client = createClient({ actor: "quickstart-ts" }); +const client = createClient(); const response = await client.summarize({ - content: "CommandLayer makes agent execution verifiable.", - style: "bullet_points" + input: "CommandLayer makes agent execution verifiable.", + mode: "brief" }); console.log(response.receipt.result?.summary); @@ -55,7 +55,22 @@ response = client.summarize( print(response["receipt"]["result"]["summary"]) ``` -## 3. Inspect the response +## 3. Inspect the request contract + +Protocol-Commons v1.1.0 Commons requests are flat top-level objects: + +```json +{ + "verb": "summarize", + "version": "1.1.0", + "input": "CommandLayer makes agent execution verifiable.", + "mode": "brief" +} +``` + +Do not send nested `input` objects, `limits`, actor wrappers, or `x402` request metadata for Commons verbs. + +## 4. Inspect the response Both SDKs return the same shape: @@ -83,9 +98,9 @@ Both SDKs return the same shape: } ``` -Use `response.receipt` as the durable protocol artifact. `runtime_metadata` is optional execution context. The retained `x402` object carries Commons verb metadata and is not a commercial feature signal. +Use `response.receipt` as the durable protocol artifact. `runtime_metadata` is optional execution context. The retained `x402` object carries Commons verb metadata in receipts and is not a request wrapper or commercial feature signal. -## 4. Verify the receipt +## 5. Verify the receipt ### TypeScript @@ -118,12 +133,12 @@ Use the same signer-discovery model in both SDKs: - signer ENS TXT: `cl.sig.pub` - signer ENS TXT: `cl.sig.kid` -## 5. Try the CLI +## 6. Try the CLI ```bash commandlayer summarize \ - --content "CommandLayer makes agent execution verifiable." \ - --style bullet_points \ + --input "CommandLayer makes agent execution verifiable." \ + --mode brief \ --json ``` @@ -135,7 +150,7 @@ commandlayer verify \ --public-key "ed25519:BASE64_PUBLIC_KEY" ``` -## 6. What is stable today? +## 7. What is stable today? Stable in this repo: - Protocol-Commons v1.1.0 verb surface, diff --git a/README.md b/README.md index d940319..ce127d3 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ This repo is aligned to the current CommandLayer v1.1.0 surface: - canonical signed receipts as the verification contract payload, and - optional `runtime_metadata` as unsigned execution context. -Protocol-Commercial / x402 payment flows are not a first-class SDK surface in this repo today. The retained `receipt.x402` metadata block is part of the Commons protocol schema here; it should not be read as commercial feature coverage. +Protocol-Commercial / x402 payment flows are not a first-class SDK surface in this repo today. The retained `receipt.x402` metadata block is part of the Commons receipt schema here; it should not be read as a Commons request envelope or commercial feature coverage. ## Install @@ -52,11 +52,11 @@ Python 3.10+ is supported. ```ts import { createClient, verifyReceipt } from "@commandlayer/sdk"; -const client = createClient({ actor: "my-app" }); +const client = createClient(); const response = await client.summarize({ - content: "CommandLayer turns agent calls into signed receipts.", - style: "bullet_points" + input: "CommandLayer turns agent calls into signed receipts.", + mode: "brief" }); console.log(response.receipt.result?.summary); @@ -70,6 +70,21 @@ const verification = await verifyReceipt(response.receipt, { console.log(verification.ok); ``` +## Commons request contract + +The active Protocol-Commons v1.1.0 request contract is flat and top-level: + +```json +{ + "verb": "summarize", + "version": "1.1.0", + "input": "CommandLayer turns agent calls into signed receipts.", + "mode": "brief" +} +``` + +Commons requests should not be wrapped in nested request bodies, actor envelopes, `limits`, or `x402` request metadata. + ## First call: Python ```python @@ -102,7 +117,7 @@ Client methods now return a command response envelope: "status": "success", "x402": { "verb": "summarize", - "version": "1.1.0", + "version": "1.1.0" }, "result": { "summary": "..." @@ -129,53 +144,3 @@ Client methods now return a command response envelope: The canonical signed object is `receipt`. `runtime_metadata` is optional and unsigned. Verification, persistence, and downstream audit should use the canonical `receipt` object. The SDK still normalizes older blended runtime responses for compatibility, but that normalization is legacy-only. The repo documents the v1.1.0 envelope as the single canonical public contract. - -## Verification - -### Offline verification - -```ts -import { verifyReceipt } from "@commandlayer/sdk"; - -const result = await verifyReceipt(response.receipt, { - publicKey: "ed25519:BASE64_PUBLIC_KEY" -}); -``` - -### ENS-backed verification - -```ts -const result = await verifyReceipt(response.receipt, { - ens: { - name: "summarizeagent.eth", - rpcUrl: process.env.MAINNET_RPC_URL! - } -}); -``` - -ENS signer discovery resolves: -1. `cl.receipt.signer` on the agent ENS name, -2. `cl.sig.pub` on the signer ENS name, -3. `cl.sig.kid` on the signer ENS name. - -## CLI - -Install the npm package and use the bundled CLI: - -```bash -commandlayer summarize --content "Test text" --style bullet_points --json -commandlayer verify --file receipt.json --public-key "ed25519:BASE64_PUBLIC_KEY" -``` - -The CLI is intended for demos, CI smoke tests, debugging, and reproducing SDK flows without writing app code. - -## Repo guide - -- Fast onboarding: `QUICKSTART.md` -- Cookbook examples: `EXAMPLES.md` -- Maintainer / architecture notes: `DEVELOPER_EXPERIENCE.md` -- Release guide: `RELEASE_GUIDE.md` -- Deployment checklist: `DEPLOYMENT_GUIDE.md` -- Changelog: `CHANGELOG.md` -- TypeScript package docs: `typescript-sdk/README.md` -- Python package docs: `python-sdk/README.md` diff --git a/typescript-sdk/README.md b/typescript-sdk/README.md index 1005e99..4d52e03 100644 --- a/typescript-sdk/README.md +++ b/typescript-sdk/README.md @@ -3,7 +3,7 @@ Official TypeScript/JavaScript SDK for CommandLayer Commons v1.1.0. Use this package to: -- call CommandLayer Commons verbs, +- call CommandLayer Commons verbs with the canonical flat v1.1.0 request shape, - receive the canonical signed `receipt`, - capture optional unsigned `runtime_metadata` separately, - verify receipts offline or through ENS, and @@ -22,11 +22,11 @@ Supported runtime: Node.js 20+. ```ts import { createClient, verifyReceipt } from "@commandlayer/sdk"; -const client = createClient({ actor: "docs-example" }); +const client = createClient(); const response = await client.summarize({ - content: "CommandLayer makes agent execution verifiable.", - style: "bullet_points" + input: "CommandLayer makes agent execution verifiable.", + mode: "brief" }); console.log(response.receipt.result?.summary); @@ -40,6 +40,21 @@ const verification = await verifyReceipt(response.receipt, { console.log(verification.ok); ``` +## Commons request shape + +Commons v1.1.0 request payloads are flat top-level objects. The SDK now emits the schema contract directly: + +```json +{ + "verb": "summarize", + "version": "1.1.0", + "input": "CommandLayer makes agent execution verifiable.", + "mode": "brief" +} +``` + +Do not wrap Commons requests in nested `input` objects, `limits`, `actor`, or `x402` request envelopes. + ## Return shape Client methods return: @@ -72,7 +87,7 @@ Client methods return: } ``` -`verifyReceipt()` accepts the canonical `receipt` object. The retained `receipt.x402` block is Commons protocol metadata, not a commercial SDK surface. The SDK also accepts a whole response envelope for legacy compatibility, but new integrations should pass `response.receipt` explicitly. +`verifyReceipt()` accepts the canonical `receipt` object. The retained `receipt.x402` block is Commons protocol metadata, not a Commons request wrapper. The SDK also accepts a whole response envelope for legacy compatibility, but new integrations should pass `response.receipt` explicitly. ## Verification modes @@ -105,7 +120,7 @@ The ENS flow resolves: The package ships the `commandlayer` CLI. ```bash -commandlayer summarize --content "hello" --style bullet_points --json +commandlayer summarize --input "hello" --mode brief --json commandlayer verify --file receipt.json --public-key "ed25519:BASE64_PUBLIC_KEY" ``` diff --git a/typescript-sdk/scripts/cli-smoke.mjs b/typescript-sdk/scripts/cli-smoke.mjs index 9ea7aaa..2111be8 100644 --- a/typescript-sdk/scripts/cli-smoke.mjs +++ b/typescript-sdk/scripts/cli-smoke.mjs @@ -31,7 +31,7 @@ runCase("help output", ["--help"], { runCase("argument validation", ["summarize"], { exitCode: 1, - includes: ["required option '--content ' not specified"] + includes: ["required option '--input ' not specified"] }); runCase("bad JSON path", ["call", "--verb", "summarize", "--body", "{not-json}"], { diff --git a/typescript-sdk/scripts/unit-tests.mjs b/typescript-sdk/scripts/unit-tests.mjs index c1e349d..ef698ff 100644 --- a/typescript-sdk/scripts/unit-tests.mjs +++ b/typescript-sdk/scripts/unit-tests.mjs @@ -88,6 +88,7 @@ const { resolveSignerKey, CommandLayerError, CommandLayerClient, + buildCommonsRequest, } = require("../dist/index.cjs"); // ---- Canonicalization ---- @@ -192,6 +193,52 @@ await assertRejects( "resolveSignerKey throws clear error when cl.sig.pub malformed" ); + +// ---- Commons request construction ---- + +const summarizeRequest = buildCommonsRequest("summarize", { input: "Protocol-Commons v1.1.0 uses a flat request shape.", mode: "brief" }); +assert(summarizeRequest.verb === "summarize", "buildCommonsRequest sets summarize verb"); +assert(summarizeRequest.version === "1.1.0", "buildCommonsRequest sets protocol version"); +assert(summarizeRequest.input === "Protocol-Commons v1.1.0 uses a flat request shape.", "buildCommonsRequest keeps flat input string"); +assert(summarizeRequest.mode === "brief", "buildCommonsRequest keeps summarize mode"); +assert(!("x402" in summarizeRequest), "buildCommonsRequest omits x402 wrapper"); +assert(!("actor" in summarizeRequest), "buildCommonsRequest omits actor wrapper"); +assertThrows( + () => buildCommonsRequest("summarize", { input: "" }), + "buildCommonsRequest rejects empty input" +); + +const outboundCalls = []; +const transportClient = new CommandLayerClient({ + runtime: "https://runtime.example", + fetchImpl: async (url, init) => { + outboundCalls.push({ url, init }); + return { + ok: true, + status: 200, + async json() { + return { + receipt: { + status: "success", + x402: { verb: "summarize", version: "1.1.0" }, + result: { summary: "ok" }, + metadata: {} + } + }; + } + }; + } +}); +await transportClient.summarize({ input: "Flat request body", mode: "brief" }); +const sent = JSON.parse(outboundCalls[0].init.body); +assert(outboundCalls[0].url === "https://runtime.example/summarize/v1.1.0", "client.summarize uses canonical runtime path"); +assert(sent.verb === "summarize", "client.summarize sends flat verb"); +assert(sent.version === "1.1.0", "client.summarize sends flat version"); +assert(sent.input === "Flat request body", "client.summarize sends flat input string"); +assert(sent.mode === "brief", "client.summarize sends flat mode"); +assert(!("x402" in sent), "client.summarize does not send x402 wrapper"); +assert(!("actor" in sent), "client.summarize does not send actor wrapper"); + // ---- Receipt verification (end-to-end) ---- const receipt = { diff --git a/typescript-sdk/src/cli.ts b/typescript-sdk/src/cli.ts index c8815ec..9de196e 100644 --- a/typescript-sdk/src/cli.ts +++ b/typescript-sdk/src/cli.ts @@ -38,30 +38,27 @@ function createConfiguredClient() { } function withCommonOptions(cmd: Command) { - return cmd.requiredOption("--content ", "Input content").option("--max-tokens ", "Max output tokens", "1000"); + return cmd.requiredOption("--input ", "Flat Commons input string"); } -withCommonOptions(program.command("summarize").description("Summarize content").option("--style