SDK recording and evidence context
Use the observation API to record a supported message or event without creating
policy authority. Python haltstate-sdk==0.8.0.dev1 is available from PyPI and
includes synchronous and asynchronous record_observation(...) helpers. Go and
Rust dev1 recording previews are also publicly available and clean-consumer
verified. Java's dev1 release is available through JitPack and a clean Java 17
consumer compiled successfully.
The TypeScript dev1 recording helper remains an unpublished source candidate.
Existing credentials, explicit capture
Use the same configured HaltState client, tenant key and registered agent that
your integration already uses. No separate customer signing key or EID setup is
required for observation or attachment submission. Connected recording uses
enabled / all_except / 90-day settings by default, with explicit per-agent
exclusions. Older saved selected_only rows remain compatible. The server's
effective setting is authoritative.
Mint one stable activity_id for each natural operation and reuse it across its
check, guard, report, observation and attachment calls. Optional evidence context
may also carry correlation, conversation, message, customer, request and operation
identifiers. A supplied operation ID is linked only after same-tenant,
same-agent validation. These are audit metadata, not extra tool parameters or a
way to change policy decisions, operation fingerprints or replay selection.
These helpers do not intercept model providers, record hidden reasoning or copy whole conversations. The application explicitly submits only supported connected message or business-event content that it is permitted to retain.
Public helper names
| SDK | Record a message/event without policy authority | Evidence-aware check | Capture/report helpers |
|---|---|---|---|
| Python | record_observation(...) (sync and async) |
check(..., evidence_context=...) |
attach_evidence(...), attach_evidence_for_guard(...); report returns an optional receipt |
| TypeScript/JavaScript | recordObservation(...) |
check(..., evidenceContext) |
attachEvidence(...), attachEvidenceForGuard(...); report returns optional capture state |
| Go | RecordObservation(...) |
CheckWithEvidence(...) |
AttachEvidence(...), AttachEvidenceForGuard(...), ReportWithEvidenceCapture(...) |
| Rust | record_observation(Observation { evidence_context: ObservationContext, ... }) |
Existing check_with_evidence(...) / guard_with_evidence(...) unchanged |
check_with_evidence_capture(...), guard_with_evidence_capture(...), report_evidence_guard_outcome_with_capture(...), report_with_evidence_capture(...) |
| Java | recordObservation(...) |
check(..., EvidenceContext) |
attachEvidence(...), attachEvidenceForGuard(...), reportWithCapture(...) |
Go and Rust return additive evidence-aware wrappers; existing public check result
types remain compatible. Rust specifically preserves the original field shapes
of EvidenceContext, EvidenceCheckResult, EvidencePermit and GuardReceipt
for existing exhaustive struct literals. New ExtendedEvidenceContext,
ObservationContext, EvidenceCheckCapture, EvidencePermitCapture and
GuardOutcomeCapture carry activity/operation identity and capture receipts.
The capture-aware methods are additive; callers may keep using the old methods
without source changes. The API models and context contract define the exact
request/response fields and guard/report integration names.
Python's async client supports the check-result path; the guard helper belongs
to its existing synchronous guard client. Guard-result helpers use the persisted
evidence ID returned by the server. They do not manufacture an evidence record
or run an extra policy check. Keep using the existing guard/start/outcome sequence
for action authority; attachment submission does not replace any of those steps.
record_observation/recordObservation is for a supported message or event
boundary that may exist outside a policy call. It sends agent_id, stable
event_id, action, direction (input, output or event), optional
timezone-aware occurrence time, allowlisted evidence context, canonical
{kind,value} external references, and optional base64 content. Content is
rejected locally above 256 KiB. The receipt must say record_state=recorded and
governance_state=not_evaluated; the SDK never converts that observation to
ALLOW, a permit or proof of execution.
Retries reuse the exact event ID and payload, and are limited to transport
failure, 429 and 5xx. A 4xx conflict, malformed success or missing durable
receipt is not labelled recorded. Automatic hooks attach only non-empty
title/content inputs and public delivery identifiers/URLs on output. They do
not retain credentials, prompts, history, hidden reasoning or arbitrary provider
responses, and a capture failure never reruns the policy call, business action or
report.
Published preview IDs and October 2 release state
| SDK | Existing published preview | October 2 dev1 state |
|---|---|---|
| Python | PyPI haltstate-sdk==0.8.0.dev0 |
0.8.0.dev1 published on PyPI; exact wheel/sdist hashes and a clean Python 3.12 public install verified. Stable 0.7.0 is unchanged. |
| TypeScript/JavaScript | npm @haltstate/sdk@2.0.0-dev.0 under next |
2.0.0-dev.1 source candidate; not yet publicly released. Stable latest=1.0.2 and next=2.0.0-dev.0 remain available. |
| Go | GitHub/module preview v1.1.0-dev.0 |
v1.1.0-dev.1 published; clean Go module-proxy resolution and tests passed. |
| Rust | GitHub Cargo tag v0.2.0-dev.0 |
v0.2.0-dev.1 published; clean Git dependency resolution and consumer compilation passed. Distribution remains Git-based, not crates.io. |
| Java | GitHub/JitPack tag v0.3.0-dev.0 |
v0.3.0-dev.1 published; JitPack build/tests and a clean Java 17 Gradle consumer passed. |
Python, Go, Rust and Java dev1 recording previews have verified public consumers. The TypeScript row describes source API shape, not a registry installation promise. Published dev0 versions predate the recording helpers and must not be described as containing them. Stable/default channels remain unchanged.
Python example
Record a supported message boundary with a stable event and activity identity:
receipt = client.record_observation(
agent_id="signal-moltbook",
event_id="conversation-42:message-7:input",
action="customer.message.received",
direction="input",
evidence_context={
"activity_id": "activity-42-message-7",
"conversation_id": "conversation-42",
"message_id": "message-7",
"customer_reference": "customer-482",
},
external_references=[{"kind": "message_id", "value": "message-7"}],
content=b"The message explicitly selected for retention",
)
if receipt.record_state != "recorded":
handle_evidence_gap(receipt.error)
Here client is the application's existing configured client and checked is
the evidence-aware result from its existing policy-check workflow. This example
only submits evidence; it does not execute or authorize the underlying action.
receipt = client.attach_evidence(
checked,
attachment_id="case-482:input:1",
content="The governed input to retain".encode("utf-8"),
content_kind="input",
)
if receipt.state != "available":
# Record a capture gap using the state/reference, not private message text.
handle_evidence_gap(receipt.state)
The async client uses the same arguments with await.
TypeScript example
const receipt = await client.attachEvidence(
checked,
'case-482:input:1',
new TextEncoder().encode('The governed input to retain'),
'input',
);
if (receipt.state !== 'available') {
handleEvidenceGap(receipt.state);
}
Retry, retention and access
Use a stable attachment ID for the same evidence, agent, content kind and bytes. A retry returns the original reference, digest and expiry, with current availability. Reusing the ID for conflicting content is an error. Use distinct input and output IDs. An expired retry does not recreate a body or extend its retention period.
Retention is selected at upload: 30, 90 or 365 days, defaulting to 90. A later setting change applies to new uploads. Capture failure or expiry remains separate from the action decision and must not be reported as successful evidence capture. Do not retry the business action merely because an attachment failed.
Upload permission does not grant read access to other retained messages. Investigation content and exports use separate account/connection permissions. Never log submitted content, API credentials or full diagnostic response bodies.
For the customer investigation workflow and assurance limits, see Investigating actions with Proof Packs.