hermes(hux): freeze hux.v1 contract 1.0.0 and move reference modules into the foundation package
HUX-11 contract freeze: identity/capabilities/manifest/error records, event turn and idempotency and delegation/side-effect kinds, memory no-store/supersedes/ retrieval removal, revisions for optimistic concurrency, artifact access, budget scope/spend/subagents and external side-effect gating, notebook notes and dedupe keys. ADR-0001 records the wire and compatibility rules. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNPhwu2bsaRNg3DETSAZoM
This commit is contained in:
parent
af5ee4697b
commit
47a9fd8354
0
dockerfiles/hermes-hux-foundation/hux/__init__.py
Normal file
0
dockerfiles/hermes-hux-foundation/hux/__init__.py
Normal file
@ -10,13 +10,18 @@ loudly rather than silently passing.
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
CONTRACT_DIR = Path(__file__).resolve().parents[1] / "contracts" / "hux"
|
||||
CONTRACT_DIR = Path(
|
||||
os.environ.get("HUX_CONTRACT_DIR")
|
||||
or Path(__file__).resolve().parents[3] / "services" / "hermes" / "contracts" / "hux"
|
||||
)
|
||||
SCHEMA_FILES = (
|
||||
"common.schema.json",
|
||||
"identity.schema.json",
|
||||
"event.schema.json",
|
||||
"memory.schema.json",
|
||||
"project.schema.json",
|
||||
@ -13,7 +13,7 @@ import os
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any
|
||||
|
||||
from hux_contracts import load_flags
|
||||
from hux.contracts import load_flags
|
||||
|
||||
EFFORT_ORDER = ("low", "medium", "high", "xhigh")
|
||||
|
||||
@ -44,11 +44,12 @@ RELEASE_EVIDENCE: dict[str, tuple[str, ...]] = {
|
||||
|
||||
CAPABILITIES = (
|
||||
"read_files", "write_files", "shell", "network", "web_search", "send_message",
|
||||
"memory_write", "artifact_write", "spend_tokens", "delegate", "deploy",
|
||||
"memory_write", "artifact_write", "spend_tokens", "delegate", "deploy", "external_side_effect",
|
||||
)
|
||||
_READ_ONLY = frozenset({"read_files", "web_search", "spend_tokens"})
|
||||
_MUTATING = frozenset({"write_files", "shell", "send_message", "memory_write", "artifact_write", "delegate"})
|
||||
_EXTERNAL = frozenset({"network", "deploy"})
|
||||
_ALWAYS_ASK = frozenset({"deploy", "external_side_effect"})
|
||||
|
||||
MODE_CATALOG: dict[str, dict[str, Any]] = {
|
||||
"fast": {
|
||||
@ -140,12 +141,12 @@ def memory_policy_violations(entry: dict[str, Any]) -> list[str]:
|
||||
|
||||
|
||||
def default_capability_matrix() -> dict[str, dict[str, str]]:
|
||||
"""Autonomy level -> capability -> allow|ask|deny. Deploy always asks."""
|
||||
"""Autonomy level -> capability -> allow|ask|deny. Deploy and external side effects always ask."""
|
||||
matrix: dict[str, dict[str, str]] = {}
|
||||
for level in ("ask_first", "safe", "autonomous"):
|
||||
row: dict[str, str] = {}
|
||||
for capability in CAPABILITIES:
|
||||
if capability == "deploy":
|
||||
if capability in _ALWAYS_ASK:
|
||||
row[capability] = "ask"
|
||||
elif capability in _READ_ONLY:
|
||||
row[capability] = "allow"
|
||||
@ -171,7 +172,7 @@ def effective_decision(policy: dict[str, Any], capability: str, now: datetime |
|
||||
if grant["decision"] == "deny":
|
||||
return "deny"
|
||||
decision = grant["decision"]
|
||||
if capability == "deploy" and decision == "allow":
|
||||
if capability in _ALWAYS_ASK and decision == "allow":
|
||||
return "ask"
|
||||
return decision
|
||||
|
||||
@ -10,9 +10,12 @@ Schemas live in `services/hermes/contracts/hux/` as JSON Schema 2020-12 with
|
||||
one worked example per record under `examples/`. The rules a schema cannot
|
||||
express (state machines, capability matrix, mode catalog, privacy defaults,
|
||||
suggestion gating, flag dependencies) live in
|
||||
`services/hermes/scripts/hux_policy.py`; `hux_contracts.py` validates records
|
||||
without external packages. `testing/tests/test_hermes_hux_contracts.py` keeps
|
||||
schemas, examples, rules and the live Switchyard catalog in agreement.
|
||||
`dockerfiles/hermes-hux-foundation/hux/rules.py`; `hux/contracts.py`
|
||||
validates records without external packages.
|
||||
`testing/tests/test_hermes_hux_contract_schemas.py` keeps schemas, examples,
|
||||
rules and the live Switchyard catalog in agreement. The contract is frozen at
|
||||
`1.0.0` (see `docs/hux/ADR-0001-hux-v1-contract-freeze.md` for identity
|
||||
headers, `If-Match`, idempotency and compatibility rules).
|
||||
|
||||
## Where the backend lives
|
||||
|
||||
@ -51,11 +54,12 @@ tenant. Responses are the records below, wrapped as `{"items": [...],
|
||||
|
||||
| Area | Routes | Record |
|
||||
|---|---|---|
|
||||
| Foundation (HUX-11) | `GET /hux/v1/capabilities`, `GET /hux/v1/manifest` | `hux.capabilities.v1`, `hux.manifest.v1`, errors as `hux.error.v1` |
|
||||
| Events (HUX-01) | `GET /hux/v1/conversations/{id}/events?after_seq=N` (JSON), `GET .../events/stream` (SSE, `id:` = seq) | `hux.event.v1` |
|
||||
| Memory (HUX-02) | `GET/POST /hux/v1/memory`, `POST /hux/v1/memory/{id}/{approve,reject,forget}`, `GET /hux/v1/memory/export` | `hux.memory.v1` |
|
||||
| Projects (HUX-03) | `GET/POST/PATCH /hux/v1/projects`, `GET/PATCH /hux/v1/conversations`, `POST /hux/v1/conversations/{id}/branch`, `GET /hux/v1/search?q=` | `hux.project.v1`, `hux.conversation.v1` |
|
||||
| Artifacts (HUX-04) | `GET/POST /hux/v1/artifacts`, `POST /hux/v1/artifacts/{id}/versions`, `GET .../versions/{n}/diff?from=`, `POST .../promote` | `hux.artifact.v1` |
|
||||
| Autonomy (HUX-05) | `GET/PUT /hux/v1/policy?scope=`, `GET /hux/v1/approvals`, `POST /hux/v1/approvals/{id}` (`once|session|always|deny`, the gateway's own choices), `POST /hux/v1/runs/{id}/stop` returns the receipt | `hux.policy.v1`, `hux.approval.v1`, `hux.cancel_receipt.v1` |
|
||||
| Autonomy (HUX-05) | `GET/PUT /hux/v1/policy?scope=`, `GET /hux/v1/approvals`, `POST /hux/v1/approvals/{id}` (`once|session|always|deny`, the gateway's own choices), `POST /hux/v1/runs/{id}/stop` returns the receipt, `GET /hux/v1/runs/{id}/budget` | `hux.policy.v1`, `hux.approval.v1`, `hux.cancel_receipt.v1`, `hux.budget_state.v1` |
|
||||
| Modes (HUX-06) | `GET /hux/v1/modes`, `PUT /hux/v1/conversations/{id}/mode` | `hux.mode.v1` |
|
||||
| Research (HUX-08) | `GET /hux/v1/messages/{id}/citations`, `GET /hux/v1/sources/{id}`, `GET/PATCH /hux/v1/notebooks/{id}` | `hux.source.v1`, `hux.passage.v1`, `hux.citation.v1`, `hux.research_notebook.v1` |
|
||||
| Onboarding (HUX-09) | `GET /hux/v1/suggestions?context=`, `POST /hux/v1/suggestions/{id}/{dismiss,never,acted}` | `hux.suggestion.v1`, `hux.suggestion_state.v1` |
|
||||
|
||||
69
docs/hux/ADR-0001-hux-v1-contract-freeze.md
Normal file
69
docs/hux/ADR-0001-hux-v1-contract-freeze.md
Normal file
@ -0,0 +1,69 @@
|
||||
# ADR-0001: freeze the hux.v1 contract at 1.0.0
|
||||
|
||||
Status: accepted, 2026-08-24. Owners: Claude (backend), Codex (integration).
|
||||
|
||||
## Decision
|
||||
|
||||
`services/hermes/contracts/hux/` is frozen as contract version `1.0.0`
|
||||
(`VERSION`, `flags.json.contract_version`). It is the only shape any HUX
|
||||
surface (Chat, Worker, Telegram, voice) codes against. The Python reference
|
||||
implementation lives in `dockerfiles/hermes-hux-foundation/hux/`:
|
||||
`contracts.py` validates records, `rules.py` holds the governance rules the
|
||||
schemas cannot express.
|
||||
|
||||
Records carry their own version in `schema` (`hux.event.v1`). Wire fixtures
|
||||
under `services/hermes/contracts/hux/examples/` are validated by
|
||||
`testing/tests/test_hermes_hux_contract_schemas.py` and are the exchange
|
||||
format with Codex: a change to a fixture is a change to the contract.
|
||||
|
||||
## What 1.0.0 covers
|
||||
|
||||
| Requirement | Where |
|
||||
|---|---|
|
||||
| Tenant and surface identity | `common.identity` (tenant_slot, hashed subject, surface, trust: router/relay/worker) — carried on every stored record that a caller can create |
|
||||
| Capability negotiation, default-off flags | `identity.capabilities` (`GET /hux/v1/capabilities`), `flags.json`; a card is enabled only when its dependency chain is enabled (`rules.flag_enabled`) |
|
||||
| Redacted activity events | `event`: `seq` (monotonic per conversation), `turn`, `correlation_id`, `idempotency_key`, replay by `after_seq`, kinds for decisions, delegation, failure/completion, cancellation, budget and side-effect gates; `detail` is allowlisted per kind and scrubbed |
|
||||
| Memory | `memory`: provenance, `source`, `reason`, `sensitivity`, `topic`, `ttl`, `approval_mode` incl. `no_store`, `status` incl. `no_store`, edit via `supersedes`, delete via `forgotten`, retrieval removal via `retrievable=false`, revisioned |
|
||||
| Projects, lineage, artifacts, versions, diffs, authorization | `project`, `conversation.branch`, `artifact.versions[]` immutable with `content_ref` hash, `diff_from`, `lineage`, `promotion`, `access`, `revision` |
|
||||
| Autonomy | `permission.policy` (levels, grants, budgets: tokens/tool calls/wall clock/spend/subagents/delegations/scope), `approval` with `request.external`, `cancellation_receipt`, `budget_state` |
|
||||
| Research | `citation.source/passage/citation/notebook` with classification, support verdict, notes, assumptions, unresolved questions, `dedupe_key` |
|
||||
| Additive migrations, rollback readers | `identity.manifest` (`data_layout_version`, `min_reader_contract_version`); rules in "Compatibility" below |
|
||||
|
||||
## Wire conventions
|
||||
|
||||
- Identity is asserted by the trusted hop, never by the client body: the
|
||||
chat router sets `X-Hermes-Tenant-Identity: slot-N`, `X-Hux-Subject:
|
||||
usr_<hash>`, `X-Hux-Surface: chat`; the Telegram relay adds the relay key
|
||||
and `trust: relay`; the Worker uses `trust: worker`. Requests without a
|
||||
complete, well-formed identity are `401 unauthorized`. The service must
|
||||
only be reachable from those hops (NetworkPolicy is Codex's to enforce).
|
||||
- Mutations of revisioned records send `If-Match: <revision>`; a mismatch is
|
||||
`409 conflict` with `hux.error.v1`. Creates accept `Idempotency-Key`; a
|
||||
repeat returns the original record with `200` instead of a duplicate.
|
||||
- Lists return `{"items": [...], "next": <cursor|null>}`. Event pages use
|
||||
`after_seq`; the SSE stream sets `id:` to `seq` so `Last-Event-ID`
|
||||
resumes exactly.
|
||||
- Errors are always `hux.error.v1`. A disabled card answers `404 flag_off`
|
||||
so an old client and a flag-off server look identical.
|
||||
- Every read and mutation writes a `common.audit_outcome`.
|
||||
|
||||
## Compatibility rules
|
||||
|
||||
1. Within `1.x`: only add optional fields, enum values or new record kinds.
|
||||
Never rename, remove, change a type, or make an optional field required.
|
||||
2. Readers ignore unknown fields and unknown enum values they do not need;
|
||||
a reader that needs a new enum value must check `contract_version`.
|
||||
3. On-disk data is append-only per family; `data_layout_version` bumps only
|
||||
for additive layout changes, and `min_reader_contract_version` says the
|
||||
oldest service that can still read it. Rolling the service back to any
|
||||
version ≥ `min_reader_contract_version` leaves data readable.
|
||||
4. Anything outside 1–3 is `hux.v2`, served beside v1 until every surface
|
||||
has moved.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Codex codes UI against the fixtures, not against the running service.
|
||||
- Claude's implementation cards (HUX-01/02/03/04/05/08/10/11) may add
|
||||
optional fields but must ship the fixture change in the same commit.
|
||||
- If Codex needs a shared-interface change, it lands as a `1.(x+1).0`
|
||||
revision of the schemas and fixtures here, never as an informal edit.
|
||||
1
services/hermes/contracts/hux/VERSION
Normal file
1
services/hermes/contracts/hux/VERSION
Normal file
@ -0,0 +1 @@
|
||||
1.0.0
|
||||
@ -5,65 +5,192 @@
|
||||
"description": "Typed, versioned output (HUX-04). Content lives behind content_ref; versions are immutable and diffable; lineage records what an artifact was derived from; promotion copies a version into a project.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "owner", "type", "title", "current_version", "versions", "sensitivity", "created_at", "updated_at"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"owner",
|
||||
"type",
|
||||
"title",
|
||||
"current_version",
|
||||
"versions",
|
||||
"sensitivity",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"revision",
|
||||
"access"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.artifact.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"owner": {"$ref": "common.schema.json#/$defs/user_ref"},
|
||||
"conversation_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"project_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"type": {"type": "string", "enum": ["markdown", "code", "html", "svg", "image", "json", "csv", "document", "audio"]},
|
||||
"language": {"type": "string", "maxLength": 40},
|
||||
"title": {"type": "string", "minLength": 1, "maxLength": 200},
|
||||
"current_version": {"type": "integer", "minimum": 1},
|
||||
"schema": {
|
||||
"const": "hux.artifact.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "common.schema.json#/$defs/user_ref"
|
||||
},
|
||||
"conversation_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"project_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"markdown",
|
||||
"code",
|
||||
"html",
|
||||
"svg",
|
||||
"image",
|
||||
"json",
|
||||
"csv",
|
||||
"document",
|
||||
"audio"
|
||||
]
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"maxLength": 40
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"current_version": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"versions": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["version", "created_at", "created_by", "content_ref"],
|
||||
"required": [
|
||||
"version",
|
||||
"created_at",
|
||||
"created_by",
|
||||
"content_ref"
|
||||
],
|
||||
"properties": {
|
||||
"version": {"type": "integer", "minimum": 1},
|
||||
"created_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"created_by": {"$ref": "common.schema.json#/$defs/actor"},
|
||||
"message_id": {"type": "string", "maxLength": 120},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"created_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"created_by": {
|
||||
"$ref": "common.schema.json#/$defs/actor"
|
||||
},
|
||||
"message_id": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"content_ref": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["hash", "bytes", "mime"],
|
||||
"required": [
|
||||
"hash",
|
||||
"bytes",
|
||||
"mime"
|
||||
],
|
||||
"properties": {
|
||||
"hash": {"$ref": "common.schema.json#/$defs/sha256"},
|
||||
"bytes": {"type": "integer", "minimum": 0},
|
||||
"mime": {"type": "string", "maxLength": 120}
|
||||
"hash": {
|
||||
"$ref": "common.schema.json#/$defs/sha256"
|
||||
},
|
||||
"bytes": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"mime": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
"diff_from": {"type": "integer", "minimum": 1},
|
||||
"diff_from": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"lineage": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["artifact_id", "version"],
|
||||
"required": [
|
||||
"artifact_id",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"artifact_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"version": {"type": "integer", "minimum": 1}
|
||||
"artifact_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"note": {"type": "string", "maxLength": 200}
|
||||
"note": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"promotion": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["project_id", "version", "at"],
|
||||
"required": [
|
||||
"project_id",
|
||||
"version",
|
||||
"at"
|
||||
],
|
||||
"properties": {
|
||||
"project_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"version": {"type": "integer", "minimum": 1},
|
||||
"at": {"$ref": "common.schema.json#/$defs/timestamp"}
|
||||
"project_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensitivity": {"$ref": "common.schema.json#/$defs/sensitivity"},
|
||||
"created_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"updated_at": {"$ref": "common.schema.json#/$defs/timestamp"}
|
||||
"sensitivity": {
|
||||
"$ref": "common.schema.json#/$defs/sensitivity"
|
||||
},
|
||||
"created_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"updated_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"revision": {
|
||||
"$ref": "common.schema.json#/$defs/revision"
|
||||
},
|
||||
"access": {
|
||||
"type": "object",
|
||||
"description": "Owner-only unless a share token is minted; shares are read-only and expire.",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"mode"
|
||||
],
|
||||
"properties": {
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"owner",
|
||||
"shared_readonly"
|
||||
]
|
||||
},
|
||||
"share_expires_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,82 +7,320 @@
|
||||
"source": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "kind", "title", "classification", "retrieved_at"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"kind",
|
||||
"title",
|
||||
"classification",
|
||||
"retrieved_at"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.source.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"kind": {"type": "string", "enum": ["web", "document", "artifact", "memory", "tool_output", "dataset"]},
|
||||
"uri": {"type": "string", "maxLength": 2000},
|
||||
"title": {"type": "string", "minLength": 1, "maxLength": 300},
|
||||
"publisher": {"type": "string", "maxLength": 200},
|
||||
"published_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"retrieved_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"classification": {"type": "string", "enum": ["primary", "secondary", "unknown"]},
|
||||
"content_hash": {"$ref": "common.schema.json#/$defs/sha256"},
|
||||
"provenance": {"$ref": "common.schema.json#/$defs/provenance"}
|
||||
"schema": {
|
||||
"const": "hux.source.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"web",
|
||||
"document",
|
||||
"artifact",
|
||||
"memory",
|
||||
"tool_output",
|
||||
"dataset"
|
||||
]
|
||||
},
|
||||
"uri": {
|
||||
"type": "string",
|
||||
"maxLength": 2000
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 300
|
||||
},
|
||||
"publisher": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
},
|
||||
"published_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"retrieved_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"classification": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"primary",
|
||||
"secondary",
|
||||
"unknown"
|
||||
]
|
||||
},
|
||||
"content_hash": {
|
||||
"$ref": "common.schema.json#/$defs/sha256"
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "common.schema.json#/$defs/provenance"
|
||||
},
|
||||
"dedupe_key": {
|
||||
"type": "string",
|
||||
"description": "Stable key used to merge duplicates: sha256 of normalised uri (source), of source_id+text hash (passage), of message_id+claim+passages (citation).",
|
||||
"pattern": "^sha256:[0-9a-f]{64}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passage": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "source_id", "text", "hash"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"source_id",
|
||||
"text",
|
||||
"hash"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.passage.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"source_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"text": {"type": "string", "minLength": 1, "maxLength": 4000},
|
||||
"hash": {"$ref": "common.schema.json#/$defs/sha256"},
|
||||
"schema": {
|
||||
"const": "hux.passage.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"source_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 4000
|
||||
},
|
||||
"hash": {
|
||||
"$ref": "common.schema.json#/$defs/sha256"
|
||||
},
|
||||
"locator": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"page": {"type": "integer", "minimum": 1},
|
||||
"selector": {"type": "string", "maxLength": 500},
|
||||
"line_start": {"type": "integer", "minimum": 1},
|
||||
"line_end": {"type": "integer", "minimum": 1},
|
||||
"char_start": {"type": "integer", "minimum": 0},
|
||||
"char_end": {"type": "integer", "minimum": 0}
|
||||
"page": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"selector": {
|
||||
"type": "string",
|
||||
"maxLength": 500
|
||||
},
|
||||
"line_start": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"line_end": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"char_start": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"char_end": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"dedupe_key": {
|
||||
"type": "string",
|
||||
"description": "Stable key used to merge duplicates: sha256 of normalised uri (source), of source_id+text hash (passage), of message_id+claim+passages (citation).",
|
||||
"pattern": "^sha256:[0-9a-f]{64}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"citation": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "message_id", "claim", "passage_ids", "support"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"message_id",
|
||||
"claim",
|
||||
"passage_ids",
|
||||
"support"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.citation.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"message_id": {"type": "string", "minLength": 1, "maxLength": 120},
|
||||
"claim": {"type": "string", "minLength": 1, "maxLength": 1000},
|
||||
"passage_ids": {"type": "array", "minItems": 1, "maxItems": 32, "uniqueItems": true, "items": {"$ref": "common.schema.json#/$defs/id"}},
|
||||
"support": {"type": "string", "enum": ["supports", "partially_supports", "contradicts", "unverified"]},
|
||||
"note": {"type": "string", "maxLength": 500}
|
||||
"schema": {
|
||||
"const": "hux.citation.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"message_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
},
|
||||
"claim": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 1000
|
||||
},
|
||||
"passage_ids": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 32,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
},
|
||||
"support": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"supports",
|
||||
"partially_supports",
|
||||
"contradicts",
|
||||
"unverified"
|
||||
]
|
||||
},
|
||||
"note": {
|
||||
"type": "string",
|
||||
"maxLength": 500
|
||||
},
|
||||
"dedupe_key": {
|
||||
"type": "string",
|
||||
"description": "Stable key used to merge duplicates: sha256 of normalised uri (source), of source_id+text hash (passage), of message_id+claim+passages (citation).",
|
||||
"pattern": "^sha256:[0-9a-f]{64}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notebook": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "conversation_id", "question", "status", "source_ids", "passage_ids", "citation_ids", "assumptions", "unresolved_questions", "updated_at"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"conversation_id",
|
||||
"question",
|
||||
"status",
|
||||
"source_ids",
|
||||
"passage_ids",
|
||||
"citation_ids",
|
||||
"assumptions",
|
||||
"unresolved_questions",
|
||||
"updated_at",
|
||||
"notes",
|
||||
"revision"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.research_notebook.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"conversation_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"question": {"type": "string", "minLength": 1, "maxLength": 1000},
|
||||
"status": {"type": "string", "enum": ["open", "answered", "abandoned"]},
|
||||
"source_ids": {"type": "array", "uniqueItems": true, "items": {"$ref": "common.schema.json#/$defs/id"}},
|
||||
"passage_ids": {"type": "array", "uniqueItems": true, "items": {"$ref": "common.schema.json#/$defs/id"}},
|
||||
"citation_ids": {"type": "array", "uniqueItems": true, "items": {"$ref": "common.schema.json#/$defs/id"}},
|
||||
"assumptions": {"type": "array", "maxItems": 64, "items": {"type": "string", "minLength": 1, "maxLength": 500}},
|
||||
"unresolved_questions": {"type": "array", "maxItems": 64, "items": {"type": "string", "minLength": 1, "maxLength": 500}},
|
||||
"updated_at": {"$ref": "common.schema.json#/$defs/timestamp"}
|
||||
"schema": {
|
||||
"const": "hux.research_notebook.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"conversation_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"question": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 1000
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"open",
|
||||
"answered",
|
||||
"abandoned"
|
||||
]
|
||||
},
|
||||
"source_ids": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
},
|
||||
"passage_ids": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
},
|
||||
"citation_ids": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
},
|
||||
"assumptions": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 500
|
||||
}
|
||||
},
|
||||
"unresolved_questions": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 500
|
||||
}
|
||||
},
|
||||
"updated_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"notes": {
|
||||
"type": "array",
|
||||
"maxItems": 128,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"at",
|
||||
"text"
|
||||
],
|
||||
"properties": {
|
||||
"at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2000
|
||||
},
|
||||
"source_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"revision": {
|
||||
"$ref": "common.schema.json#/$defs/revision"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{"$ref": "#/$defs/source"},
|
||||
{"$ref": "#/$defs/passage"},
|
||||
{"$ref": "#/$defs/citation"},
|
||||
{"$ref": "#/$defs/notebook"}
|
||||
{
|
||||
"$ref": "#/$defs/source"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/passage"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/citation"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/notebook"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -25,98 +25,317 @@
|
||||
},
|
||||
"surface": {
|
||||
"type": "string",
|
||||
"enum": ["chat", "worker", "telegram", "voice", "api"]
|
||||
"enum": [
|
||||
"chat",
|
||||
"worker",
|
||||
"telegram",
|
||||
"voice",
|
||||
"api"
|
||||
]
|
||||
},
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"description": "Provider class, never a vendor model name. Switchyard resolves the class to a concrete target.",
|
||||
"enum": ["codex", "claude", "local"]
|
||||
"enum": [
|
||||
"codex",
|
||||
"claude",
|
||||
"local"
|
||||
]
|
||||
},
|
||||
"effort": {
|
||||
"type": "string",
|
||||
"enum": ["low", "medium", "high", "xhigh"]
|
||||
"enum": [
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"xhigh"
|
||||
]
|
||||
},
|
||||
"sensitivity": {
|
||||
"type": "string",
|
||||
"description": "public: safe to show anywhere; personal: user-owned but not sensitive; sensitive: health/finance/legal/relationships; restricted: credentials, minors, biometric, location traces.",
|
||||
"enum": ["public", "personal", "sensitive", "restricted"]
|
||||
"enum": [
|
||||
"public",
|
||||
"personal",
|
||||
"sensitive",
|
||||
"restricted"
|
||||
]
|
||||
},
|
||||
"redaction": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["level"],
|
||||
"required": [
|
||||
"level"
|
||||
],
|
||||
"properties": {
|
||||
"level": {"type": "string", "enum": ["none", "partial", "full"]},
|
||||
"reason": {"type": "string", "maxLength": 200}
|
||||
"level": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none",
|
||||
"partial",
|
||||
"full"
|
||||
]
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
}
|
||||
}
|
||||
},
|
||||
"actor": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["type", "id"],
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"type": {"type": "string", "enum": ["user", "assistant", "tool", "system", "operator"]},
|
||||
"id": {"type": "string", "minLength": 1, "maxLength": 120},
|
||||
"display": {"type": "string", "maxLength": 120}
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"user",
|
||||
"assistant",
|
||||
"tool",
|
||||
"system",
|
||||
"operator"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
},
|
||||
"display": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
"route": {
|
||||
"type": "object",
|
||||
"description": "What was asked of Switchyard and what it resolved. requested is a friendly mode or a route id; resolved_target is the Switchyard target name.",
|
||||
"additionalProperties": false,
|
||||
"required": ["requested"],
|
||||
"required": [
|
||||
"requested"
|
||||
],
|
||||
"properties": {
|
||||
"requested": {"type": "string", "minLength": 1, "maxLength": 120},
|
||||
"resolved_target": {"type": "string", "maxLength": 120},
|
||||
"provider": {"$ref": "#/$defs/provider"},
|
||||
"effort": {"$ref": "#/$defs/effort"}
|
||||
"requested": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
},
|
||||
"resolved_target": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"provider": {
|
||||
"$ref": "#/$defs/provider"
|
||||
},
|
||||
"effort": {
|
||||
"$ref": "#/$defs/effort"
|
||||
}
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"},
|
||||
"image_digest": {"$ref": "#/$defs/sha256"}
|
||||
"commit": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{40}$"
|
||||
},
|
||||
"image_digest": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
}
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"type": "object",
|
||||
"description": "Who produced a record, on which surface, under which session/run, through which route, from which build.",
|
||||
"additionalProperties": false,
|
||||
"required": ["surface", "actor", "recorded_at"],
|
||||
"required": [
|
||||
"surface",
|
||||
"actor",
|
||||
"recorded_at"
|
||||
],
|
||||
"properties": {
|
||||
"surface": {"$ref": "#/$defs/surface"},
|
||||
"actor": {"$ref": "#/$defs/actor"},
|
||||
"recorded_at": {"$ref": "#/$defs/timestamp"},
|
||||
"session_id": {"type": "string", "maxLength": 120},
|
||||
"conversation_id": {"$ref": "#/$defs/id"},
|
||||
"message_id": {"type": "string", "maxLength": 120},
|
||||
"run_id": {"type": "string", "maxLength": 120},
|
||||
"route": {"$ref": "#/$defs/route"},
|
||||
"build": {"$ref": "#/$defs/build"}
|
||||
"surface": {
|
||||
"$ref": "#/$defs/surface"
|
||||
},
|
||||
"actor": {
|
||||
"$ref": "#/$defs/actor"
|
||||
},
|
||||
"recorded_at": {
|
||||
"$ref": "#/$defs/timestamp"
|
||||
},
|
||||
"session_id": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"conversation_id": {
|
||||
"$ref": "#/$defs/id"
|
||||
},
|
||||
"message_id": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"route": {
|
||||
"$ref": "#/$defs/route"
|
||||
},
|
||||
"build": {
|
||||
"$ref": "#/$defs/build"
|
||||
}
|
||||
}
|
||||
},
|
||||
"evidence_ref": {
|
||||
"type": "object",
|
||||
"description": "Pointer to the thing that justifies a record. Never inline the payload here; the UI expands it through the owning API.",
|
||||
"additionalProperties": false,
|
||||
"required": ["kind", "id"],
|
||||
"required": [
|
||||
"kind",
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["message", "tool_call", "tool_result", "artifact_version", "source", "passage", "memory", "approval", "run", "url", "file", "build", "flux", "pod"]
|
||||
"enum": [
|
||||
"message",
|
||||
"tool_call",
|
||||
"tool_result",
|
||||
"artifact_version",
|
||||
"source",
|
||||
"passage",
|
||||
"memory",
|
||||
"approval",
|
||||
"run",
|
||||
"url",
|
||||
"file",
|
||||
"build",
|
||||
"flux",
|
||||
"pod"
|
||||
]
|
||||
},
|
||||
"id": {"type": "string", "minLength": 1, "maxLength": 200},
|
||||
"uri": {"type": "string", "maxLength": 2000},
|
||||
"hash": {"$ref": "#/$defs/sha256"}
|
||||
"id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"uri": {
|
||||
"type": "string",
|
||||
"maxLength": 2000
|
||||
},
|
||||
"hash": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"maxItems": 32,
|
||||
"uniqueItems": true,
|
||||
"items": {"type": "string", "pattern": "^[a-z0-9][a-z0-9-]{0,39}$"}
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9][a-z0-9-]{0,39}$"
|
||||
}
|
||||
},
|
||||
"tenant_slot": {
|
||||
"type": "string",
|
||||
"description": "Router-assigned tenant process slot.",
|
||||
"pattern": "^slot-[0-9]{1,3}$"
|
||||
},
|
||||
"trust": {
|
||||
"type": "string",
|
||||
"description": "How the identity was asserted: router (browser via chat router), relay (Telegram relay key), worker (owner control plane).",
|
||||
"enum": [
|
||||
"router",
|
||||
"relay",
|
||||
"worker"
|
||||
]
|
||||
},
|
||||
"identity": {
|
||||
"type": "object",
|
||||
"description": "Resolved caller identity. Every record path and authorization check derives from tenant_slot + subject.",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"tenant_slot",
|
||||
"subject",
|
||||
"surface",
|
||||
"trust"
|
||||
],
|
||||
"properties": {
|
||||
"tenant_slot": {
|
||||
"$ref": "#/$defs/tenant_slot"
|
||||
},
|
||||
"subject": {
|
||||
"$ref": "#/$defs/user_ref"
|
||||
},
|
||||
"surface": {
|
||||
"$ref": "#/$defs/surface"
|
||||
},
|
||||
"trust": {
|
||||
"$ref": "#/$defs/trust"
|
||||
}
|
||||
}
|
||||
},
|
||||
"revision": {
|
||||
"type": "integer",
|
||||
"description": "Optimistic concurrency counter. Mutations send If-Match: <revision>; a mismatch is 409.",
|
||||
"minimum": 1
|
||||
},
|
||||
"idempotency_key": {
|
||||
"type": "string",
|
||||
"description": "Client-chosen key; a repeat with the same key returns the original record instead of creating a duplicate.",
|
||||
"pattern": "^[A-Za-z0-9._:-]{8,120}$"
|
||||
},
|
||||
"contract_version": {
|
||||
"type": "string",
|
||||
"pattern": "^1\\.[0-9]+\\.[0-9]+$"
|
||||
},
|
||||
"audit_outcome": {
|
||||
"type": "object",
|
||||
"description": "Result of an authorization decision, written for every read and mutation.",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"at",
|
||||
"identity",
|
||||
"action",
|
||||
"resource",
|
||||
"outcome"
|
||||
],
|
||||
"properties": {
|
||||
"at": {
|
||||
"$ref": "#/$defs/timestamp"
|
||||
},
|
||||
"identity": {
|
||||
"$ref": "#/$defs/identity"
|
||||
},
|
||||
"action": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z_]+\\.[a-z_]+$"
|
||||
},
|
||||
"resource": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
},
|
||||
"outcome": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"allow",
|
||||
"deny",
|
||||
"not_found",
|
||||
"conflict",
|
||||
"flag_off"
|
||||
]
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,38 +5,120 @@
|
||||
"description": "One entry in the per-conversation activity stream (HUX-01). seq is strictly monotonic per conversation; consumers resume with after_seq. Every other HUX record that changes state emits exactly one event.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "seq", "ts", "conversation_id", "kind", "summary", "provenance", "sensitivity", "redaction"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"seq",
|
||||
"ts",
|
||||
"conversation_id",
|
||||
"kind",
|
||||
"summary",
|
||||
"provenance",
|
||||
"sensitivity",
|
||||
"redaction",
|
||||
"turn",
|
||||
"identity"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.event.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"seq": {"type": "integer", "minimum": 0},
|
||||
"ts": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"conversation_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"run_id": {"type": "string", "maxLength": 120},
|
||||
"parent_event_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"correlation_id": {"type": "string", "maxLength": 120},
|
||||
"schema": {
|
||||
"const": "hux.event.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"seq": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"ts": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"conversation_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"parent_event_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"correlation_id": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"message.user", "message.assistant",
|
||||
"decision.route", "decision.plan",
|
||||
"tool.call", "tool.result",
|
||||
"approval.requested", "approval.resolved",
|
||||
"memory.proposed", "memory.committed", "memory.forgotten",
|
||||
"artifact.created", "artifact.version", "artifact.promoted",
|
||||
"message.user",
|
||||
"message.assistant",
|
||||
"decision.route",
|
||||
"decision.plan",
|
||||
"tool.call",
|
||||
"tool.result",
|
||||
"approval.requested",
|
||||
"approval.resolved",
|
||||
"memory.proposed",
|
||||
"memory.committed",
|
||||
"memory.forgotten",
|
||||
"artifact.created",
|
||||
"artifact.version",
|
||||
"artifact.promoted",
|
||||
"citation.attached",
|
||||
"mode.changed",
|
||||
"run.started", "run.cancelled", "run.completed", "run.failed",
|
||||
"run.started",
|
||||
"run.cancelled",
|
||||
"run.completed",
|
||||
"run.failed",
|
||||
"privacy.notice",
|
||||
"suggestion.shown", "suggestion.dismissed",
|
||||
"release.transition"
|
||||
"suggestion.shown",
|
||||
"suggestion.dismissed",
|
||||
"release.transition",
|
||||
"delegation.started",
|
||||
"delegation.completed",
|
||||
"delegation.failed",
|
||||
"memory.suppressed",
|
||||
"memory.retrieval_removed",
|
||||
"budget.exhausted",
|
||||
"side_effect.blocked",
|
||||
"side_effect.released"
|
||||
]
|
||||
},
|
||||
"summary": {"type": "string", "minLength": 1, "maxLength": 280},
|
||||
"detail": {"type": "object", "description": "Kind-specific payload. Redacted according to redaction.level before leaving the tenant."},
|
||||
"evidence": {"type": "array", "maxItems": 64, "items": {"$ref": "common.schema.json#/$defs/evidence_ref"}},
|
||||
"provenance": {"$ref": "common.schema.json#/$defs/provenance"},
|
||||
"sensitivity": {"$ref": "common.schema.json#/$defs/sensitivity"},
|
||||
"redaction": {"$ref": "common.schema.json#/$defs/redaction"}
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 280
|
||||
},
|
||||
"detail": {
|
||||
"type": "object",
|
||||
"description": "Kind-specific payload restricted to the allowlisted keys for that kind. Raw tool arguments, logs, credentials and tokens are never stored here; the redaction pipeline drops unknown keys and scrubs secret patterns before the event is written."
|
||||
},
|
||||
"evidence": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"$ref": "common.schema.json#/$defs/evidence_ref"
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "common.schema.json#/$defs/provenance"
|
||||
},
|
||||
"sensitivity": {
|
||||
"$ref": "common.schema.json#/$defs/sensitivity"
|
||||
},
|
||||
"redaction": {
|
||||
"$ref": "common.schema.json#/$defs/redaction"
|
||||
},
|
||||
"turn": {
|
||||
"type": "integer",
|
||||
"description": "User-visible turn number within the conversation; several events share a turn.",
|
||||
"minimum": 0
|
||||
},
|
||||
"idempotency_key": {
|
||||
"$ref": "common.schema.json#/$defs/idempotency_key"
|
||||
},
|
||||
"identity": {
|
||||
"$ref": "common.schema.json#/$defs/identity"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1,30 @@
|
||||
{"schema":"hux.approval.v1","id":"apr_0001aaaa","run_id":"run_9f","conversation_id":"conv_0001abcd","capability":"write_files","request":{"summary":"Write supplier-comparison.md to the workspace","risk":"low","evidence":[{"kind":"tool_call","id":"call-7"}]},"status":"approved","decision":{"choice":"session","by":{"type":"user","id":"usr_0123456789abcdef"},"at":"2026-08-23T10:00:03Z"},"requested_at":"2026-08-23T10:00:02Z","expires_at":"2026-08-23T10:10:02Z"}
|
||||
{
|
||||
"schema": "hux.approval.v1",
|
||||
"id": "apr_0001aaaa",
|
||||
"run_id": "run_9f",
|
||||
"conversation_id": "conv_0001abcd",
|
||||
"capability": "write_files",
|
||||
"request": {
|
||||
"summary": "Write supplier-comparison.md to the workspace",
|
||||
"risk": "low",
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "tool_call",
|
||||
"id": "call-7"
|
||||
}
|
||||
],
|
||||
"external": false
|
||||
},
|
||||
"status": "approved",
|
||||
"decision": {
|
||||
"choice": "session",
|
||||
"by": {
|
||||
"type": "user",
|
||||
"id": "usr_0123456789abcdef"
|
||||
},
|
||||
"at": "2026-08-23T10:00:03Z"
|
||||
},
|
||||
"requested_at": "2026-08-23T10:00:02Z",
|
||||
"expires_at": "2026-08-23T10:10:02Z",
|
||||
"idempotency_key": "run_9f:approval:call-7"
|
||||
}
|
||||
|
||||
@ -1 +1,53 @@
|
||||
{"schema":"hux.artifact.v1","id":"art_0001aaaa","owner":"usr_0123456789abcdef","conversation_id":"conv_0001abcd","project_id":"prj_0001aaaa","type":"markdown","title":"Supplier comparison","current_version":2,"versions":[{"version":1,"created_at":"2026-08-22T10:00:00Z","created_by":{"type":"assistant","id":"hermes"},"message_id":"msg-20","content_ref":{"hash":"sha256:2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881","bytes":2048,"mime":"text/markdown"}},{"version":2,"created_at":"2026-08-23T10:00:00Z","created_by":{"type":"user","id":"usr_0123456789abcdef"},"content_ref":{"hash":"sha256:2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881","bytes":2100,"mime":"text/markdown"},"diff_from":1,"note":"added delivery lead times"}],"promotion":{"project_id":"prj_0001aaaa","version":2,"at":"2026-08-23T10:00:05Z"},"sensitivity":"personal","created_at":"2026-08-22T10:00:00Z","updated_at":"2026-08-23T10:00:05Z"}
|
||||
{
|
||||
"schema": "hux.artifact.v1",
|
||||
"id": "art_0001aaaa",
|
||||
"owner": "usr_0123456789abcdef",
|
||||
"conversation_id": "conv_0001abcd",
|
||||
"project_id": "prj_0001aaaa",
|
||||
"type": "markdown",
|
||||
"title": "Supplier comparison",
|
||||
"current_version": 2,
|
||||
"versions": [
|
||||
{
|
||||
"version": 1,
|
||||
"created_at": "2026-08-22T10:00:00Z",
|
||||
"created_by": {
|
||||
"type": "assistant",
|
||||
"id": "hermes"
|
||||
},
|
||||
"message_id": "msg-20",
|
||||
"content_ref": {
|
||||
"hash": "sha256:2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881",
|
||||
"bytes": 2048,
|
||||
"mime": "text/markdown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": 2,
|
||||
"created_at": "2026-08-23T10:00:00Z",
|
||||
"created_by": {
|
||||
"type": "user",
|
||||
"id": "usr_0123456789abcdef"
|
||||
},
|
||||
"content_ref": {
|
||||
"hash": "sha256:2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881",
|
||||
"bytes": 2100,
|
||||
"mime": "text/markdown"
|
||||
},
|
||||
"diff_from": 1,
|
||||
"note": "added delivery lead times"
|
||||
}
|
||||
],
|
||||
"promotion": {
|
||||
"project_id": "prj_0001aaaa",
|
||||
"version": 2,
|
||||
"at": "2026-08-23T10:00:05Z"
|
||||
},
|
||||
"sensitivity": "personal",
|
||||
"created_at": "2026-08-22T10:00:00Z",
|
||||
"updated_at": "2026-08-23T10:00:05Z",
|
||||
"revision": 2,
|
||||
"access": {
|
||||
"mode": "owner"
|
||||
}
|
||||
}
|
||||
|
||||
20
services/hermes/contracts/hux/examples/budget_state.json
Normal file
20
services/hermes/contracts/hux/examples/budget_state.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"schema": "hux.budget_state.v1",
|
||||
"run_id": "run_9f",
|
||||
"spent": {
|
||||
"tokens": 120000,
|
||||
"tool_calls": 12,
|
||||
"wall_clock_seconds": 300,
|
||||
"delegations": 1,
|
||||
"spend_units": 10,
|
||||
"subagents": 1
|
||||
},
|
||||
"limits": {
|
||||
"tokens_per_run": 200000,
|
||||
"tool_calls_per_run": 40,
|
||||
"wall_clock_seconds": 900,
|
||||
"spend_units": 50,
|
||||
"subagents_per_run": 2
|
||||
},
|
||||
"exhausted": []
|
||||
}
|
||||
@ -1 +1,24 @@
|
||||
{"schema":"hux.cancel_receipt.v1","id":"rcpt_0001aaaa","run_id":"run_9f","requested_by":{"type":"user","id":"usr_0123456789abcdef"},"requested_at":"2026-08-23T10:00:04Z","acknowledged_at":"2026-08-23T10:00:04Z","completed_at":"2026-08-23T10:00:05Z","outcome":"cancelled","side_effects":[{"description":"Partial file supplier-comparison.md left in workspace","reverted":true,"evidence":{"kind":"file","id":"supplier-comparison.md"}}]}
|
||||
{
|
||||
"schema": "hux.cancel_receipt.v1",
|
||||
"id": "rcpt_0001aaaa",
|
||||
"run_id": "run_9f",
|
||||
"requested_by": {
|
||||
"type": "user",
|
||||
"id": "usr_0123456789abcdef"
|
||||
},
|
||||
"requested_at": "2026-08-23T10:00:04Z",
|
||||
"acknowledged_at": "2026-08-23T10:00:04Z",
|
||||
"completed_at": "2026-08-23T10:00:05Z",
|
||||
"outcome": "cancelled",
|
||||
"side_effects": [
|
||||
{
|
||||
"description": "Partial file supplier-comparison.md left in workspace",
|
||||
"reverted": true,
|
||||
"evidence": {
|
||||
"kind": "file",
|
||||
"id": "supplier-comparison.md"
|
||||
}
|
||||
}
|
||||
],
|
||||
"conversation_id": "conv_0001abcd"
|
||||
}
|
||||
|
||||
33
services/hermes/contracts/hux/examples/capabilities.json
Normal file
33
services/hermes/contracts/hux/examples/capabilities.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"schema": "hux.capabilities.v1",
|
||||
"contract_version": "1.0.0",
|
||||
"identity": {
|
||||
"tenant_slot": "slot-3",
|
||||
"subject": "usr_0123456789abcdef",
|
||||
"surface": "chat",
|
||||
"trust": "router"
|
||||
},
|
||||
"cards": [
|
||||
{
|
||||
"card": "HUX-11",
|
||||
"flag": "hux.foundation",
|
||||
"enabled": true,
|
||||
"routes": [
|
||||
"/hux/v1/capabilities"
|
||||
]
|
||||
},
|
||||
{
|
||||
"card": "HUX-01",
|
||||
"flag": "hux.activity_timeline",
|
||||
"enabled": false,
|
||||
"routes": [
|
||||
"/hux/v1/conversations/{id}/events",
|
||||
"/hux/v1/conversations/{id}/events/stream"
|
||||
]
|
||||
}
|
||||
],
|
||||
"server": {
|
||||
"commit": "d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06",
|
||||
"image_digest": "sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"
|
||||
}
|
||||
}
|
||||
@ -1 +1,11 @@
|
||||
{"schema":"hux.citation.v1","id":"cit_0001aaaa","message_id":"msg-44","claim":"Base cabinets from Example Co take three to four weeks.","passage_ids":["psg_0001aaaa"],"support":"supports"}
|
||||
{
|
||||
"schema": "hux.citation.v1",
|
||||
"id": "cit_0001aaaa",
|
||||
"message_id": "msg-44",
|
||||
"claim": "Base cabinets from Example Co take three to four weeks.",
|
||||
"passage_ids": [
|
||||
"psg_0001aaaa"
|
||||
],
|
||||
"support": "supports",
|
||||
"dedupe_key": "sha256:abababababababababababababababababababababababababababababababab"
|
||||
}
|
||||
|
||||
@ -1 +1,24 @@
|
||||
{"schema":"hux.conversation.v1","id":"conv_0001abcd","owner":"usr_0123456789abcdef","project_id":"prj_0001aaaa","title":"Compare cabinet suppliers","tags":["suppliers"],"pinned":false,"archived":false,"mode":"research","branch":{"parent_conversation_id":"conv_0000root","branch_point_message_id":"msg-12"},"artifact_ids":["art_0001aaaa"],"last_message_at":"2026-08-23T10:00:01Z","created_at":"2026-08-22T09:00:00Z","updated_at":"2026-08-23T10:00:01Z"}
|
||||
{
|
||||
"schema": "hux.conversation.v1",
|
||||
"id": "conv_0001abcd",
|
||||
"owner": "usr_0123456789abcdef",
|
||||
"project_id": "prj_0001aaaa",
|
||||
"title": "Compare cabinet suppliers",
|
||||
"tags": [
|
||||
"suppliers"
|
||||
],
|
||||
"pinned": false,
|
||||
"archived": false,
|
||||
"mode": "research",
|
||||
"branch": {
|
||||
"parent_conversation_id": "conv_0000root",
|
||||
"branch_point_message_id": "msg-12"
|
||||
},
|
||||
"artifact_ids": [
|
||||
"art_0001aaaa"
|
||||
],
|
||||
"last_message_at": "2026-08-23T10:00:01Z",
|
||||
"created_at": "2026-08-22T09:00:00Z",
|
||||
"updated_at": "2026-08-23T10:00:01Z",
|
||||
"revision": 3
|
||||
}
|
||||
|
||||
9
services/hermes/contracts/hux/examples/error.json
Normal file
9
services/hermes/contracts/hux/examples/error.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"schema": "hux.error.v1",
|
||||
"status": 409,
|
||||
"code": "conflict",
|
||||
"message": "revision 2 does not match current revision 3",
|
||||
"details": [
|
||||
"If-Match: 2"
|
||||
]
|
||||
}
|
||||
@ -1 +1,52 @@
|
||||
{"schema":"hux.event.v1","id":"evt_0001aaaa","seq":17,"ts":"2026-08-23T10:00:01Z","conversation_id":"conv_0001abcd","run_id":"run_9f","kind":"decision.route","summary":"Routed to a high-effort Claude target for a multi-step refactor","detail":{"candidates":3},"evidence":[{"kind":"run","id":"run_9f"}],"provenance":{"surface":"chat","actor":{"type":"assistant","id":"hermes"},"recorded_at":"2026-08-23T10:00:00Z","session_id":"sess-a1","conversation_id":"conv_0001abcd","run_id":"run_9f","route":{"requested":"thoughtful","resolved_target":"claude_sonnet_high","provider":"claude","effort":"high"},"build":{"commit":"d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06","image_digest":"sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"}},"sensitivity":"personal","redaction":{"level":"none"}}
|
||||
{
|
||||
"schema": "hux.event.v1",
|
||||
"id": "evt_0001aaaa",
|
||||
"seq": 17,
|
||||
"ts": "2026-08-23T10:00:01Z",
|
||||
"conversation_id": "conv_0001abcd",
|
||||
"run_id": "run_9f",
|
||||
"kind": "decision.route",
|
||||
"summary": "Routed to a high-effort Claude target for a multi-step refactor",
|
||||
"detail": {
|
||||
"candidates": 3
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "run",
|
||||
"id": "run_9f"
|
||||
}
|
||||
],
|
||||
"provenance": {
|
||||
"surface": "chat",
|
||||
"actor": {
|
||||
"type": "assistant",
|
||||
"id": "hermes"
|
||||
},
|
||||
"recorded_at": "2026-08-23T10:00:00Z",
|
||||
"session_id": "sess-a1",
|
||||
"conversation_id": "conv_0001abcd",
|
||||
"run_id": "run_9f",
|
||||
"route": {
|
||||
"requested": "thoughtful",
|
||||
"resolved_target": "claude_sonnet_high",
|
||||
"provider": "claude",
|
||||
"effort": "high"
|
||||
},
|
||||
"build": {
|
||||
"commit": "d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06",
|
||||
"image_digest": "sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"
|
||||
}
|
||||
},
|
||||
"sensitivity": "personal",
|
||||
"redaction": {
|
||||
"level": "none"
|
||||
},
|
||||
"turn": 5,
|
||||
"identity": {
|
||||
"tenant_slot": "slot-3",
|
||||
"subject": "usr_0123456789abcdef",
|
||||
"surface": "chat",
|
||||
"trust": "router"
|
||||
},
|
||||
"idempotency_key": "run_9f:decision:17"
|
||||
}
|
||||
|
||||
8
services/hermes/contracts/hux/examples/manifest.json
Normal file
8
services/hermes/contracts/hux/examples/manifest.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"schema": "hux.manifest.v1",
|
||||
"contract_version": "1.0.0",
|
||||
"data_layout_version": 1,
|
||||
"min_reader_contract_version": "1.0.0",
|
||||
"created_at": "2026-08-24T00:00:00Z",
|
||||
"updated_at": "2026-08-24T00:00:00Z"
|
||||
}
|
||||
@ -1 +1,65 @@
|
||||
{"schema":"hux.memory.v1","id":"mem_0001aaaa","owner":"usr_0123456789abcdef","scope":{"level":"project","scope_id":"prj_0001aaaa"},"kind":"preference","content":"Prefers terse answers with code first.","status":"proposed","approval_mode":"ask","sensitivity":"personal","topic":"general","ttl":{"policy":"decay","decay_days":180},"source":{"kind":"message","id":"msg-42"},"provenance":{"surface":"chat","actor":{"type":"assistant","id":"hermes"},"recorded_at":"2026-08-23T10:00:00Z","session_id":"sess-a1","conversation_id":"conv_0001abcd","run_id":"run_9f","route":{"requested":"thoughtful","resolved_target":"claude_sonnet_high","provider":"claude","effort":"high"},"build":{"commit":"d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06","image_digest":"sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"}},"created_at":"2026-08-23T10:00:00Z","updated_at":"2026-08-23T10:00:00Z","audit":[{"at":"2026-08-23T10:00:00Z","action":"proposed","actor":{"type":"assistant","id":"hermes"}}]}
|
||||
{
|
||||
"schema": "hux.memory.v1",
|
||||
"id": "mem_0001aaaa",
|
||||
"owner": "usr_0123456789abcdef",
|
||||
"scope": {
|
||||
"level": "project",
|
||||
"scope_id": "prj_0001aaaa"
|
||||
},
|
||||
"kind": "preference",
|
||||
"content": "Prefers terse answers with code first.",
|
||||
"status": "proposed",
|
||||
"approval_mode": "ask",
|
||||
"sensitivity": "personal",
|
||||
"topic": "general",
|
||||
"ttl": {
|
||||
"policy": "decay",
|
||||
"decay_days": 180
|
||||
},
|
||||
"source": {
|
||||
"kind": "message",
|
||||
"id": "msg-42"
|
||||
},
|
||||
"provenance": {
|
||||
"surface": "chat",
|
||||
"actor": {
|
||||
"type": "assistant",
|
||||
"id": "hermes"
|
||||
},
|
||||
"recorded_at": "2026-08-23T10:00:00Z",
|
||||
"session_id": "sess-a1",
|
||||
"conversation_id": "conv_0001abcd",
|
||||
"run_id": "run_9f",
|
||||
"route": {
|
||||
"requested": "thoughtful",
|
||||
"resolved_target": "claude_sonnet_high",
|
||||
"provider": "claude",
|
||||
"effort": "high"
|
||||
},
|
||||
"build": {
|
||||
"commit": "d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06",
|
||||
"image_digest": "sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"
|
||||
}
|
||||
},
|
||||
"created_at": "2026-08-23T10:00:00Z",
|
||||
"updated_at": "2026-08-23T10:00:00Z",
|
||||
"audit": [
|
||||
{
|
||||
"at": "2026-08-23T10:00:00Z",
|
||||
"action": "proposed",
|
||||
"actor": {
|
||||
"type": "assistant",
|
||||
"id": "hermes"
|
||||
}
|
||||
}
|
||||
],
|
||||
"reason": "You asked for code-first answers twice in this project.",
|
||||
"retrievable": false,
|
||||
"revision": 1,
|
||||
"identity": {
|
||||
"tenant_slot": "slot-3",
|
||||
"subject": "usr_0123456789abcdef",
|
||||
"surface": "chat",
|
||||
"trust": "router"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1,31 @@
|
||||
{"schema":"hux.research_notebook.v1","id":"nb_0001aaaa","conversation_id":"conv_0001abcd","question":"Which supplier delivers fastest under budget?","status":"open","source_ids":["src_0001aaaa"],"passage_ids":["psg_0001aaaa"],"citation_ids":["cit_0001aaaa"],"assumptions":["Budget cap is unchanged at 12k"],"unresolved_questions":["Does the lead time include installation?"],"updated_at":"2026-08-23T10:00:01Z"}
|
||||
{
|
||||
"schema": "hux.research_notebook.v1",
|
||||
"id": "nb_0001aaaa",
|
||||
"conversation_id": "conv_0001abcd",
|
||||
"question": "Which supplier delivers fastest under budget?",
|
||||
"status": "open",
|
||||
"source_ids": [
|
||||
"src_0001aaaa"
|
||||
],
|
||||
"passage_ids": [
|
||||
"psg_0001aaaa"
|
||||
],
|
||||
"citation_ids": [
|
||||
"cit_0001aaaa"
|
||||
],
|
||||
"assumptions": [
|
||||
"Budget cap is unchanged at 12k"
|
||||
],
|
||||
"unresolved_questions": [
|
||||
"Does the lead time include installation?"
|
||||
],
|
||||
"updated_at": "2026-08-23T10:00:01Z",
|
||||
"notes": [
|
||||
{
|
||||
"at": "2026-08-23T10:00:00Z",
|
||||
"text": "Lead times exclude installation per supplier FAQ.",
|
||||
"source_id": "src_0001aaaa"
|
||||
}
|
||||
],
|
||||
"revision": 4
|
||||
}
|
||||
|
||||
@ -1 +1,13 @@
|
||||
{"schema":"hux.passage.v1","id":"psg_0001aaaa","source_id":"src_0001aaaa","text":"Standard base cabinets ship in 3-4 weeks.","hash":"sha256:2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881","locator":{"selector":"#lead-times","char_start":120,"char_end":160}}
|
||||
{
|
||||
"schema": "hux.passage.v1",
|
||||
"id": "psg_0001aaaa",
|
||||
"source_id": "src_0001aaaa",
|
||||
"text": "Standard base cabinets ship in 3-4 weeks.",
|
||||
"hash": "sha256:2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881",
|
||||
"locator": {
|
||||
"selector": "#lead-times",
|
||||
"char_start": 120,
|
||||
"char_end": 160
|
||||
},
|
||||
"dedupe_key": "sha256:abababababababababababababababababababababababababababababababab"
|
||||
}
|
||||
|
||||
@ -1 +1,56 @@
|
||||
{"schema":"hux.policy.v1","id":"pol_0001aaaa","owner":"usr_0123456789abcdef","scope":{"level":"conversation","scope_id":"conv_0001abcd"},"autonomy":"safe","grants":[{"capability":"network","decision":"ask","expires_at":"2026-08-24T10:00:00Z","granted_by":{"type":"user","id":"usr_0123456789abcdef"}}],"budgets":{"tokens_per_run":200000,"tool_calls_per_run":40,"wall_clock_seconds":900},"provenance":{"surface":"chat","actor":{"type":"assistant","id":"hermes"},"recorded_at":"2026-08-23T10:00:00Z","session_id":"sess-a1","conversation_id":"conv_0001abcd","run_id":"run_9f","route":{"requested":"thoughtful","resolved_target":"claude_sonnet_high","provider":"claude","effort":"high"},"build":{"commit":"d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06","image_digest":"sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"}},"updated_at":"2026-08-23T10:00:00Z"}
|
||||
{
|
||||
"schema": "hux.policy.v1",
|
||||
"id": "pol_0001aaaa",
|
||||
"owner": "usr_0123456789abcdef",
|
||||
"scope": {
|
||||
"level": "conversation",
|
||||
"scope_id": "conv_0001abcd"
|
||||
},
|
||||
"autonomy": "safe",
|
||||
"grants": [
|
||||
{
|
||||
"capability": "network",
|
||||
"decision": "ask",
|
||||
"expires_at": "2026-08-24T10:00:00Z",
|
||||
"granted_by": {
|
||||
"type": "user",
|
||||
"id": "usr_0123456789abcdef"
|
||||
}
|
||||
}
|
||||
],
|
||||
"budgets": {
|
||||
"tokens_per_run": 200000,
|
||||
"tool_calls_per_run": 40,
|
||||
"wall_clock_seconds": 900,
|
||||
"spend_units": 50,
|
||||
"subagents_per_run": 2,
|
||||
"scope": {
|
||||
"conversations": [
|
||||
"conv_0001abcd"
|
||||
]
|
||||
}
|
||||
},
|
||||
"provenance": {
|
||||
"surface": "chat",
|
||||
"actor": {
|
||||
"type": "assistant",
|
||||
"id": "hermes"
|
||||
},
|
||||
"recorded_at": "2026-08-23T10:00:00Z",
|
||||
"session_id": "sess-a1",
|
||||
"conversation_id": "conv_0001abcd",
|
||||
"run_id": "run_9f",
|
||||
"route": {
|
||||
"requested": "thoughtful",
|
||||
"resolved_target": "claude_sonnet_high",
|
||||
"provider": "claude",
|
||||
"effort": "high"
|
||||
},
|
||||
"build": {
|
||||
"commit": "d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06",
|
||||
"image_digest": "sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"
|
||||
}
|
||||
},
|
||||
"updated_at": "2026-08-23T10:00:00Z",
|
||||
"revision": 1
|
||||
}
|
||||
|
||||
@ -1 +1,17 @@
|
||||
{"schema":"hux.project.v1","id":"prj_0001aaaa","owner":"usr_0123456789abcdef","name":"Kitchen renovation","description":"Quotes, plans and supplier research.","tags":["home","2026"],"pinned":true,"archived":false,"default_mode":"research","created_at":"2026-08-20T09:00:00Z","updated_at":"2026-08-23T10:00:00Z"}
|
||||
{
|
||||
"schema": "hux.project.v1",
|
||||
"id": "prj_0001aaaa",
|
||||
"owner": "usr_0123456789abcdef",
|
||||
"name": "Kitchen renovation",
|
||||
"description": "Quotes, plans and supplier research.",
|
||||
"tags": [
|
||||
"home",
|
||||
"2026"
|
||||
],
|
||||
"pinned": true,
|
||||
"archived": false,
|
||||
"default_mode": "research",
|
||||
"created_at": "2026-08-20T09:00:00Z",
|
||||
"updated_at": "2026-08-23T10:00:00Z",
|
||||
"revision": 3
|
||||
}
|
||||
|
||||
@ -1 +1,33 @@
|
||||
{"schema":"hux.source.v1","id":"src_0001aaaa","kind":"web","uri":"https://example.com/cabinets","title":"Cabinet pricing 2026","publisher":"Example Co","retrieved_at":"2026-08-23T09:59:00Z","classification":"primary","content_hash":"sha256:2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881","provenance":{"surface":"chat","actor":{"type":"assistant","id":"hermes"},"recorded_at":"2026-08-23T10:00:00Z","session_id":"sess-a1","conversation_id":"conv_0001abcd","run_id":"run_9f","route":{"requested":"thoughtful","resolved_target":"claude_sonnet_high","provider":"claude","effort":"high"},"build":{"commit":"d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06","image_digest":"sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"}}}
|
||||
{
|
||||
"schema": "hux.source.v1",
|
||||
"id": "src_0001aaaa",
|
||||
"kind": "web",
|
||||
"uri": "https://example.com/cabinets",
|
||||
"title": "Cabinet pricing 2026",
|
||||
"publisher": "Example Co",
|
||||
"retrieved_at": "2026-08-23T09:59:00Z",
|
||||
"classification": "primary",
|
||||
"content_hash": "sha256:2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881",
|
||||
"provenance": {
|
||||
"surface": "chat",
|
||||
"actor": {
|
||||
"type": "assistant",
|
||||
"id": "hermes"
|
||||
},
|
||||
"recorded_at": "2026-08-23T10:00:00Z",
|
||||
"session_id": "sess-a1",
|
||||
"conversation_id": "conv_0001abcd",
|
||||
"run_id": "run_9f",
|
||||
"route": {
|
||||
"requested": "thoughtful",
|
||||
"resolved_target": "claude_sonnet_high",
|
||||
"provider": "claude",
|
||||
"effort": "high"
|
||||
},
|
||||
"build": {
|
||||
"commit": "d3cbeb06d3cbeb06d3cbeb06d3cbeb06d3cbeb06",
|
||||
"image_digest": "sha256:4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd4a385fbd"
|
||||
}
|
||||
},
|
||||
"dedupe_key": "sha256:abababababababababababababababababababababababababababababababab"
|
||||
}
|
||||
|
||||
@ -2,17 +2,202 @@
|
||||
"schema": "hux.flags.v1",
|
||||
"env_var": "HUX_FLAGS",
|
||||
"cards": [
|
||||
{"card": "HUX-01", "flag": "hux.activity_timeline", "title": "Activity timeline", "wave": "A", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["event.schema.json"], "depends_on": ["HUX-11"], "default": false, "rollback": "disable flag; events keep accumulating on the tenant PVC and are not user-visible"},
|
||||
{"card": "HUX-02", "flag": "hux.memory_control", "title": "Memory control", "wave": "A", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["memory.schema.json"], "depends_on": ["HUX-11", "HUX-10"], "default": false, "rollback": "disable flag; ledger stays read-only, upstream memory tool unchanged"},
|
||||
{"card": "HUX-03", "flag": "hux.projects", "title": "Projects and chats", "wave": "B", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["project.schema.json"], "depends_on": ["HUX-11"], "default": false, "rollback": "disable flag; upstream project list remains authoritative"},
|
||||
{"card": "HUX-04", "flag": "hux.artifacts", "title": "Artifact workspace", "wave": "B", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["artifact.schema.json"], "depends_on": ["HUX-11", "HUX-03"], "default": false, "rollback": "disable flag; artifacts remain downloadable files in the tenant workspace"},
|
||||
{"card": "HUX-05", "flag": "hux.autonomy", "title": "Autonomy controls", "wave": "A", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["permission.schema.json"], "depends_on": ["HUX-11", "HUX-01"], "default": false, "rollback": "disable flag; gateway approval mode reverts to the tenant default"},
|
||||
{"card": "HUX-06", "flag": "hux.friendly_modes", "title": "Friendly modes", "wave": "B", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["mode.schema.json"], "depends_on": ["HUX-11"], "default": false, "rollback": "disable flag; routing-priority chips remain"},
|
||||
{"card": "HUX-07", "flag": "hux.multimodal", "title": "Multimodal", "wave": "C", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["artifact.schema.json", "event.schema.json"], "depends_on": ["HUX-04", "HUX-05"], "default": false, "rollback": "disable flag; upstream upload path remains"},
|
||||
{"card": "HUX-08", "flag": "hux.research", "title": "Research and citations", "wave": "B", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["citation.schema.json"], "depends_on": ["HUX-11", "HUX-06"], "default": false, "rollback": "disable flag; prose citations remain"},
|
||||
{"card": "HUX-09", "flag": "hux.onboarding", "title": "Onboarding", "wave": "C", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["suggestion.schema.json"], "depends_on": ["HUX-03", "HUX-06"], "default": false, "rollback": "disable flag; no suggestions shown"},
|
||||
{"card": "HUX-10", "flag": "hux.privacy", "title": "Privacy behaviour", "wave": "A", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["privacy.schema.json"], "depends_on": ["HUX-11"], "default": false, "rollback": "disable flag; tenant isolation remains the privacy model"},
|
||||
{"card": "HUX-11", "flag": "hux.foundation", "title": "Shared foundation", "wave": "A", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["common.schema.json"], "depends_on": [], "default": false, "rollback": "disable flag; /hux/v1 returns 404 from the router"},
|
||||
{"card": "HUX-12", "flag": "hux.release_followthrough", "title": "Deployment follow-through", "wave": "A", "backend_owner": "claude", "frontend_owner": "codex", "contracts": ["release.schema.json"], "depends_on": [], "default": false, "rollback": "disable flag; release lane evidence archive remains the source of truth"}
|
||||
]
|
||||
{
|
||||
"card": "HUX-01",
|
||||
"flag": "hux.activity_timeline",
|
||||
"title": "Activity timeline",
|
||||
"wave": "A",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"event.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-11"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; events keep accumulating on the tenant PVC and are not user-visible"
|
||||
},
|
||||
{
|
||||
"card": "HUX-02",
|
||||
"flag": "hux.memory_control",
|
||||
"title": "Memory control",
|
||||
"wave": "A",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"memory.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-11",
|
||||
"HUX-10"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; ledger stays read-only, upstream memory tool unchanged"
|
||||
},
|
||||
{
|
||||
"card": "HUX-03",
|
||||
"flag": "hux.projects",
|
||||
"title": "Projects and chats",
|
||||
"wave": "B",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"project.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-11"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; upstream project list remains authoritative"
|
||||
},
|
||||
{
|
||||
"card": "HUX-04",
|
||||
"flag": "hux.artifacts",
|
||||
"title": "Artifact workspace",
|
||||
"wave": "B",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"artifact.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-11",
|
||||
"HUX-03"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; artifacts remain downloadable files in the tenant workspace"
|
||||
},
|
||||
{
|
||||
"card": "HUX-05",
|
||||
"flag": "hux.autonomy",
|
||||
"title": "Autonomy controls",
|
||||
"wave": "A",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"permission.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-11",
|
||||
"HUX-01"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; gateway approval mode reverts to the tenant default"
|
||||
},
|
||||
{
|
||||
"card": "HUX-06",
|
||||
"flag": "hux.friendly_modes",
|
||||
"title": "Friendly modes",
|
||||
"wave": "B",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"mode.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-11"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; routing-priority chips remain"
|
||||
},
|
||||
{
|
||||
"card": "HUX-07",
|
||||
"flag": "hux.multimodal",
|
||||
"title": "Multimodal",
|
||||
"wave": "C",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"artifact.schema.json",
|
||||
"event.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-04",
|
||||
"HUX-05"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; upstream upload path remains"
|
||||
},
|
||||
{
|
||||
"card": "HUX-08",
|
||||
"flag": "hux.research",
|
||||
"title": "Research and citations",
|
||||
"wave": "B",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"citation.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-11",
|
||||
"HUX-06"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; prose citations remain"
|
||||
},
|
||||
{
|
||||
"card": "HUX-09",
|
||||
"flag": "hux.onboarding",
|
||||
"title": "Onboarding",
|
||||
"wave": "C",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"suggestion.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-03",
|
||||
"HUX-06"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; no suggestions shown"
|
||||
},
|
||||
{
|
||||
"card": "HUX-10",
|
||||
"flag": "hux.privacy",
|
||||
"title": "Privacy behaviour",
|
||||
"wave": "A",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"privacy.schema.json"
|
||||
],
|
||||
"depends_on": [
|
||||
"HUX-11"
|
||||
],
|
||||
"default": false,
|
||||
"rollback": "disable flag; tenant isolation remains the privacy model"
|
||||
},
|
||||
{
|
||||
"card": "HUX-11",
|
||||
"flag": "hux.foundation",
|
||||
"title": "Shared foundation",
|
||||
"wave": "A",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"common.schema.json",
|
||||
"identity.schema.json"
|
||||
],
|
||||
"depends_on": [],
|
||||
"default": false,
|
||||
"rollback": "disable flag; /hux/v1 returns 404 from the router"
|
||||
},
|
||||
{
|
||||
"card": "HUX-12",
|
||||
"flag": "hux.release_followthrough",
|
||||
"title": "Deployment follow-through",
|
||||
"wave": "A",
|
||||
"backend_owner": "claude",
|
||||
"frontend_owner": "codex",
|
||||
"contracts": [
|
||||
"release.schema.json"
|
||||
],
|
||||
"depends_on": [],
|
||||
"default": false,
|
||||
"rollback": "disable flag; release lane evidence archive remains the source of truth"
|
||||
}
|
||||
],
|
||||
"contract_version": "1.0.0"
|
||||
}
|
||||
|
||||
157
services/hermes/contracts/hux/identity.schema.json
Normal file
157
services/hermes/contracts/hux/identity.schema.json
Normal file
@ -0,0 +1,157 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://hermes.bstein.dev/contracts/hux/v1/identity.schema.json",
|
||||
"title": "HUX identity, capability negotiation and data manifest",
|
||||
"description": "HUX-11 foundation records. capabilities is the first call every client makes: it learns the contract version, which cards are on for this tenant (a card is on only when its whole dependency chain is on), and the server build. manifest is stored on the tenant PVC and tells a rolled-back reader whether it can read the data.",
|
||||
"$defs": {
|
||||
"capabilities": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema",
|
||||
"contract_version",
|
||||
"identity",
|
||||
"cards",
|
||||
"server"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"const": "hux.capabilities.v1"
|
||||
},
|
||||
"contract_version": {
|
||||
"$ref": "common.schema.json#/$defs/contract_version"
|
||||
},
|
||||
"identity": {
|
||||
"$ref": "common.schema.json#/$defs/identity"
|
||||
},
|
||||
"cards": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"card",
|
||||
"flag",
|
||||
"enabled",
|
||||
"routes"
|
||||
],
|
||||
"properties": {
|
||||
"card": {
|
||||
"type": "string",
|
||||
"pattern": "^HUX-[0-9]{2}$"
|
||||
},
|
||||
"flag": {
|
||||
"type": "string",
|
||||
"pattern": "^hux\\.[a-z_]+$"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"routes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^/hux/v1/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"$ref": "common.schema.json#/$defs/build"
|
||||
}
|
||||
}
|
||||
},
|
||||
"manifest": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema",
|
||||
"contract_version",
|
||||
"data_layout_version",
|
||||
"min_reader_contract_version",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"const": "hux.manifest.v1"
|
||||
},
|
||||
"contract_version": {
|
||||
"$ref": "common.schema.json#/$defs/contract_version"
|
||||
},
|
||||
"data_layout_version": {
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
},
|
||||
"min_reader_contract_version": {
|
||||
"$ref": "common.schema.json#/$defs/contract_version"
|
||||
},
|
||||
"created_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"updated_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema",
|
||||
"status",
|
||||
"code",
|
||||
"message"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"const": "hux.error.v1"
|
||||
},
|
||||
"status": {
|
||||
"type": "integer",
|
||||
"minimum": 400,
|
||||
"maximum": 599
|
||||
},
|
||||
"code": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"unauthorized",
|
||||
"forbidden",
|
||||
"not_found",
|
||||
"conflict",
|
||||
"flag_off",
|
||||
"invalid",
|
||||
"too_large",
|
||||
"rate_limited",
|
||||
"approval_required",
|
||||
"budget_exhausted"
|
||||
]
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"maxLength": 280
|
||||
},
|
||||
"details": {
|
||||
"type": "array",
|
||||
"maxItems": 32,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"maxLength": 280
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/capabilities"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/manifest"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/error"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -5,54 +5,201 @@
|
||||
"description": "A single remembered item (HUX-02). Memory is a ledger: entries are appended, approved, expired or forgotten, never edited in place. Forgotten entries keep their audit trail but drop content.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "owner", "scope", "kind", "content", "status", "approval_mode", "sensitivity", "ttl", "source", "provenance", "created_at", "updated_at", "audit"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"owner",
|
||||
"scope",
|
||||
"kind",
|
||||
"content",
|
||||
"status",
|
||||
"approval_mode",
|
||||
"sensitivity",
|
||||
"ttl",
|
||||
"source",
|
||||
"provenance",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"audit",
|
||||
"reason",
|
||||
"retrievable",
|
||||
"revision"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.memory.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"owner": {"$ref": "common.schema.json#/$defs/user_ref"},
|
||||
"schema": {
|
||||
"const": "hux.memory.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "common.schema.json#/$defs/user_ref"
|
||||
},
|
||||
"scope": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["level"],
|
||||
"required": [
|
||||
"level"
|
||||
],
|
||||
"properties": {
|
||||
"level": {"type": "string", "enum": ["global", "project", "conversation"]},
|
||||
"scope_id": {"$ref": "common.schema.json#/$defs/id"}
|
||||
"level": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"global",
|
||||
"project",
|
||||
"conversation"
|
||||
]
|
||||
},
|
||||
"scope_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kind": {"type": "string", "enum": ["preference", "fact", "instruction", "context"]},
|
||||
"content": {"type": "string", "maxLength": 2000},
|
||||
"status": {"type": "string", "enum": ["proposed", "active", "rejected", "expired", "forgotten"]},
|
||||
"approval_mode": {"type": "string", "enum": ["automatic", "ask"]},
|
||||
"sensitivity": {"$ref": "common.schema.json#/$defs/sensitivity"},
|
||||
"topic": {"type": "string", "enum": ["general", "health", "finance", "legal", "relationships", "credentials", "minors", "location", "biometric"]},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"preference",
|
||||
"fact",
|
||||
"instruction",
|
||||
"context"
|
||||
]
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"maxLength": 2000
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"proposed",
|
||||
"active",
|
||||
"rejected",
|
||||
"expired",
|
||||
"forgotten",
|
||||
"no_store"
|
||||
]
|
||||
},
|
||||
"approval_mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"automatic",
|
||||
"ask",
|
||||
"no_store"
|
||||
]
|
||||
},
|
||||
"sensitivity": {
|
||||
"$ref": "common.schema.json#/$defs/sensitivity"
|
||||
},
|
||||
"topic": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"general",
|
||||
"health",
|
||||
"finance",
|
||||
"legal",
|
||||
"relationships",
|
||||
"credentials",
|
||||
"minors",
|
||||
"location",
|
||||
"biometric"
|
||||
]
|
||||
},
|
||||
"ttl": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["policy"],
|
||||
"required": [
|
||||
"policy"
|
||||
],
|
||||
"properties": {
|
||||
"policy": {"type": "string", "enum": ["never", "expires_at", "decay"]},
|
||||
"expires_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"decay_days": {"type": "integer", "minimum": 1, "maximum": 3650}
|
||||
"policy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"never",
|
||||
"expires_at",
|
||||
"decay"
|
||||
]
|
||||
},
|
||||
"expires_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"decay_days": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 3650
|
||||
}
|
||||
}
|
||||
},
|
||||
"source": {"$ref": "common.schema.json#/$defs/evidence_ref"},
|
||||
"provenance": {"$ref": "common.schema.json#/$defs/provenance"},
|
||||
"created_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"updated_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"source": {
|
||||
"$ref": "common.schema.json#/$defs/evidence_ref"
|
||||
},
|
||||
"provenance": {
|
||||
"$ref": "common.schema.json#/$defs/provenance"
|
||||
},
|
||||
"created_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"updated_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"audit": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["at", "action", "actor"],
|
||||
"required": [
|
||||
"at",
|
||||
"action",
|
||||
"actor"
|
||||
],
|
||||
"properties": {
|
||||
"at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"action": {"type": "string", "enum": ["proposed", "approved", "rejected", "expired", "forgotten", "exported"]},
|
||||
"actor": {"$ref": "common.schema.json#/$defs/actor"},
|
||||
"note": {"type": "string", "maxLength": 200}
|
||||
"at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"action": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"proposed",
|
||||
"approved",
|
||||
"rejected",
|
||||
"expired",
|
||||
"forgotten",
|
||||
"exported",
|
||||
"no_store",
|
||||
"superseded",
|
||||
"retrieval_removed",
|
||||
"edited"
|
||||
]
|
||||
},
|
||||
"actor": {
|
||||
"$ref": "common.schema.json#/$defs/actor"
|
||||
},
|
||||
"note": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "Why the assistant proposed remembering this, in the user's terms.",
|
||||
"minLength": 1,
|
||||
"maxLength": 280
|
||||
},
|
||||
"supersedes": {
|
||||
"$ref": "common.schema.json#/$defs/id",
|
||||
"description": "An edit is a new entry that supersedes the old one; the old entry moves to forgotten."
|
||||
},
|
||||
"retrievable": {
|
||||
"type": "boolean",
|
||||
"description": "False removes the entry from retrieval without deleting audit. Forgotten, rejected, expired and no_store entries are never retrievable."
|
||||
},
|
||||
"revision": {
|
||||
"$ref": "common.schema.json#/$defs/revision"
|
||||
},
|
||||
"identity": {
|
||||
"$ref": "common.schema.json#/$defs/identity"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,39 +6,105 @@
|
||||
"$defs": {
|
||||
"capability": {
|
||||
"type": "string",
|
||||
"enum": ["read_files", "write_files", "shell", "network", "web_search", "send_message", "memory_write", "artifact_write", "spend_tokens", "delegate", "deploy"]
|
||||
"enum": [
|
||||
"read_files",
|
||||
"write_files",
|
||||
"shell",
|
||||
"network",
|
||||
"web_search",
|
||||
"send_message",
|
||||
"memory_write",
|
||||
"artifact_write",
|
||||
"spend_tokens",
|
||||
"delegate",
|
||||
"deploy",
|
||||
"external_side_effect"
|
||||
]
|
||||
},
|
||||
"decision": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"allow",
|
||||
"ask",
|
||||
"deny"
|
||||
]
|
||||
},
|
||||
"decision": {"type": "string", "enum": ["allow", "ask", "deny"]},
|
||||
"policy": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "owner", "scope", "autonomy", "grants", "budgets", "provenance", "updated_at"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"owner",
|
||||
"scope",
|
||||
"autonomy",
|
||||
"grants",
|
||||
"budgets",
|
||||
"provenance",
|
||||
"updated_at",
|
||||
"revision"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.policy.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"owner": {"$ref": "common.schema.json#/$defs/user_ref"},
|
||||
"schema": {
|
||||
"const": "hux.policy.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "common.schema.json#/$defs/user_ref"
|
||||
},
|
||||
"scope": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["level"],
|
||||
"required": [
|
||||
"level"
|
||||
],
|
||||
"properties": {
|
||||
"level": {"type": "string", "enum": ["global", "project", "conversation"]},
|
||||
"scope_id": {"$ref": "common.schema.json#/$defs/id"}
|
||||
"level": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"global",
|
||||
"project",
|
||||
"conversation"
|
||||
]
|
||||
},
|
||||
"scope_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autonomy": {"type": "string", "enum": ["ask_first", "safe", "autonomous"]},
|
||||
"autonomy": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ask_first",
|
||||
"safe",
|
||||
"autonomous"
|
||||
]
|
||||
},
|
||||
"grants": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["capability", "decision"],
|
||||
"required": [
|
||||
"capability",
|
||||
"decision"
|
||||
],
|
||||
"properties": {
|
||||
"capability": {"$ref": "#/$defs/capability"},
|
||||
"decision": {"$ref": "#/$defs/decision"},
|
||||
"expires_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"granted_by": {"$ref": "common.schema.json#/$defs/actor"}
|
||||
"capability": {
|
||||
"$ref": "#/$defs/capability"
|
||||
},
|
||||
"decision": {
|
||||
"$ref": "#/$defs/decision"
|
||||
},
|
||||
"expires_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"granted_by": {
|
||||
"$ref": "common.schema.json#/$defs/actor"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -46,85 +112,337 @@
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tokens_per_run": {"type": "integer", "minimum": 0},
|
||||
"tool_calls_per_run": {"type": "integer", "minimum": 0},
|
||||
"wall_clock_seconds": {"type": "integer", "minimum": 0},
|
||||
"delegations_per_run": {"type": "integer", "minimum": 0}
|
||||
"tokens_per_run": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"tool_calls_per_run": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"wall_clock_seconds": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"delegations_per_run": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"spend_units": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"subagents_per_run": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"scope": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"conversations": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"maxLength": 300
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"provenance": {"$ref": "common.schema.json#/$defs/provenance"},
|
||||
"updated_at": {"$ref": "common.schema.json#/$defs/timestamp"}
|
||||
"provenance": {
|
||||
"$ref": "common.schema.json#/$defs/provenance"
|
||||
},
|
||||
"updated_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"revision": {
|
||||
"$ref": "common.schema.json#/$defs/revision"
|
||||
}
|
||||
}
|
||||
},
|
||||
"approval": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "run_id", "conversation_id", "capability", "request", "status", "requested_at", "expires_at"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"run_id",
|
||||
"conversation_id",
|
||||
"capability",
|
||||
"request",
|
||||
"status",
|
||||
"requested_at",
|
||||
"expires_at"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.approval.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"run_id": {"type": "string", "minLength": 1, "maxLength": 120},
|
||||
"conversation_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"capability": {"$ref": "#/$defs/capability"},
|
||||
"schema": {
|
||||
"const": "hux.approval.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
},
|
||||
"conversation_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"capability": {
|
||||
"$ref": "#/$defs/capability"
|
||||
},
|
||||
"request": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["summary", "risk"],
|
||||
"required": [
|
||||
"summary",
|
||||
"risk",
|
||||
"external"
|
||||
],
|
||||
"properties": {
|
||||
"summary": {"type": "string", "minLength": 1, "maxLength": 280},
|
||||
"detail": {"type": "string", "maxLength": 4000},
|
||||
"risk": {"type": "string", "enum": ["low", "medium", "high"]},
|
||||
"evidence": {"type": "array", "maxItems": 16, "items": {"$ref": "common.schema.json#/$defs/evidence_ref"}}
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 280
|
||||
},
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"maxLength": 4000
|
||||
},
|
||||
"risk": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"low",
|
||||
"medium",
|
||||
"high"
|
||||
]
|
||||
},
|
||||
"evidence": {
|
||||
"type": "array",
|
||||
"maxItems": 16,
|
||||
"items": {
|
||||
"$ref": "common.schema.json#/$defs/evidence_ref"
|
||||
}
|
||||
},
|
||||
"external": {
|
||||
"type": "boolean",
|
||||
"description": "True when the action leaves the tenant (message, network call, deploy). External side effects always need an approval record regardless of autonomy level."
|
||||
}
|
||||
}
|
||||
},
|
||||
"status": {"type": "string", "enum": ["pending", "approved", "denied", "expired", "cancelled"]},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"pending",
|
||||
"approved",
|
||||
"denied",
|
||||
"expired",
|
||||
"cancelled"
|
||||
]
|
||||
},
|
||||
"decision": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["choice", "by", "at"],
|
||||
"required": [
|
||||
"choice",
|
||||
"by",
|
||||
"at"
|
||||
],
|
||||
"properties": {
|
||||
"choice": {"type": "string", "enum": ["once", "session", "always", "deny"]},
|
||||
"by": {"$ref": "common.schema.json#/$defs/actor"},
|
||||
"at": {"$ref": "common.schema.json#/$defs/timestamp"}
|
||||
"choice": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"once",
|
||||
"session",
|
||||
"always",
|
||||
"deny"
|
||||
]
|
||||
},
|
||||
"by": {
|
||||
"$ref": "common.schema.json#/$defs/actor"
|
||||
},
|
||||
"at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
}
|
||||
}
|
||||
},
|
||||
"requested_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"expires_at": {"$ref": "common.schema.json#/$defs/timestamp"}
|
||||
"requested_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"expires_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"idempotency_key": {
|
||||
"$ref": "common.schema.json#/$defs/idempotency_key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cancellation_receipt": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "run_id", "requested_by", "requested_at", "outcome", "side_effects"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"run_id",
|
||||
"requested_by",
|
||||
"requested_at",
|
||||
"outcome",
|
||||
"side_effects"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.cancel_receipt.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"run_id": {"type": "string", "minLength": 1, "maxLength": 120},
|
||||
"requested_by": {"$ref": "common.schema.json#/$defs/actor"},
|
||||
"requested_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"acknowledged_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"completed_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"outcome": {"type": "string", "enum": ["cancelled", "already_complete", "failed_to_cancel"]},
|
||||
"schema": {
|
||||
"const": "hux.cancel_receipt.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
},
|
||||
"requested_by": {
|
||||
"$ref": "common.schema.json#/$defs/actor"
|
||||
},
|
||||
"requested_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"acknowledged_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"completed_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"outcome": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"cancelled",
|
||||
"already_complete",
|
||||
"failed_to_cancel"
|
||||
]
|
||||
},
|
||||
"side_effects": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["description", "reverted"],
|
||||
"required": [
|
||||
"description",
|
||||
"reverted"
|
||||
],
|
||||
"properties": {
|
||||
"description": {"type": "string", "minLength": 1, "maxLength": 280},
|
||||
"reverted": {"type": "boolean"},
|
||||
"evidence": {"$ref": "common.schema.json#/$defs/evidence_ref"}
|
||||
"description": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 280
|
||||
},
|
||||
"reverted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"evidence": {
|
||||
"$ref": "common.schema.json#/$defs/evidence_ref"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"conversation_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"budget_state": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema",
|
||||
"run_id",
|
||||
"spent",
|
||||
"limits",
|
||||
"exhausted"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"const": "hux.budget_state.v1"
|
||||
},
|
||||
"run_id": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"spent": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tokens": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"tool_calls": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"wall_clock_seconds": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"delegations": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"spend_units": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"subagents": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"limits": {
|
||||
"$ref": "#/$defs/policy/properties/budgets"
|
||||
},
|
||||
"exhausted": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tokens_per_run",
|
||||
"tool_calls_per_run",
|
||||
"wall_clock_seconds",
|
||||
"delegations_per_run",
|
||||
"spend_units",
|
||||
"subagents_per_run"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{"$ref": "#/$defs/policy"},
|
||||
{"$ref": "#/$defs/approval"},
|
||||
{"$ref": "#/$defs/cancellation_receipt"}
|
||||
{
|
||||
"$ref": "#/$defs/policy"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/approval"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/cancellation_receipt"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/budget_state"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -7,63 +7,193 @@
|
||||
"project": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "owner", "name", "tags", "pinned", "archived", "created_at", "updated_at"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"owner",
|
||||
"name",
|
||||
"tags",
|
||||
"pinned",
|
||||
"archived",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"revision"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.project.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"owner": {"$ref": "common.schema.json#/$defs/user_ref"},
|
||||
"name": {"type": "string", "minLength": 1, "maxLength": 120},
|
||||
"description": {"type": "string", "maxLength": 2000},
|
||||
"tags": {"$ref": "common.schema.json#/$defs/tags"},
|
||||
"pinned": {"type": "boolean"},
|
||||
"archived": {"type": "boolean"},
|
||||
"default_mode": {"type": "string", "enum": ["fast", "thoughtful", "research", "create", "private"]},
|
||||
"memory_scope_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"created_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"updated_at": {"$ref": "common.schema.json#/$defs/timestamp"}
|
||||
"schema": {
|
||||
"const": "hux.project.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "common.schema.json#/$defs/user_ref"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"maxLength": 2000
|
||||
},
|
||||
"tags": {
|
||||
"$ref": "common.schema.json#/$defs/tags"
|
||||
},
|
||||
"pinned": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"default_mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"fast",
|
||||
"thoughtful",
|
||||
"research",
|
||||
"create",
|
||||
"private"
|
||||
]
|
||||
},
|
||||
"memory_scope_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"created_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"updated_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"revision": {
|
||||
"$ref": "common.schema.json#/$defs/revision"
|
||||
}
|
||||
}
|
||||
},
|
||||
"conversation": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "id", "owner", "title", "tags", "pinned", "archived", "artifact_ids", "created_at", "updated_at"],
|
||||
"required": [
|
||||
"schema",
|
||||
"id",
|
||||
"owner",
|
||||
"title",
|
||||
"tags",
|
||||
"pinned",
|
||||
"archived",
|
||||
"artifact_ids",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"revision"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {"const": "hux.conversation.v1"},
|
||||
"id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"owner": {"$ref": "common.schema.json#/$defs/user_ref"},
|
||||
"project_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"title": {"type": "string", "minLength": 1, "maxLength": 200},
|
||||
"tags": {"$ref": "common.schema.json#/$defs/tags"},
|
||||
"pinned": {"type": "boolean"},
|
||||
"archived": {"type": "boolean"},
|
||||
"mode": {"type": "string", "enum": ["fast", "thoughtful", "research", "create", "private"]},
|
||||
"schema": {
|
||||
"const": "hux.conversation.v1"
|
||||
},
|
||||
"id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "common.schema.json#/$defs/user_ref"
|
||||
},
|
||||
"project_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"tags": {
|
||||
"$ref": "common.schema.json#/$defs/tags"
|
||||
},
|
||||
"pinned": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"archived": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"fast",
|
||||
"thoughtful",
|
||||
"research",
|
||||
"create",
|
||||
"private"
|
||||
]
|
||||
},
|
||||
"branch": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["parent_conversation_id", "branch_point_message_id"],
|
||||
"required": [
|
||||
"parent_conversation_id",
|
||||
"branch_point_message_id"
|
||||
],
|
||||
"properties": {
|
||||
"parent_conversation_id": {"$ref": "common.schema.json#/$defs/id"},
|
||||
"branch_point_message_id": {"type": "string", "minLength": 1, "maxLength": 120}
|
||||
"parent_conversation_id": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
},
|
||||
"branch_point_message_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
"artifact_ids": {"type": "array", "maxItems": 500, "uniqueItems": true, "items": {"$ref": "common.schema.json#/$defs/id"}},
|
||||
"last_message_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"created_at": {"$ref": "common.schema.json#/$defs/timestamp"},
|
||||
"updated_at": {"$ref": "common.schema.json#/$defs/timestamp"}
|
||||
"artifact_ids": {
|
||||
"type": "array",
|
||||
"maxItems": 500,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"$ref": "common.schema.json#/$defs/id"
|
||||
}
|
||||
},
|
||||
"last_message_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"created_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"updated_at": {
|
||||
"$ref": "common.schema.json#/$defs/timestamp"
|
||||
},
|
||||
"revision": {
|
||||
"$ref": "common.schema.json#/$defs/revision"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search_index": {
|
||||
"type": "object",
|
||||
"description": "Fields the tenant indexes; the UI may not assume anything else is searchable.",
|
||||
"additionalProperties": false,
|
||||
"required": ["conversation"],
|
||||
"required": [
|
||||
"conversation"
|
||||
],
|
||||
"properties": {
|
||||
"conversation": {"type": "array", "items": {"type": "string", "enum": ["title", "tags", "message_text", "artifact_titles", "project_name"]}}
|
||||
"conversation": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"title",
|
||||
"tags",
|
||||
"message_text",
|
||||
"artifact_titles",
|
||||
"project_name"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{"$ref": "#/$defs/project"},
|
||||
{"$ref": "#/$defs/conversation"}
|
||||
{
|
||||
"$ref": "#/$defs/project"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/conversation"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ refuse the transitions that would let merged/built/deployed be confused.
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import importlib.util
|
||||
import json
|
||||
import sys
|
||||
import tomllib
|
||||
@ -20,21 +19,13 @@ import pytest
|
||||
import yaml
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SCRIPTS = ROOT / "services" / "hermes" / "scripts"
|
||||
FOUNDATION = ROOT / "dockerfiles" / "hermes-hux-foundation"
|
||||
CONTRACTS = ROOT / "services" / "hermes" / "contracts" / "hux"
|
||||
|
||||
|
||||
def _load(name: str):
|
||||
spec = importlib.util.spec_from_file_location(name, SCRIPTS / f"{name}.py")
|
||||
assert spec and spec.loader
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
contracts = _load("hux_contracts")
|
||||
policy = _load("hux_policy")
|
||||
if str(FOUNDATION) not in sys.path:
|
||||
sys.path.insert(0, str(FOUNDATION))
|
||||
from hux import contracts, rules as policy # noqa: E402
|
||||
SCHEMAS = contracts.load_all()
|
||||
EXAMPLES = {path.stem: json.loads(path.read_text()) for path in sorted((CONTRACTS / "examples").glob("*.json"))}
|
||||
NOW = datetime(2026, 8, 23, 12, 0, tzinfo=timezone.utc)
|
||||
@ -166,6 +157,7 @@ def test_capability_matrix_shape():
|
||||
for level, row in matrix.items():
|
||||
assert set(row) == set(policy.CAPABILITIES)
|
||||
assert row["deploy"] == "ask", level
|
||||
assert row["external_side_effect"] == "ask", level
|
||||
assert row["read_files"] == "allow"
|
||||
assert matrix["ask_first"]["shell"] == "ask"
|
||||
assert matrix["safe"]["network"] == "deny"
|
||||
Loading…
x
Reference in New Issue
Block a user