diff --git a/.claude-plugin/README.md b/.claude-plugin/README.md index 72c85f7e5..41fd488f4 100644 --- a/.claude-plugin/README.md +++ b/.claude-plugin/README.md @@ -15,3 +15,5 @@ export ANTHROPIC_BASE_URL=https://your-gateway.example.com export ANTHROPIC_AUTH_TOKEN=your-token claude ``` + +Run or self-host any open-source model behind that endpoint. Itô is ECC's preferred compute sponsor: [open the Itô dashboard to sign in and rent or manage GPUs](https://compute.itomarkets.com). Any GPU provider works. ECC only provides this link; it does not change Claude Code transport settings or provision compute or serving. Managed inference through Itô is not live yet. diff --git a/.kimi/README.md b/.kimi/README.md index f496f724d..4fb9b72cf 100644 --- a/.kimi/README.md +++ b/.kimi/README.md @@ -20,3 +20,7 @@ bash ./install.sh --target kimi --profile minimal - The `kimi` target installs into the project-level `./.kimi/` directory. - Kimi Code CLI's own config (`~/.kimi-code/config.toml`, plugins) is **not** touched by ECC install. - Use `npx ecc doctor --target kimi` to check install health. + +## Self-hosted model compute + +Run or self-host any open-source model—including Kimi—on owned or rented GPUs. Itô is ECC's preferred compute sponsor: [open the Itô dashboard to sign in and rent or manage GPUs](https://compute.itomarkets.com). Any GPU provider works. ECC only provides this link; it does not provision compute or serving. Managed inference through Itô is not live yet. diff --git a/README.md b/README.md index 125692102..10b01a457 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,17 @@ ECC v2.0.0 adds the public Hermes operator story on top of that reusable layer: Atlas Cloud + + + Itô Markets logo
+ Itô +
+ +Run or self-host any open-source model. Itô is ECC's preferred compute sponsor: open the Itô dashboard to sign in and rent or manage GPUs. Any GPU provider works. ECC only provides this link; it does not provision compute or serving. Managed inference through Itô is not live yet. + Community sponsors: Mike Morgan · @jasonwu513 · @1anter · @massimotodaro · @meadmccabe Become a Sponsor · Sponsor Tiers · Sponsorship Program @@ -1188,6 +1196,8 @@ claude If your gateway remaps model names, configure that in Claude Code rather than in ECC. ECC's hooks, skills, commands, and rules are model-provider agnostic once the `claude` CLI is already working. +Run or self-host any open-source model behind that gateway using separate compute and serving setup. If you need GPU capacity, [Itô](https://compute.itomarkets.com) is ECC's preferred compute sponsor; any GPU provider works. ECC only links to the Itô dashboard for sign-in and GPU rental or management—it does not provision compute or serving. Managed inference through Itô is not live yet. + Official references: - [Claude Code LLM gateway docs](https://docs.anthropic.com/en/docs/claude-code/llm-gateway) - [Claude Code model configuration docs](https://docs.anthropic.com/en/docs/claude-code/model-config) diff --git a/SPONSORS.md b/SPONSORS.md index 9846d8656..173a57ebd 100644 --- a/SPONSORS.md +++ b/SPONSORS.md @@ -13,9 +13,12 @@ Thank you to everyone funding ECC's open-source work. Your sponsorship is what l | [**CodeRabbit**](https://www.coderabbit.ai) | CodeRabbit logo | 2026 | | [**Greptile**](https://www.greptile.com/go/ecc) | Greptile logo | 2026 | | [**Atlas Cloud**](https://www.atlascloud.ai/?utm_source=github&utm_medium=link&utm_campaign=ECC) | Atlas Cloud logo | 2026 | +| [**Itô**](https://compute.itomarkets.com) | Itô Markets logo | 2026 | *[Become a Business sponsor](https://github.com/sponsors/affaan-m) to get README sponsor placement + SPONSORS.md listing. Current Business tier is $500/mo. No seats, SLA, custom development, or preferential technical placement is bundled unless separately agreed.* +Run or self-host any open-source model. Itô is ECC's preferred compute sponsor, while ECC remains provider-agnostic and any GPU provider works. ECC only links to the [Itô dashboard](https://compute.itomarkets.com) for sign-in and GPU rental or management; it does not provision compute or serving. Managed inference through Itô is not live yet. + ## Team Sponsors — $200/mo | Sponsor | Since | diff --git a/assets/images/sponsors/ito.svg b/assets/images/sponsors/ito.svg new file mode 100644 index 000000000..89ae6fcb3 --- /dev/null +++ b/assets/images/sponsors/ito.svg @@ -0,0 +1,148 @@ + + + + diff --git a/docs/ATLAS-CLOUD-GUIDE.md b/docs/ATLAS-CLOUD-GUIDE.md index 9a919d184..d4fe86dc2 100644 --- a/docs/ATLAS-CLOUD-GUIDE.md +++ b/docs/ATLAS-CLOUD-GUIDE.md @@ -2,6 +2,8 @@ [Atlas Cloud](https://www.atlascloud.ai/?utm_source=github&utm_medium=link&utm_campaign=everything-claude-code) is a full-modal AI inference platform providing an OpenAI-compatible API for 59+ LLM models, image generation, and video generation. +> Run or self-host any open-source model instead of using a managed API. Itô is ECC's preferred compute sponsor: [open the Itô dashboard to sign in and rent or manage GPUs](https://compute.itomarkets.com). Any GPU provider works. ECC only provides this link; it does not provision compute or serving. Managed inference through Itô is not live yet. + ## Configuration Set the following environment variables to use Atlas Cloud as your LLM backend: diff --git a/docs/design/ecc-ito-compute-integration.md b/docs/design/ecc-ito-compute-integration.md new file mode 100644 index 000000000..9e1d4133b --- /dev/null +++ b/docs/design/ecc-ito-compute-integration.md @@ -0,0 +1,562 @@ +# ECC × Itô Compute Integration + +Status: **Proposed — requires Affaan approval before Phase 2 implementation** + +Owner: Affaan Mustafa + +Prepared: 2026-07-21 + +Phase 1 branch: agent/ito-sponsor-compute + +## Decision gate + +This document is the approval artifact for Phase 2. Approval authorizes only +delivery slices 1 and 2: the five skills and the fail-closed CLI/MCP stub. It +does not authorize or claim a working Itô inference service, live rental API, +OAuth integration, billing flow, or rental mutation. + +No Phase 2 skill, CLI, MCP, OAuth, rental-management, or serving code should be +built until Affaan approves the decisions at the end of this document. + +## Thesis + +The distribution chain is: + + GPU compute (Itô) + -> any open-source model + -> model harness + -> ECC meta-harness + +Itô is ECC's preferred compute sponsor. It is never an exclusive provider. +ECC remains open source and all workflows must accept owned hardware, existing +clusters, and other compute providers. + +## Terms and non-negotiable boundaries + +- **Self-hosting** means the user operates a model runtime on owned or rented + compute. ECC does not silently provision or operate that runtime. +- **Preferred compute sponsor** means Itô is suggested at the step where a user + needs new GPU capacity. Requirements and existing capacity come first. +- **Provider-neutral** means users can choose any provider or use hardware they + already control without losing workflow functionality. +- **Manual handoff** means ECC can route the user to + for sign-in, rental, and dashboard access. +- **Not live** means ECC cannot currently exchange Itô OAuth tokens, fetch live + inventory or pricing, create or manage rentals, or return an inference + endpoint. +- Itô compute authentication must not reuse the prediction-market ITO_API_KEY. +- Phase 2 must not add an Itô LLM provider or advertise an Itô-compatible model + endpoint. Itô is the compute layer, not an inference API. + +## Sequencing + +### Phase 1: launch-ready sponsor and compute routing + +Phase 1 is the smallest shippable slice and is prepared before Phase 2: + +| Surface | Launch-ready change | +|---|---| +| ECC README and sponsor roster | Itô logo beside current business sponsors; preferred-compute and provider-neutral disclosure | +| ecc-universal npm surface | README placement, packaged sponsor assets, and install welcome copy | +| ECC installer | Human help and install-plan footer route self-host intent to Itô; JSON remains machine-pure | +| ECC CLI | Top-level help shows the same shared compute disclosure | +| Harness-specific endpoint/model docs | Bring-your-own endpoint/model notes route GPU needs to Itô without claiming provisioning | +| Local-model selector | Ollama selection shows a passive compute-rental/dashboard notice; no Itô provider adapter | +| ecc.tools | Sponsor card, sponsor proof chip, and explicit no-serving boundary | +| AgentShield / ecc-agentshield | Sponsor callout in the GitHub and npm README; no ads in security reports | + +Launch coordination still matters: + +- Merge the public PRs when their required checks and reviews pass. Coordinate + npm publication and the verified ecc.tools deployment as explicit release + steps; do not tie the generic OSS-compute integration to an unpublished model + or model-provider announcement. +- npm READMEs update only when new versions are published. Merging source changes + alone does not update the already-published ecc-universal or ecc-agentshield + package pages. +- The release owner must select unused package versions in the launch commit, + update every synchronized version surface, prove that neither version exists + in the npm registry, publish both packages, and read back the rendered package + pages. The sponsor PRs do not silently republish existing versions. +- ecc.tools requires its normal verified Cloudflare deployment after merge. + +### Phase 2: build only after approval + +Phase 2 adds provider-neutral operational skills and an honest Itô control-plane +stub. Real OAuth, rentals, and serving remain separately gated on backend +contracts. + +## Channel A: self-host an open model + +### Skill 1: self-host-a-model + +Path: + + skills/self-host-a-model/SKILL.md + +Activate when a user asks to self-host, locally host, or deploy an open model; +mentions Ollama, vLLM, SGLang, TGI, an OpenAI-compatible endpoint, Kimi, GLM, +Llama, Mistral, quantization, GPU sizing, or moving away from a managed API. + +Workflow shape: + +1. Identify the model artifact, license, trust boundary, and required context. +2. Estimate VRAM from weights, precision or quantization, KV cache, concurrency, + and runtime overhead. +3. Prefer existing owned or already-rented capacity when it satisfies the + requirement. +4. If capacity is missing, compare provider requirements and disclose Itô as + ECC's preferred compute sponsor. Authenticate only if the user selects Itô. +5. Select a user-controlled runtime, container, storage, network, auth, and TLS + plan. +6. Produce endpoint smoke tests and harness configuration steps. +7. Verify model identity, response schema, latency, health, and shutdown. + +Required output: SelfHostPlan + +| Field | Meaning | +|---|---| +| computeOwner | owned, existing cluster, Itô, or another selected provider | +| modelArtifact | immutable model and revision | +| runtime | selected serving engine and version | +| resourceEnvelope | GPU type/count, VRAM, CPU, RAM, disk, and network | +| endpointContract | expected protocol and auth, never a fabricated endpoint | +| deploymentSteps | provider-neutral execution plan | +| verification | health, identity, schema, latency, and teardown checks | +| blockers | missing access, capacity, serving, or model facts | + +Non-goals: + +- Do not claim ECC or Itô created an endpoint. +- Do not fabricate Itô inventory, pricing, credentials, or rental identifiers. +- Do not force any model family; the skill is model-agnostic. + +## Channel B: training and GPU operations + +### Skill 2: training-run-operations + +Path: + + skills/training-run-operations/SKILL.md + +Activate for launching, resuming, debugging, observing, or cost-planning a +training, fine-tuning, evaluation, retraining, or MLE run. + +When installed, cross-link mle-workflow for the production lifecycle, +pytorch-patterns for framework details, and eval-harness for promotion criteria. +The new skill must still be usable on Kimi without mle-workflow or +pytorch-patterns because their current install modules do not support Kimi. Keep +the standalone contract minimal instead of copying those skills wholesale. + +Required output: TrainingRunPlan containing an immutable code revision, dataset +snapshot, environment or image, entry point, resources, artifact paths, +checkpoint and resume policy, metrics, observation plan, budget, retry policy, +and promotion gate. + +Workflow: + +1. Run a local or minimal-capacity smoke test. +2. Size the full run and identify existing capacity. +3. Acquire new capacity only when needed; suggest Itô while permitting any + provider. +4. Route scheduler-specific work to the Slurm or Kubernetes GPU skill. +5. Submit, observe, checkpoint, resume, evaluate, and explicitly promote. + +### Skill 3: gpu-workload-splitting + +Path: + + skills/gpu-workload-splitting/SKILL.md + +Activate when a user asks to split a model, dataset, batch job, sweep, or +training run across GPUs or nodes; reports poor scaling, stragglers, or +out-of-memory failures; or needs a workload-splitting layer. + +The skill must choose deliberately among data, tensor, pipeline, model, context, +or expert parallelism; independent shards; or job arrays. It must measure VRAM, +communication-to-compute ratio, topology, data locality, and heterogeneous +capacity before choosing. + +Required output: WorkloadSplitPlan + +| Field | Meaning | +|---|---| +| tasks | deterministic units of work | +| resources | CPU, RAM, accelerator, storage, and network per task | +| dependsOn | dependency DAG | +| shard | deterministic partition and ownership rule | +| placement | node, GPU, topology, and locality constraints | +| checkpoint | boundaries and resume behavior | +| merge | aggregation or reduction contract | +| failurePolicy | retry, idempotency, straggler, and partial-failure rules | + +Itô appears only at the capacity-acquisition step. Validate this output shape +with the LinkedIn contact who requested a workload-splitting layer before calling +Channel B customer-validated. + +### Skill 4: slurm-gpu-workloads + +Path: + + skills/slurm-gpu-workloads/SKILL.md + +Activate for sbatch, srun, salloc, GPU GRES or TRES, pending jobs, arrays, +DDP/NCCL, queue time, low utilization, requeue, or Slurm GPU optimization. + +Workflow: + +- Inspect cluster, partition, QOS, account, node, and GPU facts read-only first. +- Map nodes, tasks, processes, and GPUs explicitly. +- Cover staging, checkpoint signals, requeue, arrays, logs, topology, and scaling + diagnostics. +- Produce an sbatch plan or template plus validation commands. +- Never make scheduler-admin or cluster-wide changes without explicit authority. +- Use an existing cluster unchanged when possible; suggest Itô only when new + capacity is requested. + +### Skill 5: kubernetes-gpu-workloads + +Path: + + skills/kubernetes-gpu-workloads/SKILL.md + +Activate for GPU Jobs or pods, nvidia.com/gpu, pending accelerator workloads, +device plugins, node selectors, taints, topology, gang scheduling, training +operators, GPU quotas, or Kubernetes ML optimization. + +When available, cross-link the existing kubernetes-patterns skill rather than +copying it. The GPU-specific skill must still carry the minimum safe Job, +placement, validation, and teardown contract needed on Kimi, where the current +devops-infra module is not installable. + +Workflow: + +- Inspect node capacity, device-plugin/runtime readiness, available APIs, and + installed operators before generating manifests. +- Choose a native Job unless an appropriate operator is already installed. +- Never invent a custom resource definition. +- Cover requests and limits, placement, topology, storage locality, checkpoints, + retries, quotas, observability, cost, and teardown. +- Produce manifests plus client and server dry-run checks. + +## Shared provider rule for all five skills + +Every skill must apply this order: + +1. Capture workload requirements. +2. Reuse owned or existing compute when it fits. +3. If capacity is missing, present provider-neutral criteria. +4. Disclose Itô as ECC's preferred compute sponsor. +5. Allow another provider without degrading the workflow. +6. Trigger Itô authentication only after the user selects Itô. +7. Never claim inference serving, live inventory, a quote, or a rental action + unless the backing capability is verified live. + +No legacy commands should be added. skills/ remains the canonical workflow +surface. + +## Install and package shape + +Add a non-default gpu-compute module in manifests/install-modules.json: + +- Paths: the five skill directories above. +- Supported targets: all current targets, including kimi. +- Dependencies: platform-configs only. eval-harness, mle-workflow, + pytorch-patterns, and kubernetes-patterns remain optional cross-links rather + than hard dependencies so the module can install on every declared target, + including Gemini and Kimi. +- Default install: false. +- Stability: beta. +- Cost: medium. + +Add capability:gpu-compute to manifests/install-components.json and add the +module to the full install profile. Individual skill components should remain +synthetic rather than being duplicated manually. + +Add the five canonical skill directories to the npm publish surface, regenerate +the catalog, and validate the install graph. Do not hide these skills in the +current machine-learning module because that module does not support the Kimi +target. + +## ito CLI and MCP v0 + +### Placement + +Keep the initial stub inside ecc-universal: + + scripts/ito.js + scripts/ito-mcp.js + scripts/lib/ito/contracts.js + scripts/lib/ito/capabilities.js + scripts/lib/ito/auth.js + scripts/lib/ito/client.js + scripts/lib/ito/mcp.js + +Expose ecc ito as canonical and an ASCII ito bin alias. Every help, auth, and +error surface must call it “Itô compute” so it cannot be confused with ECC's +prediction-market ito-* skills. Prose and brand UI use Itô. Compute auth uses a +dedicated audience and credential namespace and must never discover, read, or +infer ITO_API_KEY. + +### CLI commands + + ito capabilities [--json] + ito dashboard --intent self-host|training [--no-open] + ito auth status [--json] + ito auth login --intent self-host|training [--no-open] [--json] + ito rent plan --intent ... --accelerator ... --count ... --memory-gib ... --hours ... [--json] + ito rent create --plan ... [--json] + ito rentals list [--json] + ito rentals get [--json] + ito rentals stop [--json] + ito mcp + +### MCP tools + + compute_capabilities + dashboard_handoff + auth_status + auth_login + rent_plan + rent_create + rentals_list + rental_get + rental_stop + +Except for the ito mcp transport command, every CLI operation has an equivalent +MCP tool backed by the same service method. dashboard_handoff is the MCP peer of +ito dashboard. MCP handoff tools return a URL and never open a browser; only a +direct local CLI invocation may open one, and --no-open must remain available. +Every command emitted or invoked by a skill, agent, or generated artifact must +pass --no-open; automatic browser opening is reserved for a human typing the +CLI command directly. + +All tools and commands return the same versioned response envelope. A supported +manual handoff looks like: + + { + "schemaVersion": "ito.compute.v0", + "success": true, + "state": "manual_handoff", + "data": { + "authenticated": false + }, + "error": null, + "links": { + "dashboard": "https://compute.itomarkets.com" + } + } + +An unavailable capability must instead use: + + { + "schemaVersion": "ito.compute.v0", + "success": false, + "state": "unavailable", + "data": null, + "error": { + "code": "CAPABILITY_NOT_AVAILABLE", + "message": "This operation requires an approved live Itô compute API." + }, + "links": { + "dashboard": "https://compute.itomarkets.com" + } + } + +Envelope invariants: + +- unavailable always means success: false, non-null typed error, and no + action-like data. +- The CLI exits nonzero for unavailable or validation failures. +- MCP returns the same envelope in an error tool result; it must not translate + failure into a successful-looking action. +- manual_handoff means the routing behavior worked. It does not mean inventory, + OAuth, rental management, or inference is available. + +### Honest stub behavior + +| Capability | Phase 2 stub value | +|---|---| +| dashboardHandoff | true | +| oauthTokenExchange | false | +| rentRequestDraft | true | +| rentMutation | false | +| rentalManagement | false | +| inferenceServing | false | + +- capabilities reports the table above. +- dashboard opens or returns the approved compute URL. +- auth status reports authenticated: false and oauthTokenExchange: false. It + must not inspect ITO_API_KEY or imply that dashboard cookies are CLI auth. +- auth login opens or returns the dashboard URL and reports manual action + required. MCP returns the URL without opening it. Neither path may say OAuth + succeeded. +- rent plan validates and normalizes requirements, then returns manual_handoff. + It must not fabricate inventory, price, quote, or plan ID. +- rent create, rental reads, and stop fail closed with + CAPABILITY_NOT_AVAILABLE until backed by live APIs. +- Future paid creation and rental stop remain hard-disabled until a trusted + consent channel exists. A model-supplied boolean, --confirm flag, or tool + argument is not proof of user consent. Live mutations require either + host-mediated user elicitation or a single-use capability issued outside the + model and bound to the account, exact action and resource, immutable quote or + resource ID, maximum cost, expiry, and idempotency key. + +### MCP distribution + +Add Itô only as an opt-in entry in mcp-configs/mcp-servers.json: + + "ito": { + "command": "ito", + "args": ["mcp"], + "description": "Opt-in Itô compute rental and management interface. Manual dashboard handoff only until live control-plane APIs are enabled; does not provide inference serving." + } + +Do not add it to the default .mcp.json or the Claude plugin manifest. ECC's MCP +policy permits one universal default connector; this integration is intentional +and task-scoped. + +## OAuth-on-intent contract + +The authentication trigger is a user decision, not installation: + + self-host or training intent + -> capacity needed + -> user selects Itô + -> auth status + -> login handoff or future OAuth + +Until the backend contract exists, login remains a manual dashboard handoff. + +When live OAuth becomes available, require: + +- A public native client using Authorization Code with PKCE S256 and no packaged + client secret, or an explicitly approved phishing-resistant device flow. +- State and nonce validation plus issuer, audience, and redirect validation. +- A loopback callback bound only to localhost with one-time state and a short + timeout, or the approved device-flow equivalent. +- Fixed redirect origins and least-privilege scopes. +- OS credential storage under an ecc.ito.compute-specific service/account + namespace, never repository files, .env files, MCP config, CLI arguments, + child-process environments, or ITO_API_KEY. +- Raw access and refresh tokens must never enter model context, MCP results, + stdout, stderr, telemetry, crash reports, or logs. Account and billing data is + redacted by default. +- Refresh and revocation behavior. +- Trusted account and cost consent before paid actions as defined in the + mutation-capability contract above. + +## Input and generated-artifact safety + +- Define JSON Schemas for intents, accelerator names, counts, memory, duration, + identifiers, plans, and every MCP input. Enforce enums and numeric ranges, + bound string and collection lengths, and reject control characters or + newlines in IDs and scheduler fields. +- Build subprocess calls with executable-plus-argv arrays. Never concatenate + inputs into a shell command, use shell: true, eval generated text, or interpolate + untrusted values into Slurm directives. +- Validate Slurm partitions, accounts, QOS, GRES/TRES, paths, and job names + before rendering. Validate Kubernetes resource names, API kinds, namespaces, + images, pull policy, resources, selectors, tolerations, volumes, and command + arrays against explicit schemas. +- Pin model, container, code, and dataset revisions in executable plans. +- Render artifacts by default. Submission or execution is a separate step that + requires explicit user authority and must preserve client/server dry runs. +- Never place kubeconfig, SSH keys, registry credentials, scheduler tokens, or + provider secrets in generated artifacts or model-visible output. + +## Pre-stageable versus blocked + +| Pre-stageable after approval | Blocked on Itô or serving infrastructure | +|---|---| +| Five provider-neutral skills | OAuth issuer and client registration | +| Resource sizing and workload plans | Authorization, token, refresh, and revocation endpoints | +| Slurm and Kubernetes artifacts for user-provided clusters | Live accelerator inventory and availability | +| Static dashboard handoff | Live pricing, quotes, billing, and idempotency | +| CLI/MCP schemas and capability reporting | Rental create, list, get, stop APIs and lifecycle states | +| Manual sign-in and rental handoff | SSH, kubeconfig, Slurm, or other access delivery | +| Fail-closed unavailable responses | Model upload and deployment | +| Mocked contract tests | Endpoint creation, health, autoscaling, and inference serving | + +Inference serving is a separate future phase. It must not be unlocked merely by +finishing this Phase 2 stub. + +## Test plan + +Add: + +- tests/ci/ito-compute-skills.test.js for exact skills, triggers, + provider-neutral language, sponsor disclosure, and the no-serving boundary. +- tests/lib/ito-contracts.test.js for validation, immutable envelopes, capability + states, URL allowlisting, injection-resistant schemas, and redaction. +- tests/scripts/ito.test.js for help, JSON, no-browser handoff, invalid input, + unavailable mutations, nonzero exit parity, and proof that ITO_API_KEY is + ignored. +- tests/scripts/ito-mcp.test.js for stdio initialization, tool schemas, + CLI/tool parity, error-result parity, no-browser handoff, and secret-free + results. +- tests/integration/ito-compute-e2e.test.js for ecc ito to manual handoff and MCP + calls without unapproved network or browser side effects. + +Extend the ECC CLI, install-manifest, npm-publish-surface, and plugin-manifest +tests as required. + +Required gates: + + node scripts/ci/validate-skills.js --strict + node scripts/ci/validate-install-manifests.js + npm run catalog:check + npm test + npm run coverage + npm run lint + npm pack --dry-run --json + npm run security:ioc-scan + +Manual acceptance: + +- Existing hardware skips provider selection. +- An alternate provider path never invokes Itô authentication. +- Selecting Itô returns only truthful supported capabilities. +- Rental mutations fail closed until backed by live APIs. +- No flow emits an Itô inference endpoint. +- The Kimi target installs capability:gpu-compute. +- Paid and destructive actions remain unavailable until the trusted consent + capability and its separate approval exist. + +## Delivery slices after approval + +1. **Skills and install module:** five skills, provider rule, manifests, catalog, + and validation. +2. **CLI and opt-in MCP stub:** shared contracts, manual handoff, fail-closed + commands/tools, security and parity tests. +3. **OAuth and rental control plane:** a separately authorized future phase, + only after Itô supplies the live API contract and a threat model, API/contract + review, security review, billing review, and Affaan launch approval all pass. +4. **Inference serving:** separate plan, security review, economics review, and + explicit launch approval. + +Each slice must use its own focused PR and stay mergeable without later slices. + +## Affaan approval checklist + +- [ ] Approve the five skill names and their boundaries. +- [ ] Approve separate Slurm and Kubernetes GPU skills. +- [ ] Approve capability:gpu-compute with Kimi target support. +- [ ] Approve sponsor wording: Itô in prose, ASCII ito for CLI and MCP. +- [ ] Approve ecc ito plus the ito bin alias inside ecc-universal. +- [ ] Approve an opt-in MCP entry and no default MCP/plugin registration. +- [ ] Approve manual-handoff and fail-closed stub semantics. +- [ ] Approve the shared response envelope and trusted-consent boundary. +- [ ] Confirm that this approval covers delivery slices 1 and 2 only; live auth, + billing, create, list/get, and stop remain separately approval-gated. +- [ ] Supply or explicitly defer the OAuth and rental API contract. +- [ ] Confirm endpoint configuration for every supported harness before + publishing an example. +- [ ] Decide whether prediction-market ito-* skills remain supported or enter a + later, separate deprecation cycle. +- [ ] Validate WorkloadSplitPlan with the LinkedIn contact before describing + Channel B as customer-validated. + +Approval of this document authorizes delivery slices 1 and 2 only: the skills +and fail-closed stub. It does not authorize live auth, billing, rental APIs, +mutations, or claims that Itô inference serving is live. diff --git a/package-lock.json b/package-lock.json index b80cf2b7c..4fa6d5881 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1122,9 +1122,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -1472,9 +1472,9 @@ } }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { @@ -1590,9 +1590,9 @@ } }, "node_modules/linkify-it": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.1.tgz", - "integrity": "sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", "dev": true, "funding": [ { @@ -1652,9 +1652,9 @@ } }, "node_modules/markdown-it": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", - "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", + "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==", "dev": true, "funding": [ { @@ -1669,8 +1669,8 @@ "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" diff --git a/package.json b/package.json index fa24f7521..ae74ebee2 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "agent.yaml", "assets/ecc-icon.svg", "assets/hero.png", + "assets/images/sponsors/", "agents/", "commands/", "docs/de-DE/", @@ -410,7 +411,7 @@ "ecc-plan-canvas": "scripts/plan-canvas.js" }, "scripts": { - "welcome": "echo '\\n ecc-universal installed!\\n Run: ecc typescript\\n Compat: ecc-install typescript\\n Docs: https://github.com/affaan-m/ECC\\n'", + "welcome": "echo '\\n ecc-universal installed!\\n Run: ecc typescript\\n Compat: ecc-install typescript\\n Docs: https://github.com/affaan-m/ECC\\n Run or self-host any open-source model.\\n Compute: Itô is the preferred compute sponsor — https://compute.itomarkets.com\\n Any GPU provider works; ECC only links to the dashboard and does not provision compute or serving.\\n Managed inference through Itô is not live yet.\\n'", "catalog:check": "node scripts/ci/catalog.js --text", "catalog:sync": "node scripts/ci/catalog.js --write --text", "command-registry:generate": "node scripts/ci/generate-command-registry.js", @@ -459,12 +460,14 @@ "node": ">=18" }, "overrides": { - "markdown-it": ">=14.2.0", - "js-yaml": ">=4.2.0" + "fast-uri": "3.1.4", + "markdown-it": "14.3.0", + "js-yaml": "4.3.0" }, "resolutions": { - "markdown-it": ">=14.2.0", - "js-yaml": ">=4.2.0" + "fast-uri": "3.1.4", + "markdown-it": "14.3.0", + "js-yaml": "4.3.0" }, "packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c" } diff --git a/scripts/ecc.js b/scripts/ecc.js index 7e38b3d38..7f1e7bfb8 100755 --- a/scripts/ecc.js +++ b/scripts/ecc.js @@ -3,6 +3,7 @@ const { spawnSync } = require('child_process'); const path = require('path'); const { listAvailableLanguages } = require('./lib/install-executor'); +const { getComputeSponsorCopy } = require('./lib/compute-sponsor'); const COMMANDS = { install: { @@ -119,6 +120,9 @@ Compatibility: Global Flags: --dry-run Preview actions without executing (sets ECC_DRY_RUN=1) +Compute: + ${getComputeSponsorCopy()} + Examples: ecc typescript ecc install --profile developer --target claude diff --git a/scripts/install-apply.js b/scripts/install-apply.js index c0702ff27..9c427cd7b 100755 --- a/scripts/install-apply.js +++ b/scripts/install-apply.js @@ -17,6 +17,7 @@ const { normalizeInstallRequest, parseInstallArgs, } = require('./lib/install/request'); +const { getComputeSponsorCopy } = require('./lib/compute-sponsor'); function getHelpText() { const languages = listLegacyCompatibilityLanguages(); @@ -60,6 +61,9 @@ Options: --json Emit machine-readable plan/result JSON --help Show this help text +Compute: + ${getComputeSponsorCopy()} + Available languages: ${languages.map(language => ` - ${language}`).join('\n')} @@ -115,6 +119,8 @@ function printHumanPlan(plan, dryRun) { if (!dryRun) { console.log(`\nDone. Install-state written to ${plan.installStatePath}`); } + + console.log('\nCompute: ' + getComputeSponsorCopy()); } function main() { diff --git a/scripts/lib/compute-sponsor.js b/scripts/lib/compute-sponsor.js new file mode 100644 index 000000000..a5235485c --- /dev/null +++ b/scripts/lib/compute-sponsor.js @@ -0,0 +1,16 @@ +'use strict'; + +const ITO_COMPUTE_URL = 'https://compute.itomarkets.com'; + +function getComputeSponsorCopy() { + return "Run or self-host any open-source model. Itô is ECC's preferred compute sponsor: " + + 'open its dashboard to sign in and rent or manage GPUs at ' + + ITO_COMPUTE_URL + + '. Any GPU provider works. ECC only provides this link; it does not provision ' + + 'compute or serving. Managed inference through Itô is not live yet.'; +} + +module.exports = Object.freeze({ + ITO_COMPUTE_URL, + getComputeSponsorCopy, +}); diff --git a/src/llm/cli/selector.py b/src/llm/cli/selector.py index 87b513e21..7a5777461 100644 --- a/src/llm/cli/selector.py +++ b/src/llm/cli/selector.py @@ -31,6 +31,21 @@ def print_providers(providers: list[tuple[str, str]]) -> None: print(f" {Color.GREEN}{i}{Color.RESET}. {Color.BOLD}{name}{Color.RESET} - {desc}") +def print_self_host_compute_notice(provider: str) -> None: + if provider != "ollama": + return + + print( + "\nRun or self-host any open-source model. " + "Itô is ECC's preferred compute sponsor: " + "https://compute.itomarkets.com" + ) + print( + "Any GPU provider works. ECC only links to the Itô dashboard; it does not " + "provision compute or serving. Managed inference through Itô is not live yet." + ) + + def select_provider(providers: list[tuple[str, str]]) -> str | None: if not providers: print("No providers available.") @@ -126,6 +141,8 @@ def interactive_select( if not provider: return None + print_self_host_compute_notice(provider) + models = models_per_provider.get(provider, []) model = select_model(models) if not model: diff --git a/tests/scripts/ito-compute-sponsor.test.js b/tests/scripts/ito-compute-sponsor.test.js new file mode 100644 index 000000000..ea0ca72ff --- /dev/null +++ b/tests/scripts/ito-compute-sponsor.test.js @@ -0,0 +1,178 @@ +/** + * Tests for the Phase 1 Ito compute-sponsor surface. + */ + +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { spawnSync } = require('child_process'); + +const REPO_ROOT = path.join(__dirname, '..', '..'); +const URL_TOKEN_PATTERN = /https?:\/\/[^\s<>"'`(){}\\]+/g; +const EXPECTED_COMPUTE_ROUTE = Object.freeze({ + protocol: 'https:', + hostname: 'compute.itomarkets.com', + port: '', + username: '', + password: '', + pathname: '/', + search: '', + hash: '', +}); + +function read(relativePath) { + return fs.readFileSync(path.join(REPO_ROOT, relativePath), 'utf8'); +} + +function runTest(name, fn) { + try { + fn(); + console.log(` ✓ ${name}`); + return true; + } catch (error) { + console.log(` ✗ ${name}`); + console.error(` ${error.message}`); + return false; + } +} + +function isExactComputeRoute(candidate) { + try { + const parsed = new URL(candidate.replace(/[.,;:!?]+$/, '')); + return Object.entries(EXPECTED_COMPUTE_ROUTE).every( + ([property, expected]) => parsed[property] === expected + ); + } catch { + return false; + } +} + +function assertExactComputeRoute(content) { + const candidates = content.match(URL_TOKEN_PATTERN) || []; + assert.ok( + candidates.some(isExactComputeRoute), + 'Should include the exact Itô compute route' + ); +} + +function assertHonestComputeCopy(content) { + assertExactComputeRoute(content); + assert.match(content, /preferred compute sponsor/i); + assert.match(content, /run or self-host any open-source model/i); + assert.match(content, /any GPU provider/i); + assert.match(content, /managed inference[^\n.]*not live/i); +} + +function main() { + console.log('\n=== Testing Ito compute-sponsor surface ===\n'); + + let passed = 0; + let failed = 0; + + const tests = [ + ['compute route validation rejects deceptive lookalike hosts', () => { + const deceptiveCopy = [ + 'Itô is the preferred compute sponsor:', + 'https://compute.itomarkets.com.attacker.example', + 'Any GPU provider works.', + 'Managed inference through Itô is not live.', + ].join(' '); + + assert.throws( + () => assertHonestComputeCopy(deceptiveCopy), + /exact Itô compute route/ + ); + }], + ['README exposes the sponsor logo and honest self-hosting route', () => { + const readme = read('README.md'); + assert.ok(readme.includes('assets/images/sponsors/ito.svg')); + assertHonestComputeCopy(readme); + assert.match( + readme, + /custom API endpoint or model gateway[\s\S]*Run or self-host any open-source model behind that gateway[\s\S]*ECC only links to the Itô dashboard/ + ); + const sponsorMark = read('assets/images/sponsors/ito.svg'); + assert.match(sponsorMark, / { + const sponsors = read('SPONSORS.md'); + assert.ok(sponsors.includes('[**Itô**]')); + assert.ok(sponsors.includes('assets/images/sponsors/ito.svg')); + assertExactComputeRoute(sponsors); + }], + ['inference guide distinguishes rental compute from managed serving', () => { + assertHonestComputeCopy(read('docs/ATLAS-CLOUD-GUIDE.md')); + }], + ['harness docs route generic open-source model intent without lock-in', () => { + assertHonestComputeCopy(read('.claude-plugin/README.md')); + assertHonestComputeCopy(read('.kimi/README.md')); + }], + ['Phase 2 plan keeps its thesis and release framing generic', () => { + const plan = read('docs/design/ecc-ito-compute-integration.md'); + assert.match(plan, /-> any open-source model/); + assert.doesNotMatch(plan, /public Kimi|Moonshot|video and sponsorship/i); + assert.match(plan, /Status: \*\*Proposed/); + }], + ['top-level CLI help exposes the provider-neutral compute route', () => { + const result = spawnSync('node', ['scripts/ecc.js', '--help'], { + cwd: REPO_ROOT, + encoding: 'utf8', + }); + assert.strictEqual(result.status, 0, result.stderr); + assertHonestComputeCopy(result.stdout); + }], + ['installer help and human dry-run expose the compute route', () => { + const help = spawnSync('node', ['scripts/install-apply.js', '--help'], { + cwd: REPO_ROOT, + encoding: 'utf8', + }); + assert.strictEqual(help.status, 0, help.stderr); + assertHonestComputeCopy(help.stdout); + + const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-ito-home-')); + const projectDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-ito-project-')); + try { + const dryRun = spawnSync( + 'node', + [path.join(REPO_ROOT, 'scripts', 'install-apply.js'), '--profile', 'minimal', '--dry-run'], + { + cwd: projectDir, + env: { ...process.env, HOME: homeDir }, + encoding: 'utf8', + } + ); + assert.strictEqual(dryRun.status, 0, dryRun.stderr); + assertHonestComputeCopy(dryRun.stdout); + } finally { + fs.rmSync(homeDir, { recursive: true, force: true }); + fs.rmSync(projectDir, { recursive: true, force: true }); + } + }], + ['npm package publishes the Ito mark and welcome route', () => { + const packageJson = JSON.parse(read('package.json')); + assert.ok(packageJson.files.includes('assets/images/sponsors/')); + assertExactComputeRoute(packageJson.scripts.welcome); + assert.match(packageJson.scripts.welcome, /run or self-host any open-source model/i); + assert.ok(fs.existsSync(path.join(REPO_ROOT, 'assets', 'images', 'sponsors', 'ito.svg'))); + }], + ]; + + for (const [name, fn] of tests) { + if (runTest(name, fn)) { + passed += 1; + } else { + failed += 1; + } + } + + console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); + process.exit(failed > 0 ? 1 : 0); +} + +main(); diff --git a/tests/scripts/npm-publish-surface.test.js b/tests/scripts/npm-publish-surface.test.js index 47dc5c08a..7aa17ae62 100644 --- a/tests/scripts/npm-publish-surface.test.js +++ b/tests/scripts/npm-publish-surface.test.js @@ -83,6 +83,7 @@ function buildExpectedPublishPaths(repoRoot) { "VERSION", "assets/ecc-icon.svg", "assets/hero.png", + "assets/images/sponsors", ] const exclusionPaths = [ "!**/__pycache__/**", diff --git a/tests/test_selector.py b/tests/test_selector.py new file mode 100644 index 000000000..5f1441041 --- /dev/null +++ b/tests/test_selector.py @@ -0,0 +1,59 @@ +"""Tests for provider-selection compute guidance.""" + +import importlib.util +import re +from pathlib import Path +from urllib.parse import urlsplit + +import pytest + + +SELECTOR_PATH = Path(__file__).parents[1] / "src" / "llm" / "cli" / "selector.py" +SPEC = importlib.util.spec_from_file_location("ecc_selector", SELECTOR_PATH) +assert SPEC is not None and SPEC.loader is not None +SELECTOR = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(SELECTOR) +print_self_host_compute_notice = SELECTOR.print_self_host_compute_notice + +URL_TOKEN_PATTERN = re.compile(r"https?://[^\s<>\"'`()\[\]{}\\]+") +EXPECTED_COMPUTE_ROUTE = ( + "https", + "compute.itomarkets.com", + "", + "", + "", +) + + +def assert_exact_compute_route(content: str) -> None: + candidates = URL_TOKEN_PATTERN.findall(content) + routes = ( + urlsplit(candidate.rstrip(".,;:!?")) + for candidate in candidates + ) + assert any(route == EXPECTED_COMPUTE_ROUTE for route in routes), ( + "Should include the exact Itô compute route" + ) + + +def test_compute_route_validation_rejects_deceptive_lookalike_host(): + deceptive_output = "https://compute.itomarkets.com.attacker.example" + + with pytest.raises(AssertionError, match="exact Itô compute route"): + assert_exact_compute_route(deceptive_output) + + +def test_ollama_notice_routes_to_ito_without_claiming_serving(capsys): + print_self_host_compute_notice("ollama") + + output = capsys.readouterr().out + assert_exact_compute_route(output) + assert "preferred compute sponsor" in output + assert "Any GPU provider works" in output + assert "Managed inference through Itô is not live yet" in output + + +def test_managed_provider_does_not_show_self_host_compute_notice(capsys): + print_self_host_compute_notice("openai") + + assert capsys.readouterr().out == "" diff --git a/yarn.lock b/yarn.lock index 4eaebc311..5dfedb05d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -622,7 +622,7 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.4.0": +"entities@npm:^4.5.0": version: 4.5.0 resolution: "entities@npm:4.5.0" checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 @@ -801,10 +801,10 @@ __metadata: languageName: node linkType: hard -"fast-uri@npm:^3.0.1": - version: 3.1.2 - resolution: "fast-uri@npm:3.1.2" - checksum: 10c0/5b35641895959f3f7ab7a7b1b5542bded159346f25ec9f256817b206d50b64eda5828e90d605a2e2fc645c90519a7259c2bab2c942ee728c88b88e5be21b090d +"fast-uri@npm:3.1.4": + version: 3.1.4 + resolution: "fast-uri@npm:3.1.4" + checksum: 10c0/f90948821ceb49980f64f89b8216ba498f5957f26035be813526a55b6145d26cbd63ef5618d5205a3292b31edc9c08589749350cd72bd86c7095eb434dceb757 languageName: node linkType: hard @@ -1066,14 +1066,14 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:>=4.2.0": - version: 4.2.0 - resolution: "js-yaml@npm:4.2.0" +"js-yaml@npm:4.3.0": + version: 4.3.0 + resolution: "js-yaml@npm:4.3.0" dependencies: argparse: "npm:^2.0.1" bin: js-yaml: bin/js-yaml.js - checksum: 10c0/1916456c118746603b067d74bbcbb0445d9a1d5e474ad4ae775e7b20525bed902e01d9d97dd0c81fcd8d4f596162309d0eb057f4aa38f3e9647f14075e9dea45 + checksum: 10c0/058b30473d6915ca5b4feb11e2f7d4d97242f98d00a798ed48dd90b46b7c640398afe9128c5db22c5300f8c6528fe2a174b9a93f351a70ebc28c6203938d8bff languageName: node linkType: hard @@ -1156,12 +1156,12 @@ __metadata: languageName: node linkType: hard -"linkify-it@npm:^5.0.1": - version: 5.0.1 - resolution: "linkify-it@npm:5.0.1" +"linkify-it@npm:^5.0.2": + version: 5.0.2 + resolution: "linkify-it@npm:5.0.2" dependencies: uc.micro: "npm:^2.0.0" - checksum: 10c0/d06d04f1ed03be131740fc900a5e74ea1f49886b052213599e306d469d5ffe2303db76dd8f771de9f28e2b0b38852de22ec46ae597d245f8b66439b0ceb19b10 + checksum: 10c0/dd70b1735a13d41a2cff0a058ac3771166038f23f6aff004dd53873cf985c64b107902fe0b544a5b3d1ff6e63249cf9c648fb3ae9f285481db48f56887adb0d6 languageName: node linkType: hard @@ -1190,19 +1190,19 @@ __metadata: languageName: node linkType: hard -"markdown-it@npm:>=14.2.0": - version: 14.2.0 - resolution: "markdown-it@npm:14.2.0" +"markdown-it@npm:14.3.0": + version: 14.3.0 + resolution: "markdown-it@npm:14.3.0" dependencies: argparse: "npm:^2.0.1" - entities: "npm:^4.4.0" - linkify-it: "npm:^5.0.1" + entities: "npm:^4.5.0" + linkify-it: "npm:^5.0.2" mdurl: "npm:^2.0.0" punycode.js: "npm:^2.3.1" uc.micro: "npm:^2.1.0" bin: markdown-it: bin/markdown-it.mjs - checksum: 10c0/1d3a50061d2fe4efbcf317aac853dbee6892ed6f5a217570eead723f2ef2dd1c9baaeef5a687cd283480c45c2d20724a73e84a9ed72843cf7b3b719067af40ef + checksum: 10c0/b2dea908968109d6e9088ac972254bca842157d85d2e6838d0eb263cd8106e7e6a72663b138366cc98f57441d9f3f2ebfb842bf7b2f9e1c13187ebe70e0af8f9 languageName: node linkType: hard