negotium 0.1.7 → 0.1.9
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 +26 -2
- package/dist/canonical-mcp-bridge.js +14 -0
- package/dist/canonical-mcp-bridge.js.map +9 -0
- package/dist/chunk-10ct8814.js +429 -0
- package/dist/chunk-10ct8814.js.map +11 -0
- package/dist/chunk-18fzftjf.js +214 -0
- package/dist/chunk-18fzftjf.js.map +10 -0
- package/dist/chunk-1k7t7bnx.js +97 -0
- package/dist/chunk-1k7t7bnx.js.map +10 -0
- package/dist/chunk-1msje2zf.js +23 -0
- package/dist/chunk-1msje2zf.js.map +10 -0
- package/dist/chunk-2dgkah1z.js +706 -0
- package/dist/chunk-2dgkah1z.js.map +14 -0
- package/dist/chunk-3t4dtm26.js +61 -0
- package/dist/chunk-3t4dtm26.js.map +10 -0
- package/dist/chunk-3vn2vnj1.js +51 -0
- package/dist/chunk-3vn2vnj1.js.map +10 -0
- package/dist/chunk-6hknphtc.js +2054 -0
- package/dist/chunk-6hknphtc.js.map +29 -0
- package/dist/chunk-6tfzpcvk.js +2149 -0
- package/dist/chunk-6tfzpcvk.js.map +15 -0
- package/dist/chunk-8g0c4as0.js +23 -0
- package/dist/chunk-8g0c4as0.js.map +10 -0
- package/dist/chunk-9spx3h9n.js +279 -0
- package/dist/chunk-9spx3h9n.js.map +10 -0
- package/dist/chunk-c500xe9z.js +33 -0
- package/dist/chunk-c500xe9z.js.map +10 -0
- package/dist/chunk-e9b2xpff.js +147 -0
- package/dist/chunk-e9b2xpff.js.map +10 -0
- package/dist/chunk-edb07sjp.js +567 -0
- package/dist/chunk-edb07sjp.js.map +14 -0
- package/dist/chunk-eexqsee7.js +368 -0
- package/dist/chunk-eexqsee7.js.map +11 -0
- package/dist/chunk-efyxk97v.js +500 -0
- package/dist/chunk-efyxk97v.js.map +13 -0
- package/dist/chunk-epqxwkvh.js +3534 -0
- package/dist/chunk-epqxwkvh.js.map +20 -0
- package/dist/chunk-f1pzh3xh.js +115 -0
- package/dist/chunk-f1pzh3xh.js.map +10 -0
- package/dist/chunk-f5xgprg1.js +59 -0
- package/dist/chunk-f5xgprg1.js.map +10 -0
- package/dist/chunk-f97qybwk.js +584 -0
- package/dist/chunk-f97qybwk.js.map +11 -0
- package/dist/chunk-fvc7ncxd.js +252 -0
- package/dist/chunk-fvc7ncxd.js.map +10 -0
- package/dist/chunk-g7pvfj5e.js +574 -0
- package/dist/chunk-g7pvfj5e.js.map +14 -0
- package/dist/chunk-h8vmb9gf.js +315 -0
- package/dist/chunk-h8vmb9gf.js.map +10 -0
- package/dist/chunk-hgw7n8cm.js +43 -0
- package/dist/chunk-hgw7n8cm.js.map +10 -0
- package/dist/chunk-hsq7j9jk.js +126 -0
- package/dist/chunk-hsq7j9jk.js.map +10 -0
- package/dist/chunk-hwy3n31w.js +21 -0
- package/dist/chunk-hwy3n31w.js.map +9 -0
- package/dist/chunk-j0kjscb7.js +53 -0
- package/dist/chunk-j0kjscb7.js.map +10 -0
- package/dist/chunk-jzz91jwh.js +70 -0
- package/dist/chunk-jzz91jwh.js.map +10 -0
- package/dist/chunk-k9z47mab.js +247 -0
- package/dist/chunk-k9z47mab.js.map +12 -0
- package/dist/chunk-kbm35t7w.js +1331 -0
- package/dist/chunk-kbm35t7w.js.map +16 -0
- package/dist/chunk-m4veth0c.js +1649 -0
- package/dist/chunk-m4veth0c.js.map +17 -0
- package/dist/chunk-mhty3nna.js +150 -0
- package/dist/chunk-mhty3nna.js.map +11 -0
- package/dist/chunk-mmgh670x.js +248 -0
- package/dist/chunk-mmgh670x.js.map +11 -0
- package/dist/chunk-n0kk5xbq.js +2358 -0
- package/dist/chunk-n0kk5xbq.js.map +26 -0
- package/dist/chunk-nmv41qx9.js +2847 -0
- package/dist/chunk-nmv41qx9.js.map +23 -0
- package/dist/chunk-nqdp52aj.js +113 -0
- package/dist/chunk-nqdp52aj.js.map +12 -0
- package/dist/chunk-ns2m25dz.js +139 -0
- package/dist/chunk-ns2m25dz.js.map +10 -0
- package/dist/chunk-pkdmypdd.js +147 -0
- package/dist/chunk-pkdmypdd.js.map +11 -0
- package/dist/chunk-rmyfmrrr.js +1634 -0
- package/dist/chunk-rmyfmrrr.js.map +15 -0
- package/dist/chunk-sa874j5c.js +9 -0
- package/dist/chunk-sa874j5c.js.map +10 -0
- package/dist/chunk-sgsz2fpq.js +289 -0
- package/dist/chunk-sgsz2fpq.js.map +10 -0
- package/dist/chunk-sn51wnf1.js +3020 -0
- package/dist/chunk-sn51wnf1.js.map +32 -0
- package/dist/chunk-sqhr1a7e.js +587 -0
- package/dist/chunk-sqhr1a7e.js.map +11 -0
- package/dist/chunk-tmj5r2zv.js +2933 -0
- package/dist/chunk-tmj5r2zv.js.map +20 -0
- package/dist/chunk-vefj3880.js +56 -0
- package/dist/chunk-vefj3880.js.map +10 -0
- package/dist/chunk-vp2db42d.js +121 -0
- package/dist/chunk-vp2db42d.js.map +10 -0
- package/dist/chunk-wry79q55.js +108 -0
- package/dist/chunk-wry79q55.js.map +10 -0
- package/dist/chunk-x1arr7ze.js +63 -0
- package/dist/chunk-x1arr7ze.js.map +10 -0
- package/dist/chunk-z2xy5n2q.js +103 -0
- package/dist/chunk-z2xy5n2q.js.map +10 -0
- package/dist/hosted-agent.js +53 -0
- package/dist/hosted-agent.js.map +10 -0
- package/dist/main.js +132 -20
- package/dist/main.js.map +4 -4
- package/dist/runtime/scripts/faster-whisper-wrapper.py +87 -0
- package/dist/runtime/scripts/mcp-patchright-http.mjs +349 -0
- package/dist/runtime/src/agents/api-topic-agent-switch.ts +178 -0
- package/dist/runtime/src/agents/archiver.ts +396 -0
- package/dist/runtime/src/agents/auth-check.ts +64 -0
- package/dist/runtime/src/agents/claude-provider.ts +737 -0
- package/dist/runtime/src/agents/claude-registry.ts +115 -0
- package/dist/runtime/src/agents/codex-native-multi-agent.ts +177 -0
- package/dist/runtime/src/agents/codex-provider.ts +785 -0
- package/dist/runtime/src/agents/codex-registry.ts +115 -0
- package/dist/runtime/src/agents/codex-tree-kill.ts +365 -0
- package/dist/runtime/src/agents/contracts.ts +69 -0
- package/dist/runtime/src/agents/deep-map.ts +13 -0
- package/dist/runtime/src/agents/execution-host.ts +91 -0
- package/dist/runtime/src/agents/fixtures/claude-attachments.jsonl +2 -0
- package/dist/runtime/src/agents/fixtures/codex-shell.jsonl +5 -0
- package/dist/runtime/src/agents/fork.ts +65 -0
- package/dist/runtime/src/agents/hosted-agent.ts +49 -0
- package/dist/runtime/src/agents/idle-archiver.ts +128 -0
- package/dist/runtime/src/agents/index.ts +205 -0
- package/dist/runtime/src/agents/maestro-provider.ts +206 -0
- package/dist/runtime/src/agents/maestro-registry.ts +31 -0
- package/dist/runtime/src/agents/mcp-tools/ask-user.ts +280 -0
- package/dist/runtime/src/agents/mcp-tools/common.ts +21 -0
- package/dist/runtime/src/agents/mcp-tools/self-config.ts +220 -0
- package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +525 -0
- package/dist/runtime/src/agents/mcp-tools/visuals.ts +88 -0
- package/dist/runtime/src/agents/model-catalog.ts +151 -0
- package/dist/runtime/src/agents/registry.ts +24 -0
- package/dist/runtime/src/agents/rollout/claude.ts +331 -0
- package/dist/runtime/src/agents/rollout/codex.ts +569 -0
- package/dist/runtime/src/agents/rollout/shared.ts +205 -0
- package/dist/runtime/src/agents/self-config-core.ts +415 -0
- package/dist/runtime/src/agents/task-events.ts +44 -0
- package/dist/runtime/src/agents/tool-format.ts +352 -0
- package/dist/runtime/src/agents/topic-agent-switch.ts +193 -0
- package/dist/runtime/src/agents/topic-cleanup.ts +231 -0
- package/dist/runtime/src/agents/vault-tool-policy.ts +33 -0
- package/dist/runtime/src/application/execute-external-user-turn.ts +23 -0
- package/dist/runtime/src/application/submit-user-message.ts +50 -0
- package/dist/runtime/src/application/switch-topic-effort.ts +48 -0
- package/dist/runtime/src/application/switch-topic-model.ts +79 -0
- package/dist/runtime/src/application/topic-service.ts +139 -0
- package/dist/runtime/src/application/vault-command.ts +170 -0
- package/dist/runtime/src/bus.ts +319 -0
- package/dist/runtime/src/index.ts +333 -0
- package/dist/runtime/src/mcp/agent-health-server.ts +309 -0
- package/dist/runtime/src/mcp/background-bash-server.ts +346 -0
- package/dist/runtime/src/mcp/canonical-bridge-config.ts +84 -0
- package/dist/runtime/src/mcp/canonical-proxy-server.ts +171 -0
- package/dist/runtime/src/mcp/dm-ipc.ts +60 -0
- package/dist/runtime/src/mcp/mcp-helpers.ts +40 -0
- package/dist/runtime/src/mcp/paddleocr-server.ts +105 -0
- package/dist/runtime/src/mcp/peer-bridge.ts +114 -0
- package/dist/runtime/src/mcp/runtime-spec.ts +137 -0
- package/dist/runtime/src/mcp/session-comm/bridge-ipc-config.ts +27 -0
- package/dist/runtime/src/mcp/session-comm/peer-forward.ts +165 -0
- package/dist/runtime/src/mcp/session-comm/runtime.ts +115 -0
- package/dist/runtime/src/mcp/session-comm/server.ts +649 -0
- package/dist/runtime/src/mcp/session-comm/topic-config.ts +131 -0
- package/dist/runtime/src/mcp/session-comm/topics.ts +229 -0
- package/dist/runtime/src/mcp/stdio-protect.ts +84 -0
- package/dist/runtime/src/mcp/system-health-server.ts +130 -0
- package/dist/runtime/src/mcp/task-server.ts +173 -0
- package/dist/runtime/src/mcp/token-stats-server.ts +98 -0
- package/dist/runtime/src/mcp/vault-http.ts +219 -0
- package/dist/runtime/src/mcp/vault-run.ts +153 -0
- package/dist/runtime/src/mcp/vault-server.ts +104 -0
- package/dist/runtime/src/mcp/wiki-server.ts +755 -0
- package/dist/runtime/src/media/file-events.ts +33 -0
- package/dist/runtime/src/media/text-extractor.ts +312 -0
- package/dist/runtime/src/media/video.ts +92 -0
- package/dist/runtime/src/outbox/file-ops.ts +210 -0
- package/dist/runtime/src/outbox/index.ts +8 -0
- package/dist/runtime/src/outbox/utils.ts +51 -0
- package/dist/runtime/src/platform/background-bash/manager.ts +279 -0
- package/dist/runtime/src/platform/config.ts +371 -0
- package/dist/runtime/src/platform/constants.ts +18 -0
- package/dist/runtime/src/platform/debug-users.ts +14 -0
- package/dist/runtime/src/platform/delay.ts +1 -0
- package/dist/runtime/src/platform/error.ts +9 -0
- package/dist/runtime/src/platform/file-utils.ts +16 -0
- package/dist/runtime/src/platform/jsonl.ts +202 -0
- package/dist/runtime/src/platform/lifecycle.ts +174 -0
- package/dist/runtime/src/platform/logger.ts +31 -0
- package/dist/runtime/src/platform/maestro-bootstrap-env.ts +3 -0
- package/dist/runtime/src/platform/mcp-config.ts +878 -0
- package/dist/runtime/src/platform/modules.ts +145 -0
- package/dist/runtime/src/platform/node-plugins.ts +46 -0
- package/dist/runtime/src/platform/playwright/manager.ts +1233 -0
- package/dist/runtime/src/prompts/agents/wiki-archiver.md +261 -0
- package/dist/runtime/src/prompts/builders.ts +368 -0
- package/dist/runtime/src/prompts/sessions/channel-system.md +48 -0
- package/dist/runtime/src/prompts/sessions/manager-system.md +37 -0
- package/dist/runtime/src/prompts/sessions/topic-system.md +47 -0
- package/dist/runtime/src/prompts/sessions/visual-design.md +86 -0
- package/dist/runtime/src/query/active-rooms.ts +534 -0
- package/dist/runtime/src/query/control.ts +313 -0
- package/dist/runtime/src/query/index.ts +24 -0
- package/dist/runtime/src/query/session-inbox-cleanup.ts +44 -0
- package/dist/runtime/src/query/session-inbox-path.ts +42 -0
- package/dist/runtime/src/query/state.ts +94 -0
- package/dist/runtime/src/query/types.ts +83 -0
- package/dist/runtime/src/runtime/ask-callbacks.ts +153 -0
- package/dist/runtime/src/runtime/attachments.ts +151 -0
- package/dist/runtime/src/runtime/background-sessions.ts +83 -0
- package/dist/runtime/src/runtime/channel-context.ts +107 -0
- package/dist/runtime/src/runtime/errors.ts +69 -0
- package/dist/runtime/src/runtime/event-heartbeat.ts +77 -0
- package/dist/runtime/src/runtime/file-hooks.ts +81 -0
- package/dist/runtime/src/runtime/footer.ts +23 -0
- package/dist/runtime/src/runtime/inbox.ts +946 -0
- package/dist/runtime/src/runtime/tasks.ts +56 -0
- package/dist/runtime/src/runtime/topic-config.ts +29 -0
- package/dist/runtime/src/runtime/turn-runner.ts +2298 -0
- package/dist/runtime/src/runtime/usage-alert.ts +84 -0
- package/dist/runtime/src/runtime/visual-html.ts +109 -0
- package/dist/runtime/src/runtime/visual-store.ts +430 -0
- package/dist/runtime/src/runtime/visuals.ts +171 -0
- package/dist/runtime/src/security/sanitize.ts +27 -0
- package/dist/runtime/src/security/sensitive-path.ts +34 -0
- package/dist/runtime/src/storage/activity-log.ts +152 -0
- package/dist/runtime/src/storage/api-messages.ts +617 -0
- package/dist/runtime/src/storage/api-topic-brief.ts +127 -0
- package/dist/runtime/src/storage/api-topic-config.ts +192 -0
- package/dist/runtime/src/storage/api-topics.ts +844 -0
- package/dist/runtime/src/storage/app-settings.ts +44 -0
- package/dist/runtime/src/storage/browser-profiles.ts +133 -0
- package/dist/runtime/src/storage/conversations.ts +268 -0
- package/dist/runtime/src/storage/forum/agent-settings.ts +20 -0
- package/dist/runtime/src/storage/forum/index.ts +4 -0
- package/dist/runtime/src/storage/forum/repository.ts +283 -0
- package/dist/runtime/src/storage/forum/schema.ts +300 -0
- package/dist/runtime/src/storage/forum-db.ts +23 -0
- package/dist/runtime/src/storage/runtime-events.ts +130 -0
- package/dist/runtime/src/storage/runtime-leases.ts +189 -0
- package/dist/runtime/src/storage/runtime-process-leases.ts +216 -0
- package/dist/runtime/src/storage/runtime-topic-state.ts +148 -0
- package/dist/runtime/src/storage/runtime-turn-requests.ts +301 -0
- package/dist/runtime/src/storage/self-schedules.ts +298 -0
- package/dist/runtime/src/storage/session-asks.ts +347 -0
- package/dist/runtime/src/storage/sqlite.ts +129 -0
- package/dist/runtime/src/storage/tasks.ts +179 -0
- package/dist/runtime/src/storage/token-stats.ts +144 -0
- package/dist/runtime/src/storage/topic-archive-state.ts +59 -0
- package/dist/runtime/src/storage/topic-archive.ts +74 -0
- package/dist/runtime/src/storage/topic-settings.ts +67 -0
- package/dist/runtime/src/storage/topic-transcript.ts +98 -0
- package/dist/runtime/src/storage/vault-crypto.ts +65 -0
- package/dist/runtime/src/storage/vault.ts +225 -0
- package/dist/runtime/src/storage/wiki-summary-names.ts +33 -0
- package/dist/runtime/src/storage/wiki.ts +85 -0
- package/dist/runtime/src/topics/create.ts +123 -0
- package/dist/runtime/src/topics/derive.ts +398 -0
- package/dist/runtime/src/topics/lifecycle.ts +311 -0
- package/dist/runtime/src/topics/links.ts +7 -0
- package/dist/runtime/src/topics/personal-general.ts +65 -0
- package/dist/runtime/src/topics/session.ts +381 -0
- package/dist/runtime/src/types/api.ts +497 -0
- package/dist/runtime/src/types.ts +257 -0
- package/dist/runtime/src/version.ts +1 -0
- package/dist/runtime/tsconfig.json +13 -0
- package/package.json +28 -3
- package/dist/main.d.ts +0 -3
- package/dist/main.d.ts.map +0 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../packages/node/src/index.ts", "../../../packages/mcp/src/node-tools.ts", "../../../packages/mcp/src/server.ts", "../../../packages/mcp/src/sse-transport.ts", "../../../packages/node/src/control.ts", "../../../packages/node/src/files.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Node bootstrap shared by `negotium chat` and `negotium serve`.\n *\n * Starting a node means: bind the single open port (runtime MCP endpoint),\n * tell core which port agents should dial back to, start the session-inbox\n * consumer that drains ask/tell/abort queues, bring up the node's assigned\n * MCP servers (manifest → running instances → agent turn catalog), and\n * register shutdown cleanup so agent/browser/bash children never outlive the\n * node. External integrations mount extra routes on the same port through\n * core's registerNodeRequestHandler (plugin chain ahead of the MCP handler).\n */\n\nimport { createServer } from \"node:net\";\nimport {\n abortAllRooms,\n acquireRuntimeProcessLease,\n DATA_DIR,\n killAllBgBash,\n killAllPlaywright,\n killOwnedCodexTreesForShutdown,\n logger,\n NEGOTIUM_PORT,\n type NegotiumNodeModule,\n type NodeMcpEntry,\n nodeRequestHandlerNames,\n onShutdown,\n RUN_DIR,\n runNodeRequestHandlers,\n runShutdown,\n runtimeBus,\n STATE_DIR,\n type StartedNegotiumNodeModules,\n setFileHooks,\n setNodeMcpServers,\n setRuntimeMcpPort,\n startDurableTurnRequestWorker,\n startNegotiumNodeModules,\n startSessionInboxWorker,\n sweepStaleSubagentCards,\n WORKSPACE_DIR,\n} from \"@negotium/core\";\nimport { handleNegotiumMcpRequest } from \"@negotium/mcp\";\nimport { McpHost, McpManifest } from \"@negotium/mcp-host\";\nimport {\n createNodeControlHandler,\n NODE_CONTROL_PROTOCOL_VERSION,\n NODE_DAEMON_ROLE,\n removeNodeDaemonInfo,\n writeNodeDaemonInfo,\n} from \"./control\";\nimport { nodeFileStore } from \"./files\";\n\nexport type {\n NodeDaemonConnection,\n NodeDaemonInfo,\n NodeDaemonStatus,\n} from \"./control\";\nexport {\n inspectNodeDaemon,\n NODE_CONTROL_BASE_PATH,\n NODE_CONTROL_PROTOCOL_VERSION,\n NODE_DAEMON_INFO_PATH,\n readNodeDaemonInfo,\n stopNodeDaemon,\n waitForNodeDaemon,\n} from \"./control\";\n\nexport interface NodeHandle {\n port: number;\n /** Settles after every registered node cleanup handler has completed. */\n completed: Promise<void>;\n stop: () => Promise<void>;\n}\n\nexport interface StartNodeOptions {\n port?: number;\n /** Reject request bodies above this size before route handlers parse them. */\n maxRequestBodySize?: number;\n modules?: readonly NegotiumNodeModule[];\n /** Publish this node as the state directory's client-connectable process. */\n advertise?: boolean;\n /** Refuse to start while another healthy node owns this state directory. */\n singleton?: boolean;\n}\n\nasync function availableLoopbackPort(): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n const probe = createServer();\n probe.unref();\n probe.once(\"error\", reject);\n probe.listen(0, \"127.0.0.1\", () => {\n const address = probe.address();\n const port = address && typeof address === \"object\" ? address.port : 0;\n probe.close((error) => {\n if (error) reject(error);\n else if (port > 0) resolve(port);\n else reject(new Error(\"failed to allocate a loopback port\"));\n });\n });\n });\n}\n\n/**\n * Resolve the node's MCP manifest into live servers and install them into the\n * agent-turn catalog. Long-lived http servers are ensured (spawned + port\n * allocated) via mcp-host; stdio specs pass through as launch commands.\n * Best-effort per entry — one broken server must not block the node.\n */\nasync function wireNodeMcps(host: McpHost, manifest: McpManifest): Promise<void> {\n const entries: NodeMcpEntry[] = [];\n for (const spec of manifest.list()) {\n if (!manifest.isEnabled(spec.key)) continue;\n try {\n if (spec.transport === \"http\") {\n const instance = await host.ensure(spec.key);\n if (!instance.port) throw new Error(\"no port allocated\");\n entries.push({ key: spec.key, kind: \"http\", port: instance.port });\n } else {\n entries.push({\n key: spec.key,\n kind: \"stdio\",\n command: spec.command,\n args: spec.args,\n ...(spec.env ? { env: spec.env } : {}),\n });\n }\n } catch (err) {\n logger.warn({ err, key: spec.key }, \"node mcp: failed to bring up manifest server\");\n }\n }\n setNodeMcpServers(entries);\n if (entries.length > 0) {\n logger.info({ keys: entries.map((e) => e.key) }, \"node mcp: manifest servers installed\");\n }\n}\n\nexport function startNode(opts: StartNodeOptions = {}): NodeHandle {\n sweepStaleSubagentCards();\n setFileHooks(nodeFileStore.hooks);\n const startedAt = new Date().toISOString();\n const processLease = opts.singleton\n ? acquireRuntimeProcessLease(NODE_DAEMON_ROLE, {\n onLost: () => {\n logger.error(\"node daemon: singleton lease lost; shutting down\");\n void runShutdown(\"test\");\n },\n })\n : null;\n if (opts.singleton && !processLease) {\n throw new Error(`a Negotium node is already running for ${STATE_DIR}`);\n }\n\n let requestStop = () => {\n void runShutdown(\"test\");\n };\n let server: ReturnType<typeof Bun.serve>;\n const control = createNodeControlHandler({\n port: () => server?.port ?? 0,\n startedAt,\n requestShutdown: () => requestStop(),\n });\n try {\n server = Bun.serve({\n port: opts.port ?? NEGOTIUM_PORT,\n hostname: \"127.0.0.1\",\n idleTimeout: 240,\n ...(opts.maxRequestBodySize ? { maxRequestBodySize: opts.maxRequestBodySize } : {}),\n async fetch(req) {\n const controlResponse = await control(req);\n if (controlResponse) return controlResponse;\n // External integrations (otium worker, future peers, webhooks) mount\n // ahead of the built-in routes via registerNodeRequestHandler.\n const plugin = await runNodeRequestHandlers(req);\n if (plugin) return plugin;\n const mcp = await handleNegotiumMcpRequest(req);\n if (mcp) return mcp;\n const url = new URL(req.url);\n if (url.pathname === \"/health\") {\n return Response.json({\n ok: true,\n name: \"negotium\",\n pid: process.pid,\n protocolVersion: NODE_CONTROL_PROTOCOL_VERSION,\n stateDir: STATE_DIR,\n });\n }\n return new Response(\"negotium node\", { status: 404 });\n },\n });\n } catch (error) {\n processLease?.stop();\n throw error;\n }\n const port = server.port;\n if (!port) {\n processLease?.stop();\n throw new Error(\"negotium node failed to bind a port\");\n }\n setRuntimeMcpPort(port);\n const stopTurnRequests = startDurableTurnRequestWorker();\n const stopInbox = startSessionInboxWorker();\n let modules: StartedNegotiumNodeModules;\n try {\n modules = startNegotiumNodeModules(opts.modules ?? [], {\n port,\n stateDir: STATE_DIR,\n dataDir: DATA_DIR,\n runDir: RUN_DIR,\n workspaceDir: WORKSPACE_DIR,\n bus: runtimeBus(),\n });\n } catch (error) {\n stopTurnRequests();\n stopInbox();\n server.stop(true);\n processLease?.stop();\n throw error;\n }\n\n // Node-assigned MCPs come up in the background — turns that start before\n // they're ready simply run without them for that turn.\n const mcpHost = new McpHost();\n const manifest = new McpManifest();\n const stopSweeper = mcpHost.startSweeper();\n\n let resolveCompleted!: () => void;\n const completed = new Promise<void>((resolve) => {\n resolveCompleted = resolve;\n });\n let advertised: ReturnType<typeof writeNodeDaemonInfo> | null = null;\n try {\n advertised = opts.advertise ? writeNodeDaemonInfo(port, startedAt) : null;\n } catch (error) {\n stopSweeper();\n stopTurnRequests();\n stopInbox();\n server.stop(true);\n processLease?.stop();\n void modules.stop();\n void mcpHost.stopAll();\n throw error;\n }\n void wireNodeMcps(mcpHost, manifest);\n\n // Priority convention (see core lifecycle.ts): 100 = graceful\n // network/queue closes, 50 = external-process reapers.\n onShutdown(\"node-server\", 130, () => {\n stopTurnRequests();\n stopInbox();\n server.stop(true);\n });\n if (advertised) {\n onShutdown(\"node-daemon-advertisement\", 129, () => {\n removeNodeDaemonInfo({ pid: advertised.pid, port: advertised.port });\n });\n }\n if (processLease) onShutdown(\"node-daemon-lease\", 128, () => processLease.stop());\n onShutdown(\"active-agent-turns\", 120, async () => {\n abortAllRooms();\n await killOwnedCodexTreesForShutdown();\n });\n onShutdown(\"node-modules\", 110, () => modules.stop());\n onShutdown(\"node-mcp-host\", 50, async () => {\n stopSweeper();\n await mcpHost.stopAll();\n });\n onShutdown(\"playwright\", 50, () => killAllPlaywright());\n onShutdown(\"background-bash\", 50, () => killAllBgBash());\n onShutdown(\"node-completed\", -100, resolveCompleted);\n\n logger.info(\n {\n port,\n stateDir: STATE_DIR,\n plugins: nodeRequestHandlerNames(),\n modules: modules.names,\n capabilities: modules.capabilities,\n },\n \"negotium node started\",\n );\n\n const stop = () => runShutdown(\"test\");\n requestStop = () => {\n void stop();\n };\n return {\n port,\n completed,\n // Manual stop routes through the same registry as SIGINT/SIGTERM so\n // cleanup never diverges between the two paths (idempotent once-guard).\n stop,\n };\n}\n\n/** Reference-host composition. Disabled modules are never imported. */\nexport async function startDefaultNode(\n opts: Omit<StartNodeOptions, \"modules\"> = {},\n): Promise<NodeHandle> {\n const modules: NegotiumNodeModule[] = [];\n if (process.env.NEGOTIUM_CRON !== \"0\") {\n const { createCronModule } = await import(\"@negotium/module-cron\");\n modules.push(createCronModule());\n }\n const port = opts.port === 0 ? await availableLoopbackPort() : opts.port;\n return startNode({ ...opts, port, modules });\n}\n\n/** Long-lived local node entry used by the CLI's detached child process. */\nexport async function runNodeDaemon(opts: { port?: number } = {}): Promise<void> {\n const node = await startDefaultNode({\n port: opts.port ?? 0,\n advertise: true,\n singleton: true,\n });\n await node.completed;\n}\n",
|
|
6
|
+
"/**\n * Node tools — the negotium MCP's headline tool surface for driving the\n * local runtime: create/list topics and abort/restart/delete lifecycle control.\n * Cross-topic messaging belongs exclusively to the session-comm MCP\n * (`tell_session` / `ask_session`) so there is one unambiguous contract.\n *\n * Every tool is keyed by `ctx.userId` from the verified per-turn token; a\n * caller can never see or touch another user's topics.\n */\n\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport {\n abortRoom,\n appendJsonlEntry,\n deleteTopicCascade,\n EFFORT_VALUES,\n type EffortLevel,\n errMsg,\n errorResult,\n getRoomQuery,\n getTopic,\n getTopicByNameForUser,\n getTopics,\n isParticipant,\n logger,\n type RuntimeMcpContext,\n registerTopic,\n restartTopicSession,\n sessionInboxPath,\n TopicArchiveRequiredError,\n type TopicDto,\n TopicValidationError,\n textResult,\n} from \"@negotium/core\";\nimport { z } from \"zod\";\n\n/**\n * Resolve a topic reference (canonical id or title) to a topic the calling\n * user participates in. Non-member topics resolve as \"not found\" so the\n * error never leaks another user's topic existence.\n */\nfunction resolveTopicForUser(\n ctx: RuntimeMcpContext,\n ref: string,\n): { topic: TopicDto } | { error: string } {\n const trimmed = ref.trim();\n if (!trimmed) return { error: \"Error: topic is required.\" };\n const notFound = `Error: topic '${trimmed}' not found (or not uniquely named). Use list_topics to see available topics.`;\n const byId = getTopic(trimmed);\n if (byId) {\n if (!isParticipant(byId, ctx.userId)) return { error: notFound };\n return { topic: byId };\n }\n const byTitle = getTopicByNameForUser(trimmed, ctx.userId);\n if (byTitle) return { topic: byTitle };\n return { error: notFound };\n}\n\nfunction describeTopic(topic: TopicDto): string {\n const running = getRoomQuery(topic.id) ? \"turn running\" : \"idle\";\n const agent = topic.agent ? `agent: ${topic.agent}` : \"no agent\";\n return `- \"${topic.title}\" (id: ${topic.id}, kind: ${topic.kind ?? \"agent\"}, ${agent}, ${running})`;\n}\n\nexport function registerNodeTools(server: McpServer, ctx: RuntimeMcpContext): void {\n server.tool(\n \"register_topic\",\n \"Create a new topic (agent room) on this negotium node, owned by the calling user. \" +\n \"Returns the new topic's id, title, agent, and model. Use the session-comm tell_session \" +\n \"tool to hand the new topic work.\",\n {\n title: z.string().describe(\"Unique title for the new topic.\"),\n agent: z\n .enum([\"claude\", \"codex\", \"maestro\"])\n .optional()\n .describe(\"AI backend for the room. Defaults to maestro.\"),\n model: z.string().optional().describe(\"Model override, must be valid for the agent.\"),\n effort: z.enum(EFFORT_VALUES).optional().describe(\"Reasoning effort override for the room.\"),\n description: z.string().optional().describe(\"Short description of the topic's purpose.\"),\n },\n async ({ title, agent, model, effort, description }) => {\n try {\n const topic = registerTopic({\n title,\n userId: ctx.userId,\n agent,\n model,\n effort: effort as EffortLevel | undefined,\n description,\n });\n return textResult(\n [\n `Topic registered.`,\n `id: ${topic.id}`,\n `title: ${topic.title}`,\n `agent: ${topic.agent ?? \"none\"}`,\n `model: ${topic.defaultModel}`,\n ].join(\"\\n\"),\n );\n } catch (err) {\n if (err instanceof TopicValidationError) return errorResult(`Error: ${err.message}`);\n logger.error({ err, title }, \"negotium MCP: register_topic failed\");\n return errorResult(`Error: failed to register topic: ${errMsg(err)}`);\n }\n },\n );\n\n server.tool(\n \"list_topics\",\n \"List the calling user's topics on this negotium node: title, id, kind, agent, and whether a turn is currently running.\",\n {},\n async () => {\n const topics = getTopics().filter((topic) => isParticipant(topic, ctx.userId));\n if (topics.length === 0) {\n return textResult(\"No topics found. Use register_topic to create one.\");\n }\n return textResult(topics.map(describeTopic).join(\"\\n\"));\n },\n );\n\n server.tool(\n \"abort_topic\",\n \"Abort the running turn in another topic on this node. Fire-and-forget: also queues an abort \" +\n \"signal in the topic's inbox so a turn that has not started yet is cancelled too. Returns whether an active turn was aborted.\",\n {\n topic: z.string().describe(\"Target topic title or id.\"),\n },\n async ({ topic }) => {\n const resolved = resolveTopicForUser(ctx, topic);\n if (\"error\" in resolved) return errorResult(resolved.error);\n const target = resolved.topic;\n if (target.id === ctx.topicId) {\n return errorResult(\"Error: cannot abort the current topic from within its own turn.\");\n }\n\n const aborted = abortRoom(target.id);\n try {\n // Cover the not-yet-started case: the inbox consumer drops queued work\n // when it sees the abort entry.\n appendJsonlEntry(sessionInboxPath(ctx.userId, target.id), {\n type: \"abort\",\n timestamp: new Date().toISOString(),\n });\n } catch (err) {\n logger.warn({ err, topicId: target.id }, \"negotium MCP: abort inbox write failed\");\n }\n return textResult(\n aborted\n ? `Aborted the active turn in \"${target.title}\".`\n : `No active turn in \"${target.title}\"; abort signal queued in its inbox.`,\n );\n },\n );\n\n server.tool(\n \"restart_topic\",\n \"Reset another topic's AI context while preserving the topic and its visible message history. \" +\n \"The next message starts a fresh provider session. Only the topic owner can restart it.\",\n {\n topic: z.string().describe(\"Target topic title or id.\"),\n },\n async ({ topic }) => {\n const resolved = resolveTopicForUser(ctx, topic);\n if (\"error\" in resolved) return errorResult(resolved.error);\n const target = resolved.topic;\n if (target.id === ctx.topicId) {\n return errorResult(\"Error: cannot restart the current topic from within its own turn.\");\n }\n if (target.kind === \"manager\") {\n return errorResult(\"Error: manager rooms are system-managed and cannot be restarted.\");\n }\n\n const result = await restartTopicSession(target.id, ctx.userId, \"runtime-mcp-session-reset\");\n return result.isError ? errorResult(`Error: ${result.text}`) : textResult(result.text);\n },\n );\n\n server.tool(\n \"delete_topic\",\n \"Delete a topic owned by the calling user after archiving its conversation history. Deletion is blocked \" +\n \"when the archive cannot be written; pass force: true only as an explicit escape hatch that accepts losing history.\",\n {\n topic: z.string().describe(\"Target topic title or id.\"),\n force: z\n .boolean()\n .optional()\n .describe(\"Delete even if archiving the conversation history fails. Default false.\"),\n },\n async ({ topic, force }) => {\n const resolved = resolveTopicForUser(ctx, topic);\n if (\"error\" in resolved) return errorResult(resolved.error);\n const target = resolved.topic;\n if (target.id === ctx.topicId) {\n return errorResult(\"Error: cannot delete the current topic from within its own turn.\");\n }\n if (target.kind === \"manager\") {\n return errorResult(\"Error: manager rooms are system-managed and cannot be deleted.\");\n }\n const isOwner = target.participants.some(\n (participant) => participant.userId === ctx.userId && participant.role === \"owner\",\n );\n if (!isOwner) {\n return errorResult(\"Error: only the topic owner can delete it.\");\n }\n\n try {\n await deleteTopicCascade(target, ctx.userId, { force: force === true });\n } catch (err) {\n if (err instanceof TopicArchiveRequiredError) {\n return errorResult(\n [\n `Error: deleting \"${target.title}\" was blocked because its conversation history could not be archived.`,\n \"Topics are archived before deletion so no history is lost. Fix the archive failure and retry, or pass force: true to delete anyway and accept losing the history.\",\n ].join(\"\\n\"),\n );\n }\n logger.error({ err, topicId: target.id }, \"negotium MCP: delete_topic failed\");\n return errorResult(`Error: failed to delete \"${target.title}\": ${errMsg(err)}`);\n }\n return textResult(`Topic \"${target.title}\" (id: ${target.id}) deleted.`);\n },\n );\n}\n",
|
|
7
|
+
"/**\n * The negotium runtime MCP server — the single HTTP MCP endpoint that makes a\n * machine's negotium runtime usable by any agent host.\n *\n * This is a module, not a standalone process: the runtime host (CLI daemon,\n * Telegram bot, …) imports `handleNegotiumMcpRequest` and mounts it on its\n * Bun.serve. Agents connect back with per-turn signed tokens issued by\n * `@negotium/core`'s `buildRuntimeMcpSpec`, so the MCP layer never trusts the\n * agent to say who it is.\n *\n * Ported from otium runtime-api `mcp/runtime-server.ts`; placement adapters\n * may install peer-bridge handlers for canonical hub mutations (currently\n * spawn_subagent), while token/spec logic lives in `@negotium/core`.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { realpathSync, statSync } from \"node:fs\";\nimport { basename, extname, resolve } from \"node:path\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { WebStandardStreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js\";\nimport {\n appendApiMessage,\n appendJsonlEntry,\n createAskUserToolDefinition,\n createSelfConfigToolDefinitions,\n createSpawnSubagentToolDefinition,\n createSubagentManagementToolDefinitions,\n dispatchPeerRuntimeAskUser,\n dispatchPeerRuntimeFile,\n dispatchPeerRuntimeSelfConfig,\n dispatchPeerRuntimeSpawn,\n errorResult,\n FROM_AUTO_CONTINUE,\n getApiTopicConfig,\n getTopic,\n isSensitivePath,\n logger,\n type MessageDto,\n RUNTIME_MCP_BASE_PATH,\n RUNTIME_MCP_KEY,\n type RuntimeMcpContext,\n resolveRuntimeMcpToken,\n type SelfConfigContext,\n sessionInboxPath,\n storeLocalFileAsUpload,\n textResult,\n visualToolDefinitions,\n WsHub,\n} from \"@negotium/core\";\nimport { z } from \"zod\";\nimport { registerNodeTools } from \"#node-tools\";\nimport { SseTransport } from \"#sse-transport\";\n\nconst SSE_PATH = `${RUNTIME_MCP_BASE_PATH}/sse`;\nconst SSE_MESSAGE_PATH = `${RUNTIME_MCP_BASE_PATH}/message`;\nconst STREAMABLE_PATH = `${RUNTIME_MCP_BASE_PATH}/mcp`;\n\nconst sseSessions = new Map<\n string,\n { token: string; transport: SseTransport; server: McpServer }\n>();\nconst streamableSessions = new Map<\n string,\n { token: string; transport: WebStandardStreamableHTTPServerTransport; server: McpServer }\n>();\n\nfunction requireTopicAccess(\n ctx: RuntimeMcpContext,\n): { topic: NonNullable<ReturnType<typeof getTopic>> } | { error: string } {\n const topic = getTopic(ctx.topicId);\n if (!topic) return { error: `Error: topic '${ctx.topicId}' not found.` };\n if (!topic.participants.some((p: { userId: string }) => p.userId === ctx.userId)) {\n return { error: \"Error: user is not a member of this topic.\" };\n }\n return { topic };\n}\n\nfunction isPathInside(baseDir: string, filePath: string): boolean {\n const cwd = resolve(baseDir);\n const normalized = resolve(filePath);\n try {\n const realCwd = realpathSync(cwd);\n const real = realpathSync(normalized);\n return real === realCwd || real.startsWith(`${realCwd}/`);\n } catch (err) {\n // For a missing output, preserve the precise not-found error only when its\n // lexical path is inside the workspace. Existing paths use realpath above\n // so symlink escapes and platform aliases are handled correctly.\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return normalized === cwd || normalized.startsWith(`${cwd}/`);\n }\n return false;\n }\n}\n\nfunction localFileInfo(ctx: RuntimeMcpContext, filePath: string) {\n if (!isPathInside(ctx.cwd, filePath)) {\n return { error: \"Access denied. File must be within the topic workspace.\" };\n }\n const normalizedPath = resolve(filePath);\n if (isSensitivePath(normalizedPath)) {\n return { error: \"Access denied. Path matches the sensitive-file blacklist.\" };\n }\n try {\n const stats = statSync(normalizedPath);\n if (!stats.isFile()) return { error: `${filePath} is not a file` };\n return {\n normalizedPath,\n name: basename(filePath),\n ext: extname(filePath).toLowerCase(),\n sizeBytes: stats.size,\n sizeMB: (stats.size / (1024 * 1024)).toFixed(2),\n };\n } catch {\n return { error: `File not found at ${filePath}` };\n }\n}\n\nasync function deliverFile(ctx: RuntimeMcpContext, filePath: string) {\n const access = requireTopicAccess(ctx);\n if (\"error\" in access) return errorResult(access.error);\n\n const info = localFileInfo(ctx, filePath);\n if (\"error\" in info) return errorResult(`Error: ${info.error}`);\n\n if (ctx.peerBridge) {\n const bridged = await dispatchPeerRuntimeFile({\n bridge: ctx.peerBridge,\n userId: ctx.userId,\n agent: ctx.agent,\n model: ctx.model,\n path: info.normalizedPath,\n source: \"runtime.send_file\",\n });\n if (!bridged) return errorResult(\"Error: the peer file bridge is not available on this node.\");\n if (!bridged.ok) return errorResult(`Error: Failed to send file on hub: ${bridged.error}`);\n return textResult(\n `File sent to chat: ${info.name} (${info.ext || \"no extension\"}, ${info.sizeMB} MB)`,\n );\n }\n\n const attachment = storeLocalFileAsUpload(info.normalizedPath, {\n ownerUserId: ctx.userId,\n topicId: ctx.topicId,\n });\n if (!attachment) return errorResult(\"Error: Failed to store file for delivery.\");\n\n const cfg = getApiTopicConfig(ctx.topicId);\n const msg: MessageDto = {\n id: randomUUID(),\n topicId: ctx.topicId,\n authorId: \"ai\",\n text: `📎 ${attachment.filename}`,\n agentType: ctx.agent,\n model: ctx.model ?? cfg?.model ?? \"unknown\",\n attachments: [attachment],\n createdAt: new Date().toISOString(),\n };\n appendApiMessage(msg);\n WsHub.get().broadcastMessage(ctx.topicId, msg);\n\n return textResult(\n [\n `File sent to chat: ${info.name} (${info.ext || \"no extension\"}, ${info.sizeMB} MB)`,\n `Path: ${info.normalizedPath}`,\n ].join(\"\\n\"),\n );\n}\n\nfunction appendAutoContinue(ctx: RuntimeMcpContext, field: \"agent\" | \"model\" | \"effort\") {\n if (!ctx.autoContinue) return;\n appendJsonlEntry(sessionInboxPath(ctx.userId, ctx.topicId), {\n type: \"tell\",\n from: FROM_AUTO_CONTINUE,\n message: `The topic ${field} setting changed. Continue the user's previous work with the new configuration.`,\n depth: 0,\n silent: false,\n timestamp: new Date().toISOString(),\n });\n}\n\nexport function buildNegotiumMcpServer(ctx: RuntimeMcpContext): McpServer {\n const server = new McpServer({ name: RUNTIME_MCP_KEY, version: \"1.0.0\" });\n\n if (ctx.visualTools === true) {\n for (const def of visualToolDefinitions) {\n const handler =\n ctx.peerBridge && (def.name === \"show_html\" || def.name === \"show_mermaid\")\n ? async () => textResult(\"Visual queued for ordered display on the canonical hub.\")\n : def.handler;\n server.tool(def.name, def.description, def.schema as any, handler as any);\n }\n }\n\n const selfConfigCtx: SelfConfigContext = {\n topicId: ctx.topicId,\n userId: ctx.userId,\n cwd: ctx.cwd,\n currentUserPrompt: ctx.currentUserPrompt,\n onConfigChanged: (field) => appendAutoContinue(ctx, field),\n };\n for (const def of createSelfConfigToolDefinitions(selfConfigCtx)) {\n const handler = ctx.peerBridge\n ? async (input: Record<string, unknown>) => {\n const dispatched = dispatchPeerRuntimeSelfConfig({\n bridge: ctx.peerBridge!,\n userId: ctx.userId,\n tool: def.name,\n input,\n ...(ctx.currentUserPrompt ? { currentUserPrompt: ctx.currentUserPrompt } : {}),\n });\n if (!dispatched) {\n return errorResult(\"Error: the peer self-config bridge is not available on this node.\");\n }\n return dispatched;\n }\n : def.handler;\n server.tool(def.name, def.description, def.schema as any, handler as any);\n }\n\n const askUserTool = createAskUserToolDefinition({\n userId: ctx.userId,\n topicId: ctx.topicId,\n queryId: ctx.queryId,\n agent: ctx.agent,\n model: ctx.model,\n });\n const askHandler = ctx.peerBridge\n ? async (input: Record<string, unknown>) => {\n const dispatched = dispatchPeerRuntimeAskUser({\n bridge: ctx.peerBridge!,\n userId: ctx.userId,\n agent: ctx.agent,\n model: ctx.model,\n input,\n });\n if (!dispatched) {\n return errorResult(\"Error: the peer ask-user bridge is not available on this node.\");\n }\n return dispatched;\n }\n : askUserTool.handler;\n server.tool(\n askUserTool.name,\n askUserTool.description,\n askUserTool.schema as any,\n askHandler as any,\n );\n\n // Delegation is for top-level agent rooms only: subagent rooms never get the\n // tool (recursion guard by construction), nor do channels/manager rooms.\n const topic = getTopic(ctx.topicId);\n const peerBridge = ctx.peerBridge;\n const canSpawnSubagents = peerBridge\n ? peerBridge.canSpawnSubagents\n : topic?.kind === \"agent\" && !topic.isSubagent;\n if (canSpawnSubagents) {\n const spawnTool = createSpawnSubagentToolDefinition({\n userId: ctx.userId,\n topicId: ctx.topicId,\n queryId: ctx.queryId,\n agent: ctx.agent,\n model: ctx.model,\n });\n const spawnHandler = peerBridge\n ? async (input: Record<string, unknown>) => {\n const dispatched = dispatchPeerRuntimeSpawn({\n bridge: peerBridge,\n userId: ctx.userId,\n agent: ctx.agent,\n model: ctx.model,\n input,\n });\n if (!dispatched) {\n return errorResult(\"Error: the peer runtime bridge is not available on this node.\");\n }\n return dispatched;\n }\n : spawnTool.handler;\n server.tool(\n spawnTool.name,\n spawnTool.description,\n spawnTool.schema as any,\n spawnHandler as any,\n );\n if (!peerBridge) {\n for (const def of createSubagentManagementToolDefinitions({\n userId: ctx.userId,\n topicId: ctx.topicId,\n })) {\n server.tool(def.name, def.description, def.schema as any, def.handler as any);\n }\n }\n }\n\n server.tool(\n \"send_file\",\n \"Send a local file to the user in the chat. Use this when you want to share a file (image, document, PDF, code, etc.) with the user. The file will appear as a downloadable item in the chat.\",\n { file_path: z.string().describe(\"Absolute path to the file to send\") },\n async ({ file_path }) => deliverFile(ctx, file_path),\n );\n\n server.tool(\n \"send_files\",\n \"Send multiple local files to the user in the chat at once.\",\n { file_paths: z.array(z.string()).describe(\"Array of absolute file paths to send\") },\n async ({ file_paths }) => {\n const results = await Promise.all(file_paths.map((filePath) => deliverFile(ctx, filePath)));\n const hasError = results.some((result) => \"isError\" in result && result.isError);\n return {\n content: [\n {\n type: \"text\" as const,\n text: results\n .map((result) => result.content.map((c) => c.text).join(\"\\n\"))\n .join(\"\\n\\n\"),\n },\n ],\n ...(hasError ? { isError: true as const } : {}),\n };\n },\n );\n\n registerNodeTools(server, ctx);\n\n return server;\n}\n\nfunction jsonRpcError(status: number, code: number, message: string): Response {\n return Response.json({ jsonrpc: \"2.0\", error: { code, message }, id: null }, { status });\n}\n\nfunction unauthorized(): Response {\n return jsonRpcError(401, -32001, \"Unauthorized\");\n}\n\n// McpServer.close() closes its transport, whose onclose hook comes back here.\n// Keep the teardown idempotent so that callback cannot recursively close the\n// same server until the stack overflows.\nconst closingServers = new WeakSet<McpServer>();\n\nasync function closeServer(server: McpServer): Promise<void> {\n if (closingServers.has(server)) return;\n closingServers.add(server);\n try {\n await server.close();\n } catch (err) {\n logger.warn({ err }, \"negotium MCP: server close failed\");\n }\n}\n\nasync function handleSse(req: Request, token: string, ctx: RuntimeMcpContext): Promise<Response> {\n if (req.method !== \"GET\") return new Response(\"Method Not Allowed\", { status: 405 });\n\n const endpoint = `${SSE_MESSAGE_PATH}?token=${encodeURIComponent(token)}`;\n const server = buildNegotiumMcpServer(ctx);\n const transport = new SseTransport(endpoint, req);\n const response = transport.response();\n\n sseSessions.set(transport.sessionId, { token, transport, server });\n transport.onclose = () => {\n sseSessions.delete(transport.sessionId);\n void closeServer(server);\n };\n transport.onerror = (err) => {\n logger.warn({ err }, \"negotium MCP: SSE transport error\");\n };\n\n try {\n await server.connect(transport);\n } catch (err) {\n sseSessions.delete(transport.sessionId);\n await transport.close();\n await closeServer(server);\n throw err;\n }\n\n return response;\n}\n\nasync function handleSseMessage(req: Request, url: URL, token: string): Promise<Response> {\n if (req.method !== \"POST\") return new Response(\"Method Not Allowed\", { status: 405 });\n const sessionId = url.searchParams.get(\"sessionId\");\n if (!sessionId) return new Response(\"Missing sessionId\", { status: 400 });\n const session = sseSessions.get(sessionId);\n if (!session || session.token !== token)\n return new Response(\"SSE session not found\", { status: 404 });\n\n let body: unknown;\n try {\n body = await req.json();\n } catch {\n return new Response(\"Invalid JSON\", { status: 400 });\n }\n\n try {\n await session.transport.handleMessage(body, req);\n } catch (err) {\n logger.warn({ err, sessionId }, \"negotium MCP: invalid SSE message\");\n return new Response(\"Invalid JSON-RPC message\", { status: 400 });\n }\n\n return new Response(\"Accepted\", { status: 202 });\n}\n\nasync function createStreamableSession(token: string, ctx: RuntimeMcpContext) {\n const server = buildNegotiumMcpServer(ctx);\n let transport!: WebStandardStreamableHTTPServerTransport;\n transport = new WebStandardStreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (sessionId) => {\n streamableSessions.set(sessionId, { token, transport, server });\n },\n onsessionclosed: (sessionId) => {\n streamableSessions.delete(sessionId);\n void closeServer(server);\n },\n });\n transport.onclose = () => {\n if (transport.sessionId) streamableSessions.delete(transport.sessionId);\n void closeServer(server);\n };\n transport.onerror = (err) => {\n logger.warn({ err }, \"negotium MCP: streamable HTTP transport error\");\n };\n await server.connect(transport);\n return { token, transport, server };\n}\n\nasync function handleStreamable(\n req: Request,\n token: string,\n ctx: RuntimeMcpContext | null,\n): Promise<Response> {\n const sessionId = req.headers.get(\"mcp-session-id\");\n let session = sessionId ? streamableSessions.get(sessionId) : undefined;\n if (session && session.token !== token) return unauthorized();\n\n if (!session) {\n if (sessionId) return jsonRpcError(404, -32001, \"Session not found\");\n if (req.method !== \"POST\") {\n return jsonRpcError(400, -32000, \"Mcp-Session-Id header is required\");\n }\n // New streamable session requires a valid (non-expired) token.\n if (!ctx) return unauthorized();\n session = await createStreamableSession(token, ctx);\n }\n\n return session.transport.handleRequest(req);\n}\n\n/**\n * Route an incoming request to the negotium MCP endpoint. Returns null when\n * the path is not under `/mcp/runtime` so the host can fall through to its\n * own routes.\n */\nexport async function handleNegotiumMcpRequest(req: Request): Promise<Response | null> {\n const url = new URL(req.url);\n if (\n url.pathname !== RUNTIME_MCP_BASE_PATH &&\n !url.pathname.startsWith(`${RUNTIME_MCP_BASE_PATH}/`)\n ) {\n return null;\n }\n\n const token = url.searchParams.get(\"token\") ?? \"\";\n\n try {\n // SSE init: resolve context from token (required to build the per-session server).\n if (url.pathname === SSE_PATH) {\n const ctx = resolveRuntimeMcpToken(token);\n if (!ctx) return unauthorized();\n return handleSse(req, token, ctx);\n }\n // Established SSE messages: the session-level token check in handleSseMessage\n // is sufficient — skip the short-lived top-level token so 4h+ turns keep working.\n if (url.pathname === SSE_MESSAGE_PATH) return handleSseMessage(req, url, token);\n // Streamable: existing sessions don't need a fresh token lookup either.\n if (url.pathname === STREAMABLE_PATH) {\n return handleStreamable(req, token, resolveRuntimeMcpToken(token));\n }\n return jsonRpcError(404, -32001, `${RUNTIME_MCP_KEY} endpoint not found`);\n } catch (err) {\n logger.error({ err, path: url.pathname }, \"negotium MCP: request failed\");\n return jsonRpcError(500, -32603, \"Internal MCP server error\");\n }\n}\n",
|
|
8
|
+
"import { randomUUID } from \"node:crypto\";\nimport type {\n Transport,\n TransportSendOptions,\n} from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n type JSONRPCMessage,\n JSONRPCMessageSchema,\n type MessageExtraInfo,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\n/**\n * SSE transport for Claude SDK clients that speak the older two-endpoint\n * protocol (GET /sse to open the stream, POST /message to send).\n *\n * Ported from otium runtime-api `mcp/sse-transport.ts` for the negotium MCP\n * endpoint so fixes to framing, back-pressure, or abort handling stay shared.\n */\nexport class SseTransport implements Transport {\n readonly sessionId = randomUUID();\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: <T extends JSONRPCMessage>(message: T, extra?: MessageExtraInfo) => void;\n\n private readonly encoder = new TextEncoder();\n private controller?: ReadableStreamDefaultController<Uint8Array>;\n private closed = false;\n private readonly stream: ReadableStream<Uint8Array>;\n private keepAliveTimer?: ReturnType<typeof setInterval>;\n\n constructor(\n private readonly endpoint: string,\n req?: Request,\n private readonly keepAliveMs = 20_000,\n ) {\n this.stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n this.controller = controller;\n },\n cancel: () => {\n void this.close();\n },\n });\n req?.signal.addEventListener(\"abort\", () => {\n void this.close();\n });\n }\n\n response(): Response {\n return new Response(this.stream, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\",\n },\n });\n }\n\n async start(): Promise<void> {\n if (this.closed) return;\n const url = new URL(this.endpoint, \"http://localhost\");\n url.searchParams.set(\"sessionId\", this.sessionId);\n this.enqueue(`event: endpoint\\ndata: ${url.pathname}${url.search}${url.hash}\\n\\n`);\n this.startKeepAlive();\n }\n\n async send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void> {\n this.enqueue(`event: message\\ndata: ${JSON.stringify(message)}\\n\\n`);\n }\n\n async handleMessage(raw: unknown, req: Request): Promise<void> {\n const parsed = JSONRPCMessageSchema.parse(raw);\n this.onmessage?.(parsed, {\n requestInfo: {\n headers: Object.fromEntries(req.headers.entries()),\n url: new URL(req.url),\n },\n });\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n try {\n this.controller?.close();\n } catch {\n // Client already closed the stream.\n }\n if (this.keepAliveTimer) {\n clearInterval(this.keepAliveTimer);\n this.keepAliveTimer = undefined;\n }\n this.onclose?.();\n }\n\n private startKeepAlive(): void {\n if (this.closed || this.keepAliveTimer || this.keepAliveMs <= 0) return;\n this.keepAliveTimer = setInterval(() => {\n this.enqueue(\": ping\\n\\n\");\n }, this.keepAliveMs);\n }\n\n private enqueue(payload: string): void {\n if (this.closed) return;\n try {\n this.controller?.enqueue(this.encoder.encode(payload));\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.onerror?.(error);\n void this.close();\n }\n }\n}\n",
|
|
9
|
+
"import { randomUUID, timingSafeEqual } from \"node:crypto\";\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport {\n type AgentKind,\n type compactTopicSession,\n deleteVaultEntry,\n ensurePersonalGeneral,\n executeVaultCommand,\n getTopic,\n getVisibleTopics,\n isParticipant,\n latestRuntimeEventSeq,\n listApiMessages,\n listBackgroundSessionsForUser,\n listRecentRuntimeEventsForTopic,\n listRunningTopicQueries,\n listRuntimeEventsAfter,\n listVaultEntries,\n NEGOTIUM_VERSION,\n NODE_CONTROL_TOKEN,\n RUN_DIR,\n type RuntimeBusEvent,\n STATE_DIR,\n type StoredRuntimeEvent,\n saveVaultEntry,\n type startAiTurn,\n submitUserMessage,\n switchTopicEffort,\n switchTopicModel,\n TopicDeriveBusyError,\n type TopicDto,\n TopicServiceError,\n TopicTitleConflictError,\n topicService,\n} from \"@negotium/core\";\nimport { nodeFileStore } from \"./files\";\n\nexport const NODE_CONTROL_PROTOCOL_VERSION = 1;\nexport const NODE_CONTROL_BASE_PATH = \"/api/v1/control\";\nexport const NODE_DAEMON_ROLE = \"node-daemon\";\nexport const NODE_DAEMON_INFO_PATH = resolve(RUN_DIR, \"node-daemon.json\");\nconst NODE_VERSION = NEGOTIUM_VERSION;\n\nexport interface NodeDaemonInfo {\n schemaVersion: 1;\n protocolVersion: number;\n nodeVersion: string;\n pid: number;\n port: number;\n stateDir: string;\n startedAt: string;\n}\n\nexport interface NodeDaemonConnection {\n baseUrl: string;\n token: string;\n info?: NodeDaemonInfo;\n}\n\nexport interface NodeDaemonStatus {\n running: boolean;\n info?: NodeDaemonInfo;\n error?: string;\n}\n\ninterface ControlHandlerOptions {\n port: () => number;\n startedAt: string;\n requestShutdown: () => void;\n startTurn?: typeof startAiTurn;\n compactSession?: typeof compactTopicSession;\n}\n\nfunction jsonError(status: number, error: string): Response {\n return Response.json({ ok: false, error }, { status });\n}\n\nfunction topicServiceError(error: TopicServiceError): Response {\n const status =\n error.code === \"TOPIC_NOT_FOUND\" ? 404 : error.code === \"TOPIC_FORBIDDEN\" ? 403 : 400;\n return jsonError(status, error.message);\n}\n\nfunction safeEqual(left: string, right: string): boolean {\n const a = Buffer.from(left);\n const b = Buffer.from(right);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\nfunction authorized(req: Request): boolean {\n const header = req.headers.get(\"authorization\") ?? \"\";\n const token = header.startsWith(\"Bearer \") ? header.slice(7) : \"\";\n return Boolean(token) && safeEqual(token, NODE_CONTROL_TOKEN);\n}\n\nasync function bodyRecord(req: Request): Promise<Record<string, unknown>> {\n try {\n const value = await req.json();\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction requiredText(value: unknown, name: string): string {\n if (typeof value !== \"string\" || !value.trim()) throw new Error(`${name} is required`);\n return value.trim();\n}\n\nfunction topicsForUser(userId: string): TopicDto[] {\n const runningTopics = listRunningTopicQueries();\n return getVisibleTopics()\n .filter((topic) => isParticipant(topic, userId))\n .map((topic) => {\n const runningQueryId = runningTopics.get(topic.id);\n return { ...topic, running: Boolean(runningQueryId), runningQueryId };\n });\n}\n\nfunction topicForUser(topicId: string, userId: string): TopicDto | null {\n const topic = getTopic(topicId);\n return topic && isParticipant(topic, userId) ? topic : null;\n}\n\nfunction runtimeEvent(event: StoredRuntimeEvent): RuntimeBusEvent {\n return {\n type: event.type,\n topicId: event.topicId,\n payload: event.payload,\n seq: event.seq,\n createdAt: event.createdAt,\n };\n}\n\nfunction createEventStream(req: Request, userId: string, after: number): Response {\n const encoder = new TextEncoder();\n const allowedTopics = new Set(topicsForUser(userId).map((topic) => topic.id));\n let cursor = Math.max(0, after);\n let closed = false;\n let pumping = false;\n let pollTimer: ReturnType<typeof setInterval> | undefined;\n let heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n const send = (event: string, data: unknown, id?: number) => {\n if (closed) return;\n const lines = [\n id === undefined ? \"\" : `id: ${id}`,\n `event: ${event}`,\n `data: ${JSON.stringify(data)}`,\n ]\n .filter(Boolean)\n .join(\"\\n\");\n controller.enqueue(encoder.encode(`${lines}\\n\\n`));\n };\n const close = () => {\n if (closed) return;\n closed = true;\n if (pollTimer) clearInterval(pollTimer);\n if (heartbeatTimer) clearInterval(heartbeatTimer);\n try {\n controller.close();\n } catch {\n // The peer may already have closed the stream.\n }\n };\n const pump = () => {\n if (closed || pumping) return;\n pumping = true;\n try {\n while (!closed) {\n const events = listRuntimeEventsAfter(cursor, 500);\n if (events.length === 0) break;\n for (const event of events) {\n cursor = event.seq;\n if (event.type === \"topic-created\" || event.type === \"topic-updated\") {\n const topic = getTopic(event.topicId);\n if (topic && isParticipant(topic, userId)) allowedTopics.add(event.topicId);\n else allowedTopics.delete(event.topicId);\n }\n const visible = allowedTopics.has(event.topicId);\n if (visible) send(\"runtime\", runtimeEvent(event), event.seq);\n if (event.type === \"topic-deleted\") allowedTopics.delete(event.topicId);\n }\n // Advance reconnect cursors even when a batch only contained topics\n // that are not visible to this user.\n send(\"cursor\", { cursor }, cursor);\n if (events.length < 500) break;\n }\n } finally {\n pumping = false;\n }\n };\n\n send(\"ready\", { protocolVersion: NODE_CONTROL_PROTOCOL_VERSION, cursor });\n pump();\n pollTimer = setInterval(pump, 100);\n heartbeatTimer = setInterval(() => {\n if (!closed) controller.enqueue(encoder.encode(`: heartbeat ${Date.now()}\\n\\n`));\n }, 15_000);\n req.signal.addEventListener(\"abort\", close, { once: true });\n },\n cancel() {\n closed = true;\n if (pollTimer) clearInterval(pollTimer);\n if (heartbeatTimer) clearInterval(heartbeatTimer);\n },\n });\n\n return new Response(stream, {\n headers: {\n \"cache-control\": \"no-cache, no-transform\",\n connection: \"keep-alive\",\n \"content-type\": \"text/event-stream; charset=utf-8\",\n \"x-accel-buffering\": \"no\",\n },\n });\n}\n\n/** Authenticated loopback REST/SSE surface used by short-lived UI clients. */\nexport function createNodeControlHandler(\n options: ControlHandlerOptions,\n): (req: Request) => Promise<Response | null> {\n return async (req) => {\n const url = new URL(req.url);\n if (!url.pathname.startsWith(NODE_CONTROL_BASE_PATH)) return null;\n if (!authorized(req)) return jsonError(401, \"Unauthorized\");\n\n const path = url.pathname.slice(NODE_CONTROL_BASE_PATH.length) || \"/\";\n try {\n if (req.method === \"GET\" && path === \"/status\") {\n return Response.json({\n ok: true,\n protocolVersion: NODE_CONTROL_PROTOCOL_VERSION,\n nodeVersion: NODE_VERSION,\n pid: process.pid,\n port: options.port(),\n stateDir: STATE_DIR,\n startedAt: options.startedAt,\n });\n }\n\n if (req.method === \"POST\" && path === \"/shutdown\") {\n setTimeout(options.requestShutdown, 10);\n return Response.json({ ok: true });\n }\n\n if (req.method === \"GET\" && path === \"/session\") {\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n ensurePersonalGeneral(userId);\n return Response.json({\n ok: true,\n protocolVersion: NODE_CONTROL_PROTOCOL_VERSION,\n nodeVersion: NODE_VERSION,\n topics: topicsForUser(userId),\n cursor: latestRuntimeEventSeq(),\n });\n }\n\n if (req.method === \"GET\" && path === \"/topics\") {\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n return Response.json({ ok: true, topics: topicsForUser(userId) });\n }\n\n if (req.method === \"GET\" && path === \"/background-sessions\") {\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n return Response.json({ ok: true, sessions: listBackgroundSessionsForUser(userId) });\n }\n\n const fileMatch = path.match(/^\\/files\\/([0-9a-f-]+)$/i);\n if (fileMatch && req.method === \"GET\") {\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n return nodeFileStore.response(fileMatch[1], userId) ?? jsonError(404, \"File not found\");\n }\n\n if (req.method === \"POST\" && path === \"/vault/command\") {\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n const commandLine = requiredText(body.commandLine, \"commandLine\");\n const result = executeVaultCommand(userId, commandLine);\n if (result === null) return jsonError(400, \"Invalid Vault command\");\n return Response.json({ ok: true, result });\n }\n\n if (req.method === \"GET\" && path === \"/vault\") {\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n return Response.json({ ok: true, entries: listVaultEntries(userId) });\n }\n\n if (req.method === \"POST\" && path === \"/vault\") {\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n const key = requiredText(body.key, \"key\");\n if (typeof body.value !== \"string\") throw new Error(\"value is required\");\n const description = body.description === undefined ? \"\" : String(body.description);\n return Response.json({\n ok: true,\n result: saveVaultEntry(userId, key, body.value, description),\n });\n }\n\n if (req.method === \"DELETE\" && path === \"/vault\") {\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n const key = requiredText(url.searchParams.get(\"key\"), \"key\");\n return Response.json({ ok: true, deleted: deleteVaultEntry(userId, key) });\n }\n\n if (req.method === \"POST\" && path === \"/topics\") {\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n const title = requiredText(body.title, \"title\");\n const agent = body.agent;\n if (agent !== undefined && ![\"claude\", \"codex\", \"maestro\"].includes(String(agent))) {\n return jsonError(400, \"Invalid agent\");\n }\n const topic = topicService.create({\n title,\n userId,\n kind: \"agent\",\n ...(agent ? { agent: agent as AgentKind } : {}),\n });\n return Response.json({ ok: true, topic }, { status: 201 });\n }\n\n if (req.method === \"GET\" && path === \"/events\") {\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n const parsed = Number.parseInt(url.searchParams.get(\"after\") ?? \"0\", 10);\n return createEventStream(req, userId, Number.isFinite(parsed) ? parsed : 0);\n }\n\n const messagesMatch = path.match(/^\\/topics\\/([^/]+)\\/messages$/);\n if (messagesMatch && req.method === \"GET\") {\n const topicId = decodeURIComponent(messagesMatch[1]);\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n if (!topicForUser(topicId, userId)) return jsonError(404, \"Topic not found\");\n const cursor = url.searchParams.get(\"cursor\");\n const parsedLimit = Number.parseInt(url.searchParams.get(\"limit\") ?? \"50\", 10);\n const result = listApiMessages(topicId, {\n cursor,\n limit: Number.isFinite(parsedLimit) ? parsedLimit : 50,\n });\n return Response.json({\n ok: true,\n messages: result.page,\n cursor: result.cursor,\n hasMore: result.hasMore,\n });\n }\n if (messagesMatch && req.method === \"POST\") {\n const topicId = decodeURIComponent(messagesMatch[1]);\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n const text = requiredText(body.text, \"text\");\n const topic = topicForUser(topicId, userId);\n if (!topic) return jsonError(404, \"Topic not found\");\n const { message } = submitUserMessage({\n topic,\n userId,\n text,\n sourceAdapter: \"terminal\",\n startTurn: options.startTurn,\n });\n return Response.json({ ok: true, message }, { status: 201 });\n }\n\n const recentMatch = path.match(/^\\/topics\\/([^/]+)\\/recent-events$/);\n if (recentMatch && req.method === \"GET\") {\n const topicId = decodeURIComponent(recentMatch[1]);\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n if (!topicForUser(topicId, userId)) return jsonError(404, \"Topic not found\");\n const events = listRecentRuntimeEventsForTopic(topicId).map(runtimeEvent);\n return Response.json({ ok: true, events });\n }\n\n const modelMatch = path.match(/^\\/topics\\/([^/]+)\\/model$/);\n if (modelMatch && req.method === \"POST\") {\n const topicId = decodeURIComponent(modelMatch[1]);\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n const model = requiredText(body.model, \"model\");\n if (!topicForUser(topicId, userId)) return jsonError(404, \"Topic not found\");\n const result = switchTopicModel({ topicId, userId, model });\n if (!result.ok) return jsonError(400, result.error);\n return Response.json({ ok: true, model: result.model, result: result.text });\n }\n\n const effortMatch = path.match(/^\\/topics\\/([^/]+)\\/effort$/);\n if (effortMatch && req.method === \"POST\") {\n const topicId = decodeURIComponent(effortMatch[1]);\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n const effort = requiredText(body.effort, \"effort\");\n if (!topicForUser(topicId, userId)) return jsonError(404, \"Topic not found\");\n const result = switchTopicEffort({ topicId, userId, effort });\n if (!result.ok) return jsonError(400, result.error);\n return Response.json({ ok: true, effort: result.effort, result: result.text });\n }\n\n const deleteMatch = path.match(/^\\/topics\\/([^/]+)$/);\n if (deleteMatch && req.method === \"DELETE\") {\n const topicId = decodeURIComponent(deleteMatch[1]);\n const userId = requiredText(url.searchParams.get(\"user\"), \"user\");\n await topicService.delete({ topicId, userId });\n return Response.json({ ok: true });\n }\n\n const abortMatch = path.match(/^\\/topics\\/([^/]+)\\/abort$/);\n if (abortMatch && req.method === \"POST\") {\n const topicId = decodeURIComponent(abortMatch[1]);\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n return Response.json({ ok: true, aborted: topicService.abortTurn(topicId, userId) });\n }\n\n const resetMatch = path.match(/^\\/topics\\/([^/]+)\\/session\\/reset$/);\n if (resetMatch && req.method === \"POST\") {\n const topicId = decodeURIComponent(resetMatch[1]);\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n const result = await topicService.reset({\n topicId,\n userId,\n reason: \"node-control-session-reset\",\n });\n if (result.isError) return jsonError(409, result.text);\n return Response.json({ ok: true, result: result.text });\n }\n\n const compactMatch = path.match(/^\\/topics\\/([^/]+)\\/session\\/compact$/);\n if (compactMatch && req.method === \"POST\") {\n const topicId = decodeURIComponent(compactMatch[1]);\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n const result = await topicService.compact({\n topicId,\n userId,\n reason: \"node-control-session-compact\",\n compactSession: options.compactSession,\n });\n if (result.isError) return jsonError(409, result.text);\n return Response.json({ ok: true, result: result.text });\n }\n\n const deriveMatch = path.match(/^\\/topics\\/([^/]+)\\/derive$/);\n if (deriveMatch && req.method === \"POST\") {\n const topicId = decodeURIComponent(deriveMatch[1]);\n const body = await bodyRecord(req);\n const userId = requiredText(body.userId, \"userId\");\n if (typeof body.copyHistory !== \"boolean\") {\n return jsonError(400, \"copyHistory must be a boolean\");\n }\n const copyHistory = body.copyHistory;\n const source = topicForUser(topicId, userId);\n if (!source || source.kind === \"manager\") return jsonError(404, \"Topic not found\");\n if (body.name !== undefined && typeof body.name !== \"string\") {\n return jsonError(400, \"name must be a string\");\n }\n const name =\n typeof body.name === \"string\" && body.name.trim() ? body.name.trim() : undefined;\n const derived = await topicService.derive({\n sourceTopicId: topicId,\n userId,\n copyHistory,\n ...(name ? { name } : {}),\n });\n if (!derived) return jsonError(500, \"Failed to derive topic\");\n return Response.json({ ok: true, topic: derived }, { status: 201 });\n }\n\n const answerMatch = path.match(/^\\/questions\\/([^/]+)\\/answer$/);\n if (answerMatch && req.method === \"POST\") {\n const messageId = decodeURIComponent(answerMatch[1]);\n const body = await bodyRecord(req);\n const topicId = requiredText(body.topicId, \"topicId\");\n const userId = requiredText(body.userId, \"userId\");\n const label = requiredText(body.label, \"label\");\n const result = topicService.answerQuestion(topicId, messageId, label, userId);\n return Response.json(result, { status: result.ok ? 200 : 409 });\n }\n\n return jsonError(404, \"Control route not found\");\n } catch (error) {\n if (error instanceof TopicServiceError) return topicServiceError(error);\n if (error instanceof TopicDeriveBusyError) return jsonError(409, error.message);\n if (error instanceof TopicTitleConflictError) return jsonError(409, error.message);\n return jsonError(400, error instanceof Error ? error.message : String(error));\n }\n };\n}\n\nexport function writeNodeDaemonInfo(port: number, startedAt: string): NodeDaemonInfo {\n const info: NodeDaemonInfo = {\n schemaVersion: 1,\n protocolVersion: NODE_CONTROL_PROTOCOL_VERSION,\n nodeVersion: NODE_VERSION,\n pid: process.pid,\n port,\n stateDir: STATE_DIR,\n startedAt,\n };\n mkdirSync(dirname(NODE_DAEMON_INFO_PATH), { recursive: true });\n const temporary = `${NODE_DAEMON_INFO_PATH}.${process.pid}.${randomUUID()}.tmp`;\n writeFileSync(temporary, `${JSON.stringify(info, null, 2)}\\n`, { mode: 0o600 });\n chmodSync(temporary, 0o600);\n renameSync(temporary, NODE_DAEMON_INFO_PATH);\n chmodSync(NODE_DAEMON_INFO_PATH, 0o600);\n return info;\n}\n\nexport function readNodeDaemonInfo(): NodeDaemonInfo | null {\n if (!existsSync(NODE_DAEMON_INFO_PATH)) return null;\n try {\n const value = JSON.parse(readFileSync(NODE_DAEMON_INFO_PATH, \"utf8\")) as NodeDaemonInfo;\n if (\n value?.schemaVersion !== 1 ||\n value.stateDir !== STATE_DIR ||\n !Number.isInteger(value.protocolVersion) ||\n !Number.isInteger(value.pid) ||\n !Number.isInteger(value.port) ||\n value.port <= 0\n ) {\n return null;\n }\n return value;\n } catch {\n return null;\n }\n}\n\nexport function removeNodeDaemonInfo(expected: { pid: number; port: number }): void {\n const current = readNodeDaemonInfo();\n if (!current || current.pid !== expected.pid || current.port !== expected.port) return;\n try {\n unlinkSync(NODE_DAEMON_INFO_PATH);\n } catch {\n // A competing replacement may already have removed it.\n }\n}\n\nasync function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function inspectNodeDaemon(timeoutMs = 750): Promise<NodeDaemonStatus> {\n const info = readNodeDaemonInfo();\n if (!info) return { running: false };\n try {\n const response = await fetchWithTimeout(\n `http://127.0.0.1:${info.port}${NODE_CONTROL_BASE_PATH}/status`,\n { headers: { authorization: `Bearer ${NODE_CONTROL_TOKEN}` } },\n timeoutMs,\n );\n if (!response.ok) throw new Error(`status returned HTTP ${response.status}`);\n const status = (await response.json()) as Record<string, unknown>;\n if (\n status.protocolVersion !== NODE_CONTROL_PROTOCOL_VERSION ||\n status.stateDir !== STATE_DIR ||\n status.pid !== info.pid\n ) {\n throw new Error(\"node identity does not match the local state directory\");\n }\n return { running: true, info };\n } catch (error) {\n return {\n running: false,\n info,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport async function waitForNodeDaemon(timeoutMs = 10_000): Promise<NodeDaemonConnection> {\n const deadline = Date.now() + timeoutMs;\n let lastError = \"node did not publish connection information\";\n while (Date.now() < deadline) {\n const status = await inspectNodeDaemon();\n if (status.running && status.info) {\n return {\n baseUrl: `http://127.0.0.1:${status.info.port}`,\n token: NODE_CONTROL_TOKEN,\n info: status.info,\n };\n }\n if (status.error) lastError = status.error;\n await new Promise((resolvePromise) => setTimeout(resolvePromise, 100));\n }\n throw new Error(`Negotium node failed to start: ${lastError}`);\n}\n\nexport async function stopNodeDaemon(timeoutMs = 3_000): Promise<boolean> {\n const status = await inspectNodeDaemon();\n if (!status.running || !status.info) return false;\n const response = await fetchWithTimeout(\n `http://127.0.0.1:${status.info.port}${NODE_CONTROL_BASE_PATH}/shutdown`,\n {\n method: \"POST\",\n headers: { authorization: `Bearer ${NODE_CONTROL_TOKEN}` },\n },\n timeoutMs,\n );\n if (!response.ok) throw new Error(`node shutdown returned HTTP ${response.status}`);\n return true;\n}\n",
|
|
10
|
+
"import { randomUUID } from \"node:crypto\";\nimport {\n copyFileSync,\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { basename, extname, join } from \"node:path\";\nimport {\n type AttachmentDto,\n DATA_DIR,\n type FileHooks,\n getTopic,\n isParticipant,\n logger,\n type UploadAccess,\n} from \"@negotium/core\";\n\nconst MAX_UPLOAD_BYTES = 2 * 1024 * 1024 * 1024;\nconst FILE_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ninterface NodeUploadMetadata {\n filename: string;\n mimeType: string;\n sizeBytes: number;\n savedName: string;\n ownerUserId?: string;\n topicId?: string;\n visibility?: \"private\" | \"workspace\";\n}\n\nconst MIME_BY_EXT: Record<string, string> = {\n \".csv\": \"text/csv\",\n \".gif\": \"image/gif\",\n \".html\": \"text/html\",\n \".jpeg\": \"image/jpeg\",\n \".jpg\": \"image/jpeg\",\n \".json\": \"application/json\",\n \".md\": \"text/markdown\",\n \".mov\": \"video/quicktime\",\n \".mp3\": \"audio/mpeg\",\n \".mp4\": \"video/mp4\",\n \".pdf\": \"application/pdf\",\n \".png\": \"image/png\",\n \".svg\": \"image/svg+xml\",\n \".txt\": \"text/plain\",\n \".wav\": \"audio/wav\",\n \".webm\": \"video/webm\",\n \".webp\": \"image/webp\",\n \".zip\": \"application/zip\",\n};\n\nfunction safeExtension(filename: string): string {\n const extension = extname(basename(filename));\n return /^\\.[A-Za-z0-9]{1,16}$/.test(extension) ? extension.toLowerCase() : \"\";\n}\n\nfunction attachmentType(mimeType: string): AttachmentDto[\"type\"] {\n if (mimeType.startsWith(\"image/\")) return \"image\";\n if (mimeType.startsWith(\"audio/\")) return \"audio\";\n return \"file\";\n}\n\nfunction contentDisposition(filename: string): string {\n return `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`;\n}\n\nexport class NodeFileStore {\n readonly uploadDir: string;\n\n constructor(uploadDir = join(DATA_DIR, \"uploads\")) {\n this.uploadDir = uploadDir;\n }\n\n #ensureDir(): void {\n mkdirSync(this.uploadDir, { recursive: true });\n }\n\n #metadataPath(fileId: string): string {\n return join(this.uploadDir, `${fileId}.meta.json`);\n }\n\n #metadata(fileId: string): NodeUploadMetadata | null {\n if (!FILE_ID_RE.test(fileId)) return null;\n try {\n const parsed = JSON.parse(\n readFileSync(this.#metadataPath(fileId), \"utf8\"),\n ) as NodeUploadMetadata;\n if (\n typeof parsed.filename !== \"string\" ||\n typeof parsed.mimeType !== \"string\" ||\n typeof parsed.sizeBytes !== \"number\" ||\n typeof parsed.savedName !== \"string\"\n ) {\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n }\n\n #attachment(fileId: string, metadata: NodeUploadMetadata): AttachmentDto {\n const userQuery = metadata.ownerUserId\n ? `?user=${encodeURIComponent(metadata.ownerUserId)}`\n : \"\";\n return {\n id: fileId,\n type: attachmentType(metadata.mimeType),\n filename: metadata.filename,\n url: `/api/v1/control/files/${fileId}${userQuery}`,\n mimeType: metadata.mimeType,\n sizeBytes: metadata.sizeBytes,\n };\n }\n\n readonly hooks: FileHooks = {\n resolveAttachmentByFileId: (fileId) => {\n const metadata = this.#metadata(fileId);\n if (!metadata || !existsSync(join(this.uploadDir, metadata.savedName))) return null;\n return this.#attachment(fileId, metadata);\n },\n resolveUploadedFilePathByFileId: (fileId) => {\n const metadata = this.#metadata(fileId);\n if (!metadata) return null;\n const path = join(this.uploadDir, metadata.savedName);\n return existsSync(path) ? path : null;\n },\n storeLocalFileAsUpload: (absPath, access = {}) => this.store(absPath, access),\n deleteFilesForTopic: (topicId) => this.deleteForTopic(topicId),\n };\n\n store(absPath: string, access: UploadAccess = {}): AttachmentDto | null {\n this.#ensureDir();\n const fileId = randomUUID();\n const extension = safeExtension(absPath);\n const savedName = `${fileId}${extension}`;\n const savedPath = join(this.uploadDir, savedName);\n try {\n const stats = statSync(absPath);\n if (!stats.isFile() || stats.size > MAX_UPLOAD_BYTES) return null;\n const metadata: NodeUploadMetadata = {\n filename: basename(absPath),\n mimeType: MIME_BY_EXT[extension] ?? \"application/octet-stream\",\n sizeBytes: stats.size,\n savedName,\n ...(access.ownerUserId ? { ownerUserId: access.ownerUserId } : {}),\n ...(access.topicId ? { topicId: access.topicId } : {}),\n ...(access.visibility ? { visibility: access.visibility } : {}),\n };\n copyFileSync(absPath, savedPath);\n writeFileSync(this.#metadataPath(fileId), JSON.stringify(metadata), { mode: 0o600 });\n return this.#attachment(fileId, metadata);\n } catch (error) {\n rmSync(savedPath, { force: true });\n rmSync(this.#metadataPath(fileId), { force: true });\n logger.warn({ err: error, absPath }, \"node files: failed to store local upload\");\n return null;\n }\n }\n\n response(fileId: string, userId: string): Response | null {\n const metadata = this.#metadata(fileId);\n if (!metadata) return null;\n const topic = metadata.topicId ? getTopic(metadata.topicId) : null;\n const allowed =\n metadata.visibility === \"workspace\" ||\n metadata.ownerUserId === userId ||\n Boolean(topic && isParticipant(topic, userId));\n if (!allowed) return null;\n const path = join(this.uploadDir, metadata.savedName);\n if (!existsSync(path)) return null;\n return new Response(Bun.file(path), {\n headers: {\n \"content-disposition\": contentDisposition(metadata.filename),\n \"content-length\": String(metadata.sizeBytes),\n \"content-type\": metadata.mimeType,\n \"x-content-type-options\": \"nosniff\",\n },\n });\n }\n\n deleteForTopic(topicId: string): void {\n this.#ensureDir();\n for (const name of new Bun.Glob(\"*.meta.json\").scanSync({ cwd: this.uploadDir })) {\n const fileId = name.slice(0, -\".meta.json\".length);\n const metadata = this.#metadata(fileId);\n if (metadata?.topicId !== topicId) continue;\n rmSync(join(this.uploadDir, metadata.savedName), { force: true });\n rmSync(this.#metadataPath(fileId), { force: true });\n }\n }\n}\n\nexport const nodeFileStore = new NodeFileStore();\n"
|
|
11
|
+
],
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "04B1D1F9342F602564756E2164756E21",
|
|
14
|
+
"names": []
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ../../packages/adapter-sdk/src/index.ts
|
|
2
|
+
var NEGOTIUM_ADAPTER_API_VERSION = 3;
|
|
3
|
+
function defineNegotiumAdapter(definition) {
|
|
4
|
+
return { apiVersion: NEGOTIUM_ADAPTER_API_VERSION, ...definition };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export { defineNegotiumAdapter };
|
|
8
|
+
|
|
9
|
+
//# debugId=4B9EA4CD81D4F9FD64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../packages/adapter-sdk/src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/** Stable lifecycle contract implemented by every first-party channel adapter. */\n\nexport const NEGOTIUM_ADAPTER_API_VERSION = 3 as const;\n\nexport type Awaitable<T> = T | Promise<T>;\n\nexport interface NegotiumAdapterHandle<Name extends string = string> {\n /** Stable adapter key used in logs, configuration, and contract tests. */\n readonly name: Name;\n /** Stop accepting channel input and release every owned resource. Idempotent. */\n stop(): Awaitable<void>;\n}\n\n/** User-facing transcript semantics; transport gaps stay explicit metadata. */\nexport interface NegotiumAdapterProjectionCapabilities {\n /** Whether the surface can render a room timeline or only newly delivered events. */\n readonly transcript: \"full\" | \"live-only\";\n /** Whether pre-existing topic messages can populate the surface on binding. */\n readonly historyBackfill: boolean;\n /** How authors originating in another adapter are represented. */\n readonly externalAuthors: \"native\" | \"relayed\";\n}\n\n/** Optional behavioral surfaces are declared independently from lifecycle. */\nexport interface NegotiumAdapterCapabilities {\n /** Accepts a human message originating on this adapter. */\n readonly localUserInput: boolean;\n /** Exposes create/delete/reset/compact topic commands. */\n readonly topicManagement: boolean;\n /** Executes a turn placed by an external peer runtime. */\n readonly externalPlacedTurn: boolean;\n}\n\nexport interface NegotiumAdapterDefinition<\n Name extends string,\n Options,\n Handle extends NegotiumAdapterHandle<Name>,\n> {\n readonly apiVersion: typeof NEGOTIUM_ADAPTER_API_VERSION;\n readonly name: Name;\n readonly capabilities: NegotiumAdapterCapabilities;\n readonly projection: NegotiumAdapterProjectionCapabilities;\n start(options: Options): Awaitable<Handle>;\n}\n\nexport function defineNegotiumAdapter<\n const Name extends string,\n Options,\n Handle extends NegotiumAdapterHandle<Name>,\n>(\n definition: Omit<NegotiumAdapterDefinition<Name, Options, Handle>, \"apiVersion\">,\n): NegotiumAdapterDefinition<Name, Options, Handle> {\n return { apiVersion: NEGOTIUM_ADAPTER_API_VERSION, ...definition };\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AAEO,IAAM,+BAA+B;AA2CrC,SAAS,qBAIf,CACC,YACkD;AAAA,EAClD,OAAO,EAAE,YAAY,iCAAiC,WAAW;AAAA;",
|
|
8
|
+
"debugId": "4B9EA4CD81D4F9FD64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
SESSION_ASKS_DIR
|
|
4
|
+
} from "./chunk-k9z47mab.js";
|
|
5
|
+
import"./chunk-hwy3n31w.js";
|
|
6
|
+
|
|
7
|
+
// ../../packages/core/src/storage/session-asks.ts
|
|
8
|
+
import {
|
|
9
|
+
closeSync,
|
|
10
|
+
existsSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
openSync,
|
|
13
|
+
readdirSync,
|
|
14
|
+
readFileSync,
|
|
15
|
+
renameSync,
|
|
16
|
+
statSync,
|
|
17
|
+
unlinkSync,
|
|
18
|
+
writeFileSync
|
|
19
|
+
} from "fs";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
var PENDING_ASK_TTL_MS = 15 * 60 * 1000;
|
|
22
|
+
function pendingAskDir(userId) {
|
|
23
|
+
return join(SESSION_ASKS_DIR, String(userId));
|
|
24
|
+
}
|
|
25
|
+
var ASK_FILENAME_PREFIX = "v2-";
|
|
26
|
+
function encodeAskKey(key) {
|
|
27
|
+
return Buffer.from(JSON.stringify([key.from, key.to]), "utf8").toString("base64url");
|
|
28
|
+
}
|
|
29
|
+
function pendingAskPath(key) {
|
|
30
|
+
return join(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${encodeAskKey(key)}.pending`);
|
|
31
|
+
}
|
|
32
|
+
function legacyPendingAskPath(key) {
|
|
33
|
+
if (key.from.includes("___") || /[\\/]/.test(key.from) || /[\\/]/.test(key.to))
|
|
34
|
+
return null;
|
|
35
|
+
return join(pendingAskDir(key.userId), `${key.from}___${key.to}.pending`);
|
|
36
|
+
}
|
|
37
|
+
function resolvePendingAskPath(key) {
|
|
38
|
+
const current = pendingAskPath(key);
|
|
39
|
+
if (existsSync(current))
|
|
40
|
+
return current;
|
|
41
|
+
const legacy = legacyPendingAskPath(key);
|
|
42
|
+
if (!legacy || !existsSync(legacy))
|
|
43
|
+
return current;
|
|
44
|
+
try {
|
|
45
|
+
renameSync(legacy, current);
|
|
46
|
+
return current;
|
|
47
|
+
} catch {
|
|
48
|
+
return existsSync(current) ? current : legacy;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function parsePendingAskFilename(fileName) {
|
|
52
|
+
if (!fileName.endsWith(".pending"))
|
|
53
|
+
return null;
|
|
54
|
+
const raw = fileName.slice(0, -".pending".length);
|
|
55
|
+
if (raw.startsWith(ASK_FILENAME_PREFIX)) {
|
|
56
|
+
try {
|
|
57
|
+
const decoded = JSON.parse(Buffer.from(raw.slice(ASK_FILENAME_PREFIX.length), "base64url").toString("utf8"));
|
|
58
|
+
if (Array.isArray(decoded) && decoded.length === 2 && decoded.every((part) => typeof part === "string")) {
|
|
59
|
+
return { from: decoded[0], to: decoded[1] };
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const sep = raw.indexOf("___");
|
|
67
|
+
if (sep < 0)
|
|
68
|
+
return null;
|
|
69
|
+
return { from: raw.slice(0, sep), to: raw.slice(sep + 3) };
|
|
70
|
+
}
|
|
71
|
+
function readPendingAskFile(path, fallback) {
|
|
72
|
+
try {
|
|
73
|
+
const raw = readFileSync(path, "utf8").trim();
|
|
74
|
+
if (!raw)
|
|
75
|
+
return null;
|
|
76
|
+
if (!raw.startsWith("{")) {
|
|
77
|
+
const ts = new Date(statSync(path).mtimeMs).toISOString();
|
|
78
|
+
return {
|
|
79
|
+
userId: fallback.userId,
|
|
80
|
+
from: fallback.from,
|
|
81
|
+
to: fallback.to,
|
|
82
|
+
requestId: raw,
|
|
83
|
+
state: "requested",
|
|
84
|
+
createdAt: ts,
|
|
85
|
+
updatedAt: ts
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const parsed = JSON.parse(raw);
|
|
89
|
+
if (!parsed.requestId || !parsed.from || !parsed.to)
|
|
90
|
+
return null;
|
|
91
|
+
const now = new Date().toISOString();
|
|
92
|
+
return {
|
|
93
|
+
userId: parsed.userId ?? fallback.userId,
|
|
94
|
+
from: parsed.from,
|
|
95
|
+
to: parsed.to,
|
|
96
|
+
requestId: parsed.requestId,
|
|
97
|
+
contextId: parsed.contextId,
|
|
98
|
+
state: parsed.state ?? "requested",
|
|
99
|
+
createdAt: parsed.createdAt ?? now,
|
|
100
|
+
updatedAt: parsed.updatedAt ?? parsed.createdAt ?? now
|
|
101
|
+
};
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function isStale(record, path) {
|
|
107
|
+
const ts = record ? Date.parse(record.updatedAt || record.createdAt) : Number.NaN;
|
|
108
|
+
if (Number.isFinite(ts))
|
|
109
|
+
return Date.now() - ts > PENDING_ASK_TTL_MS;
|
|
110
|
+
try {
|
|
111
|
+
return Date.now() - statSync(path).mtimeMs > PENDING_ASK_TTL_MS;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function writePendingAsk(record, path = pendingAskPath(record)) {
|
|
117
|
+
mkdirSync(pendingAskDir(record.userId), { recursive: true });
|
|
118
|
+
writeFileSync(path, `${JSON.stringify(record)}
|
|
119
|
+
`);
|
|
120
|
+
}
|
|
121
|
+
function createPendingAsk(args) {
|
|
122
|
+
const path = resolvePendingAskPath(args);
|
|
123
|
+
const now = new Date().toISOString();
|
|
124
|
+
const record = {
|
|
125
|
+
userId: args.userId,
|
|
126
|
+
from: args.from,
|
|
127
|
+
to: args.to,
|
|
128
|
+
requestId: args.requestId,
|
|
129
|
+
contextId: args.contextId,
|
|
130
|
+
state: "requested",
|
|
131
|
+
createdAt: now,
|
|
132
|
+
updatedAt: now
|
|
133
|
+
};
|
|
134
|
+
mkdirSync(pendingAskDir(args.userId), { recursive: true });
|
|
135
|
+
for (let attempt = 0;attempt < 2; attempt++) {
|
|
136
|
+
let fd = null;
|
|
137
|
+
try {
|
|
138
|
+
fd = openSync(path, "wx");
|
|
139
|
+
writeFileSync(fd, `${JSON.stringify(record)}
|
|
140
|
+
`);
|
|
141
|
+
return { ok: true, record };
|
|
142
|
+
} catch (err) {
|
|
143
|
+
if (err.code !== "EEXIST")
|
|
144
|
+
throw err;
|
|
145
|
+
const existing = readPendingAskFile(path, args);
|
|
146
|
+
if (!isStale(existing, path))
|
|
147
|
+
return { ok: false, existing, stale: false };
|
|
148
|
+
try {
|
|
149
|
+
unlinkSync(path);
|
|
150
|
+
} catch {
|
|
151
|
+
return { ok: false, existing, stale: true };
|
|
152
|
+
}
|
|
153
|
+
} finally {
|
|
154
|
+
if (fd !== null)
|
|
155
|
+
closeSync(fd);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { ok: false, existing: readPendingAskFile(path, args), stale: true };
|
|
159
|
+
}
|
|
160
|
+
function markPendingAskState(args) {
|
|
161
|
+
const path = resolvePendingAskPath(args);
|
|
162
|
+
const record = readPendingAskFile(path, args);
|
|
163
|
+
if (!record)
|
|
164
|
+
return null;
|
|
165
|
+
if (args.requestId && record.requestId !== args.requestId)
|
|
166
|
+
return record;
|
|
167
|
+
const next = {
|
|
168
|
+
...record,
|
|
169
|
+
state: args.state,
|
|
170
|
+
updatedAt: new Date().toISOString()
|
|
171
|
+
};
|
|
172
|
+
writePendingAsk(next, path);
|
|
173
|
+
return next;
|
|
174
|
+
}
|
|
175
|
+
function clearPendingAsk(args) {
|
|
176
|
+
const path = resolvePendingAskPath(args);
|
|
177
|
+
const record = readPendingAskFile(path, args);
|
|
178
|
+
if (args.requestId && record && record.requestId !== args.requestId)
|
|
179
|
+
return false;
|
|
180
|
+
try {
|
|
181
|
+
unlinkSync(path);
|
|
182
|
+
return true;
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function markPendingAskSources(args) {
|
|
188
|
+
for (const source of args.sources ?? []) {
|
|
189
|
+
if (!source.from)
|
|
190
|
+
continue;
|
|
191
|
+
markPendingAskState({
|
|
192
|
+
userId: args.userId,
|
|
193
|
+
from: args.callerTopic,
|
|
194
|
+
to: source.from,
|
|
195
|
+
requestId: source.requestId,
|
|
196
|
+
state: args.state
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function clearPendingAskSources(args) {
|
|
201
|
+
for (const source of args.sources ?? []) {
|
|
202
|
+
if (!source.from)
|
|
203
|
+
continue;
|
|
204
|
+
clearPendingAsk({
|
|
205
|
+
userId: args.userId,
|
|
206
|
+
from: args.callerTopic,
|
|
207
|
+
to: source.from,
|
|
208
|
+
requestId: source.requestId
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function listPendingAsksForCaller(args) {
|
|
213
|
+
const dir = pendingAskDir(args.userId);
|
|
214
|
+
let files;
|
|
215
|
+
try {
|
|
216
|
+
files = readdirSync(dir);
|
|
217
|
+
} catch {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
const records = [];
|
|
221
|
+
for (const fileName of files) {
|
|
222
|
+
const parsed = parsePendingAskFilename(fileName);
|
|
223
|
+
if (!parsed || parsed.from !== args.from)
|
|
224
|
+
continue;
|
|
225
|
+
const path = join(dir, fileName);
|
|
226
|
+
const record = readPendingAskFile(path, {
|
|
227
|
+
userId: args.userId,
|
|
228
|
+
from: parsed.from,
|
|
229
|
+
to: parsed.to
|
|
230
|
+
});
|
|
231
|
+
if (!record)
|
|
232
|
+
continue;
|
|
233
|
+
if (isStale(record, path)) {
|
|
234
|
+
try {
|
|
235
|
+
unlinkSync(path);
|
|
236
|
+
} catch {}
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
records.push(record);
|
|
240
|
+
}
|
|
241
|
+
return records;
|
|
242
|
+
}
|
|
243
|
+
function deletePendingAsksForTopic(args) {
|
|
244
|
+
const dir = pendingAskDir(args.userId);
|
|
245
|
+
let files;
|
|
246
|
+
try {
|
|
247
|
+
files = readdirSync(dir);
|
|
248
|
+
} catch {
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
let deleted = 0;
|
|
252
|
+
for (const fileName of files) {
|
|
253
|
+
const parsed = parsePendingAskFilename(fileName);
|
|
254
|
+
if (!parsed || parsed.from !== args.topicName && parsed.to !== args.topicName)
|
|
255
|
+
continue;
|
|
256
|
+
try {
|
|
257
|
+
unlinkSync(join(dir, fileName));
|
|
258
|
+
deleted++;
|
|
259
|
+
} catch {}
|
|
260
|
+
}
|
|
261
|
+
return deleted;
|
|
262
|
+
}
|
|
263
|
+
function describePendingAskState(state) {
|
|
264
|
+
switch (state) {
|
|
265
|
+
case "requested":
|
|
266
|
+
return "\uB300\uC0C1 \uC138\uC158 \uCC98\uB9AC \uC911";
|
|
267
|
+
case "reply_ready":
|
|
268
|
+
return "\uC751\uB2F5 \uC0DD\uC131 \uC644\uB8CC, \uC774 \uC138\uC158 \uC8FC\uC785 \uC900\uBE44 \uC911";
|
|
269
|
+
case "queued_for_caller":
|
|
270
|
+
return "\uC751\uB2F5 \uC0DD\uC131 \uC644\uB8CC, \uC774 \uC138\uC158 \uC2E4\uD589 \uC885\uB8CC \uD6C4 \uC8FC\uC785 \uB300\uAE30 \uC911";
|
|
271
|
+
case "injecting_to_caller":
|
|
272
|
+
return "\uC751\uB2F5\uC744 \uC774 \uC138\uC158\uC5D0 \uC8FC\uC785 \uC911";
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
export {
|
|
276
|
+
markPendingAskState,
|
|
277
|
+
markPendingAskSources,
|
|
278
|
+
listPendingAsksForCaller,
|
|
279
|
+
describePendingAskState,
|
|
280
|
+
deletePendingAsksForTopic,
|
|
281
|
+
createPendingAsk,
|
|
282
|
+
clearPendingAskSources,
|
|
283
|
+
clearPendingAsk,
|
|
284
|
+
PENDING_ASK_TTL_MS
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export { PENDING_ASK_TTL_MS, clearPendingAsk, deletePendingAsksForTopic };
|
|
288
|
+
|
|
289
|
+
//# debugId=184D5346E7B6A60C64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../packages/core/src/storage/session-asks.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import {\n closeSync,\n existsSync,\n mkdirSync,\n openSync,\n readdirSync,\n readFileSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { SESSION_ASKS_DIR } from \"#platform/config\";\n\nexport const PENDING_ASK_TTL_MS = 15 * 60 * 1000;\n\nexport type PendingAskState =\n | \"requested\"\n | \"reply_ready\"\n | \"queued_for_caller\"\n | \"injecting_to_caller\";\n\nexport type PendingAskUserId = number | string;\n\nexport interface PendingAskRecord {\n userId: PendingAskUserId;\n from: string;\n to: string;\n requestId: string;\n contextId?: string;\n state: PendingAskState;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface AskReplySource {\n from?: string;\n requestId?: string;\n contextId?: string;\n}\n\ninterface AskKey {\n userId: PendingAskUserId;\n from: string;\n to: string;\n}\n\ninterface AskIdentity extends AskKey {\n requestId?: string;\n}\n\nfunction pendingAskDir(userId: PendingAskUserId): string {\n return join(SESSION_ASKS_DIR, String(userId));\n}\n\nconst ASK_FILENAME_PREFIX = \"v2-\";\n\nfunction encodeAskKey(key: Pick<AskKey, \"from\" | \"to\">): string {\n return Buffer.from(JSON.stringify([key.from, key.to]), \"utf8\").toString(\"base64url\");\n}\n\nfunction pendingAskPath(key: AskKey): string {\n return join(pendingAskDir(key.userId), `${ASK_FILENAME_PREFIX}${encodeAskKey(key)}.pending`);\n}\n\nfunction legacyPendingAskPath(key: AskKey): string | null {\n if (key.from.includes(\"___\") || /[\\\\/]/.test(key.from) || /[\\\\/]/.test(key.to)) return null;\n return join(pendingAskDir(key.userId), `${key.from}___${key.to}.pending`);\n}\n\nfunction resolvePendingAskPath(key: AskKey): string {\n const current = pendingAskPath(key);\n if (existsSync(current)) return current;\n const legacy = legacyPendingAskPath(key);\n if (!legacy || !existsSync(legacy)) return current;\n\n // Keep one durable identity across the filename transition. renameSync is\n // atomic within this directory, so concurrent creators observe either the\n // legacy request or its v2 name, never a copied half-state.\n try {\n renameSync(legacy, current);\n return current;\n } catch {\n return existsSync(current) ? current : legacy;\n }\n}\n\nfunction parsePendingAskFilename(fileName: string): { from: string; to: string } | null {\n if (!fileName.endsWith(\".pending\")) return null;\n const raw = fileName.slice(0, -\".pending\".length);\n if (raw.startsWith(ASK_FILENAME_PREFIX)) {\n try {\n const decoded = JSON.parse(\n Buffer.from(raw.slice(ASK_FILENAME_PREFIX.length), \"base64url\").toString(\"utf8\"),\n ) as unknown;\n if (\n Array.isArray(decoded) &&\n decoded.length === 2 &&\n decoded.every((part) => typeof part === \"string\")\n ) {\n return { from: decoded[0], to: decoded[1] };\n }\n } catch {\n return null;\n }\n return null;\n }\n\n // Read files created before the opaque v2 key was introduced.\n const sep = raw.indexOf(\"___\");\n if (sep < 0) return null;\n return { from: raw.slice(0, sep), to: raw.slice(sep + 3) };\n}\n\nfunction readPendingAskFile(path: string, fallback: AskKey): PendingAskRecord | null {\n try {\n const raw = readFileSync(path, \"utf8\").trim();\n if (!raw) return null;\n if (!raw.startsWith(\"{\")) {\n const ts = new Date(statSync(path).mtimeMs).toISOString();\n return {\n userId: fallback.userId,\n from: fallback.from,\n to: fallback.to,\n requestId: raw,\n state: \"requested\",\n createdAt: ts,\n updatedAt: ts,\n };\n }\n const parsed = JSON.parse(raw) as Partial<PendingAskRecord>;\n if (!parsed.requestId || !parsed.from || !parsed.to) return null;\n const now = new Date().toISOString();\n return {\n userId: parsed.userId ?? fallback.userId,\n from: parsed.from,\n to: parsed.to,\n requestId: parsed.requestId,\n contextId: parsed.contextId,\n state: parsed.state ?? \"requested\",\n createdAt: parsed.createdAt ?? now,\n updatedAt: parsed.updatedAt ?? parsed.createdAt ?? now,\n };\n } catch {\n return null;\n }\n}\n\nfunction isStale(record: PendingAskRecord | null, path: string): boolean {\n const ts = record ? Date.parse(record.updatedAt || record.createdAt) : Number.NaN;\n if (Number.isFinite(ts)) return Date.now() - ts > PENDING_ASK_TTL_MS;\n try {\n return Date.now() - statSync(path).mtimeMs > PENDING_ASK_TTL_MS;\n } catch {\n return false;\n }\n}\n\nfunction writePendingAsk(record: PendingAskRecord, path = pendingAskPath(record)): void {\n mkdirSync(pendingAskDir(record.userId), { recursive: true });\n writeFileSync(path, `${JSON.stringify(record)}\\n`);\n}\n\nexport function createPendingAsk(args: {\n userId: PendingAskUserId;\n from: string;\n to: string;\n requestId: string;\n contextId?: string;\n}):\n | { ok: true; record: PendingAskRecord }\n | { ok: false; existing: PendingAskRecord | null; stale: boolean } {\n const path = resolvePendingAskPath(args);\n const now = new Date().toISOString();\n const record: PendingAskRecord = {\n userId: args.userId,\n from: args.from,\n to: args.to,\n requestId: args.requestId,\n contextId: args.contextId,\n state: \"requested\",\n createdAt: now,\n updatedAt: now,\n };\n\n mkdirSync(pendingAskDir(args.userId), { recursive: true });\n for (let attempt = 0; attempt < 2; attempt++) {\n let fd: number | null = null;\n try {\n fd = openSync(path, \"wx\");\n writeFileSync(fd, `${JSON.stringify(record)}\\n`);\n return { ok: true, record };\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n const existing = readPendingAskFile(path, args);\n if (!isStale(existing, path)) return { ok: false, existing, stale: false };\n try {\n unlinkSync(path);\n } catch {\n return { ok: false, existing, stale: true };\n }\n } finally {\n if (fd !== null) closeSync(fd);\n }\n }\n\n return { ok: false, existing: readPendingAskFile(path, args), stale: true };\n}\n\nexport function markPendingAskState(\n args: AskIdentity & { state: PendingAskState },\n): PendingAskRecord | null {\n const path = resolvePendingAskPath(args);\n const record = readPendingAskFile(path, args);\n if (!record) return null;\n if (args.requestId && record.requestId !== args.requestId) return record;\n const next = {\n ...record,\n state: args.state,\n updatedAt: new Date().toISOString(),\n };\n writePendingAsk(next, path);\n return next;\n}\n\nexport function clearPendingAsk(args: AskIdentity): boolean {\n const path = resolvePendingAskPath(args);\n const record = readPendingAskFile(path, args);\n if (args.requestId && record && record.requestId !== args.requestId) return false;\n try {\n unlinkSync(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function markPendingAskSources(args: {\n userId: PendingAskUserId;\n callerTopic: string;\n sources?: AskReplySource[];\n state: PendingAskState;\n}): void {\n for (const source of args.sources ?? []) {\n if (!source.from) continue;\n markPendingAskState({\n userId: args.userId,\n from: args.callerTopic,\n to: source.from,\n requestId: source.requestId,\n state: args.state,\n });\n }\n}\n\nexport function clearPendingAskSources(args: {\n userId: PendingAskUserId;\n callerTopic: string;\n sources?: AskReplySource[];\n}): void {\n for (const source of args.sources ?? []) {\n if (!source.from) continue;\n clearPendingAsk({\n userId: args.userId,\n from: args.callerTopic,\n to: source.from,\n requestId: source.requestId,\n });\n }\n}\n\nexport function listPendingAsksForCaller(args: {\n userId: PendingAskUserId;\n from: string;\n}): PendingAskRecord[] {\n const dir = pendingAskDir(args.userId);\n let files: string[];\n try {\n files = readdirSync(dir);\n } catch {\n return [];\n }\n\n const records: PendingAskRecord[] = [];\n for (const fileName of files) {\n const parsed = parsePendingAskFilename(fileName);\n if (!parsed || parsed.from !== args.from) continue;\n const path = join(dir, fileName);\n const record = readPendingAskFile(path, {\n userId: args.userId,\n from: parsed.from,\n to: parsed.to,\n });\n if (!record) continue;\n if (isStale(record, path)) {\n try {\n unlinkSync(path);\n } catch {\n // Best-effort stale cleanup only.\n }\n continue;\n }\n records.push(record);\n }\n return records;\n}\n\n/** Remove durable ask edges to or from a topic that no longer exists. */\nexport function deletePendingAsksForTopic(args: {\n userId: PendingAskUserId;\n topicName: string;\n}): number {\n const dir = pendingAskDir(args.userId);\n let files: string[];\n try {\n files = readdirSync(dir);\n } catch {\n return 0;\n }\n\n let deleted = 0;\n for (const fileName of files) {\n const parsed = parsePendingAskFilename(fileName);\n if (!parsed || (parsed.from !== args.topicName && parsed.to !== args.topicName)) continue;\n try {\n unlinkSync(join(dir, fileName));\n deleted++;\n } catch {\n // Best-effort teardown; stale cleanup will retry any file that survives.\n }\n }\n return deleted;\n}\n\nexport function describePendingAskState(state: PendingAskState): string {\n switch (state) {\n case \"requested\":\n return \"대상 세션 처리 중\";\n case \"reply_ready\":\n return \"응답 생성 완료, 이 세션 주입 준비 중\";\n case \"queued_for_caller\":\n return \"응답 생성 완료, 이 세션 실행 종료 후 주입 대기 중\";\n case \"injecting_to_caller\":\n return \"응답을 이 세션에 주입 중\";\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "184D5346E7B6A60C64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|