Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/design_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ live.
| Prompts, models, routing, optimisation, or fine-tuning | [prompt programmes and model routing](engineering/prompt_programs_and_model_routing_playbook.md) |
| Retrieval, memory, RAG, KB growth, or long-horizon state | [agent memory and enduring knowledge](engineering/agent_memory_and_enduring_knowledge.md) |
| Represented, experience- or discussion-derived learning for role, capability, workflow, tool, acquisition, introspection, message, or task decisions | Draft [represented advice design](engineering/represented_advice_design.md), including Phase 0 subtraction, Phase 0.5 non-active candidates, and the [completed bounded negative learning cycle](engineering/jvnautosci_2720_learning_cycle_2026-09-05.md) under JVNAUTOSCI-2720; then the applicable prompt, memory, evaluation, workflow, or security guidance. The cycle links source-derived formation, experimental exposure, outcome evidence, rejection and subsequent verified absence. It does not establish positive advice efficacy, an ordinary runtime consumer, production activation, represented Arm C or autonomous promotion. |
| Private Otter archive access, conversation images or research-slide reading | [Private Otter archive and conversation images](engineering/otter_archive_and_conversation_images.md) |
| Importing external human/agent transcripts into conversation carriers | [External conversation import](engineering/external_conversation_import.md) |
| Durable assertions, text/logical assertion typing, propositions, context-sensitive retrieval, hypotheses, publication or promotion, or user-, organisation-, project-, source-, theory-, or time-relative knowledge | [Assertion and propositional-sentence ontology](engineering/assertion_and_propositional_sentence_ontology.md), then [contextual knowledge evolution](engineering/contextual_knowledge_evolution.md); for canonical ontology publication or scope change, the active [ontology publication authority boundary](engineering/ontology_publication_authority.md) |
| Evaluation, benchmarks, or research-sensitive architecture | [agent evaluation and research uptake](engineering/agent_evaluation_and_research_uptake.md) |
Expand Down
75 changes: 75 additions & 0 deletions docs/engineering/otter_archive_and_conversation_images.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Private Otter archive and conversation images

Implementation and operating reference for [JVNAUTOSCI-2726](https://naoinstitute.atlassian.net/browse/JVNAUTOSCI-2726).
Delivery evidence and current release decision belong in Jira. This page describes the bounded interface;
it does not establish that a particular deployment has been configured or activated.

## Private archive configuration

Provision the separate private OtterArchiveMCP checkout, its SQLite index, and the original backup
on the machine running Von. Keep that server on stdio. Set these variables in the runtime environment:

| Variable | Meaning |
| --- | --- |
| `VON_OTTER_ARCHIVE_OWNER_USER_CONCEPT_ID` | Verified owning Von user; required before access |
| `VON_OTTER_ARCHIVE_RESOURCE_ID` | Deployment selector; defaults to `personal_otter_archive` |
| `VON_OTTER_ARCHIVE_MCP_PROJECT_DIR` | Private server checkout; required |
| `OTTER_ARCHIVE_DB` | Existing archive index; required |
| `VON_OTTER_ARCHIVE_MCP_COMMAND` | Optional executable override; otherwise the checkout's virtual environment entry point |
| `VON_OTTER_ARCHIVE_MCP_TIMEOUT_SEC` | Stdio process liveness bound (default 30 seconds); adjust from actual operation evidence |

The owner is resolved before launching the process. Ordinary turns receive a hidden trusted resource
binding; model arguments cannot choose a path, executable or owner. The subprocess receives only a
small operating environment and the database path, without provider credentials. Changing configuration
requires restarting the affected runtime. Removing the owner binding also denies subsequent reads of
staged private screenshots and derived crops. Existing LinkedIn and hosted Otter integrations remain
independent.

All ten upstream tools are available with the `otter_archive_` prefix. Orient broad questions with
status/overview, then search or retrieve bounded sources. `find_entities` frequencies describe source
records and observed labels, not reconciled meetings or verified identities. Unresolved export-group
associations stay unresolved. Ordinary reads do not start OCR, vision inference or corpus maintenance.

`get_artifact`/search results supply an authenticated `source_url`. That view displays original images,
source text, labelled OCR and cached inference, with extraction provenance available for inspection.
`get_screenshot` stages a checksum-verified original in Von's existing blob/file-copy store. Original
chunk retrieval returns bounded metadata and an authenticated download URL, keeping base64 out of model
text. Consumers reassembling originals must check the complete upstream hash.

## Reusable image handling

The existing drop, paste and picker routes feed one image pipeline. Up to eight still PNG, JPEG or WebP
images may accompany a message (8 MiB and 25 million pixels per image). Media is decoded rather than
trusted by filename. The composer shows progress, previews, removal and failures; unresolved preparation
blocks submission. Removing an unsent image removes its message association, not its durable source.

Original bytes live in the existing durable blob store. File-copy records retain detected media type,
dimensions, checksum and source provenance. New image records explicitly use `attributes.v1` as their
file metadata authority to avoid duplicate per-field text-relation writes and reads; existing file-copy
records retain their earlier metadata contract. The normal concept name and type remain represented.

Messages and history carry small attachment descriptors. Only the provider boundary hydrates access-
checked original bytes into native image inputs, with source IDs, dimensions and citation URLs alongside
the images. Retained continuations and ordinary telemetry contain descriptors rather than base64.
OpenAI Chat Completions/Responses and Ollama have image transport; the Gemini structured transport
currently reports unsupported image input explicitly. A transport does not establish that every model
on that provider supports vision; use a vision-capable configured model and inspect failures.

`crop_conversation_image` creates a labelled crop/enlargement for small details. It preserves parent
checksum, pixel rectangle, scale and source audience. Resizing cannot reconstruct destroyed information.
Originals remain independently inspectable. Conversation image reads require the owning actor; an Otter
`shared` label or organisation membership does not grant archive access.

## Validation and limits

The CC0 fixtures in `tests/fixtures/research_images` and their generator/reference file cover columns,
symbols, an exact recurrence, a logarithmic chart, table/code structure, directed feedback and degradation.
Reference criteria precede tuning. Tests cover authority before proxy launch, hidden bindings, original
checksums, SDK image bytes and references, crop provenance, history projection and composer failures.
Use the browser protocol for live upload/reload/follow-up and source-view acceptance.

Private live receipts stay outside Git. Report the selected model, input/preprocessing, source hashes,
actual crop/cache use, latency and any failed attempts. Sparse OCR or model enrichment is not evidence
that a topic is absent. Cached model interpretations can be wrong; scientific claims must remain tied
to inspectable original evidence. This integration does not reconcile the whole archive, prove
corpus-wide OCR accuracy, cancel Otter, deploy a network archive service or grant wider access.
6 changes: 6 additions & 0 deletions src/backend/integrations/internal_mcp/catalogue.py
Original file line number Diff line number Diff line change
Expand Up @@ -41325,6 +41325,8 @@ def _build_default_catalogue_knowledge_io_definitions() -> List[MethodDefinition
"inclusion for direct jira_add_attachment calls."
),
),
*otter_archive_definitions(),
*conversation_image_definitions(),
# Owner-scoped LinkedIn export search via a local external MCP server.
MethodDefinition(
name="linkedin_index_status",
Expand Down Expand Up @@ -46111,6 +46113,10 @@ def _register_dynamic_catalogue_methods(
)


from .conversation_image_tools import definitions as conversation_image_definitions
from .otter_archive_tools import definitions as otter_archive_definitions


def build_default_catalogue() -> MethodCatalogue:
"""Return a catalogue pre-populated with the baseline method set."""

Expand Down
43 changes: 43 additions & 0 deletions src/backend/integrations/internal_mcp/conversation_image_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Reusable detail recovery for conversation images from any supported source."""

from .gateway import MethodDefinition
from .schemas import Schema


def crop_conversation_image(**kwargs):
from ...security.access_control import get_effective_user_concept_id
from ...services.conversation_image_service import crop_image
from .catalogue import make_error_response

try:
image = crop_image(user_concept_id=get_effective_user_concept_id(), **kwargs)
return {
"success": True,
"image_attachments": [image],
"note": "Derived detail view. Resizing does not restore information absent from the original.",
}
except (ValueError, PermissionError) as exc:
return make_error_response("image_detail_unavailable", str(exc))


def definitions():
return [
MethodDefinition(
name="crop_conversation_image",
handler=crop_conversation_image,
input_schema=Schema(
required={
"concept_id": str,
"x": int,
"y": int,
"width": int,
"height": int,
},
optional={"scale": int},
allow_unknown=False,
),
output_schema=Schema(required={}, optional={}, allow_unknown=True),
category="read",
description="Inspect a crop of a conversation image, preserving its source and private audience. Supply pixel x/y/width/height and optional integer scale 1–4 (default 2). Use the resulting actual image to recover small text or diagram detail; report what remains unreadable. Original bytes remain unchanged.",
)
]
Loading
Loading