neotoma 0.18.8 → 0.19.0

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.
Files changed (192) hide show
  1. package/dist/actions.d.ts +1 -0
  2. package/dist/actions.d.ts.map +1 -1
  3. package/dist/actions.js +201 -6
  4. package/dist/actions.js.map +1 -1
  5. package/dist/docs/developer/cli_reference.md +1 -1
  6. package/dist/docs/developer/mcp/proxy.md +6 -4
  7. package/dist/docs/foundation/entity_resolution.md +34 -7
  8. package/dist/docs/foundation/what_to_store.md +1 -1
  9. package/dist/docs/infrastructure/client_instance_deployment.md +151 -0
  10. package/dist/docs/reference/error_codes.md +75 -23
  11. package/dist/docs/testing/automated_test_catalog.md +16 -8
  12. package/dist/inspector/assets/access_policies-6-6DPI39.js +1 -0
  13. package/dist/inspector/assets/{agent_badge-D5c8UAE9.js → agent_badge-CXSO0sXJ.js} +1 -1
  14. package/dist/inspector/assets/{agent_detail-lZm2dWDR.js → agent_detail-Cr7iqdye.js} +1 -1
  15. package/dist/inspector/assets/{agent_filter-_s9ctnZg.js → agent_filter-DQvQyxMp.js} +1 -1
  16. package/dist/inspector/assets/{agent_grant_detail-jPlKCXYO.js → agent_grant_detail-DRRM7KWw.js} +1 -1
  17. package/dist/inspector/assets/{agent_grant_form-BUQdfq4A.js → agent_grant_form-DNfWiVBQ.js} +1 -1
  18. package/dist/inspector/assets/{agent_grants-D7dYlLNX.js → agent_grants-BWgOPiOe.js} +1 -1
  19. package/dist/inspector/assets/{agents-Bl50GiDS.js → agents-CPLAQelS.js} +1 -1
  20. package/dist/inspector/assets/{api_not_configured_state-GASK-mhp.js → api_not_configured_state-DHesochj.js} +1 -1
  21. package/dist/inspector/assets/{attribution_card-Dks_zpQx.js → attribution_card-CCINKjRY.js} +1 -1
  22. package/dist/inspector/assets/{attribution_summary-Cli2qL-J.js → attribution_summary-Dkwz1aNS.js} +1 -1
  23. package/dist/inspector/assets/{bundles-4y41Dsvz.js → bundles-f-bRlQYm.js} +1 -1
  24. package/dist/inspector/assets/{card-DyP1iupa.js → card-18-eZw0x.js} +1 -1
  25. package/dist/inspector/assets/{checkbox-CIUQh9NP.js → checkbox-DUu6AMCH.js} +1 -1
  26. package/dist/inspector/assets/{compliance-CZT3IVp5.js → compliance-C1iqMRYK.js} +1 -1
  27. package/dist/inspector/assets/{confirm-dialog-DwPqhney.js → confirm-dialog-KIPuwTQR.js} +1 -1
  28. package/dist/inspector/assets/{conversation_common-DqXXK2sj.js → conversation_common-aPvd5zmX.js} +1 -1
  29. package/dist/inspector/assets/{conversation_detail-BUqgnrpM.js → conversation_detail-nJDDMCfc.js} +1 -1
  30. package/dist/inspector/assets/{copy_id_button-CFvm8hKT.js → copy_id_button-BwaRGKNA.js} +1 -1
  31. package/dist/inspector/assets/{copyable_code_block-Ds_m6kph.js → copyable_code_block-DDi03yAz.js} +1 -1
  32. package/dist/inspector/assets/{corrections-CYBwDzLB.js → corrections-Dz2v9ju9.js} +1 -1
  33. package/dist/inspector/assets/{dashboard-Bn74vHnA.js → dashboard-CZOdnoJJ.js} +1 -1
  34. package/dist/inspector/assets/{data-table-BdvHAu9o.js → data-table-5i_gqEfj.js} +1 -1
  35. package/dist/inspector/assets/{design-CvkXmrg8.js → design-BdKBUVmX.js} +1 -1
  36. package/dist/inspector/assets/{dialog-ClLKFBmR.js → dialog-CNJoZoQS.js} +1 -1
  37. package/dist/inspector/assets/{docs-DsWM97KO.js → docs-sXzc0LTz.js} +1 -1
  38. package/dist/inspector/assets/{docs_page_layout-B2uZRfVC.js → docs_page_layout-CGg8ubQX.js} +1 -1
  39. package/dist/inspector/assets/{embed_graph-dvewK9nr.js → embed_graph-BpLLD_vX.js} +1 -1
  40. package/dist/inspector/assets/{empty_state-Bc-ujW2X.js → empty_state-DNk61WLW.js} +1 -1
  41. package/dist/inspector/assets/{entities-BOrwMORJ.js → entities-Cexhp3qr.js} +1 -1
  42. package/dist/inspector/assets/{entity_correct-r178klDI.js → entity_correct-CfSggXgT.js} +1 -1
  43. package/dist/inspector/assets/{entity_history-CKLn3lKb.js → entity_history-CGila0eu.js} +1 -1
  44. package/dist/inspector/assets/{entity_history_sections-FVZdr00Y.js → entity_history_sections-CXYX1gph.js} +1 -1
  45. package/dist/inspector/assets/{entity_link-yKDYqbhx.js → entity_link-DUdsaj-A.js} +1 -1
  46. package/dist/inspector/assets/{entity_relationships_by_type-SQLvwlXL.js → entity_relationships_by_type-B_PH1APK.js} +1 -1
  47. package/dist/inspector/assets/{entity_segment_page-BXDQbUUp.js → entity_segment_page-DRYjT1AU.js} +1 -1
  48. package/dist/inspector/assets/{entity_table_column_toggle-D7CwUiAH.js → entity_table_column_toggle-DwwHY7Y8.js} +1 -1
  49. package/dist/inspector/assets/{entity_timeline-Dg0wEuRA.js → entity_timeline-B4XYY56n.js} +1 -1
  50. package/dist/inspector/assets/{entity_types-DLlkd4ex.js → entity_types-ByB2PWZM.js} +1 -1
  51. package/dist/inspector/assets/{faq-KbswV10B.js → faq-B-PEHwkf.js} +1 -1
  52. package/dist/inspector/assets/{graph_auto_fit-SqyYePaZ.js → graph_auto_fit-ClZnIOoR.js} +1 -1
  53. package/dist/inspector/assets/{graph_explorer-DZgjc-hi.js → graph_explorer-C-Xo8VWP.js} +1 -1
  54. package/dist/inspector/assets/{home-CUYUSmke.js → home-D1UVMr6_.js} +1 -1
  55. package/dist/inspector/assets/{index-BGqcUFFW.js → index-C6fxoS_Z.js} +1582 -1582
  56. package/dist/inspector/assets/{index-CBJY_OOI.js → index-Cp0BQ57U.js} +1 -1
  57. package/dist/inspector/assets/{index-DOxiDdC1.js → index-MnCv_KTr.js} +1 -1
  58. package/dist/inspector/assets/interpretations-BCs9qtw0.js +1 -0
  59. package/dist/inspector/assets/{interpretations-CSeyL3hr.js → interpretations-DG5kUN-V.js} +1 -1
  60. package/dist/inspector/assets/{issue_author_attribution-Czyui6JR.js → issue_author_attribution-D7_vwrHJ.js} +1 -1
  61. package/dist/inspector/assets/{issue_detail-DgdqvrIo.js → issue_detail-DKYUwKt_.js} +1 -1
  62. package/dist/inspector/assets/{issues-dw8Q41vn.js → issues-DcSMjt-p.js} +1 -1
  63. package/dist/inspector/assets/{json_viewer-CEMz7ftn.js → json_viewer-BKa7wwrs.js} +1 -1
  64. package/dist/inspector/assets/{label-qbFxe1yC.js → label-BO-aVMjO.js} +1 -1
  65. package/dist/inspector/assets/{live_relative_time-CVpdUZRq.js → live_relative_time-Dx00M9I6.js} +1 -1
  66. package/dist/inspector/assets/mobile_filter_popover-I9fJvTGr.js +1 -0
  67. package/dist/inspector/assets/{not_found-DFUrM9eP.js → not_found-DuCJouBh.js} +1 -1
  68. package/dist/inspector/assets/oauth_callback-w0kyGAb4.js +1 -0
  69. package/dist/inspector/assets/oauth_signin-3LQrxsKz.js +1 -0
  70. package/dist/inspector/assets/{observations-BO6KImWs.js → observations-Bq6mvl5m.js} +1 -1
  71. package/dist/inspector/assets/{page_shell-COxcPFXG.js → page_shell-BoTb5of6.js} +1 -1
  72. package/dist/inspector/assets/{pagination-NLnVe1CU.js → pagination-BJtT1Wgy.js} +1 -1
  73. package/dist/inspector/assets/{peer_detail-DdtQsiwD.js → peer_detail-BM1jhSS2.js} +1 -1
  74. package/dist/inspector/assets/{peers-D0bzQeyz.js → peers-CYSx-jAx.js} +1 -1
  75. package/dist/inspector/assets/{pin_primitive_button-BtuY3Ig7.js → pin_primitive_button-BdAR3e5K.js} +1 -1
  76. package/dist/inspector/assets/{recent_activity-8t3t2hCi.js → recent_activity-Dio9UBCA.js} +1 -1
  77. package/dist/inspector/assets/{recent_conversations-xURWFQ1G.js → recent_conversations-Be7S63h9.js} +1 -1
  78. package/dist/inspector/assets/{recent_conversations-B351X_is.js → recent_conversations-iJTSJf5e.js} +1 -1
  79. package/dist/inspector/assets/{recent_records_feed-C0auJYwP.js → recent_records_feed-ZxHjVjQ6.js} +1 -1
  80. package/dist/inspector/assets/record_activity-BmMC7Q7l.js +1 -0
  81. package/dist/inspector/assets/{relationship_detail-B9y50i1N.js → relationship_detail-i9rsGxA6.js} +1 -1
  82. package/dist/inspector/assets/{relationship_panel_groups-DgC2aJin.js → relationship_panel_groups-CXFlrRHU.js} +1 -1
  83. package/dist/inspector/assets/{relationships-u7QTLzYr.js → relationships-DqAzfnlB.js} +1 -1
  84. package/dist/inspector/assets/{relationships-DvN6JCJC.js → relationships-DvWtOU7W.js} +1 -1
  85. package/dist/inspector/assets/{sandbox-DzEfLQAo.js → sandbox-Cn4vq2Pz.js} +1 -1
  86. package/dist/inspector/assets/{schema_detail-BI3lLXFK.js → schema_detail-BdORXewP.js} +1 -1
  87. package/dist/inspector/assets/{schemas-DpAQVcki.js → schemas-HYsBtoHT.js} +1 -1
  88. package/dist/inspector/assets/{search-CjE75jdm.js → search-LzJnqvTB.js} +1 -1
  89. package/dist/inspector/assets/{segmented_control-D9QbgEtB.js → segmented_control-CHJO5zMt.js} +1 -1
  90. package/dist/inspector/assets/{select-KOFjYHgz.js → select-DFzP9laV.js} +1 -1
  91. package/dist/inspector/assets/settings-Bm0jNa4p.js +1 -0
  92. package/dist/inspector/assets/{source_content-Bi_jkeg2.js → source_content-PwVSPaIU.js} +1 -1
  93. package/dist/inspector/assets/{source_content_open_button-B9mhDPoj.js → source_content_open_button-C7NKRG7q.js} +1 -1
  94. package/dist/inspector/assets/{source_detail-B3jsntel.js → source_detail-C2D885Zy.js} +1 -1
  95. package/dist/inspector/assets/{source_link-DrAAN7IB.js → source_link-6lMlhQnK.js} +1 -1
  96. package/dist/inspector/assets/{sources-B7H4Tgy7.js → sources-BvGo5OZp.js} +1 -1
  97. package/dist/inspector/assets/{subscriptions-B79INOqC.js → subscriptions-BGYmtTLV.js} +1 -1
  98. package/dist/inspector/assets/{switch-DEw5ZD9Y.js → switch-DkusEYsE.js} +1 -1
  99. package/dist/inspector/assets/{tabs-CZDCOqHp.js → tabs-hkbPHxT0.js} +1 -1
  100. package/dist/inspector/assets/{textarea-DM2MsnIS.js → textarea-BhC7xtLk.js} +1 -1
  101. package/dist/inspector/assets/{timeline-DtGp-dM7.js → timeline-CHi-lYcV.js} +1 -1
  102. package/dist/inspector/assets/{timeline-CwKOl2cK.js → timeline-DmjoODDd.js} +1 -1
  103. package/dist/inspector/assets/{timeline_event_detail-D-gU-DZY.js → timeline_event_detail-BfKrsQih.js} +1 -1
  104. package/dist/inspector/assets/{turn_detail-D20KYWIY.js → turn_detail-DGj1lT0k.js} +1 -1
  105. package/dist/inspector/assets/{turns-CCukFhV6.js → turns-CHfOIg9X.js} +1 -1
  106. package/dist/inspector/assets/{use_agents-Duqes0yE.js → use_agents-6sUtEOpZ.js} +1 -1
  107. package/dist/inspector/assets/{use_entities-CAYbeIfx.js → use_entities-DeIlqnzx.js} +1 -1
  108. package/dist/inspector/assets/use_interpretations-DvfzvGu3.js +1 -0
  109. package/dist/inspector/assets/{use_mutations-CaZO8lpD.js → use_mutations-CQ3_5Ppg.js} +1 -1
  110. package/dist/inspector/assets/{use_peers-CGLvEf5V.js → use_peers-CW3OAs41.js} +1 -1
  111. package/dist/inspector/assets/{use_relationships-B8fSaOmY.js → use_relationships-Dbtmx8X8.js} +1 -1
  112. package/dist/inspector/assets/{use_stats-DHAaJFKE.js → use_stats-OyHBzetJ.js} +1 -1
  113. package/dist/inspector/assets/{use_timeline-BMIPVqoi.js → use_timeline-FINsibXm.js} +1 -1
  114. package/dist/inspector/assets/{use_turns-DRAnBf4e.js → use_turns-D4AhGAvM.js} +1 -1
  115. package/dist/inspector/assets/{world_time_event_timeline-CWFM4Q3K.js → world_time_event_timeline-F0elR7Vc.js} +1 -1
  116. package/dist/inspector/index.html +1 -1
  117. package/dist/proxy/mcp_stdio_proxy.d.ts +11 -5
  118. package/dist/proxy/mcp_stdio_proxy.d.ts.map +1 -1
  119. package/dist/proxy/mcp_stdio_proxy.js +12 -6
  120. package/dist/proxy/mcp_stdio_proxy.js.map +1 -1
  121. package/dist/repositories/sqlite/local_db_adapter.d.ts.map +1 -1
  122. package/dist/repositories/sqlite/local_db_adapter.js +12 -0
  123. package/dist/repositories/sqlite/local_db_adapter.js.map +1 -1
  124. package/dist/seed_schemas_entry.d.ts +28 -0
  125. package/dist/seed_schemas_entry.d.ts.map +1 -0
  126. package/dist/seed_schemas_entry.js +142 -0
  127. package/dist/seed_schemas_entry.js.map +1 -0
  128. package/dist/server.d.ts +9 -0
  129. package/dist/server.d.ts.map +1 -1
  130. package/dist/server.js +92 -2
  131. package/dist/server.js.map +1 -1
  132. package/dist/services/company_query.d.ts +54 -0
  133. package/dist/services/company_query.d.ts.map +1 -0
  134. package/dist/services/company_query.js +175 -0
  135. package/dist/services/company_query.js.map +1 -0
  136. package/dist/services/company_resolution.d.ts +86 -0
  137. package/dist/services/company_resolution.d.ts.map +1 -0
  138. package/dist/services/company_resolution.js +195 -0
  139. package/dist/services/company_resolution.js.map +1 -0
  140. package/dist/services/entity_resolution.d.ts.map +1 -1
  141. package/dist/services/entity_resolution.js +12 -2
  142. package/dist/services/entity_resolution.js.map +1 -1
  143. package/dist/services/google_oidc.d.ts +110 -0
  144. package/dist/services/google_oidc.d.ts.map +1 -0
  145. package/dist/services/google_oidc.js +249 -0
  146. package/dist/services/google_oidc.js.map +1 -0
  147. package/dist/services/inspector_mount.d.ts.map +1 -1
  148. package/dist/services/inspector_mount.js +9 -0
  149. package/dist/services/inspector_mount.js.map +1 -1
  150. package/dist/services/issues/observer_import.d.ts +1 -1
  151. package/dist/services/issues/observer_import.d.ts.map +1 -1
  152. package/dist/services/issues/observer_import.js +3 -3
  153. package/dist/services/issues/observer_import.js.map +1 -1
  154. package/dist/services/mcp_oauth.d.ts +1 -1
  155. package/dist/services/mcp_oauth.d.ts.map +1 -1
  156. package/dist/services/mcp_oauth.js +11 -1
  157. package/dist/services/mcp_oauth.js.map +1 -1
  158. package/dist/services/schema_definitions.d.ts.map +1 -1
  159. package/dist/services/schema_definitions.js +27 -1
  160. package/dist/services/schema_definitions.js.map +1 -1
  161. package/dist/services/schema_reference_linking.d.ts +61 -2
  162. package/dist/services/schema_reference_linking.d.ts.map +1 -1
  163. package/dist/services/schema_reference_linking.js +264 -21
  164. package/dist/services/schema_reference_linking.js.map +1 -1
  165. package/dist/services/schema_registry.d.ts +13 -0
  166. package/dist/services/schema_registry.d.ts.map +1 -1
  167. package/dist/services/schema_registry.js +3 -0
  168. package/dist/services/schema_registry.js.map +1 -1
  169. package/dist/shared/action_handlers/entity_identifier_handler.d.ts.map +1 -1
  170. package/dist/shared/action_handlers/entity_identifier_handler.js +131 -4
  171. package/dist/shared/action_handlers/entity_identifier_handler.js.map +1 -1
  172. package/dist/shared/action_schemas.d.ts +43 -18
  173. package/dist/shared/action_schemas.d.ts.map +1 -1
  174. package/dist/shared/action_schemas.js +17 -0
  175. package/dist/shared/action_schemas.js.map +1 -1
  176. package/dist/shared/contract_mappings.d.ts.map +1 -1
  177. package/dist/shared/contract_mappings.js +9 -0
  178. package/dist/shared/contract_mappings.js.map +1 -1
  179. package/dist/shared/openapi_types.d.ts +130 -0
  180. package/dist/shared/openapi_types.d.ts.map +1 -1
  181. package/dist/tool_definitions.d.ts +1 -1
  182. package/dist/tool_definitions.d.ts.map +1 -1
  183. package/dist/tool_definitions.js +6 -0
  184. package/dist/tool_definitions.js.map +1 -1
  185. package/openapi.yaml +122 -0
  186. package/package.json +1 -1
  187. package/dist/inspector/assets/access_policies-C0QzIgOo.js +0 -1
  188. package/dist/inspector/assets/interpretations-B1shK6Is.js +0 -1
  189. package/dist/inspector/assets/mobile_filter_popover-CI_BhFTn.js +0 -1
  190. package/dist/inspector/assets/record_activity-DUwg1WBC.js +0 -1
  191. package/dist/inspector/assets/settings-CHzeuzGL.js +0 -1
  192. package/dist/inspector/assets/use_interpretations-D_RbNlHg.js +0 -1
package/dist/actions.d.ts CHANGED
@@ -180,6 +180,7 @@ export declare function storeStructuredForApi(params: {
180
180
  observation_index: number;
181
181
  entity_type: string;
182
182
  entity_id: string;
183
+ details?: Record<string, unknown>;
183
184
  }[] | undefined;
184
185
  warnings?: {
185
186
  code: string;
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"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"}
package/dist/actions.js CHANGED
@@ -53,6 +53,7 @@ import { resolveSandboxReportTransport } from "./services/sandbox/transport.js";
53
53
  import { getSqliteDb } from "./repositories/sqlite/sqlite_client.js";
54
54
  import { getMcpAuthToken } from "./crypto/mcp_auth_token.js";
55
55
  import { isOauthKeyCredentialValid, normalizeOauthNextPath, OAuthKeySessionStore, } from "./services/oauth_key_gate.js";
56
+ import { buildGoogleAuthorizeUrl, createGoogleSigninNonce, consumeGoogleSigninNonce, exchangeGoogleAuthorizationCode, getSharedGraphUserId, GoogleIdTokenError, isGoogleSigninEnabled, verifyGoogleIdToken, } from "./services/google_oidc.js";
56
57
  import { AnalyzeSchemaCandidatesRequestSchema, AuditUndeclaredFragmentsRequestSchema, CorrectEntityRequestSchema, CreateInterpretationRequestSchema, CreateRelationshipsRequestSchema, CreateRelationshipRequestSchema, DeleteEntityRequestSchema, DeleteRelationshipRequestSchema, IssuesBulkEntityIdsRequestSchema, IssuesAddMessageRequestSchema, IssuesGetStatusRequestSchema, IssuesSubmitRequestSchema, IssuesSyncRequestSchema, EntitiesQueryRequestSchema, EntitySnapshotRequestSchema, FieldProvenanceRequestSchema, GetSchemaRecommendationsRequestSchema, ListObservationsRequestSchema, ListRelationshipsRequestSchema, MergeEntitiesRequestSchema, SplitEntityRequestSchema, ObservationsQueryRequestSchema, RegisterSchemaRequestSchema, RelationshipSnapshotRequestSchema, RestoreEntityRequestSchema, RestoreRelationshipRequestSchema, RetrieveEntityByIdentifierSchema, RetrieveGraphNeighborhoodSchema, RetrieveRelatedEntitiesSchema, RELATIONSHIP_ENTITY_ID_FORMAT_HINT, RELATIONSHIP_ENTITY_ID_FORMAT_ISSUE_CODE, StoreRequestSchema, UpdateSchemaIncrementalRequestSchema, } from "./shared/action_schemas.js";
57
58
  import { getMimeTypeFromExtension } from "./services/file_text_extraction.js";
58
59
  import { queryEntitiesWithCount } from "./shared/action_handlers/entity_handlers.js";
@@ -1096,6 +1097,16 @@ export function isLocalRequest(req) {
1096
1097
  }
1097
1098
  const OAUTH_KEY_SESSION_COOKIE = "neotoma_oauth_key_session";
1098
1099
  const oauthKeySessions = new OAuthKeySessionStore();
1100
+ /**
1101
+ * Maps an OAuth key-session token to the user_id resolved via Google
1102
+ * sign-in for that browser session. Populated only by the
1103
+ * `/mcp/oauth/google/callback` route; every other credential path
1104
+ * (private key / mnemonic / bearer) never writes here, so
1105
+ * `/mcp/oauth/local-login` falls through to `ensureLocalDevUser()`
1106
+ * exactly as before when this map has no entry — behavior for every
1107
+ * existing deploy (Google flag off) is unchanged.
1108
+ */
1109
+ const googleVerifiedUserBySessionToken = new Map();
1099
1110
  function readCookie(req, name) {
1100
1111
  const header = (req.headers["cookie"] || req.headers["Cookie"] || "");
1101
1112
  if (!header)
@@ -1335,6 +1346,14 @@ function setOAuthKeySessionCookie(req, res) {
1335
1346
  path: "/mcp/oauth",
1336
1347
  maxAge: 15 * 60 * 1000,
1337
1348
  });
1349
+ return token;
1350
+ }
1351
+ /** Resolve the local-auth user_id a Google-verified session (if any) mapped to this request. */
1352
+ function getGoogleVerifiedUserId(req) {
1353
+ const token = readCookie(req, OAUTH_KEY_SESSION_COOKIE);
1354
+ if (!token || !oauthKeySessions.isValid(token))
1355
+ return undefined;
1356
+ return googleVerifiedUserBySessionToken.get(token);
1338
1357
  }
1339
1358
  /**
1340
1359
  * Derive the canonical `@authority` the AAuth profile requires (AAuth §10.3.1).
@@ -1631,11 +1650,11 @@ app.all("/mcp", async (req, res) => {
1631
1650
  const hadSessionHeader = typeof sessionId === "string" && sessionId.length > 0;
1632
1651
  if (hadSessionHeader) {
1633
1652
  logger.warn(`[MCP HTTP] Unknown or expired Mcp-Session-Id (first 8 chars): ${sessionId.slice(0, 8)}... often wrong replica behind a load balancer, API restart, or stale client state`);
1634
- return res.status(503).json({
1653
+ return res.status(404).json({
1635
1654
  jsonrpc: "2.0",
1636
1655
  error: {
1637
1656
  code: -32001,
1638
- message: "Service Unavailable: MCP session is unknown on this API instance. If you run multiple replicas, enable sticky sessions for POST /mcp (or route /mcp to a single instance). Otherwise restart the MCP client so initialize runs again after a server restart.",
1657
+ message: "Not Found: MCP session is unknown or expired on this API instance. The client should re-initialize by sending a new InitializeRequest without a session ID. If you run multiple replicas, enable sticky sessions for POST /mcp (or route /mcp to a single instance).",
1639
1658
  },
1640
1659
  id: rpcId,
1641
1660
  });
@@ -2104,10 +2123,19 @@ app.get("/mcp/oauth/key-auth", async (req, res) => {
2104
2123
  const nextPathEscaped = escapeHtml(nextPath);
2105
2124
  const hasBearerFallback = Boolean((process.env.NEOTOMA_BEARER_TOKEN || "").trim());
2106
2125
  const defaultCredentialMode = hasBearerFallback ? "bearer" : "private_key";
2126
+ const googleEnabled = isGoogleSigninEnabled();
2127
+ const googleStartHref = `/mcp/oauth/google/start?next=${encodeURIComponent(nextPath)}`;
2107
2128
  return res.send(renderOauthPage({
2108
2129
  title: "Authenticate OAuth preflight",
2109
2130
  subtitle: "Choose one credential path below, then continue to complete OAuth authorization.",
2110
2131
  contentHtml: `
2132
+ ${googleEnabled
2133
+ ? `<div class="actions" style="margin-bottom: 1rem;">
2134
+ <a class="btn-link" href="${escapeHtml(googleStartHref)}">Sign in with Google</a>
2135
+ </div>
2136
+ <div class="notice">Signs you in as your own Neotoma user if your Google account's verified email is on this server's approved list. Doesn't require a private key.</div>
2137
+ <hr style="margin: 1.5rem 0; border: none; border-top: 1px solid hsl(var(--border));" />`
2138
+ : ""}
2111
2139
  <form method="post" action="/mcp/oauth/key-auth" id="oauth-key-auth-form">
2112
2140
  <input type="hidden" name="next" value="${nextPathEscaped}" />
2113
2141
  <input type="hidden" name="credential_mode" id="credential_mode" value="${defaultCredentialMode}" />
@@ -2213,6 +2241,107 @@ app.post("/mcp/oauth/key-auth", express.urlencoded({ extended: true }), async (r
2213
2241
  setOAuthKeySessionCookie(req, res);
2214
2242
  return res.redirect(nextPath);
2215
2243
  });
2244
+ /**
2245
+ * "Sign in with Google" entry point. Additive to, and independent of, the
2246
+ * key/mnemonic/bearer credential form above — feature-flagged off unless
2247
+ * NEOTOMA_GOOGLE_CLIENT_ID + NEOTOMA_APPROVED_EMAILS are both configured.
2248
+ * AUTH-LAYER ONLY: this establishes who the person is (their own user_id
2249
+ * via the existing per-email primitive); it does not touch data-sharing or
2250
+ * read-scoping.
2251
+ */
2252
+ app.get("/mcp/oauth/google/start", async (req, res) => {
2253
+ if (!isGoogleSigninEnabled()) {
2254
+ return res.status(404).send("Not found");
2255
+ }
2256
+ const nextPath = normalizeOauthNextPath(req.query.next || undefined);
2257
+ const nonce = createGoogleSigninNonce(nextPath);
2258
+ const redirectUri = `${req.protocol}://${req.get("host") || ""}/mcp/oauth/google/callback`;
2259
+ try {
2260
+ const authorizeUrl = buildGoogleAuthorizeUrl({ redirectUri, nonce });
2261
+ return res.redirect(authorizeUrl);
2262
+ }
2263
+ catch (error) {
2264
+ logError("GoogleOAuthStart", req, error);
2265
+ return res.status(500).send(error?.message ?? "Failed to start Google sign-in");
2266
+ }
2267
+ });
2268
+ app.get("/mcp/oauth/google/callback", async (req, res) => {
2269
+ if (!isGoogleSigninEnabled()) {
2270
+ return res.status(404).send("Not found");
2271
+ }
2272
+ const errorParam = req.query.error?.trim();
2273
+ const code = req.query.code?.trim();
2274
+ const nonceParam = req.query.state?.trim();
2275
+ const failWith = (subtitle) => {
2276
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
2277
+ return res.status(401).send(renderOauthPage({
2278
+ title: "Google sign-in failed",
2279
+ subtitle,
2280
+ contentHtml: `<div class="actions">
2281
+ <a class="btn-link secondary" href="/mcp/oauth/key-auth">Back to authentication</a>
2282
+ </div>`,
2283
+ }));
2284
+ };
2285
+ if (errorParam) {
2286
+ logger.warn("[Google OAuth] Callback returned an error", { error: errorParam });
2287
+ return failWith("Google sign-in was cancelled or denied.");
2288
+ }
2289
+ if (!code || !nonceParam) {
2290
+ return failWith("Missing code or state from Google callback.");
2291
+ }
2292
+ const nonceEntry = consumeGoogleSigninNonce(nonceParam);
2293
+ if (!nonceEntry) {
2294
+ return failWith("This sign-in link has expired or was already used. Please try again.");
2295
+ }
2296
+ // Same nextPath contract as the key/mnemonic/bearer path above: normalized
2297
+ // back to a /mcp/oauth/* path (typically /mcp/oauth/authorize?...), never
2298
+ // an external URL.
2299
+ const nextPath = normalizeOauthNextPath(nonceEntry.next);
2300
+ const redirectUri = `${req.protocol}://${req.get("host") || ""}/mcp/oauth/google/callback`;
2301
+ try {
2302
+ const { idToken } = await exchangeGoogleAuthorizationCode({ code, redirectUri });
2303
+ const { email } = await verifyGoogleIdToken(idToken);
2304
+ // Resolve the verified email to a stable user_id via the EXISTING per-email
2305
+ // primitive (local_auth.ts) — the same primitive createLocalAuthUser /
2306
+ // hashEmailToUserId already uses elsewhere. By default this person is
2307
+ // admitted as their own isolated user_id, same as any other local-auth user.
2308
+ const { createLocalAuthUser } = await import("./services/local_auth.js");
2309
+ const { randomBytes } = await import("node:crypto");
2310
+ // Password is unused for Google-authenticated users (they never log in
2311
+ // with it) — generate an unguessable throwaway so createLocalAuthUser's
2312
+ // shared code path is satisfied without weakening any other account.
2313
+ const throwawayPassword = randomBytes(32).toString("hex");
2314
+ const perEmailUser = createLocalAuthUser(email, throwawayPassword);
2315
+ // Shared-graph opt-in: when NEOTOMA_SHARED_GRAPH_USER_ID is set, an
2316
+ // approved teammate (the email already passed the allowlist inside
2317
+ // verifyGoogleIdToken) is bound to the shared graph's user_id instead of
2318
+ // their isolated per-email one — so the whole allowlisted team, plus the
2319
+ // bearer/MCP identity that owns the data, share one populated graph. Unset
2320
+ // → isolated per-email behavior (unchanged). See getSharedGraphUserId.
2321
+ const resolvedUserId = getSharedGraphUserId() ?? perEmailUser.id;
2322
+ // Mark this browser session as Google-verified and bound to the resolved
2323
+ // user_id, exactly the same OAuth key-session cookie the private-key/
2324
+ // mnemonic/bearer path sets. `/mcp/oauth/authorize` -> `/mcp/oauth/
2325
+ // local-login` then runs completely unmodified, except local-login now
2326
+ // resolves the user via this session instead of always ensureLocalDevUser().
2327
+ const sessionToken = setOAuthKeySessionCookie(req, res);
2328
+ googleVerifiedUserBySessionToken.set(sessionToken, resolvedUserId);
2329
+ return res.redirect(nextPath);
2330
+ }
2331
+ catch (error) {
2332
+ logError("GoogleOAuthCallback", req, error);
2333
+ if (error instanceof GoogleIdTokenError) {
2334
+ if (error.code === "EMAIL_NOT_APPROVED") {
2335
+ return failWith("Your Google account is not on the approved list for this server.");
2336
+ }
2337
+ if (error.code === "EMAIL_NOT_VERIFIED") {
2338
+ return failWith("Your Google account's email is not verified.");
2339
+ }
2340
+ return failWith("Google sign-in could not be verified.");
2341
+ }
2342
+ return failWith(error?.message ?? "Google sign-in failed.");
2343
+ }
2344
+ });
2216
2345
  // RFC 8414 authorization endpoint (GET) for Cursor and other OAuth clients
2217
2346
  app.get("/mcp/oauth/authorize", async (req, res) => {
2218
2347
  try {
@@ -2263,7 +2392,10 @@ app.get("/mcp/oauth/authorize", async (req, res) => {
2263
2392
  // When reached via tunnel, only allow redirect_uri to localhost, app schemes, or OpenAI Custom GPT
2264
2393
  if (!isLocalRequest(req)) {
2265
2394
  const { isRedirectUriAllowedForTunnel } = await import("./services/mcp_oauth.js");
2266
- if (!isRedirectUriAllowedForTunnel(redirect_uri)) {
2395
+ // Public host as seen from outside the tunnel (x-forwarded-host set by the
2396
+ // proxy) so an Inspector callback on this instance's own origin is allowed.
2397
+ const selfHost = req.header("x-forwarded-host")?.split(",")[0]?.trim() || req.get("host");
2398
+ if (!isRedirectUriAllowedForTunnel(redirect_uri, selfHost)) {
2267
2399
  logger.warn("[MCP OAuth] Authorize rejected: redirect_uri not allowed for tunnel", {
2268
2400
  redirect_uri: sanitizeRedirectUriForLog(redirect_uri),
2269
2401
  });
@@ -2378,9 +2510,15 @@ app.get("/mcp/oauth/local-login", async (req, res) => {
2378
2510
  }));
2379
2511
  }
2380
2512
  try {
2381
- const devUser = ensureLocalDevUser();
2513
+ // If this browser session was admitted via Google sign-in (see
2514
+ // /mcp/oauth/google/callback), complete authorization as THAT verified
2515
+ // user's own user_id instead of the shared dev user. Every other path
2516
+ // (no Google session on this cookie — the default, and the only
2517
+ // possibility when the Google feature flag is off) is unchanged.
2518
+ const googleUserId = getGoogleVerifiedUserId(req);
2519
+ const resolvedUserId = googleUserId ?? ensureLocalDevUser().id;
2382
2520
  const { completeLocalAuthorization } = await import("./services/mcp_oauth.js");
2383
- const { connectionId, redirectUri, clientState } = await completeLocalAuthorization(state, devUser.id);
2521
+ const { connectionId, redirectUri, clientState } = await completeLocalAuthorization(state, resolvedUserId);
2384
2522
  const frontendBase = process.env.NEOTOMA_FRONTEND_URL || process.env.FRONTEND_URL || "http://localhost:5195";
2385
2523
  const frontendOauth = `${frontendBase}/oauth`;
2386
2524
  if (redirectUri) {
@@ -5834,6 +5972,12 @@ export async function storeStructuredForApi(params) {
5834
5972
  // entity_id.
5835
5973
  const priorSnapshotByEntityId = new Map();
5836
5974
  const newSnapshotByEntityId = new Map();
5975
+ // #1963: auto-link may retract a stale reference-field edge (e.g. a contact's
5976
+ // organization moved companies). A retraction *failure* must not be silent —
5977
+ // that would reproduce the very silent-drift bug this fix exists to close, one
5978
+ // layer up. Accumulate per-observation retraction outcomes here so the
5979
+ // schemaStoreWarnings pass below can surface them on the store response.
5980
+ const autoLinkRetractionByObsIndex = new Map();
5837
5981
  for (const r of resolved) {
5838
5982
  let observation_id = null;
5839
5983
  let snapshotAfter = null;
@@ -5917,14 +6061,25 @@ export async function storeStructuredForApi(params) {
5917
6061
  const schemaEntry = await schemaRegistry.loadActiveSchema(r.entity_type, userId);
5918
6062
  if (schemaEntry?.schema_definition?.reference_fields?.length) {
5919
6063
  const { autoLinkReferenceFields } = await import("./services/schema_reference_linking.js");
5920
- await autoLinkReferenceFields({
6064
+ const autoLinkResult = await autoLinkReferenceFields({
5921
6065
  entityId: r.entity_id,
5922
6066
  entityType: r.entity_type,
5923
6067
  fields: r.fields,
5924
6068
  schema: schemaEntry.schema_definition,
5925
6069
  userId,
5926
6070
  sourceId: observationSourceId,
6071
+ commit,
5927
6072
  });
6073
+ if (autoLinkResult.retracted > 0 || autoLinkResult.retraction_failures > 0) {
6074
+ autoLinkRetractionByObsIndex.set(r.observation_index, {
6075
+ entity_type: r.entity_type,
6076
+ entity_id: r.entity_id,
6077
+ retracted: autoLinkResult.retracted,
6078
+ retraction_failures: autoLinkResult.retraction_failures,
6079
+ failed_target_entity_ids: autoLinkResult.failed_retraction_target_entity_ids,
6080
+ failed_retractions: autoLinkResult.failed_retractions,
6081
+ });
6082
+ }
5928
6083
  }
5929
6084
  }
5930
6085
  catch (linkErr) {
@@ -6254,6 +6409,46 @@ export async function storeStructuredForApi(params) {
6254
6409
  }
6255
6410
  }
6256
6411
  }
6412
+ // #1963: surface auto-link edge retractions on the store response so a
6413
+ // retraction — and especially a retraction *failure* — is never silent. A
6414
+ // failure means a stale reference-field edge may still be live despite the
6415
+ // store reporting success, so it is emitted as its own actionable warning.
6416
+ for (const [obsIndex, rec] of autoLinkRetractionByObsIndex) {
6417
+ if (rec.retraction_failures > 0) {
6418
+ schemaStoreWarnings.push({
6419
+ code: "AUTO_LINK_RETRACTION_FAILED",
6420
+ message: `Auto-link retraction failed for ${rec.retraction_failures} stale ` +
6421
+ `reference-field edge(s) on ${rec.entity_type}/${rec.entity_id}. ` +
6422
+ `A superseded edge (e.g. a prior works_at) may still be live even though ` +
6423
+ `the store succeeded. To clean up, call delete_relationship for each ` +
6424
+ `stale edge in details.stale_edges, or re-run the store.`,
6425
+ observation_index: obsIndex,
6426
+ entity_type: rec.entity_type,
6427
+ entity_id: rec.entity_id,
6428
+ // Structured remediation: each entry is a ready-to-use delete_relationship
6429
+ // argument set, so the caller needs no knowledge of its own schema.
6430
+ details: {
6431
+ stale_edges: rec.failed_retractions.map((f) => ({
6432
+ source_entity_id: rec.entity_id,
6433
+ target_entity_id: f.target_entity_id,
6434
+ relationship_type: f.relationship_type,
6435
+ field_name: f.field,
6436
+ })),
6437
+ },
6438
+ });
6439
+ }
6440
+ else if (rec.retracted > 0) {
6441
+ schemaStoreWarnings.push({
6442
+ code: "AUTO_LINK_EDGE_RETRACTED",
6443
+ message: `Retracted ${rec.retracted} stale auto-linked reference-field edge(s) on ` +
6444
+ `${rec.entity_type}/${rec.entity_id} because the field's resolved target ` +
6445
+ `changed (#1963).`,
6446
+ observation_index: obsIndex,
6447
+ entity_type: rec.entity_type,
6448
+ entity_id: rec.entity_id,
6449
+ });
6450
+ }
6451
+ }
6257
6452
  const unknownFieldNames = Array.from(unknownFieldNamesSet).sort();
6258
6453
  // Conditional hint (#1549): update_schema_incremental requires the schema to
6259
6454
  // declare canonical_name_fields, else it dead-ends on