neotoma 0.9.1 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -7
- package/dist/actions.d.ts +9 -4
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +222 -29
- package/dist/actions.js.map +1 -1
- package/dist/cli/aauth_signer.d.ts.map +1 -1
- package/dist/cli/aauth_signer.js +17 -3
- package/dist/cli/aauth_signer.js.map +1 -1
- package/dist/cli/auth_keygen.d.ts +11 -0
- package/dist/cli/auth_keygen.d.ts.map +1 -0
- package/dist/cli/auth_keygen.js +110 -0
- package/dist/cli/auth_keygen.js.map +1 -0
- package/dist/cli/commands/processes.d.ts +33 -0
- package/dist/cli/commands/processes.d.ts.map +1 -0
- package/dist/cli/commands/processes.js +385 -0
- package/dist/cli/commands/processes.js.map +1 -0
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +14 -7
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +85 -7
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/hooks_detect.d.ts.map +1 -1
- package/dist/cli/hooks_detect.js +6 -2
- package/dist/cli/hooks_detect.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +199 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp_config_scan.d.ts +30 -0
- package/dist/cli/mcp_config_scan.d.ts.map +1 -1
- package/dist/cli/mcp_config_scan.js +250 -27
- package/dist/cli/mcp_config_scan.js.map +1 -1
- package/dist/cli/mcp_proxy.d.ts +9 -0
- package/dist/cli/mcp_proxy.d.ts.map +1 -0
- package/dist/cli/mcp_proxy.js +114 -0
- package/dist/cli/mcp_proxy.js.map +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +6 -1
- package/dist/cli/setup.js.map +1 -1
- package/dist/inspector/assets/{Combination-BP0-kPZX.js → Combination-BM2JQOww.js} +1 -1
- package/dist/inspector/assets/{agent_badge-BZT-JO2h.js → agent_badge-CpN0F7Uw.js} +1 -1
- package/dist/inspector/assets/{agent_detail-BGMLF8-n.js → agent_detail-BiJ3HPkP.js} +1 -1
- package/dist/inspector/assets/{agent_filter-DMC4CzhM.js → agent_filter-BazKcQ9w.js} +1 -1
- package/dist/inspector/assets/{agent_grant_detail-Brqy5K7M.js → agent_grant_detail-DXgCn9N4.js} +1 -1
- package/dist/inspector/assets/{agent_grant_form-BLDkUh1Y.js → agent_grant_form-DZQjBAcw.js} +1 -1
- package/dist/inspector/assets/{agent_grants-B2StunRb.js → agent_grants-DCKfkpwq.js} +1 -1
- package/dist/inspector/assets/{agents-lYmKs-fG.js → agents-ThrWnZTX.js} +1 -1
- package/dist/inspector/assets/{arrow-left-D1s7DOns.js → arrow-left-DaYPjbWH.js} +1 -1
- package/dist/inspector/assets/{attribution_card-D-bp008l.js → attribution_card-D8wccbDX.js} +1 -1
- package/dist/inspector/assets/{attribution_summary-Cs9Ccvt3.js → attribution_summary-Ds3UqXE_.js} +1 -1
- package/dist/inspector/assets/{card-Btqgzh6p.js → card-gNKW1s0y.js} +1 -1
- package/dist/inspector/assets/{check-DAyRtq63.js → check-CsKn_Wh2.js} +1 -1
- package/dist/inspector/assets/{checkbox-BrpwHaRo.js → checkbox-Ckyj4vrh.js} +1 -1
- package/dist/inspector/assets/{chevron-down-CCk_jMPN.js → chevron-down-7OlcRYdt.js} +1 -1
- package/dist/inspector/assets/{chevron-right-C9NbdZtC.js → chevron-right-BLI_oHPA.js} +1 -1
- package/dist/inspector/assets/{compliance-BHiHtgfg.js → compliance-CBrpHE20.js} +1 -1
- package/dist/inspector/assets/{confirm-dialog-BcxtVONz.js → confirm-dialog--bNglOrP.js} +1 -1
- package/dist/inspector/assets/{conversation_common-Dw5j3QuN.js → conversation_common-BtftGfBd.js} +1 -1
- package/dist/inspector/assets/{conversation_detail-BR_rBMFV.js → conversation_detail-DcL600xx.js} +1 -1
- package/dist/inspector/assets/{copy_id_button-CyjfY7dx.js → copy_id_button-V_cdlRRU.js} +1 -1
- package/dist/inspector/assets/{corrections-DFExbcsm.js → corrections-CSny2Vo_.js} +1 -1
- package/dist/inspector/assets/{dashboard-CxnNRphy.js → dashboard-QDIRlmWG.js} +1 -1
- package/dist/inspector/assets/{data-table-CazqdSem.js → data-table-qmF3Z4gV.js} +1 -1
- package/dist/inspector/assets/{dialog-X5X7rLah.js → dialog-DYUGw6eQ.js} +1 -1
- package/dist/inspector/assets/{dropdown-menu-CmZjxUWM.js → dropdown-menu-F92-sh_x.js} +1 -1
- package/dist/inspector/assets/{entities-l6icu6fc.js → entities-Dsipnex_.js} +1 -1
- package/dist/inspector/assets/{entity_detail-daoxB-h1.js → entity_detail-BfgIPU8x.js} +1 -1
- package/dist/inspector/assets/{entity_link-DtMv__WC.js → entity_link-Ux3LzoaM.js} +1 -1
- package/dist/inspector/assets/{external-link-BBoTnT-P.js → external-link-CWkNK5Tk.js} +1 -1
- package/dist/inspector/assets/feedback-CJK1vjsI.js +35 -0
- package/dist/inspector/assets/{graph_explorer-BvicLJEW.js → graph_explorer-BmseNUoQ.js} +1 -1
- package/dist/inspector/assets/index-BAvZ5vLC.css +1 -0
- package/dist/inspector/assets/{index-D5i6AEXI.js → index-BykFtejE.js} +1 -1
- package/dist/inspector/assets/index-CYCtoVGs.js +224 -0
- package/dist/inspector/assets/{index-Czej0Y93.js → index-DV_dUptd.js} +1 -1
- package/dist/inspector/assets/{index-DJuPlRtP.js → index-DwNCMlAU.js} +1 -1
- package/dist/inspector/assets/{interpretations-E0sIBf-l.js → interpretations-CMfzQ1Ln.js} +1 -1
- package/dist/inspector/assets/interpretations-T0E9nJx-.js +1 -0
- package/dist/inspector/assets/{json_viewer-ojLDPDtf.js → json_viewer-Bq_YbebZ.js} +1 -1
- package/dist/inspector/assets/{label-DWyQNl4E.js → label-Bl-6LEeg.js} +1 -1
- package/dist/inspector/assets/{live_relative_time-DzLnsA9y.js → live_relative_time-B1_XT7na.js} +1 -1
- package/dist/inspector/assets/{observations-Ds0kFmaM.js → observations-DBIz6sjx.js} +1 -1
- package/dist/inspector/assets/{page_shell-C-4AKr0Y.js → page_shell-BEmO9wa5.js} +1 -1
- package/dist/inspector/assets/{pagination-lSg-a95h.js → pagination-BUY-_ZUw.js} +1 -1
- package/dist/inspector/assets/{plus-CQMoR71F.js → plus-CN3iw613.js} +1 -1
- package/dist/inspector/assets/{query_refresh_indicator-Bewf0Dj1.js → query_refresh_indicator-BvQCvZ7y.js} +1 -1
- package/dist/inspector/assets/{recent_activity-Csh_YraY.js → recent_activity-8sqCYQa2.js} +1 -1
- package/dist/inspector/assets/{recent_conversations-CBengQjb.js → recent_conversations-Dm7bsH19.js} +1 -1
- package/dist/inspector/assets/{recent_conversations-MKmxYevd.js → recent_conversations-ZmrvYZt6.js} +1 -1
- package/dist/inspector/assets/{recent_records_feed-CCrBRfkG.js → recent_records_feed-BCwEB-gO.js} +1 -1
- package/dist/inspector/assets/{relationship_detail-CUz_GhPI.js → relationship_detail-AY29Q_zg.js} +1 -1
- package/dist/inspector/assets/{relationships-Z9ALu9Oa.js → relationships-BfjaoQF3.js} +1 -1
- package/dist/inspector/assets/{relationships-Ulajo16_.js → relationships-C9JOmG6k.js} +1 -1
- package/dist/inspector/assets/{sandbox-CfD5QvC5.js → sandbox-2sw7hAPG.js} +1 -1
- package/dist/inspector/assets/{schema_detail-B1W8rbzV.js → schema_detail-BwWRhFNS.js} +1 -1
- package/dist/inspector/assets/{schemas-DVlEFPuf.js → schemas-UX8RWzu0.js} +1 -1
- package/dist/inspector/assets/{search-BccQXyTN.js → search-sJaK1X54.js} +1 -1
- package/dist/inspector/assets/{select-Dv1QM6oO.js → select-Sk4XRvus.js} +1 -1
- package/dist/inspector/assets/{settings-B4U8tFYI.js → settings-D9mhRMBN.js} +1 -1
- package/dist/inspector/assets/{source_detail-B1JlZBBx.js → source_detail-BAF1G5q0.js} +1 -1
- package/dist/inspector/assets/{source_link-p8KzI1os.js → source_link-DTok2HA9.js} +1 -1
- package/dist/inspector/assets/{sources-B5ssCN-s.js → sources-DUcCw0Vc.js} +1 -1
- package/dist/inspector/assets/{switch-BPI_y_Z3.js → switch-DepRZ0--.js} +1 -1
- package/dist/inspector/assets/{tabs-HUG-sxc2.js → tabs-B0Dy5is1.js} +1 -1
- package/dist/inspector/assets/{textarea-DNz92WE1.js → textarea-pfrE_cJC.js} +1 -1
- package/dist/inspector/assets/{timeline-HN2EoMGt.js → timeline-Bh9ngotn.js} +1 -1
- package/dist/inspector/assets/{timeline-DRqxyQUF.js → timeline-COztEDwn.js} +1 -1
- package/dist/inspector/assets/{timeline_event_detail-DbA5EpiN.js → timeline_event_detail-CN1g0r15.js} +1 -1
- package/dist/inspector/assets/{trash-2-BAfHKatZ.js → trash-2-CGNk7jcZ.js} +1 -1
- package/dist/inspector/assets/{turn_detail-BUHzIhWX.js → turn_detail-DYjGa2Qt.js} +1 -1
- package/dist/inspector/assets/{turns-ADRkuqKL.js → turns-qZVfg6Dm.js} +1 -1
- package/dist/inspector/assets/{use_agents-CTZrpsvS.js → use_agents-B8yN5BKA.js} +1 -1
- package/dist/inspector/assets/{use_entities-BEhy6HWn.js → use_entities-l4hLQEUh.js} +1 -1
- package/dist/inspector/assets/use_interpretations-B_ssuETV.js +1 -0
- package/dist/inspector/assets/{use_mutations-B4y1qmV5.js → use_mutations-BjaD74zY.js} +1 -1
- package/dist/inspector/assets/use_recent_conversations-BgZIIrU6.js +1 -0
- package/dist/inspector/assets/{use_relationships-Cl-o_7u6.js → use_relationships-6dqoUmCP.js} +1 -1
- package/dist/inspector/assets/{use_schemas-Bl11WNgP.js → use_schemas-BkMuCTZx.js} +1 -1
- package/dist/inspector/assets/{use_sources-DkJZZBDp.js → use_sources-Bu_vb2VA.js} +1 -1
- package/dist/inspector/assets/{use_stats-Cn1a3yt-.js → use_stats-DvcjJbZf.js} +1 -1
- package/dist/inspector/assets/{use_timeline-DZkbwA-7.js → use_timeline-BXlKLsFO.js} +1 -1
- package/dist/inspector/assets/{use_turns-BHfaal9v.js → use_turns-BYjwOq0j.js} +1 -1
- package/dist/inspector/index.html +2 -2
- package/dist/proxy/aauth_client_signer.d.ts +37 -0
- package/dist/proxy/aauth_client_signer.d.ts.map +1 -0
- package/dist/proxy/aauth_client_signer.js +172 -0
- package/dist/proxy/aauth_client_signer.js.map +1 -0
- package/dist/proxy/index.d.ts +7 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +4 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/mcp_stdio_proxy.d.ts +32 -0
- package/dist/proxy/mcp_stdio_proxy.d.ts.map +1 -0
- package/dist/proxy/mcp_stdio_proxy.js +234 -0
- package/dist/proxy/mcp_stdio_proxy.js.map +1 -0
- package/dist/proxy/preflight.d.ts +15 -0
- package/dist/proxy/preflight.d.ts.map +1 -0
- package/dist/proxy/preflight.js +71 -0
- package/dist/proxy/preflight.js.map +1 -0
- package/dist/repositories/sqlite/sqlite_client.d.ts.map +1 -1
- package/dist/repositories/sqlite/sqlite_client.js +2 -0
- package/dist/repositories/sqlite/sqlite_client.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +269 -115
- package/dist/server.js.map +1 -1
- package/dist/services/entity_queries.d.ts.map +1 -1
- package/dist/services/entity_queries.js +68 -20
- package/dist/services/entity_queries.js.map +1 -1
- package/dist/services/feedback/admin_proxy.d.ts +28 -9
- package/dist/services/feedback/admin_proxy.d.ts.map +1 -1
- package/dist/services/feedback/admin_proxy.js +145 -24
- package/dist/services/feedback/admin_proxy.js.map +1 -1
- package/dist/services/feedback/admin_session.d.ts +36 -0
- package/dist/services/feedback/admin_session.d.ts.map +1 -0
- package/dist/services/feedback/admin_session.js +147 -0
- package/dist/services/feedback/admin_session.js.map +1 -0
- package/dist/services/inspector_mount.d.ts.map +1 -1
- package/dist/services/inspector_mount.js +9 -0
- package/dist/services/inspector_mount.js.map +1 -1
- package/dist/services/interpretation.d.ts.map +1 -1
- package/dist/services/interpretation.js +8 -4
- package/dist/services/interpretation.js.map +1 -1
- package/dist/services/mcp_oauth.d.ts +9 -7
- package/dist/services/mcp_oauth.d.ts.map +1 -1
- package/dist/services/mcp_oauth.js +170 -13
- package/dist/services/mcp_oauth.js.map +1 -1
- package/dist/services/raw_fragments.d.ts +3 -0
- package/dist/services/raw_fragments.d.ts.map +1 -1
- package/dist/services/raw_fragments.js +18 -8
- package/dist/services/raw_fragments.js.map +1 -1
- package/dist/services/root_landing/harness_snippets.d.ts +12 -0
- package/dist/services/root_landing/harness_snippets.d.ts.map +1 -1
- package/dist/services/root_landing/harness_snippets.js +49 -0
- package/dist/services/root_landing/harness_snippets.js.map +1 -1
- package/dist/services/root_landing/site_nav.d.ts.map +1 -1
- package/dist/services/root_landing/site_nav.js +8 -3
- package/dist/services/root_landing/site_nav.js.map +1 -1
- package/dist/services/schema_registry.d.ts +2 -1
- package/dist/services/schema_registry.d.ts.map +1 -1
- package/dist/services/schema_registry.js +31 -9
- package/dist/services/schema_registry.js.map +1 -1
- package/dist/shared/action_schemas.d.ts +166 -0
- package/dist/shared/action_schemas.d.ts.map +1 -1
- package/dist/shared/action_schemas.js +17 -0
- package/dist/shared/action_schemas.js.map +1 -1
- package/dist/shared/contract_mappings.d.ts.map +1 -1
- package/dist/shared/contract_mappings.js +15 -2
- package/dist/shared/contract_mappings.js.map +1 -1
- package/dist/shared/openapi_types.d.ts +102 -0
- package/dist/shared/openapi_types.d.ts.map +1 -1
- package/dist/tool_definitions.d.ts.map +1 -1
- package/dist/tool_definitions.js +12 -2
- package/dist/tool_definitions.js.map +1 -1
- package/openapi.yaml +111 -0
- package/package.json +1 -1
- package/dist/inspector/assets/feedback-DeFhdWId.js +0 -35
- package/dist/inspector/assets/index-B2zHigxN.js +0 -199
- package/dist/inspector/assets/index-Df569_c9.css +0 -1
- package/dist/inspector/assets/interpretations-D9gWqVhy.js +0 -1
- package/dist/inspector/assets/use_interpretations-CKN63UxX.js +0 -1
- package/dist/inspector/assets/use_recent_conversations-CIBgmz9B.js +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Your agents forget. Neotoma makes them remember.
|
|
4
4
|
|
|
5
|
-
Versioned records — contacts, tasks, decisions, finances — that persist across Claude, Cursor, ChatGPT, OpenClaw, and every agent you run. Open-source. Local-first. Deterministic. MIT licensed.
|
|
5
|
+
Versioned records — contacts, tasks, decisions, finances — that persist across Claude, Cursor, ChatGPT, OpenClaw, IronClaw, and every agent you run. Open-source. Local-first. Deterministic. MIT licensed.
|
|
6
6
|
|
|
7
7
|
**[neotoma.io](https://neotoma.io)** · **[Evaluate](https://neotoma.io/evaluate)** · **[Install](https://neotoma.io/install)** · **[Documentation](https://neotoma.io/docs)**
|
|
8
8
|
|
|
@@ -38,6 +38,7 @@ graph LR
|
|
|
38
38
|
MCP --> ChatGPT
|
|
39
39
|
MCP --> Cursor
|
|
40
40
|
MCP --> OpenClaw
|
|
41
|
+
MCP --> IronClaw
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
- **Deterministic.** Same observations always produce the same versioned entity snapshots. No ordering sensitivity.
|
|
@@ -51,7 +52,7 @@ graph LR
|
|
|
51
52
|
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
53
|
| **Privacy-first** | Your data stays local. Never used for training. User-controlled storage, optional encryption at rest. Full export and deletion control. |
|
|
53
54
|
| **Deterministic** | Same input always produces same output. Schema-first extraction, hash-based entity IDs, full provenance. No silent mutation. |
|
|
54
|
-
| **Cross-platform** | One memory graph across Claude, ChatGPT, Cursor, OpenClaw, Codex, and CLI. MCP-based access. No platform lock-in. Works alongside native memory. |
|
|
55
|
+
| **Cross-platform** | One memory graph across Claude, ChatGPT, Cursor, OpenClaw, IronClaw, Codex, and CLI. MCP-based access. No platform lock-in. Works alongside native memory. |
|
|
55
56
|
|
|
56
57
|
## State guarantees
|
|
57
58
|
|
|
@@ -100,6 +101,7 @@ The agent handles npm install, initialization, and MCP configuration. **Manual i
|
|
|
100
101
|
npm install -g neotoma
|
|
101
102
|
neotoma init
|
|
102
103
|
neotoma mcp config
|
|
104
|
+
neotoma mcp check --mcp-transport a
|
|
103
105
|
```
|
|
104
106
|
|
|
105
107
|
More options: [Docker](docs/developer/docker.md) | [CLI reference](docs/developer/cli_reference.md) | [Getting started](docs/developer/getting_started.md)
|
|
@@ -121,14 +123,14 @@ Three interfaces. One state invariant. Every interface provides the same determi
|
|
|
121
123
|
| Interface | Description |
|
|
122
124
|
| -------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
123
125
|
| **REST API** | Full HTTP interface for application integration. Entities, relationships, observations, schema, timeline, and version history. |
|
|
124
|
-
| **MCP Server** | Model Context Protocol for Claude, ChatGPT, Cursor, OpenClaw, Codex, and more. Agents store and retrieve state through structured tool calls. |
|
|
126
|
+
| **MCP Server** | Model Context Protocol for Claude, ChatGPT, Cursor, OpenClaw, IronClaw, Codex, and more. Agents store and retrieve state through structured tool calls. |
|
|
125
127
|
| **CLI** | Command-line for scripting and direct access. Inspect entities, replay timelines, and manage state from the terminal. |
|
|
126
128
|
|
|
127
129
|
All three map to the same OpenAPI-backed operations. MCP tool calls log the equivalent CLI invocation.
|
|
128
130
|
|
|
129
131
|
## Who this is for
|
|
130
132
|
|
|
131
|
-
People building a personal operating system with AI agents across their life — wiring together tools like Claude, Cursor, ChatGPT, OpenClaw, and custom scripts to manage contacts, tasks, finances, code, content, and other domains. The same person operates their agents, builds new pipelines, and debugs state drift. These are three operational modes, not separate personas:
|
|
133
|
+
People building a personal operating system with AI agents across their life — wiring together tools like Claude, Cursor, ChatGPT, OpenClaw, IronClaw, and custom scripts to manage contacts, tasks, finances, code, content, and other domains. The same person operates their agents, builds new pipelines, and debugs state drift. These are three operational modes, not separate personas:
|
|
132
134
|
|
|
133
135
|
| Mode | What you're doing | The tax you pay without Neotoma | What you get back |
|
|
134
136
|
| ---- | ----------------- | ------------------------------- | ----------------- |
|
|
@@ -155,7 +157,7 @@ Schema is flexible — store any entity type with whatever fields the message im
|
|
|
155
157
|
|
|
156
158
|
## Current status
|
|
157
159
|
|
|
158
|
-
**Version:** v0.
|
|
160
|
+
**Version:** v0.9.1 · **Releases:** 26 · **License:** MIT
|
|
159
161
|
|
|
160
162
|
### What is guaranteed (even in preview)
|
|
161
163
|
|
|
@@ -217,9 +219,9 @@ npm test
|
|
|
217
219
|
|
|
218
220
|
Neotoma exposes state via MCP. Local storage only in preview. Local built-in auth.
|
|
219
221
|
|
|
220
|
-
**Setup guides:** [Cursor](https://neotoma.io/neotoma-with-cursor) · [Claude Code](https://neotoma.io/neotoma-with-claude-code) · [Claude](https://neotoma.io/neotoma-with-claude) · [ChatGPT](https://neotoma.io/neotoma-with-chatgpt) · [Codex](https://neotoma.io/neotoma-with-codex) · [OpenClaw](https://neotoma.io/neotoma-with-openclaw)
|
|
222
|
+
**Setup guides:** [Cursor](https://neotoma.io/neotoma-with-cursor) · [Claude Code](https://neotoma.io/neotoma-with-claude-code) · [Claude](https://neotoma.io/neotoma-with-claude) · [ChatGPT](https://neotoma.io/neotoma-with-chatgpt) · [Codex](https://neotoma.io/neotoma-with-codex) · [OpenCode](docs/integrations/hooks/opencode.md) · [OpenClaw](https://neotoma.io/neotoma-with-openclaw) · [IronClaw](https://neotoma.io/neotoma-with-ironclaw)
|
|
221
223
|
|
|
222
|
-
For local source iteration, use the stable dev shim (`scripts/run_neotoma_mcp_stdio_dev_shim.sh` or
|
|
224
|
+
For local source iteration, use the stable dev shim (`scripts/run_neotoma_mcp_stdio_dev_shim.sh`) or signed shim (`scripts/run_neotoma_mcp_signed_stdio_dev_shim.sh`) instead of pointing installed MCP clients at a `tsx watch` stdio process. `neotoma mcp check` defaults to **`a`**: signed + AAuth with **neotoma-dev → dev** and **neotoma → prod** HTTP `/mcp` behind stdio; use `--mcp-transport c` for direct stdio, **`d`** if both MCP entries should target prod.
|
|
223
225
|
|
|
224
226
|
**Agent behavior contract:** Store first, retrieve before storing, extract entities from user input, create tasks for commitments, and attach bounded host context such as repository name/root scope when available. Full instructions: [MCP instructions](docs/developer/mcp/instructions.md) and [CLI agent instructions](docs/developer/cli_agent_instructions.md).
|
|
225
227
|
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import express from "express";
|
|
2
|
+
import { type StoreInterpretationInput } from "./shared/action_schemas.js";
|
|
2
3
|
export declare const app: import("express-serve-static-core").Express;
|
|
3
4
|
/**
|
|
4
5
|
* True when the request arrived over a loopback socket.
|
|
@@ -38,6 +39,8 @@ export declare function storeStructuredForApi(params: {
|
|
|
38
39
|
idempotencyKey: string;
|
|
39
40
|
originalFilename?: string;
|
|
40
41
|
relationships?: StoreRelationshipRef[];
|
|
42
|
+
interpretation?: StoreInterpretationInput;
|
|
43
|
+
interpretationSourceId?: string;
|
|
41
44
|
commit?: boolean;
|
|
42
45
|
strict?: boolean;
|
|
43
46
|
}): Promise<{
|
|
@@ -61,10 +64,6 @@ export declare function storeStructuredForApi(params: {
|
|
|
61
64
|
identity_basis: string;
|
|
62
65
|
identity_rule: string;
|
|
63
66
|
}[] | undefined;
|
|
64
|
-
success: boolean;
|
|
65
|
-
replayed: boolean;
|
|
66
|
-
commit: boolean;
|
|
67
|
-
source_id: string | null;
|
|
68
67
|
entities_created: number;
|
|
69
68
|
observations_created: number;
|
|
70
69
|
entities: {
|
|
@@ -91,6 +90,12 @@ export declare function storeStructuredForApi(params: {
|
|
|
91
90
|
source_entity_id: string;
|
|
92
91
|
target_entity_id: string;
|
|
93
92
|
}[];
|
|
93
|
+
interpretation_id?: string | undefined;
|
|
94
|
+
interpretation_source_id?: string | undefined;
|
|
95
|
+
success: boolean;
|
|
96
|
+
replayed: boolean;
|
|
97
|
+
commit: boolean;
|
|
98
|
+
source_id: string | null;
|
|
94
99
|
}>;
|
|
95
100
|
export declare function startHTTPServer(): Promise<{
|
|
96
101
|
server: import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
|
package/dist/actions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AA2G9B,OAAO,EAyBL,KAAK,wBAAwB,EAG9B,MAAM,4BAA4B,CAAC;AA+BpC,eAAO,MAAM,GAAG,6CAAY,CAAC;AAygB7B;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAU5D;AA4PD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAmBhD;AAiyHD,KAAK,oBAAoB,GAAG;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAuDF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,4BAA4B,EAAE,iBAAiB,CAAC;IAC3E,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACvC,cAAc,CAAC,EAAE,wBAAwB,CAAC;IAC1C,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;;;;;;;;;;;;;cA+gBS,MAAM;gBACJ,MAAM;2BACK,MAAM;qBACZ,MAAM;mBACR,MAAM;wBACD,MAAM;uBACP,MAAM;;;;;mBA3JV,MAAM;qBACJ,MAAM;wBACH,MAAM,GAAG,IAAI;2BACV,MAAM;;wBAET,MAAM;uBACP,MAAM,EAAE;wBACP,MAAM;uBACP,MAAM;;kBA9NX,MAAM;oBACJ,MAAM;yBACD,MAAM;4BACH,MAAM;2BACP,MAAM;;+BA4NF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;;;2BAkFlC,MAAM;0BACP,MAAM;0BACN,MAAM;;;;;;;;GAsG3B;AA+iED,wBAAsB,eAAe;;;eA2FpC"}
|
package/dist/actions.js
CHANGED
|
@@ -19,10 +19,10 @@ import { aauthVerify, getAAuthContextFromRequest, getAttributionDecisionFromRequ
|
|
|
19
19
|
import { attributionContext } from "./middleware/attribution_context.js";
|
|
20
20
|
import { aauthAdmission, getAAuthAdmissionFromRequest, } from "./middleware/aauth_admission.js";
|
|
21
21
|
import { buildSessionInfo } from "./services/session_info.js";
|
|
22
|
-
import { AttributionPolicyError } from "./services/attribution_policy.js";
|
|
22
|
+
import { AttributionPolicyError, enforceAttributionPolicy } from "./services/attribution_policy.js";
|
|
23
23
|
import { registerFeedbackAdminProxyRoutes } from "./services/feedback/admin_proxy.js";
|
|
24
24
|
import { AgentCapabilityError, contextFromAgentIdentity, enforceAgentCapability, } from "./services/agent_capabilities.js";
|
|
25
|
-
import { getCurrentAAuthAdmission, getCurrentAgentIdentity, runWithRequestContext, } from "./services/request_context.js";
|
|
25
|
+
import { getCurrentAAuthAdmission, getCurrentAgentIdentity, getCurrentAttribution, runWithRequestContext, } from "./services/request_context.js";
|
|
26
26
|
import { assertCanWriteProtectedBatch } from "./services/protected_entity_types.js";
|
|
27
27
|
import { createAgentIdentity as buildAgentIdentity, getAgentIdentityFromRequest, normaliseClientName, } from "./crypto/agent_identity.js";
|
|
28
28
|
import { initServerKeys } from "./services/encryption_service.js";
|
|
@@ -43,7 +43,7 @@ import { resolveSandboxReportTransport } from "./services/sandbox/transport.js";
|
|
|
43
43
|
import { getSqliteDb } from "./repositories/sqlite/sqlite_client.js";
|
|
44
44
|
import { getMcpAuthToken } from "./crypto/mcp_auth_token.js";
|
|
45
45
|
import { isOauthKeyCredentialValid, normalizeOauthNextPath, OAuthKeySessionStore, } from "./services/oauth_key_gate.js";
|
|
46
|
-
import { AnalyzeSchemaCandidatesRequestSchema, CorrectEntityRequestSchema, CreateRelationshipsRequestSchema, CreateRelationshipRequestSchema, DeleteEntityRequestSchema, DeleteRelationshipRequestSchema, EntitiesQueryRequestSchema, EntitySnapshotRequestSchema, FieldProvenanceRequestSchema, GetSchemaRecommendationsRequestSchema, ListObservationsRequestSchema, ListRelationshipsRequestSchema, MergeEntitiesRequestSchema, SplitEntityRequestSchema, ObservationsQueryRequestSchema, RegisterSchemaRequestSchema, RelationshipSnapshotRequestSchema, RestoreEntityRequestSchema, RestoreRelationshipRequestSchema, RetrieveEntityByIdentifierSchema, RetrieveGraphNeighborhoodSchema, RetrieveRelatedEntitiesSchema, StoreRequestSchema, StoreUnstructuredRequestSchema, UpdateSchemaIncrementalRequestSchema, } from "./shared/action_schemas.js";
|
|
46
|
+
import { AnalyzeSchemaCandidatesRequestSchema, CorrectEntityRequestSchema, CreateInterpretationRequestSchema, CreateRelationshipsRequestSchema, CreateRelationshipRequestSchema, DeleteEntityRequestSchema, DeleteRelationshipRequestSchema, EntitiesQueryRequestSchema, EntitySnapshotRequestSchema, FieldProvenanceRequestSchema, GetSchemaRecommendationsRequestSchema, ListObservationsRequestSchema, ListRelationshipsRequestSchema, MergeEntitiesRequestSchema, SplitEntityRequestSchema, ObservationsQueryRequestSchema, RegisterSchemaRequestSchema, RelationshipSnapshotRequestSchema, RestoreEntityRequestSchema, RestoreRelationshipRequestSchema, RetrieveEntityByIdentifierSchema, RetrieveGraphNeighborhoodSchema, RetrieveRelatedEntitiesSchema, StoreRequestSchema, StoreUnstructuredRequestSchema, UpdateSchemaIncrementalRequestSchema, } from "./shared/action_schemas.js";
|
|
47
47
|
import { getMimeTypeFromExtension } from "./services/file_text_extraction.js";
|
|
48
48
|
import { queryEntitiesWithCount } from "./shared/action_handlers/entity_handlers.js";
|
|
49
49
|
import { retrieveEntityByIdentifierWithFallback } from "./shared/action_handlers/entity_identifier_handler.js";
|
|
@@ -496,6 +496,24 @@ app.get("/server-info", (_req, res) => {
|
|
|
496
496
|
neotoma_env: readNeotomaConfigEnvironment(),
|
|
497
497
|
});
|
|
498
498
|
});
|
|
499
|
+
app.get("/mcp-interaction-instructions", (_req, res) => {
|
|
500
|
+
const instructionsPath = path.join(config.projectRoot, "docs", "developer", "mcp", "instructions.md");
|
|
501
|
+
try {
|
|
502
|
+
const raw = fs.readFileSync(instructionsPath, "utf-8");
|
|
503
|
+
const match = raw.match(/```\s*\n?([\s\S]*?)```/);
|
|
504
|
+
if (match && match[1]) {
|
|
505
|
+
const text = match[1].trim();
|
|
506
|
+
if (text) {
|
|
507
|
+
res.type("text/plain").send(text);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
catch {
|
|
513
|
+
// fall through to 404
|
|
514
|
+
}
|
|
515
|
+
res.status(404).json({ error: "instructions_not_found" });
|
|
516
|
+
});
|
|
499
517
|
// ============================================================================
|
|
500
518
|
// MCP StreamableHTTP Endpoint (OAuth-enabled MCP transport)
|
|
501
519
|
// ============================================================================
|
|
@@ -1689,20 +1707,37 @@ app.post("/mcp/oauth/token", oauthTokenLimit, express.urlencoded({ extended: tru
|
|
|
1689
1707
|
try {
|
|
1690
1708
|
const grant_type = req.body?.grant_type;
|
|
1691
1709
|
const code = req.body?.code;
|
|
1710
|
+
const refresh_token = req.body?.refresh_token;
|
|
1692
1711
|
logger.info("[MCP OAuth] Token request received", {
|
|
1693
1712
|
grant_type: grant_type ?? null,
|
|
1694
1713
|
has_code: typeof code === "string" && code.length > 0,
|
|
1695
1714
|
code_hint: typeof code === "string" ? code.slice(0, 8) : null,
|
|
1715
|
+
has_refresh_token: typeof refresh_token === "string" && refresh_token.length > 0,
|
|
1696
1716
|
host: req.header("host") ?? null,
|
|
1697
1717
|
});
|
|
1698
|
-
if (grant_type !== "authorization_code") {
|
|
1718
|
+
if (grant_type !== "authorization_code" && grant_type !== "refresh_token") {
|
|
1699
1719
|
logger.warn("[MCP OAuth] Token rejected: unsupported grant_type", {
|
|
1700
1720
|
grant_type: grant_type ?? null,
|
|
1701
1721
|
});
|
|
1702
1722
|
return res.status(400).json({
|
|
1703
1723
|
error: "unsupported_grant_type",
|
|
1704
|
-
error_description: "Only authorization_code
|
|
1724
|
+
error_description: "Only authorization_code and refresh_token are supported",
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
if (grant_type === "refresh_token") {
|
|
1728
|
+
if (!refresh_token || typeof refresh_token !== "string") {
|
|
1729
|
+
logger.warn("[MCP OAuth] Token refresh rejected: missing refresh_token");
|
|
1730
|
+
return res
|
|
1731
|
+
.status(400)
|
|
1732
|
+
.json({ error: "invalid_request", error_description: "refresh_token is required" });
|
|
1733
|
+
}
|
|
1734
|
+
const { refreshAccessToken } = await import("./services/mcp_oauth.js");
|
|
1735
|
+
const token = await refreshAccessToken(refresh_token);
|
|
1736
|
+
logger.info("[MCP OAuth] Token refreshed", {
|
|
1737
|
+
has_refresh_token: Boolean(token.refresh_token),
|
|
1705
1738
|
});
|
|
1739
|
+
res.setHeader("Content-Type", "application/json");
|
|
1740
|
+
return res.json(token);
|
|
1706
1741
|
}
|
|
1707
1742
|
if (!code || typeof code !== "string") {
|
|
1708
1743
|
logger.warn("[MCP OAuth] Token rejected: missing code");
|
|
@@ -3774,6 +3809,83 @@ app.get("/interpretations", async (req, res) => {
|
|
|
3774
3809
|
return sendError(res, 500, "DB_QUERY_FAILED", message);
|
|
3775
3810
|
}
|
|
3776
3811
|
});
|
|
3812
|
+
app.post("/interpretations/create", async (req, res) => {
|
|
3813
|
+
const parsed = CreateInterpretationRequestSchema.safeParse(req.body);
|
|
3814
|
+
if (!parsed.success) {
|
|
3815
|
+
logWarn("ValidationError:interpretations_create", req, { issues: parsed.error.issues });
|
|
3816
|
+
return sendValidationError(res, parsed.error.issues);
|
|
3817
|
+
}
|
|
3818
|
+
try {
|
|
3819
|
+
const userId = await getAuthenticatedUserId(req, parsed.data.user_id);
|
|
3820
|
+
const { data: source, error: sourceError } = await db
|
|
3821
|
+
.from("sources")
|
|
3822
|
+
.select("id")
|
|
3823
|
+
.eq("id", parsed.data.source_id)
|
|
3824
|
+
.eq("user_id", userId)
|
|
3825
|
+
.maybeSingle();
|
|
3826
|
+
if (sourceError)
|
|
3827
|
+
throw sourceError;
|
|
3828
|
+
if (!source) {
|
|
3829
|
+
return sendError(res, 404, "SOURCE_NOT_FOUND", "Source not found for authenticated user");
|
|
3830
|
+
}
|
|
3831
|
+
const { runInterpretation } = await import("./services/interpretation.js");
|
|
3832
|
+
const interpretationResult = await runInterpretation({
|
|
3833
|
+
userId,
|
|
3834
|
+
sourceId: parsed.data.source_id,
|
|
3835
|
+
extractedData: parsed.data.entities,
|
|
3836
|
+
config: normalizeInterpretationConfig(parsed.data.interpretation_config),
|
|
3837
|
+
});
|
|
3838
|
+
const relationshipsCreated = [];
|
|
3839
|
+
if (parsed.data.relationships?.length) {
|
|
3840
|
+
const { relationshipsService } = await import("./services/relationships.js");
|
|
3841
|
+
for (const rel of parsed.data.relationships) {
|
|
3842
|
+
const sourceEntityId = typeof rel.source_entity_id === "string"
|
|
3843
|
+
? rel.source_entity_id
|
|
3844
|
+
: typeof rel.source_index === "number"
|
|
3845
|
+
? interpretationResult.entities[rel.source_index]?.entityId
|
|
3846
|
+
: undefined;
|
|
3847
|
+
const targetEntityId = typeof rel.target_entity_id === "string"
|
|
3848
|
+
? rel.target_entity_id
|
|
3849
|
+
: typeof rel.target_index === "number"
|
|
3850
|
+
? interpretationResult.entities[rel.target_index]?.entityId
|
|
3851
|
+
: undefined;
|
|
3852
|
+
if (!sourceEntityId || !targetEntityId)
|
|
3853
|
+
continue;
|
|
3854
|
+
await relationshipsService.createRelationship({
|
|
3855
|
+
source_entity_id: sourceEntityId,
|
|
3856
|
+
target_entity_id: targetEntityId,
|
|
3857
|
+
relationship_type: rel.relationship_type,
|
|
3858
|
+
source_id: parsed.data.source_id,
|
|
3859
|
+
metadata: rel.metadata ?? {},
|
|
3860
|
+
user_id: userId,
|
|
3861
|
+
});
|
|
3862
|
+
relationshipsCreated.push({
|
|
3863
|
+
relationship_type: rel.relationship_type,
|
|
3864
|
+
source_entity_id: sourceEntityId,
|
|
3865
|
+
target_entity_id: targetEntityId,
|
|
3866
|
+
});
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
return res.json({
|
|
3870
|
+
success: true,
|
|
3871
|
+
interpretation_id: interpretationResult.interpretationId,
|
|
3872
|
+
source_id: parsed.data.source_id,
|
|
3873
|
+
entities: interpretationResult.entities.map((entity) => ({
|
|
3874
|
+
entity_id: entity.entityId,
|
|
3875
|
+
entity_type: entity.entityType,
|
|
3876
|
+
observation_id: entity.observationId,
|
|
3877
|
+
})),
|
|
3878
|
+
observations_created: interpretationResult.observationsCreated,
|
|
3879
|
+
unknown_fields_count: interpretationResult.unknownFieldsCount,
|
|
3880
|
+
relationships_created: relationshipsCreated,
|
|
3881
|
+
});
|
|
3882
|
+
}
|
|
3883
|
+
catch (error) {
|
|
3884
|
+
logError("APIError:interpretations_create", req, error);
|
|
3885
|
+
const message = error instanceof Error ? error.message : "Failed to create interpretation";
|
|
3886
|
+
return sendError(res, 500, "INTERPRETATION_CREATE_FAILED", message);
|
|
3887
|
+
}
|
|
3888
|
+
});
|
|
3777
3889
|
// GET /api/observations - Get observations with filters (FU-302, FU-601)
|
|
3778
3890
|
// REQUIRES AUTHENTICATION - all queries filtered by authenticated user_id
|
|
3779
3891
|
app.get("/observations", async (req, res) => {
|
|
@@ -3889,8 +4001,47 @@ app.post("/observations/create", async (req, res) => {
|
|
|
3889
4001
|
return res.status(500).json(buildErrorEnvelope("DB_QUERY_FAILED", message));
|
|
3890
4002
|
}
|
|
3891
4003
|
});
|
|
4004
|
+
function normalizeInterpretationConfig(configInput) {
|
|
4005
|
+
return {
|
|
4006
|
+
extractor_type: "agent",
|
|
4007
|
+
extractor_version: "unknown",
|
|
4008
|
+
schema_version: "1.0",
|
|
4009
|
+
...configInput,
|
|
4010
|
+
};
|
|
4011
|
+
}
|
|
4012
|
+
async function createInterpretationRunForSource(params) {
|
|
4013
|
+
enforceAttributionPolicy("interpretations", getCurrentAgentIdentity());
|
|
4014
|
+
const attribution = getCurrentAttribution();
|
|
4015
|
+
const { data, error } = await db
|
|
4016
|
+
.from("interpretations")
|
|
4017
|
+
.insert({
|
|
4018
|
+
user_id: params.userId,
|
|
4019
|
+
source_id: params.sourceId,
|
|
4020
|
+
interpretation_config: normalizeInterpretationConfig(params.interpretationConfig),
|
|
4021
|
+
status: "running",
|
|
4022
|
+
started_at: new Date().toISOString(),
|
|
4023
|
+
...(Object.keys(attribution).length > 0 ? { provenance: attribution } : {}),
|
|
4024
|
+
})
|
|
4025
|
+
.select("id")
|
|
4026
|
+
.single();
|
|
4027
|
+
if (error) {
|
|
4028
|
+
throw new Error(`Failed to create interpretation run: ${error.message}`);
|
|
4029
|
+
}
|
|
4030
|
+
return data.id;
|
|
4031
|
+
}
|
|
4032
|
+
async function completeInterpretationRun(params) {
|
|
4033
|
+
await db
|
|
4034
|
+
.from("interpretations")
|
|
4035
|
+
.update({
|
|
4036
|
+
status: "completed",
|
|
4037
|
+
completed_at: new Date().toISOString(),
|
|
4038
|
+
observations_created: params.observationsCreated,
|
|
4039
|
+
unknown_fields_count: params.unknownFieldsCount ?? 0,
|
|
4040
|
+
})
|
|
4041
|
+
.eq("id", params.interpretationId);
|
|
4042
|
+
}
|
|
3892
4043
|
export async function storeStructuredForApi(params) {
|
|
3893
|
-
const { userId, entities, sourcePriority, observationSource, idempotencyKey, originalFilename, relationships, commit: commitInput, strict: strictInput, } = params;
|
|
4044
|
+
const { userId, entities, sourcePriority, observationSource, idempotencyKey, originalFilename, relationships, interpretation, interpretationSourceId, commit: commitInput, strict: strictInput, } = params;
|
|
3894
4045
|
const commit = commitInput !== false;
|
|
3895
4046
|
const strict = strictInput === true;
|
|
3896
4047
|
// Capability scoping: when the caller is an AAuth-verified agent covered
|
|
@@ -3989,6 +4140,17 @@ export async function storeStructuredForApi(params) {
|
|
|
3989
4140
|
source_priority: sourcePriority,
|
|
3990
4141
|
},
|
|
3991
4142
|
});
|
|
4143
|
+
const resolvedInterpretationSourceId = interpretation?.source_id ??
|
|
4144
|
+
interpretationSourceId ??
|
|
4145
|
+
(interpretation?.source_ref === "structured" ? storageResult.sourceId : undefined);
|
|
4146
|
+
const interpretationId = commit && interpretation && resolvedInterpretationSourceId
|
|
4147
|
+
? await createInterpretationRunForSource({
|
|
4148
|
+
userId,
|
|
4149
|
+
sourceId: resolvedInterpretationSourceId,
|
|
4150
|
+
interpretationConfig: interpretation.interpretation_config,
|
|
4151
|
+
})
|
|
4152
|
+
: null;
|
|
4153
|
+
const observationSourceId = resolvedInterpretationSourceId ?? storageResult.sourceId;
|
|
3992
4154
|
// v0.5.1: structured guidance for the v0.5.0 breaking change where callers
|
|
3993
4155
|
// nested fields under `attributes`. If resolution failed and the only
|
|
3994
4156
|
// observed top-level keys are `attributes` (plus optionally `entity_type`),
|
|
@@ -4163,8 +4325,8 @@ export async function storeStructuredForApi(params) {
|
|
|
4163
4325
|
entity_id: r.entity_id,
|
|
4164
4326
|
entity_type: r.entity_type,
|
|
4165
4327
|
schema_version: "1.0",
|
|
4166
|
-
source_id:
|
|
4167
|
-
interpretation_id:
|
|
4328
|
+
source_id: observationSourceId,
|
|
4329
|
+
interpretation_id: interpretationId,
|
|
4168
4330
|
observed_at: new Date().toISOString(),
|
|
4169
4331
|
specificity_score: 1.0,
|
|
4170
4332
|
source_priority: sourcePriority,
|
|
@@ -4199,7 +4361,7 @@ export async function storeStructuredForApi(params) {
|
|
|
4199
4361
|
fields: r.fields,
|
|
4200
4362
|
schema: schemaEntry.schema_definition,
|
|
4201
4363
|
userId,
|
|
4202
|
-
sourceId:
|
|
4364
|
+
sourceId: observationSourceId,
|
|
4203
4365
|
});
|
|
4204
4366
|
}
|
|
4205
4367
|
}
|
|
@@ -4251,7 +4413,7 @@ export async function storeStructuredForApi(params) {
|
|
|
4251
4413
|
source_entity_id: sourceEntityId,
|
|
4252
4414
|
target_entity_id: targetEntityId,
|
|
4253
4415
|
relationship_type: rel.relationship_type,
|
|
4254
|
-
source_id:
|
|
4416
|
+
source_id: observationSourceId,
|
|
4255
4417
|
metadata: rel.metadata ?? {},
|
|
4256
4418
|
user_id: userId,
|
|
4257
4419
|
});
|
|
@@ -4286,11 +4448,21 @@ export async function storeStructuredForApi(params) {
|
|
|
4286
4448
|
});
|
|
4287
4449
|
}
|
|
4288
4450
|
}
|
|
4451
|
+
if (commit && interpretationId) {
|
|
4452
|
+
await completeInterpretationRun({
|
|
4453
|
+
interpretationId,
|
|
4454
|
+
observationsCreated: createdEntities.filter((e) => e.observation_id).length,
|
|
4455
|
+
unknownFieldsCount: 0,
|
|
4456
|
+
});
|
|
4457
|
+
}
|
|
4289
4458
|
return {
|
|
4290
4459
|
success: true,
|
|
4291
4460
|
replayed: false,
|
|
4292
4461
|
commit,
|
|
4293
4462
|
source_id: commit ? storageResult.sourceId : null,
|
|
4463
|
+
...(interpretationId
|
|
4464
|
+
? { interpretation_id: interpretationId, interpretation_source_id: observationSourceId }
|
|
4465
|
+
: {}),
|
|
4294
4466
|
entities_created: commit
|
|
4295
4467
|
? createdEntities.filter((e) => e.action === "created" || e.action === "extended").length
|
|
4296
4468
|
: 0,
|
|
@@ -4347,23 +4519,7 @@ async function handleStorePost(req, res) {
|
|
|
4347
4519
|
const hasUnstructured = hasFileContent || hasFilePath;
|
|
4348
4520
|
let structuredResult;
|
|
4349
4521
|
let unstructuredResult;
|
|
4350
|
-
|
|
4351
|
-
if (!parsed.data.idempotency_key) {
|
|
4352
|
-
return sendError(res, 400, "VALIDATION_ERROR", "idempotency_key is required when entities are provided");
|
|
4353
|
-
}
|
|
4354
|
-
structuredResult = await storeStructuredForApi({
|
|
4355
|
-
userId,
|
|
4356
|
-
entities: parsed.data.entities,
|
|
4357
|
-
sourcePriority: parsed.data.source_priority ?? 100,
|
|
4358
|
-
observationSource: parsed.data.observation_source,
|
|
4359
|
-
idempotencyKey: parsed.data.idempotency_key,
|
|
4360
|
-
originalFilename: parsed.data.original_filename,
|
|
4361
|
-
relationships: parsed.data.relationships,
|
|
4362
|
-
commit: parsed.data.commit,
|
|
4363
|
-
strict: parsed.data.strict,
|
|
4364
|
-
});
|
|
4365
|
-
}
|
|
4366
|
-
if (hasUnstructured) {
|
|
4522
|
+
const storeUnstructuredFromRequest = async () => {
|
|
4367
4523
|
const fileContent = parsed.data.file_content;
|
|
4368
4524
|
let mimeType = parsed.data.mime_type;
|
|
4369
4525
|
let originalFilename = parsed.data.original_filename;
|
|
@@ -4380,9 +4536,10 @@ async function handleStorePost(req, res) {
|
|
|
4380
4536
|
originalFilename = originalFilename || path.basename(resolvedPath);
|
|
4381
4537
|
}
|
|
4382
4538
|
if ((!fileContent && !resolvedFileBuffer) || !mimeType) {
|
|
4383
|
-
|
|
4539
|
+
sendError(res, 400, "VALIDATION_ERROR", "Unstructured store requires file_content+mime_type or file_path");
|
|
4540
|
+
return null;
|
|
4384
4541
|
}
|
|
4385
|
-
|
|
4542
|
+
return await storeUnstructuredForApi({
|
|
4386
4543
|
userId,
|
|
4387
4544
|
fileContent,
|
|
4388
4545
|
fileBuffer: resolvedFileBuffer,
|
|
@@ -4391,6 +4548,42 @@ async function handleStorePost(req, res) {
|
|
|
4391
4548
|
(!hasEntities ? parsed.data.idempotency_key : undefined),
|
|
4392
4549
|
originalFilename,
|
|
4393
4550
|
});
|
|
4551
|
+
};
|
|
4552
|
+
if (hasUnstructured && parsed.data.interpretation?.source_ref === "unstructured") {
|
|
4553
|
+
const result = await storeUnstructuredFromRequest();
|
|
4554
|
+
if (!result) {
|
|
4555
|
+
return;
|
|
4556
|
+
}
|
|
4557
|
+
unstructuredResult = result;
|
|
4558
|
+
}
|
|
4559
|
+
if (hasEntities) {
|
|
4560
|
+
if (!parsed.data.idempotency_key) {
|
|
4561
|
+
return sendError(res, 400, "VALIDATION_ERROR", "idempotency_key is required when entities are provided");
|
|
4562
|
+
}
|
|
4563
|
+
structuredResult = await storeStructuredForApi({
|
|
4564
|
+
userId,
|
|
4565
|
+
entities: parsed.data.entities,
|
|
4566
|
+
sourcePriority: parsed.data.source_priority ?? 100,
|
|
4567
|
+
observationSource: parsed.data.observation_source,
|
|
4568
|
+
idempotencyKey: parsed.data.idempotency_key,
|
|
4569
|
+
originalFilename: parsed.data.original_filename,
|
|
4570
|
+
relationships: parsed.data.relationships,
|
|
4571
|
+
interpretation: parsed.data.interpretation,
|
|
4572
|
+
interpretationSourceId: parsed.data.interpretation?.source_ref === "unstructured" &&
|
|
4573
|
+
unstructuredResult &&
|
|
4574
|
+
typeof unstructuredResult.source_id === "string"
|
|
4575
|
+
? unstructuredResult.source_id
|
|
4576
|
+
: undefined,
|
|
4577
|
+
commit: parsed.data.commit,
|
|
4578
|
+
strict: parsed.data.strict,
|
|
4579
|
+
});
|
|
4580
|
+
}
|
|
4581
|
+
if (hasUnstructured && !unstructuredResult) {
|
|
4582
|
+
const result = await storeUnstructuredFromRequest();
|
|
4583
|
+
if (!result) {
|
|
4584
|
+
return;
|
|
4585
|
+
}
|
|
4586
|
+
unstructuredResult = result;
|
|
4394
4587
|
}
|
|
4395
4588
|
if (structuredResult && unstructuredResult) {
|
|
4396
4589
|
return res.json({
|