neotoma 0.8.0 → 0.9.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.
- package/README.md +2 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +181 -30
- package/dist/actions.js.map +1 -1
- package/dist/cli/bootstrap.js +0 -0
- package/dist/cli/index.js +4 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/triage.d.ts.map +1 -1
- package/dist/cli/triage.js +9 -0
- package/dist/cli/triage.js.map +1 -1
- package/dist/inspector/assets/Combination-CujEssa-.js +41 -0
- package/dist/inspector/assets/agent_badge-Cy2xdM19.js +1 -0
- package/dist/inspector/assets/agent_detail-34_3u913.js +1 -0
- package/dist/inspector/assets/agent_filter-9Y5V9TSY.js +1 -0
- package/dist/inspector/assets/agent_grant_detail-fs0DKTd5.js +1 -0
- package/dist/inspector/assets/agent_grant_form-DjoTByJj.js +1 -0
- package/dist/inspector/assets/agent_grants-DUtqBZkK.js +1 -0
- package/dist/inspector/assets/agents-CIUBwgRT.js +1 -0
- package/dist/inspector/assets/arrow-left-Bxer0ezV.js +6 -0
- package/dist/inspector/assets/attribution_card-C48p_bBB.js +1 -0
- package/dist/inspector/assets/attribution_summary-B7KAy0mn.js +1 -0
- package/dist/inspector/assets/card-Bz-JK2L1.js +1 -0
- package/dist/inspector/assets/check-DN6vjWJ7.js +6 -0
- package/dist/inspector/assets/checkbox-DuNHytV3.js +1 -0
- package/dist/inspector/assets/chevron-down-BGEwSYyI.js +6 -0
- package/dist/inspector/assets/chevron-right-D5OasTAw.js +6 -0
- package/dist/inspector/assets/compliance-B7gRLVKA.js +1 -0
- package/dist/inspector/assets/confirm-dialog-2InCKZky.js +6 -0
- package/dist/inspector/assets/copy_id_button-CoFHQieu.js +6 -0
- package/dist/inspector/assets/corrections-C-GqSum_.js +1 -0
- package/dist/inspector/assets/dashboard-CB0ynQ0H.js +73 -0
- package/dist/inspector/assets/data-table-Nyryp-JO.js +22 -0
- package/dist/inspector/assets/dialog-DS3fUAm1.js +10 -0
- package/dist/inspector/assets/dropdown-menu-Cypcul5-.js +6 -0
- package/dist/inspector/assets/entities-B1ZBrker.js +1 -0
- package/dist/inspector/assets/entity_detail-DlsbmdeN.js +17 -0
- package/dist/inspector/assets/entity_link-DujmD1Yr.js +1 -0
- package/dist/inspector/assets/external-link-C4V3VgZf.js +6 -0
- package/dist/inspector/assets/feedback-BQFBT-KP.js +35 -0
- package/dist/inspector/assets/graph_explorer-BZV40eAE.css +1 -0
- package/dist/inspector/assets/graph_explorer-Ckir-IQl.js +23 -0
- package/dist/inspector/assets/index-Bx2zn3Hd.js +1 -0
- package/dist/inspector/assets/index-ClljmJBP.js +199 -0
- package/dist/inspector/assets/index-Dqo226iA.js +1 -0
- package/dist/inspector/assets/index-NU89qCAK.css +1 -0
- package/dist/inspector/assets/index-ZmrnhI6W.js +1 -0
- package/dist/inspector/assets/interpretations-B7wb65mL.js +1 -0
- package/dist/inspector/assets/interpretations-CUaxEt1j.js +1 -0
- package/dist/inspector/assets/json_viewer-BkvSv8xS.js +1 -0
- package/dist/inspector/assets/label-CbDLvpxl.js +1 -0
- package/dist/inspector/assets/observations-lXPbAxMw.js +1 -0
- package/dist/inspector/assets/page_shell-_vozYBwF.js +1 -0
- package/dist/inspector/assets/pagination-aHI1LrUp.js +6 -0
- package/dist/inspector/assets/pdf.worker.min-yatZIOMy.mjs +21 -0
- package/dist/inspector/assets/plus-CPWsVXx9.js +6 -0
- package/dist/inspector/assets/recent_activity-DOx6FPz3.js +11 -0
- package/dist/inspector/assets/recent_conversations-CgG1tewt.js +1 -0
- package/dist/inspector/assets/recent_records_feed-CQN5zaI8.js +1 -0
- package/dist/inspector/assets/relationship_detail-LsEDdRi1.js +1 -0
- package/dist/inspector/assets/relationships-DnfqeglP.js +1 -0
- package/dist/inspector/assets/relationships-bLyNDRTv.js +1 -0
- package/dist/inspector/assets/sandbox-CHH6I9Ww.js +1 -0
- package/dist/inspector/assets/schema_detail-DnkpfNNX.js +11 -0
- package/dist/inspector/assets/schemas-CvrYW_Oj.js +5 -0
- package/dist/inspector/assets/search-Bj5zl6Sn.js +1 -0
- package/dist/inspector/assets/select-MlFuilHt.js +6 -0
- package/dist/inspector/assets/settings-D5tFC9Ho.js +1 -0
- package/dist/inspector/assets/source_detail-C2OkmsHz.js +17 -0
- package/dist/inspector/assets/source_link-B0ZU_1AG.js +1 -0
- package/dist/inspector/assets/sources-BDnp2oAz.js +9 -0
- package/dist/inspector/assets/switch-DXGlOHG4.js +1 -0
- package/dist/inspector/assets/tabs-C0mWq9S1.js +1 -0
- package/dist/inspector/assets/textarea-wLUG8kv6.js +1 -0
- package/dist/inspector/assets/timeline-DXEE4O4U.js +1 -0
- package/dist/inspector/assets/timeline-LjwviNGU.js +1 -0
- package/dist/inspector/assets/timeline_event_detail-C0M5GhTm.js +1 -0
- package/dist/inspector/assets/trash-2-DWBXKl-O.js +6 -0
- package/dist/inspector/assets/turn_detail-DXCnGJQ4.js +1 -0
- package/dist/inspector/assets/turns-C6blPs3y.js +1 -0
- package/dist/inspector/assets/use_agents-Cl-ZvBxZ.js +1 -0
- package/dist/inspector/assets/use_entities-DtfxOiM5.js +1 -0
- package/dist/inspector/assets/use_interpretations-usFKH4p9.js +1 -0
- package/dist/inspector/assets/use_mutations-C-WJL341.js +1 -0
- package/dist/inspector/assets/use_recent_conversations-CcGV91zn.js +1 -0
- package/dist/inspector/assets/use_relationships-Ci7lTjAZ.js +1 -0
- package/dist/inspector/assets/use_schemas-D_uXgmEv.js +1 -0
- package/dist/inspector/assets/use_sources-CFY0i0dw.js +1 -0
- package/dist/inspector/assets/use_stats-DBERXcdx.js +1 -0
- package/dist/inspector/assets/use_timeline-BqlPwIlB.js +1 -0
- package/dist/inspector/assets/use_turns-vQjPAKeW.js +1 -0
- package/dist/inspector/assets/value-BTdN53H7.js +1 -0
- package/dist/inspector/favicon.svg +10 -0
- package/dist/inspector/index.html +14 -0
- package/dist/repositories/sqlite/sqlite_client.d.ts.map +1 -1
- package/dist/repositories/sqlite/sqlite_client.js +12 -0
- package/dist/repositories/sqlite/sqlite_client.js.map +1 -1
- package/dist/scripts/seed_sandbox.js +11 -1
- package/dist/scripts/seed_sandbox.js.map +1 -1
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +33 -23
- package/dist/server.js.map +1 -1
- package/dist/services/conversation_turn.d.ts +20 -51
- package/dist/services/conversation_turn.d.ts.map +1 -1
- package/dist/services/conversation_turn.js +136 -373
- package/dist/services/conversation_turn.js.map +1 -1
- package/dist/services/feedback/admin_proxy.d.ts +21 -7
- package/dist/services/feedback/admin_proxy.d.ts.map +1 -1
- package/dist/services/feedback/admin_proxy.js +142 -16
- package/dist/services/feedback/admin_proxy.js.map +1 -1
- package/dist/services/feedback/mirror_local_to_entity.d.ts +20 -53
- package/dist/services/feedback/mirror_local_to_entity.d.ts.map +1 -1
- package/dist/services/feedback/mirror_local_to_entity.js +46 -76
- package/dist/services/feedback/mirror_local_to_entity.js.map +1 -1
- package/dist/services/feedback/neotoma_payload.d.ts +72 -33
- package/dist/services/feedback/neotoma_payload.d.ts.map +1 -1
- package/dist/services/feedback/neotoma_payload.js +12 -24
- package/dist/services/feedback/neotoma_payload.js.map +1 -1
- package/dist/services/feedback_transport_local.d.ts.map +1 -1
- package/dist/services/feedback_transport_local.js +4 -0
- package/dist/services/feedback_transport_local.js.map +1 -1
- package/dist/services/inspector_mount.d.ts +32 -70
- package/dist/services/inspector_mount.d.ts.map +1 -1
- package/dist/services/inspector_mount.js +219 -183
- package/dist/services/inspector_mount.js.map +1 -1
- package/dist/services/recent_conversations.d.ts +2 -0
- package/dist/services/recent_conversations.d.ts.map +1 -1
- package/dist/services/recent_conversations.js +17 -0
- package/dist/services/recent_conversations.js.map +1 -1
- package/dist/services/root_landing/harness_snippets.d.ts +2 -0
- package/dist/services/root_landing/harness_snippets.d.ts.map +1 -1
- package/dist/services/root_landing/harness_snippets.js +15 -8
- package/dist/services/root_landing/harness_snippets.js.map +1 -1
- package/dist/services/root_landing/html_template.d.ts +9 -0
- package/dist/services/root_landing/html_template.d.ts.map +1 -1
- package/dist/services/root_landing/html_template.js +66 -1
- package/dist/services/root_landing/html_template.js.map +1 -1
- package/dist/services/root_landing/index.d.ts +11 -0
- package/dist/services/root_landing/index.d.ts.map +1 -1
- package/dist/services/root_landing/index.js +26 -8
- package/dist/services/root_landing/index.js.map +1 -1
- package/dist/services/root_landing/md_template.d.ts.map +1 -1
- package/dist/services/root_landing/md_template.js +22 -2
- package/dist/services/root_landing/md_template.js.map +1 -1
- package/dist/services/sandbox/pack_registry.d.ts +6 -50
- package/dist/services/sandbox/pack_registry.d.ts.map +1 -1
- package/dist/services/sandbox/pack_registry.js +74 -86
- package/dist/services/sandbox/pack_registry.js.map +1 -1
- package/dist/services/sandbox/seeder.d.ts +8 -47
- package/dist/services/sandbox/seeder.d.ts.map +1 -1
- package/dist/services/sandbox/seeder.js +51 -89
- package/dist/services/sandbox/seeder.js.map +1 -1
- package/dist/services/sandbox/sessions.d.ts +23 -114
- package/dist/services/sandbox/sessions.d.ts.map +1 -1
- package/dist/services/sandbox/sessions.js +99 -288
- package/dist/services/sandbox/sessions.js.map +1 -1
- package/dist/services/schema_definitions.d.ts.map +1 -1
- package/dist/services/schema_definitions.js +152 -0
- package/dist/services/schema_definitions.js.map +1 -1
- package/dist/shared/openapi_types.d.ts +3 -0
- package/dist/shared/openapi_types.d.ts.map +1 -1
- package/openapi.yaml +4 -0
- package/package.json +23 -9
package/README.md
CHANGED
|
@@ -23,6 +23,8 @@ Neotoma is a deterministic state layer for AI agents. It stores structured recor
|
|
|
23
23
|
|
|
24
24
|
Not retrieval memory (RAG, vector search, semantic lookup). Neotoma enforces deterministic state evolution: same observations always produce the same entity state, regardless of when or in what order they are processed.
|
|
25
25
|
|
|
26
|
+
The **Inspector** — Neotoma's visual control plane for browsing the entity graph, timeline, schema editor, and agent attribution — is bundled and served at `/inspector` by default when the server starts. No separate build or configuration required. Override with `NEOTOMA_INSPECTOR_DISABLE`, `NEOTOMA_PUBLIC_INSPECTOR_URL`, `NEOTOMA_INSPECTOR_STATIC_DIR`, or `NEOTOMA_INSPECTOR_BASE_PATH` (see `.env.example`).
|
|
27
|
+
|
|
26
28
|
## Architecture
|
|
27
29
|
|
|
28
30
|
```mermaid
|
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;AAiK9B,eAAO,MAAM,GAAG,6CAAY,CAAC;AAif7B;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAU5D;AA4PD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAmBhD;AA6nHD,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,KAAK,CAAC;QACpB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;;;;;;;;;;;;;cAkfS,MAAM;gBACJ,MAAM;2BACK,MAAM;qBACZ,MAAM;mBACR,MAAM;wBACD,MAAM;uBACP,MAAM;;;;;;;;;mBA9IV,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;;GA8E3B;AA29DD,wBAAsB,eAAe;;;eA2FpC"}
|
package/dist/actions.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import express from "express";
|
|
2
|
+
import cookieParser from "cookie-parser";
|
|
2
3
|
import cors from "cors";
|
|
3
4
|
import helmet from "helmet";
|
|
4
5
|
import morgan from "morgan";
|
|
@@ -34,7 +35,9 @@ import { logger } from "./utils/logger.js";
|
|
|
34
35
|
import { OAuthError } from "./services/mcp_oauth_errors.js";
|
|
35
36
|
import { ensureLocalDevUser, ensureSandboxAauthUser, ensureSandboxPublicUser, LOCAL_DEV_USER_ID, SANDBOX_PUBLIC_USER_ID, } from "./services/local_auth.js";
|
|
36
37
|
import { isSandboxMode, sandboxDestructiveGuard, sandboxHeaderMiddleware, } from "./services/sandbox_mode.js";
|
|
37
|
-
import {
|
|
38
|
+
import { createSandboxSession, redeemOneTimeCode, resolveSessionFromRequest, revokeSession, purgeSessionUserData, sweepExpiredSessions, SESSION_COOKIE_NAME, } from "./services/sandbox/sessions.js";
|
|
39
|
+
import { buildLandingContext, buildRootLandingHtml, buildRootLandingJson, buildRootLandingMarkdown, buildRobotsTxt, readNeotomaConfigEnvironment, wantsHtml as acceptWantsHtml, wantsMarkdown as acceptWantsMarkdown, } from "./services/root_landing/index.js";
|
|
40
|
+
import { installInspectorMount } from "./services/inspector_mount.js";
|
|
38
41
|
import { getSandboxTermsResponse } from "./services/sandbox/terms.js";
|
|
39
42
|
import { resolveSandboxReportTransport } from "./services/sandbox/transport.js";
|
|
40
43
|
import { getSqliteDb } from "./repositories/sqlite/sqlite_client.js";
|
|
@@ -49,6 +52,7 @@ import { readOpenApiActionsFile, readOpenApiFile } from "./shared/openapi_file.j
|
|
|
49
52
|
import { buildSmitheryServerCard } from "./mcp_server_card.js";
|
|
50
53
|
import { listRecentRecordActivity, parseRecordActivityTypesQuery, } from "./services/recent_record_activity.js";
|
|
51
54
|
import { listRecentConversations } from "./services/recent_conversations.js";
|
|
55
|
+
import { listConversationTurns, getConversationTurn, } from "./services/conversation_turn.js";
|
|
52
56
|
import { getAgent, listAgentRecords, listAgents } from "./services/agents_directory.js";
|
|
53
57
|
export const app = express();
|
|
54
58
|
// Trust proxy headers (required for express-rate-limit when X-Forwarded-For is present)
|
|
@@ -102,6 +106,7 @@ app.use(express.json({
|
|
|
102
106
|
},
|
|
103
107
|
}));
|
|
104
108
|
app.use(morgan("dev"));
|
|
109
|
+
app.use(cookieParser());
|
|
105
110
|
app.use(unknownFieldsGuard);
|
|
106
111
|
// Sandbox-mode response header. Stamped on every response so clients can
|
|
107
112
|
// detect public-sandbox deployments (sandbox.neotoma.io) without an extra
|
|
@@ -110,37 +115,125 @@ if (isSandboxMode()) {
|
|
|
110
115
|
app.use(sandboxHeaderMiddleware);
|
|
111
116
|
logger.info("[Sandbox] NEOTOMA_SANDBOX_MODE=1 — bearer bypass to SANDBOX_PUBLIC_USER_ID, destructive routes gated, weekly reset expected");
|
|
112
117
|
}
|
|
113
|
-
// Inspector SPA mount.
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
118
|
+
// Inspector SPA mount. Deliberately registered before all auth / rate-limit
|
|
119
|
+
// middleware so the SPA shell + assets are reachable without a bearer — the
|
|
120
|
+
// API calls the Inspector makes still flow through the normal auth stack below.
|
|
121
|
+
installInspectorMount(app, process.env, logger);
|
|
122
|
+
// ── Sandbox session endpoints ───────────────────────────────────────────
|
|
123
|
+
// Registered before general auth so the session handshake works for
|
|
124
|
+
// unauthenticated visitors. Routes exist only when NEOTOMA_SANDBOX_MODE=1.
|
|
125
|
+
if (isSandboxMode()) {
|
|
126
|
+
const sessionRateLimit = rateLimit({
|
|
127
|
+
windowMs: 60 * 60 * 1000,
|
|
128
|
+
limit: 10,
|
|
129
|
+
standardHeaders: true,
|
|
130
|
+
legacyHeaders: false,
|
|
131
|
+
keyGenerator: (req) => `ip:${ipKeyGenerator(req.ip || "")}`,
|
|
132
|
+
validate: { trustProxy: false },
|
|
133
|
+
});
|
|
134
|
+
app.post("/sandbox/session/new", sessionRateLimit, (req, res) => {
|
|
135
|
+
try {
|
|
136
|
+
const packId = typeof req.body?.pack_id === "string" ? req.body.pack_id : "generic";
|
|
137
|
+
const session = createSandboxSession(packId);
|
|
138
|
+
res.cookie(SESSION_COOKIE_NAME, session.bearerToken, {
|
|
139
|
+
httpOnly: true,
|
|
140
|
+
sameSite: "lax",
|
|
141
|
+
path: "/",
|
|
142
|
+
expires: new Date(session.expiresAt),
|
|
143
|
+
});
|
|
144
|
+
res.json({
|
|
145
|
+
one_time_code: session.oneTimeCode,
|
|
146
|
+
expires_at: session.expiresAt,
|
|
147
|
+
pack_id: session.packId,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
logger.error(`[Sandbox] session/new failed: ${err.message}`);
|
|
152
|
+
res.status(500).json({ error_code: "SESSION_CREATE_FAILED", message: err.message });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
app.post("/sandbox/session/redeem", (req, res) => {
|
|
156
|
+
try {
|
|
157
|
+
const code = typeof req.body?.code === "string" ? req.body.code : "";
|
|
158
|
+
if (!code) {
|
|
159
|
+
res.status(400).json({ error_code: "MISSING_CODE", message: "code is required" });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const result = redeemOneTimeCode(code);
|
|
163
|
+
if (!result) {
|
|
164
|
+
res.status(404).json({ error_code: "INVALID_CODE", message: "Code expired or already redeemed" });
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
res.cookie(SESSION_COOKIE_NAME, result.bearerToken, {
|
|
168
|
+
httpOnly: true,
|
|
169
|
+
sameSite: "lax",
|
|
170
|
+
path: "/",
|
|
171
|
+
expires: new Date(result.expiresAt),
|
|
137
172
|
});
|
|
173
|
+
res.json({
|
|
174
|
+
bearer_token: result.bearerToken,
|
|
175
|
+
user_id: result.userId,
|
|
176
|
+
expires_at: result.expiresAt,
|
|
177
|
+
pack_id: result.packId,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
logger.error(`[Sandbox] session/redeem failed: ${err.message}`);
|
|
182
|
+
res.status(500).json({ error_code: "SESSION_REDEEM_FAILED", message: err.message });
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
app.get("/sandbox/session", (req, res) => {
|
|
186
|
+
const session = resolveSessionFromRequest(req);
|
|
187
|
+
if (!session) {
|
|
188
|
+
res.status(401).json({ error_code: "NO_SESSION", message: "No active sandbox session" });
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
res.json({
|
|
192
|
+
user_id: session.userId,
|
|
193
|
+
pack_id: session.packId,
|
|
194
|
+
created_at: session.createdAt,
|
|
195
|
+
expires_at: session.expiresAt,
|
|
138
196
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
197
|
+
});
|
|
198
|
+
app.post("/sandbox/session/reset", (req, res) => {
|
|
199
|
+
const session = resolveSessionFromRequest(req);
|
|
200
|
+
if (!session) {
|
|
201
|
+
res.status(401).json({ error_code: "NO_SESSION", message: "No active sandbox session" });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const packId = typeof req.body?.pack_id === "string" ? req.body.pack_id : undefined;
|
|
205
|
+
purgeSessionUserData(session.userId);
|
|
206
|
+
const newSession = createSandboxSession(packId ?? session.packId);
|
|
207
|
+
res.cookie(SESSION_COOKIE_NAME, newSession.bearerToken, {
|
|
208
|
+
httpOnly: true,
|
|
209
|
+
sameSite: "lax",
|
|
210
|
+
path: "/",
|
|
211
|
+
expires: new Date(newSession.expiresAt),
|
|
212
|
+
});
|
|
213
|
+
res.json({
|
|
214
|
+
user_id: newSession.userId,
|
|
215
|
+
pack_id: newSession.packId,
|
|
216
|
+
expires_at: newSession.expiresAt,
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
app.delete("/sandbox/session", (req, res) => {
|
|
220
|
+
const session = resolveSessionFromRequest(req);
|
|
221
|
+
if (!session) {
|
|
222
|
+
res.status(401).json({ error_code: "NO_SESSION", message: "No active sandbox session" });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
revokeSession(session.userId);
|
|
226
|
+
purgeSessionUserData(session.userId);
|
|
227
|
+
res.clearCookie(SESSION_COOKIE_NAME, { path: "/" });
|
|
228
|
+
res.json({ ok: true });
|
|
229
|
+
});
|
|
230
|
+
setInterval(() => {
|
|
231
|
+
const purged = sweepExpiredSessions();
|
|
232
|
+
if (purged > 0) {
|
|
233
|
+
logger.info(`[Sandbox] Swept ${purged} expired session(s)`);
|
|
234
|
+
}
|
|
235
|
+
}, 15 * 60 * 1000);
|
|
236
|
+
logger.info("[Sandbox] Session endpoints registered");
|
|
144
237
|
}
|
|
145
238
|
// Rate limiters for OAuth endpoints
|
|
146
239
|
// validate.trustProxy: false — we use trust proxy behind one proxy; skip strict IP check
|
|
@@ -399,6 +492,7 @@ app.get("/server-info", (_req, res) => {
|
|
|
399
492
|
httpPort,
|
|
400
493
|
apiBase: config.apiBase,
|
|
401
494
|
mcpUrl,
|
|
495
|
+
neotoma_env: readNeotomaConfigEnvironment(),
|
|
402
496
|
});
|
|
403
497
|
});
|
|
404
498
|
// ============================================================================
|
|
@@ -1803,6 +1897,16 @@ app.use(async (req, res, next) => {
|
|
|
1803
1897
|
logger.info(`[Auth] ${req.method} ${req.path} auth_method=local_no_bearer user_id=${devUser.id}`);
|
|
1804
1898
|
return next();
|
|
1805
1899
|
}
|
|
1900
|
+
// Sandbox ephemeral session: resolve from cookie or Bearer token before
|
|
1901
|
+
// falling back to the shared public user. Expired/revoked sessions 401.
|
|
1902
|
+
if (isSandboxMode()) {
|
|
1903
|
+
const sessionInfo = resolveSessionFromRequest(req);
|
|
1904
|
+
if (sessionInfo) {
|
|
1905
|
+
req.authenticatedUserId = sessionInfo.userId;
|
|
1906
|
+
logger.info(`[Auth] ${req.method} ${req.path} auth_method=sandbox_session user_id=${sessionInfo.userId}`);
|
|
1907
|
+
return next();
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1806
1910
|
// Sandbox mode: public deployment at sandbox.neotoma.io where anonymous
|
|
1807
1911
|
// callers are attributed to SANDBOX_PUBLIC_USER_ID without a Bearer. AAuth
|
|
1808
1912
|
// still runs (earlier in the chain via aauthVerify) so agents exercising the
|
|
@@ -3187,6 +3291,43 @@ app.get("/recent_conversations", async (req, res) => {
|
|
|
3187
3291
|
return handleApiError(req, res, error, "Failed to list recent conversations", "DB_QUERY_FAILED", "APIError:recent_conversations");
|
|
3188
3292
|
}
|
|
3189
3293
|
});
|
|
3294
|
+
// GET /turns — Inspector: paginated conversation_turn index
|
|
3295
|
+
app.get("/turns", async (req, res) => {
|
|
3296
|
+
try {
|
|
3297
|
+
const userId = await getAuthenticatedUserId(req, req.query.user_id);
|
|
3298
|
+
const limit = parseInt(String(req.query.limit ?? "25"), 10) || 25;
|
|
3299
|
+
const offset = parseInt(String(req.query.offset ?? "0"), 10) || 0;
|
|
3300
|
+
const harness = typeof req.query.harness === "string" ? req.query.harness.trim() || null : null;
|
|
3301
|
+
const status = typeof req.query.status === "string" ? req.query.status.trim() || null : null;
|
|
3302
|
+
const activity_after = typeof req.query.activity_after === "string" ? req.query.activity_after.trim() || null : null;
|
|
3303
|
+
const activity_before = typeof req.query.activity_before === "string" ? req.query.activity_before.trim() || null : null;
|
|
3304
|
+
const result = listConversationTurns(userId, limit, offset, {
|
|
3305
|
+
harness,
|
|
3306
|
+
status,
|
|
3307
|
+
activity_after,
|
|
3308
|
+
activity_before,
|
|
3309
|
+
});
|
|
3310
|
+
return res.json(result);
|
|
3311
|
+
}
|
|
3312
|
+
catch (error) {
|
|
3313
|
+
return handleApiError(req, res, error, "Failed to list turns", "DB_QUERY_FAILED", "APIError:turns");
|
|
3314
|
+
}
|
|
3315
|
+
});
|
|
3316
|
+
// GET /turns/:turn_key — Inspector: conversation_turn detail
|
|
3317
|
+
app.get("/turns/:turn_key", async (req, res) => {
|
|
3318
|
+
try {
|
|
3319
|
+
const userId = await getAuthenticatedUserId(req, req.query.user_id);
|
|
3320
|
+
const turnKey = decodeURIComponent(req.params.turn_key);
|
|
3321
|
+
const result = getConversationTurn(userId, turnKey);
|
|
3322
|
+
if (!result) {
|
|
3323
|
+
return res.status(404).json({ error: "Turn not found", code: "NOT_FOUND" });
|
|
3324
|
+
}
|
|
3325
|
+
return res.json(result);
|
|
3326
|
+
}
|
|
3327
|
+
catch (error) {
|
|
3328
|
+
return handleApiError(req, res, error, "Failed to get turn", "DB_QUERY_FAILED", "APIError:turn_detail");
|
|
3329
|
+
}
|
|
3330
|
+
});
|
|
3190
3331
|
// GET /api/sources - Get source list (FU-301)
|
|
3191
3332
|
app.get("/sources", async (req, res) => {
|
|
3192
3333
|
try {
|
|
@@ -5665,6 +5806,16 @@ export async function startHTTPServer() {
|
|
|
5665
5806
|
}
|
|
5666
5807
|
// Initialize encryption service
|
|
5667
5808
|
await initServerKeys();
|
|
5809
|
+
// Seed `neotoma_feedback` schema unconditionally so local-only installs
|
|
5810
|
+
// (no AGENT_SITE_BASE_URL) can mirror feedback into the entity graph.
|
|
5811
|
+
try {
|
|
5812
|
+
const { seedNeotomaFeedbackSchema } = await import("./services/feedback/seed_schema.js");
|
|
5813
|
+
await seedNeotomaFeedbackSchema();
|
|
5814
|
+
logger.info("[Feedback] neotoma_feedback schema seeded");
|
|
5815
|
+
}
|
|
5816
|
+
catch (err) {
|
|
5817
|
+
logger.warn(`[Feedback] failed to seed neotoma_feedback schema: ${err.message}`);
|
|
5818
|
+
}
|
|
5668
5819
|
// Sandbox mode: ensure the `sandbox_abuse_report` entity type is registered
|
|
5669
5820
|
// before any report comes in so forwarded records can attach cleanly to the
|
|
5670
5821
|
// entity graph. Non-sandbox deployments still benefit from having the schema
|