NIPs by PolleramaCommunity NIPs, surfaced by trustConnect
npub19a86gzxctwt...

AI Agent Communication

Published Feb 18, 2026
kind 25802kind 25803kind 25805kind 31430kind 25800kind 25801kind 25804kind 25806

NIP-XX

AI Agent Messages

draft optional

This NIP defines a protocol for bidirectional communication between Nostr clients and AI agent runtimes over Nostr relays.

Until a NIP number is assigned, this document uses placeholder number XX in the title and filename.

Kinds

This NIP reserves the following kinds for AI Agent communication:

eryoung.com. | ai.info | No |

Prompt events (25802), terminal events (25803, 25805), and AI info events (31340) are non-ephemeral, allowing durable replay and capability discovery for state restoration and audit. Streaming/tooling telemetry (25800, 25801, 25804, 25806) remains ephemeral.

Rationale

Nostr has emerged as a universal transport for decentralized applications. AI agents are increasingly being deployed as networked services, and this protocol adds messaging shape for interactive, sessioned, streaming agent workflows.

  • Decentralized identity: Agents and clients identify via Nostr pubkeys.
  • Interactive sessions: Support multi-turn workflows with optional session grouping.
  • Streaming: Delta events let clients render partial output.
  • Tool telemetry: Agents can expose tool activity.
  • Structured discovery: ai.info advertises capabilities, limits, and supported formats.

Definitions

Run

A run is a single prompt/response interaction.

  • One prompt event (ai.prompt, kind 25802).
  • Optional cancellation request (ai.cancel, kind 25806) from client while non-terminal.
  • Optional status (ai.status, kind 25800) and delta (ai.delta, kind 25801) events.
  • Optional tool-call events (ai.tool_call, kind 25804).
  • Exactly one terminal event: ai.response (25803) or ai.error (25805).

The run identifier is the prompt event id (hex string). Non-prompt events MUST reference it in an e tag with marker root.

Session

A session groups related runs.

  • The s tag identifies a session.
  • s is optional on protocol events.
  • If s is omitted, recipients SHOULD use sender:<lowercase-hex-pubkey>.
  • The default is deterministic and can be reproduced by all parties.

Clients MAY also use ["s","session:<opaque-hash>"] for higher-level session IDs.

Actors

  • Client: User-facing app that sends prompts and renders agent output.
  • Agent Runtime: Nostr-native service that processes prompts.
  • Relays: Transport/storage layer.

Encryption

All protocol payloads in encrypted event kinds (25800, 25801, 25802, 25803, 25804, 25805, 25806) MUST use [NIP-44](44.md).

Required tag

All encrypted events in this NIP MUST include:

["encryption", "nip44"]

If an agent and client both publish additional supported encryption schemes, senders MUST choose a scheme supported by both sides. If no overlap exists, implementations MUST fail the request with UNSUPPORTED_ENCRYPTION.

Key agreement

Encryption uses the sender’s private key and recipient’s public key. In nostr-tools this is:

const conversationKey = nip44.utils.getConversationKey({
  privateKey: senderPrivateKey,
  publicKey: recipientPublicKey
})
const encrypted = nip44.encrypt(plaintext, conversationKey)

The plaintext must be JSON with a ver field.

Each message MUST use a fresh nonce as defined by NIP-44.

NIP-59 metadata privacy (optional)

NIP-44 encrypts content, but event metadata remains visible: authors, pubkeys, tags, timestamps, and kind.

Clients and runtimes MAY wrap these events using [NIP-59](59.md) for metadata privacy. When gift-wrapped:

  • routing SHOULD use the outer wrapper’s p tag and kinds filters;
  • inner tags are not intended for relay indexing;
  • clients still need to unwrap before applying payload validation.

This NIP defines two privacy profiles:

  1. default: NIP-44 encryption only.
  2. privacy-first: NIP-44 + NIP-59 wrapping + minimal disclosure in tool output.

If wrapped, tool metadata in ai.tool_call should avoid sensitive argument/output fields.

Event Formats

All fields marked required in tables and schemas below MUST be present.

For any event type where a field is repeated in both encrypted content and a tag (tool, phase, etc.), the encrypted content is the canonical source of truth. If a mirror tag is present and differs from the encrypted payload, implementations MAY reject the event as INVALID_SCHEMA.

Structured text envelope (compatibility profile)

For compatibility with runtimes that currently emit telemetry as a compact text payload, implementations MAY encode structured metadata inside text using this format:

  • delimiter between fields: |
  • field shape: key=value
  • value encoding: percent-encoding (encodeURIComponent / URL-encoding)

Example:

event=tool|phase=start|name=web_fetch|call_id=call_123|target=https%3A%2F%2Fexample.com|ts=1771402416

When this envelope is used:

  • event, phase, and ts MUST be present.
  • consumers SHOULD ignore unknown keys.
  • producers SHOULD keep key names stable and lowercase snake_case.
  • if canonical JSON fields are present (for example name, phase, arguments), they

remain authoritative over mirrored envelope keys.

Per-event required envelope keys:

  • ai.response (25803): event=final, phase=end, text
  • ai.delta (25801): event=delta, phase, and at least one of text or block
  • ai.tool_call (25804): event=tool, phase, and text or name
  • ai.error (25805): event=error, phase=end, code, text

Recommended common keys for all envelope payloads:

  • run_id (or runId)
  • session_id (or sessionId)
  • timestamp (or ts)

Canonical keys SHOULD use snake_case (run_id, session_id, ts). Implementations SHOULD also accept camelCase aliases for interoperability.

This format is intended as a wire-compatibility strategy and does not replace canonical JSON fields when those fields are available.

ai.info (kind 31340)

Agent capability discovery event. This is a replaceable event (kind 31340). Agents SHOULD publish this in their pubkey namespace.

Tags

TagRequiredDescription
dYesFixed identifier, e.g. "agent-info"

Agents SHOULD keep exactly one active d value and SHOULD not change it after first publication, so clients can reliably cache capabilities.

Clients SHOULD cache the newest valid ai.info publication by (created_at, id) and refresh cached capabilities when capability entries change.

Content (JSON, unencrypted)

{
  "ver": 1,
  "supports_streaming": true,
  "supports_nip59": true,
  "dvm_compatible": false,
  "encryption": ["nip44"],
  "supported_models": ["gpt-4.1-mini", "llama-3.1-70b"],
  "default_model": "gpt-4.1-mini",
  "tool_names": ["web_fetch", "calculator"],
  "tool_schema_version": 1,
  "max_prompt_bytes": 32000,
  "max_context_tokens": 128000,
  "tool_schemas": {
    "calculator": {
      "schema_version": 1,
      "description": "Evaluate arithmetic expressions",
      "requires_approval": false,
      "input_schema": {
        "type": "object",
        "properties": {
          "expr": { "type": "string" },
          "precision": { "type": "number" }
        },
        "required": ["expr"]
      }
    }
  },
  "pricing_hints": {
    "currency": "USD",
    "per_1k_prompt_tokens": 0.002,
    "per_1k_output_tokens": 0.004
  }
}

Model and schema negotiation

Clients SHOULD use ai.info before sending prompts. For each prompt:

  • If model is omitted, agents SHOULD use default_model.
  • If model is set, the agent MUST have it listed in supported_models.
  • If the sender supplies tool_schema_version, the agent MUST use that exact version.
  • If the sender omits tool_schema_version, agents MUST use tool_schema_version from

their latest ai.info.

  • If no compatible model/schema is advertised/supported, the agent MUST return an

ai.error with:

  • UNSUPPORTED_MODEL when the requested model is unknown.
  • UNSUPPORTED_SCHEMA_VERSION when the requested tool_schema_version is

incompatible. and MUST NOT continue execution.

The tool_schema_version requested in a prompt binds accepted ai.tool_call JSON shapes for that run.

Prompt (kind 25802)

Client → agent invocation.

Tags

TagRequiredDescription
pYesAgent recipient pubkey
encryptionYesMust be "nip44"
sNoSession identifier

Content (JSON, encrypted)

{
  "ver": 1,
  "message": "user's message text",
  "thinking": "low|medium|high|max",
  "provider": "optional provider identifier",
  "model": "optional model name",
  "tool_schema_version": 1,
  "fallback_models": ["list", "of", "fallbacks"]
}

Cancel (kind 25806)

Client → agent cancellation request.

Clients MAY emit this if they lose UI interest in a run. Agents SHOULD treat ai.cancel as idempotent for the same run (p + e). Agents MUST ignore ai.cancel for completed runs and MUST NOT emit additional terminal events in that case.

Tags

TagRequiredDescription
pYesAgent recipient pubkey
eYesPrompt id (#e root)
encryptionYesMust be "nip44"
sNoSession identifier

Content (JSON, encrypted)

{
  "ver": 1,
  "reason": "user_cancel|timeout|policy"
}

Response (kind 25803)

Agent → client terminal response.

Tags

TagRequiredDescription
pYesClient recipient pubkey
eYesPrompt id (#e root)
encryptionYesMust be "nip44"
sNoSession identifier

Content (JSON, encrypted)

{
  "ver": 1,
  "text": "complete agent response",
  "timestamp": 1710000000,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 250
  }
}

text MAY be plain response text, or MAY carry a compatibility envelope string such as:

event=final|phase=end|text=Here%20is%20the%20answer...|finish_reason=stop|run_id=abc123|session_id=sender%3Aabc|ts=1771402425

Delta (kind 25801)

Agent → client streaming fragment.

Tags

TagRequiredDescription
pYesClient recipient pubkey
eYesPrompt id (#e root)
encryptionYesMust be "nip44"
sNoSession identifier

Content (JSON, encrypted)

{
  "ver": 1,
  "text": "partial response text",
  "seq": 0
}

text MAY alternatively carry a compatibility envelope string such as:

event=delta|mode=thinking|status=update|phase=update|block=Looking%20up%20latest%20pricing|text=Looking%20up%20latest%20pricing|run_id=abc123|session_id=sender%3Aabc|ts=1771402418

If seq is present, it MUST be strictly increasing by 1 within a run.

Status (kind 25800)

Agent → client state updates.

Tags

TagRequiredDescription
pYesClient recipient pubkey
eYesPrompt id (#e root)
encryptionYesMust be "nip44"
sNoSession identifier

Content (JSON, encrypted)

{
  "ver": 1,
  "state": "thinking|tool_use|done",
  "progress": 50,
  "info": "additional status info"
}

Tool Call (kind 25804)

Agent tool-call telemetry. Agents own tool execution.

For this section, payload fields are canonical (name and phase), and tool and phase tags are optional index hints only.

If any optional hint tag is present, it SHOULD match the encrypted payload field. A mismatch MAY be treated as INVALID_SCHEMA.

To reduce telemetry leakage, agents SHOULD avoid including sensitive data in output unless strictly required for user intent; clients SHOULD treat tool output as potentially untrusted and avoid surfacing secrets.

Tags

TagRequiredDescription
pYesClient recipient pubkey
eYesPrompt id (#e root)
encryptionYesMust be "nip44"
sNoSession identifier
toolNoOptional index hint
phaseNoOptional index hint: start/result

Content (JSON, encrypted)

{
  "ver": 1,
  "name": "calculator",
  "phase": "start|result",
  "arguments": {
    "expr": "12 * 7"
  },
  "output": {
    "stdout": "84",
    "stderr": "",
    "exit_code": 0
  },
  "success": true,
  "duration_ms": 120
}

Compatibility envelope form inside text is also allowed, for example:

{
  "ver": 1,
  "text": "event=tool|phase=start|name=web_fetch|call_id=call_123|target=https%3A%2F%2Fexample.com|ts=1771402416",
  "timestamp": 1771402416
}

Error (kind 25805)

Terminal failure event.

Tags

TagRequiredDescription
pYesClient recipient pubkey
eYesPrompt id (#e root)
encryptionYesMust be "nip44"
sNoSession identifier

Content (JSON, encrypted)

{
  "ver": 1,
  "code": "RATE_LIMIT",
  "message": "provider unavailable",
  "retry_after": 30,
  "details": {
    "provider": "provider-id"
  }
}

Error codes:

CodeMeaning
UNSUPPORTED_ENCRYPTIONRequested encryption scheme unsupported
UNSUPPORTED_MODELRequested model unavailable
UNSUPPORTEDSCHEMAVERSIONRequested tool schema version unsupported
CANCELLEDRun cancelled
RATE_LIMITRequest throttled
UNAUTHORIZEDSender or agent unauthorized
BLOCKED_SENDERSender blocked by policy
MODEL_UNAVAILABLERequested model/provider unavailable
SESSION_LIMITSession turns exceeded
PARSE_ERRORPrompt payload invalid
EMPTY_RESPONSENo response produced
TOOL_ERRORTool execution failed
INVALID_SCHEMAPayload failed schema validation
UNSUPPORTED_FEATURERequested feature/tool/scheme unsupported
INVALID_SEQUENCEDelta sequence invalid for this run
INTERNAL_ERRORUnexpected runtime failure

JSON Schema

Common fields

{
  "$id": "https://example.com/nip-xx-agent-message.json",
  "type": "object",
  "required": ["ver"],
  "properties": {
    "ver": { "const": 1 }
  },
  "additionalProperties": true
}

Prompt schema (25802)

{
  "$id": "https://example.com/nip-xx-prompt.json",
  "type": "object",
  "required": ["ver", "message"],
  "properties": {
    "ver": { "const": 1 },
    "message": { "type": "string", "minLength": 1 },
    "thinking": { "type": "string", "enum": ["low", "medium", "high", "max"] },
    "provider": { "type": "string", "minLength": 1 },
    "model": { "type": "string", "minLength": 1 },
    "tool_schema_version": { "type": "integer", "minimum": 1 },
    "fallback_models": {
      "type": "array",
      "items": { "type": "string" }
    }
  }
}

Cancel schema (25806)

{
  "$id": "https://example.com/nip-xx-cancel.json",
  "type": "object",
  "required": ["ver", "reason"],
  "properties": {
    "ver": { "const": 1 },
    "reason": { "type": "string", "enum": ["user_cancel", "timeout", "policy"] }
  }
}

Response schema (25803)

{
  "$id": "https://example.com/nip-xx-response.json",
  "type": "object",
  "required": ["ver", "text"],
  "properties": {
    "ver": { "const": 1 },
    "text": { "type": "string" },
    "timestamp": { "type": "integer", "minimum": 0 },
    "usage": {
      "type": "object",
      "properties": {
        "input_tokens": { "type": "integer", "minimum": 0 },
        "output_tokens": { "type": "integer", "minimum": 0 }
      },
      "required": ["input_tokens", "output_tokens"]
    }
  }
}

Delta schema (25801)

{
  "$id": "https://example.com/nip-xx-delta.json",
  "type": "object",
  "required": ["ver", "text"],
  "properties": {
    "ver": { "const": 1 },
    "text": { "type": "string" },
    "seq": { "type": "integer", "minimum": 0 },
    "timestamp": { "type": "integer", "minimum": 0 }
  }
}

Status schema (25800)

{
  "$id": "https://example.com/nip-xx-status.json",
  "type": "object",
  "required": ["ver", "state"],
  "properties": {
    "ver": { "const": 1 },
    "state": { "type": "string", "enum": ["thinking", "tool_use", "done"] },
    "progress": { "type": "integer", "minimum": 0, "maximum": 100 },
    "info": { "type": "string" }
  }
}

Tool-call schema (25804)

{
  "$id": "https://example.com/nip-xx-tool-call.json",
  "type": "object",
  "required": ["ver"],
  "oneOf": [
    {
      "required": ["text"]
    },
    {
      "required": ["name", "phase"]
    }
  ],
  "properties": {
    "ver": { "const": 1 },
    "text": { "type": "string", "minLength": 1 },
    "name": { "type": "string", "minLength": 1 },
    "phase": { "type": "string", "enum": ["start", "result"] },
    "arguments": { "type": "object" },
    "output": { "type": "object" },
    "success": { "type": "boolean" },
    "duration_ms": { "type": "integer", "minimum": 0 },
    "timestamp": { "type": "integer", "minimum": 0 }
  }
}

Error schema (25805)

{
  "$id": "https://example.com/nip-xx-error.json",
  "type": "object",
  "required": ["ver", "code", "message"],
  "properties": {
    "ver": { "const": 1 },
    "code": {
      "type": "string",
      "enum": [
        "UNSUPPORTED_ENCRYPTION",
        "UNSUPPORTED_MODEL",
        "UNSUPPORTED_SCHEMA_VERSION",
        "CANCELLED",
        "RATE_LIMIT",
        "UNAUTHORIZED",
        "BLOCKED_SENDER",
        "MODEL_UNAVAILABLE",
        "SESSION_LIMIT",
        "PARSE_ERROR",
        "EMPTY_RESPONSE",
        "TOOL_ERROR",
        "INVALID_SCHEMA",
        "UNSUPPORTED_FEATURE",
        "INVALID_SEQUENCE",
        "INTERNAL_ERROR"
      ]
    },
    "message": { "type": "string", "minLength": 1 },
    "retry_after": { "type": "integer", "minimum": 1 },
    "details": { "type": "object" }
  }
}

Info schema (31340)

{
  "$id": "https://example.com/nip-xx-info.json",
  "type": "object",
  "required": ["ver", "encryption", "tool_names"],
  "properties": {
    "ver": { "const": 1 },
    "supports_streaming": { "type": "boolean" },
    "supports_nip59": { "type": "boolean" },
    "dvm_compatible": { "type": "boolean" },
    "encryption": {
      "type": "array",
      "items": { "type": "string" },
      "contains": { "const": "nip44" }
    },
    "supported_models": { "type": "array", "items": { "type": "string" } },
    "default_model": { "type": "string" },
    "tool_names": { "type": "array", "items": { "type": "string" } },
    "tool_schema_version": { "type": "integer", "minimum": 1 },
    "tool_schemas": {
      "type": "object",
      "additionalProperties": {
        "type": "object",
        "required": ["schema_version", "description", "input_schema"],
        "properties": {
          "schema_version": { "type": "integer", "minimum": 1 },
          "description": { "type": "string" },
          "requires_approval": { "type": "boolean" },
          "input_schema": { "type": "object" },
          "output_schema": { "type": "object" }
        }
      }
    },
    "max_prompt_bytes": { "type": "integer", "minimum": 1 },
    "max_context_tokens": { "type": "integer", "minimum": 1 },
    "pricing_hints": { "type": "object" }
  }
}

Validation and failure rules

Implementations MUST follow these validation and failure rules:

  • JSON parse failures in encrypted payloads MUST be reported with code = PARSE_ERROR.
  • Missing required tags (p, e where required, encryption) or malformed tag values MUST be treated as INVALID_SCHEMA. The d tag is required only for ai.info (31340).
  • Invalid protocol content (for example unknown state/thinking/code) MUST be treated as

INVALID_SCHEMA.

  • e tags on non-prompt events MUST reference an existing or referenced prompt event id.
  • Non-ai.prompt events MUST require e marker root.
  • ai.delta with non-monotonic seq for the same run MUST be treated as

INVALID_SEQUENCE when seq is present.

  • ai.cancel without matching active run MUST be ignored.
  • Duplicate ai.cancel for unfinished runs SHOULD be treated as idempotent and MUST NOT

change accepted terminal selection.

  • Clients SHOULD ignore ai.status, ai.delta, and ai.tool_call for runs already in a

terminal state, except when retained for audit/debug tooling.

  • Agents SHOULD emit at most one terminal ai.error with code=CANCELLED for a given run.
  • If a run reaches a terminal state and emits additional terminal events, clients MUST keep

only the terminal event with the highest created_at and highest id as tie-breaker.

  • If a run is missing a terminal response after reasonable timeout, clients MAY treat it as

an incomplete run and surface an implementation-specific state.

Streaming and reconciliation

For ai.delta events (kind 25801):

  • Clients MUST ignore deltas where (e, p, encryption) do not match the subscribed run and

recipient.

  • If seq is present, it MUST be contiguous starting at 0.
  • Clients SHOULD collect deltas and order by (seq, created_at, id) when seq is present, otherwise by (created_at, id).
  • Clients MUST dedupe duplicates by (event.id) and SHOULD dedupe identical (seq, text) tuples when seq is present.
  • If a gap is detected (missing seq), clients SHOULD continue best-effort rendering and

can display a soft placeholder (“streaming degraded”) until ai.response arrives.

  • Final render MUST be taken from ai.response text, not from the delta stream.
  • Clients MUST NOT apply deltas after a terminal event has been accepted for a run.

Envelope parsing helper (non-normative)

function parseTextEnvelope(text: string): Record<string, string> {
  const out: Record<string, string> = {};
  for (const part of text.split("|")) {
    const idx = part.indexOf("=");
    if (idx <= 0) continue;
    const key = part.slice(0, idx).trim();
    const value = part.slice(idx + 1);
    out[key] = decodeURIComponent(value);
  }
  return out;
}

Protocol Flow

  1. Client SHOULD read ai.info for capabilities:

``text {"kinds":[31340],"authors":["<agent-pubkey>"]} ` If no ai.info` exists, clients SHOULD:

  • proceed with supports_streaming = true
  • assume encryption = ["nip44"]
  • assume tool_names = []
  • disable tool-related UI
  • If supports_nip59 is true and stronger privacy is desired, client and agent SHOULD

use NIP-59 wrappers.

  1. Client sends 25802 prompt:
  2. p = agent pubkey
  3. optional s
  4. optional model and tool_schema_version matching ai.info
  5. encryption = nip44
  6. encrypted JSON payload
  7. Agent subscribes to prompts:

``text {"kinds":[25802],"#p":["<agent-pubkey>"]} ``

  1. Agent emits 25800, 25801, and optional 25804.
  2. Client MAY cancel a non-terminal run by emitting 25806 (same prompt id in e).
  3. If cancellation is effective before terminal output, agent SHOULD emit 25805 with

code = CANCELLED.

  1. Agent emits terminal event:
  2. 25803 on success
  3. 25805 on failure

If a cancellation arrives after a terminal event has already been emitted, the cancellation MUST be treated as no-op.

  1. Client subscribes to terminal and streaming events:

``text { "kinds": [25800,25801,25803,25804,25805], "#p": ["<client-pubkey>"], "#e": ["<prompt-event-id>"], "authors": ["<agent-pubkey>"] } ` Clients SHOULD additionally enforce run-local ordering by matching the selected #e value and authors` set.

Session recovery recipe

  • Find active session:

``text {"kinds":[25802],"#s":["sender:<client-pubkey>"],"#p":["<agent-pubkey>"]} ``

  • Resume a run:

``text {"#e":["<prompt-id>"], "authors":["<agent-pubkey>"], "kinds":[25800,25801,25803,25804,25805]} ``

  • Latest terminal event should be taken as the current run state; deltas may be stale on relay replay.

Tool model and security

This NIP defines a runtime-exec model:

  • Agents own tool execution and emit ai.tool_call telemetry only.
  • Clients MUST NOT execute tools based on ai.tool_call.
  • Tool schemas advertised in ai.info let clients present expected UI affordances.
  • Agents MUST negotiate model and tool_schema_version; if unsupported, MUST return

UNSUPPORTED_MODEL or UNSUPPORTED_SCHEMA_VERSION.

Security and abuse controls

  • Agents SHOULD reject unknown senders not in allowlists and return UNAUTHORIZED.
  • Agents SHOULD reject senders blocked by policy and return BLOCKED_SENDER.
  • Agents SHOULD reject unsupported tools and return UNSUPPORTED_FEATURE.
  • Agents SHOULD validate schemas before execution and return INVALID_SCHEMA on mismatch.
  • Agents SHOULD map execution or transport throttling to RATE_LIMIT.
  • Agents SHOULD return UNSUPPORTED_ENCRYPTION when the requested scheme is unsupported.
  • retry_after SHOULD be present for transient failures where retry is useful.
  • Clients SHOULD prefer the newest terminal event by (created_at,id) when multiple terminal

events are observed.

Full example (encrypted payloads shown as placeholders)

Prompt (25802, from client A to agent B)
{
  "kind": 25802,
  "pubkey": "A",
  "tags": [
    ["p","B"],
    ["s","sender:A"],
    ["encryption","nip44"]
  ],
  "content": "<nip44-ciphertext>"
}

Status (25800)
{
  "kind": 25800,
  "tags": [
    ["p","A"],
    ["e","d5f...3a","", "root"],
    ["s","sender:A"],
    ["encryption","nip44"]
  ],
  "content": "<nip44-ciphertext>"
}

Delta (25801, seq 0)
{
  "kind": 25801,
  "tags": [
    ["p","A"],
    ["e","d5f...3a","", "root"],
    ["encryption","nip44"]
  ],
  "content": "<nip44-ciphertext>"
}

Delta (25801, seq 1)
{
  "kind": 25801,
  "tags": [
    ["p","A"],
    ["e","d5f...3a","", "root"],
    ["s","sender:A"],
    ["encryption","nip44"]
  ],
  "content": "<nip44-ciphertext>"
}

Tool call telemetry (25804)
{
  "kind": 25804,
  "tags": [
    ["p","A"],
    ["e","d5f...3a","", "root"],
    ["tool","calculator"],
    ["phase","start"],
    ["encryption","nip44"]
  ],
  "content": "<nip44-ciphertext>"
}

Cancel request (25806)
{
  "kind": 25806,
  "tags": [
    ["p","A"],
    ["e","d5f...3a","", "root"],
    ["s","sender:A"],
    ["encryption","nip44"]
  ],
  "content": "<nip44-ciphertext>"
}

Response (25803)
{
  "kind": 25803,
  "tags": [
    ["p","A"],
    ["e","d5f...3a","", "root"],
    ["encryption","nip44"]
  ],
  "content": "<nip44-ciphertext>"
}

Conformance examples

  1. Invalid schema: ai.prompt with message missing MUST be rejected and MAY

return ai.error with INVALID_SCHEMA.

  1. Unknown tool: tool_call.name not in ai.info.tool_names SHOULD be treated as

unsupported and MAY return UNSUPPORTED_FEATURE.

  1. Out-of-order deltas: clients reorder by seq as described in reconciliation.
  2. Encryption mismatch: noncompliant encryption tag content MUST be rejected as

UNSUPPORTED_ENCRYPTION or INVALID_SCHEMA.

  1. Duplicate terminal events: clients MUST dedupe by (created_at,id) and render with

only the newest terminal event.

  1. Cancel path: client can cancel a run by 25806 and should treat subsequent

25805 with code = CANCELLED as expected terminal completion.

  1. Response/cancel race: if both 25803 and 25805 for the same run exist, clients

MUST keep the terminal event with highest (created_at,id) and apply normal ordering.

Backward Compatibility

Implementations MUST:

  • Ignore unknown encrypted JSON fields.
  • Use canonical fields.
  • Avoid field duplication for historical aliases in future versions.
  • Keep s optional and default to sender:<lowercase-hex-pubkey> when absent.

This document defines ver: 1; field names and meanings MUST NOT be redefined in the same major version.

Implementations

Copyright

This document is placed in the public domain.