Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7b27731
feat(cua): add candidate browser lifecycle
jyaunches Aug 6, 2026
cc58878
merge(main): refresh PR #8484
jyaunches Aug 6, 2026
26d20d5
fix(cua): address lifecycle review findings
jyaunches Aug 6, 2026
0d02908
docs(cua): clarify authority host validation
jyaunches Aug 6, 2026
5e7ca39
fix(cua): address automated review follow-up
jyaunches Aug 6, 2026
19b9a11
docs(cua): complete lifecycle command examples
jyaunches Aug 6, 2026
391b71a
fix(cua): reject circular canonical values
jyaunches Aug 6, 2026
1ecc7d6
merge(main): refresh PR #8484
jyaunches Aug 6, 2026
c8b5c1b
merge: resolve conflicts with main
github-actions[bot] Aug 7, 2026
648875f
merge: resolve conflicts with main
github-actions[bot] Aug 7, 2026
090e5dd
merge: resolve conflicts with main
github-actions[bot] Aug 7, 2026
9649c50
fix(cua): restore gated candidate-only scope
jyaunches Aug 7, 2026
546e8a4
Merge remote-tracking branch 'origin/main' into codex/pr8484-ghas-sco…
jyaunches Aug 7, 2026
3ae734b
refactor(cua): keep onboarding wiring focused
jyaunches Aug 7, 2026
b89885c
test(cua): linearize candidate fixtures
jyaunches Aug 7, 2026
df741fe
test(cua): align private candidate coverage
jyaunches Aug 7, 2026
2e0eac6
fix(cua): hold authority through execution
jyaunches Aug 7, 2026
8b352da
test(cua): cover gated status diagnostics
jyaunches Aug 7, 2026
94f71bd
test(cua): keep authority checks linear
jyaunches Aug 7, 2026
1ac6702
merge: resolve conflicts with main
github-actions[bot] Aug 10, 2026
b8ae63e
merge(cua): refresh candidate slice from main
jyaunches Aug 10, 2026
d410de4
docs(cua): clarify private candidate contract
jyaunches Aug 10, 2026
64f85b6
test(shields): stabilize policy stress coverage
jyaunches Aug 10, 2026
e580a37
merge(cua): refresh candidate slice from main
jyaunches Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ci/source-architecture-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"src/lib/actions/inference-set.ts": 32,
"src/lib/actions/sandbox/connect.ts": 38,
"src/lib/actions/sandbox/destroy.ts": 29,
"src/lib/actions/sandbox/doctor.ts": 29,
"src/lib/actions/sandbox/doctor.ts": 30,
"src/lib/actions/sandbox/status-snapshot.ts": 21,
"src/lib/actions/sandbox/policy-channel.ts": 29,
"src/lib/actions/sandbox/process-recovery.ts": 21,
"src/lib/actions/sandbox/rebuild-pipeline.ts": 28,
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@
"nemoclaw-blueprint/",
"managed-inference/",
"schemas/network-policy.schema.json",
"schemas/cua-lifecycle.schema.json",
"schemas/cua-target-manifest.schema.json",
"schemas/sandbox-policy.schema.json",
"scripts/",
"docs/resources/local-credential-form.html",
Expand Down
192 changes: 192 additions & 0 deletions schemas/cua-lifecycle.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
{
"$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/NVIDIA/NemoClaw/schemas/cua-lifecycle.schema.json",
"title": "NemoClaw CUA candidate lifecycle contract",
"description": "Credential-free serialized readiness for the private CUA candidate install-and-inspect slice.",
"$ref": "#/$defs/runtimeReadiness",
"$defs": {
"digest": {
"type": "string",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"safeId": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._+-]*$"
},
"safeModel": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}(?:/[A-Za-z0-9][A-Za-z0-9._-]{0,127}){0,7}$"
},
"component": {
"type": "object",
"additionalProperties": false,
"required": ["name", "version", "digest", "owner"],
"properties": {
"name": { "$ref": "#/$defs/safeId" },
"version": { "$ref": "#/$defs/safeId" },
"digest": { "$ref": "#/$defs/digest" },
"owner": { "$ref": "#/$defs/safeId" }
}
},
"candidateQualification": {
"type": "object",
"additionalProperties": false,
"required": ["state", "environmentDigest", "bundleReceiptDigest"],
"properties": {
"state": { "const": "candidate" },
"environmentDigest": { "$ref": "#/$defs/digest" },
"bundleReceiptDigest": { "$ref": "#/$defs/digest" }
}
},
"runtimeReadiness": {
"type": "object",
"additionalProperties": false,
"required": [
"schemaVersion",
"kind",
"agent",
"mode",
"status",
"sourceRevision",
"sourceClean",
"runtimeManifestDigest",
"providerAuthorityDigest",
"qualification",
"components",
"inference",
"appliedPolicy",
"commands",
"limits",
"requiredCapabilities",
"targetOperations",
"securityOperations",
"taskOperations"
],
"properties": {
"schemaVersion": { "const": "1.0.0" },
"kind": { "const": "runtime-readiness" },
"agent": { "const": "nemocua" },
"mode": { "const": "standalone" },
"status": { "enum": ["candidate", "unavailable", "incompatible"] },
"sourceRevision": {
"type": "string",
"pattern": "^[a-f0-9]{40}$"
},
"sourceClean": { "const": true },
"runtimeManifestDigest": { "$ref": "#/$defs/digest" },
"providerAuthorityDigest": { "$ref": "#/$defs/digest" },
"qualification": {
"oneOf": [
{ "$ref": "#/$defs/candidateQualification" },
{ "type": "null" }
]
},
"components": {
"type": "object",
"additionalProperties": false,
"required": [
"openshell",
"runtime",
"sandboxImage",
"targetAdapter",
"policy",
"taskProtocol",
"securityVerifier"
],
"properties": {
"openshell": { "$ref": "#/$defs/component" },
"runtime": { "$ref": "#/$defs/component" },
"sandboxImage": { "$ref": "#/$defs/component" },
"targetAdapter": { "$ref": "#/$defs/component" },
"policy": { "$ref": "#/$defs/component" },
"taskProtocol": { "$ref": "#/$defs/component" },
"securityVerifier": { "$ref": "#/$defs/component" }
}
},
"inference": {
"type": "object",
"additionalProperties": false,
"required": ["provider", "model", "routeDigest"],
"properties": {
"provider": { "$ref": "#/$defs/safeId" },
"model": { "$ref": "#/$defs/safeModel" },
"routeDigest": { "$ref": "#/$defs/digest" }
}
},
"appliedPolicy": {
"type": "object",
"additionalProperties": false,
"required": ["revision", "digest"],
"properties": {
"revision": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"digest": { "$ref": "#/$defs/digest" }
}
},
"commands": {
"type": "object",
"additionalProperties": false,
"required": ["interactive", "headless", "version", "smoke"],
"properties": {
"interactive": { "const": true },
"headless": { "const": true },
"version": { "const": true },
"smoke": { "const": true }
}
},
"limits": {
"type": "object",
"additionalProperties": false,
"required": ["targetsPerWorker", "activeTasksPerTarget"],
"properties": {
"targetsPerWorker": { "const": 1 },
"activeTasksPerTarget": { "const": 1 }
}
},
"requiredCapabilities": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"uniqueItems": true,
"items": { "enum": ["browser", "computer", "terminal"] }
},
"targetOperations": {
"type": "array",
"maxItems": 0
},
"securityOperations": {
"type": "array",
"maxItems": 0
},
"taskOperations": {
"type": "array",
"maxItems": 0
}
},
"oneOf": [
{
"required": ["status", "qualification"],
"properties": {
"status": { "const": "candidate" },
"qualification": { "$ref": "#/$defs/candidateQualification" }
}
},
{
"required": ["status", "qualification"],
"properties": {
"status": { "enum": ["unavailable", "incompatible"] },
"qualification": { "type": "null" }
}
}
]
}
}
}
115 changes: 115 additions & 0 deletions schemas/cua-target-manifest.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/NVIDIA/NemoClaw/schemas/cua-target-manifest.schema.json",
"$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\nSPDX-License-Identifier: Apache-2.0",
"title": "NemoClaw CUA target manifest",
"description": "Credential-free immutable identities in the private CUA candidate target bundle.",
"type": "object",
"additionalProperties": false,
"required": [
"schemaVersion",
"kind",
"identityDigest",
"platform",
"image",
"serviceBundle",
"capabilities"
],
"properties": {
"schemaVersion": {
"type": "string",
"pattern": "^1\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
},
"kind": {
"const": "target-manifest"
},
"identityDigest": {
"$ref": "#/$defs/digest"
},
"platform": {
"$ref": "#/$defs/safeSelector"
},
"image": {
"$ref": "#/$defs/componentIdentity"
},
"serviceBundle": {
"$ref": "#/$defs/componentIdentity"
},
"capabilities": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"items": {
"$ref": "#/$defs/capabilityIdentity"
}
}
},
"$defs": {
"digest": {
"type": "string",
"pattern": "^sha256:[a-f0-9]{64}$"
},
"safeId": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"
},
"safeSelector": {
"type": "string",
"minLength": 1,
"maxLength": 256,
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]*$"
},
"componentIdentity": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"version",
"digest",
"owner"
],
"properties": {
"name": {
"$ref": "#/$defs/safeId"
},
"version": {
"type": "string",
"minLength": 1,
"maxLength": 128
},
"digest": {
"$ref": "#/$defs/digest"
},
"owner": {
"type": "string",
"minLength": 1,
"maxLength": 128
}
}
},
"capabilityIdentity": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"protocolVersion"
],
"properties": {
"id": {
"enum": [
"browser",
"computer",
"terminal"
]
},
"protocolVersion": {
"type": "string",
"minLength": 1,
"maxLength": 128
}
}
}
}
}
3 changes: 1 addition & 2 deletions src/lib/actions/inference-get.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { captureOpenshell } from "../adapters/openshell/runtime";
import { OPENSHELL_PROBE_TIMEOUT_MS } from "../adapters/openshell/timeouts";
import { sanitizeRouteValueForDisplay } from "../inference/config";
import { getLiveGatewayInference } from "../inference/live";
import { captureOpenshell, getLiveGatewayInference } from "../inference/live";

export interface InferenceGetOptions {
json?: boolean;
Expand Down
11 changes: 9 additions & 2 deletions src/lib/actions/inference-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export interface InferenceSetDeps extends InferenceGatewayRestartDeps {
getSandbox: (name: string) => SandboxEntry | null;
listSandboxes: () => { sandboxes: SandboxEntry[]; defaultSandbox: string | null };
updateSandbox: (name: string, updates: Partial<SandboxEntry>) => boolean;
updateSandboxInferenceRoute?: (name: string, updates: Partial<SandboxEntry>) => boolean;
getRequestedAgent: () => string | null | undefined;
loadSession: () => onboardSession.Session | null;
updateSession: (
Expand Down Expand Up @@ -243,6 +244,7 @@ function defaultDeps(): InferenceSetDeps {
getSandbox: registry.getSandbox,
listSandboxes: registry.listSandboxes,
updateSandbox: registry.updateSandbox,
updateSandboxInferenceRoute: registry.updateSandboxInferenceRoute,
getRequestedAgent: () => process.env.NEMOCLAW_AGENT,
loadSession: onboardSession.loadSession,
updateSession: onboardSession.updateSession,
Expand Down Expand Up @@ -1130,7 +1132,7 @@ async function runInferenceSetWithoutHostLock(
nimContainer: registryMetadata.nimContainer ?? null,
});
if (
!deps.updateSandbox(
!(deps.updateSandboxInferenceRoute ?? deps.updateSandbox)(
sandboxName,
registryFields(
resolveAgentInferenceApi(
Expand Down Expand Up @@ -1164,7 +1166,12 @@ async function runInferenceSetWithoutHostLock(
// Refresh the registry with config-derived API-family metadata before the
// crash-prone in-sandbox sync (#3725/#3726). Explicit operator-supplied
// metadata remains authoritative when present.
if (!deps.updateSandbox(sandboxName, registryFields(preferredInferenceApi))) {
if (
!(deps.updateSandboxInferenceRoute ?? deps.updateSandbox)(
sandboxName,
registryFields(preferredInferenceApi),
)
) {
throw new InferenceSetError(
`Failed to update NemoClaw registry for sandbox '${sandboxName}'.`,
);
Expand Down
Loading
Loading