@hasna/recordings 0.1.20 → 0.1.21

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/dist/mcp/index.js CHANGED
@@ -21,9 +21,13 @@ var __require = import.meta.require;
21
21
  var require_package = __commonJS((exports, module) => {
22
22
  module.exports = {
23
23
  name: "@hasna/recordings",
24
- version: "0.1.11",
24
+ version: "0.1.21",
25
25
  type: "module",
26
26
  description: "Speech-to-text recording tool with MCP and CLI \u2014 records, transcribes, and optionally enhances text using AI",
27
+ repository: {
28
+ type: "git",
29
+ url: "git+https://github.com/hasna/recordings.git"
30
+ },
27
31
  main: "dist/index.js",
28
32
  types: "dist/index.d.ts",
29
33
  bin: {
@@ -86,6 +90,51 @@ var require_package = __commonJS((exports, module) => {
86
90
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
87
91
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
88
92
 
93
+ // src/mcp/http.ts
94
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
95
+ var DEFAULT_MCP_HTTP_PORT = 8829;
96
+ var MCP_HTTP_HOST = "127.0.0.1";
97
+ function isHttpMode(args) {
98
+ return args.includes("--http") || process.env.MCP_HTTP === "1";
99
+ }
100
+ function resolveMcpHttpPort(args) {
101
+ const portIdx = args.indexOf("--port");
102
+ if (portIdx >= 0 && args[portIdx + 1]) {
103
+ return Number(args[portIdx + 1]);
104
+ }
105
+ const envPort = process.env.MCP_HTTP_PORT;
106
+ if (envPort)
107
+ return Number(envPort);
108
+ return DEFAULT_MCP_HTTP_PORT;
109
+ }
110
+ async function handleMcpRequest(req, buildServer) {
111
+ const transport = new WebStandardStreamableHTTPServerTransport({
112
+ sessionIdGenerator: undefined
113
+ });
114
+ const server = buildServer();
115
+ await server.connect(transport);
116
+ return transport.handleRequest(req);
117
+ }
118
+ function startMcpHttpServer(options) {
119
+ const { name, port, buildServer } = options;
120
+ const server = Bun.serve({
121
+ hostname: MCP_HTTP_HOST,
122
+ port,
123
+ async fetch(req) {
124
+ const url = new URL(req.url);
125
+ if (url.pathname === "/health" && req.method === "GET") {
126
+ return Response.json({ status: "ok", name });
127
+ }
128
+ if (url.pathname === "/mcp") {
129
+ return handleMcpRequest(req, buildServer);
130
+ }
131
+ return new Response("Not Found", { status: 404 });
132
+ }
133
+ });
134
+ console.error(`${name}-mcp HTTP listening on http://${MCP_HTTP_HOST}:${port}/mcp`);
135
+ return server;
136
+ }
137
+
89
138
  // node_modules/@hasna/cloud/dist/index.js
90
139
  import { createRequire } from "module";
91
140
  import { Database } from "bun:sqlite";
@@ -14526,15 +14575,40 @@ function runMigrations(db) {
14526
14575
  const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
14527
14576
  const currentLevel = result?.max_id ?? -1;
14528
14577
  for (let i = currentLevel + 1;i < MIGRATIONS.length; i++) {
14529
- db.run(MIGRATIONS[i]);
14530
14578
  try {
14579
+ db.run(MIGRATIONS[i]);
14531
14580
  db.query("INSERT INTO _migrations (id) VALUES (?)").run(i);
14532
14581
  } catch (e) {
14582
+ if (isBenignMigrationError(e)) {
14583
+ db.query("INSERT OR IGNORE INTO _migrations (id) VALUES (?)").run(i);
14584
+ continue;
14585
+ }
14533
14586
  if (!(e instanceof Error && e.message.includes("UNIQUE constraint failed"))) {
14534
14587
  throw e;
14535
14588
  }
14536
14589
  }
14537
14590
  }
14591
+ repairSchemaDrift(db);
14592
+ }
14593
+ function isBenignMigrationError(error) {
14594
+ if (!(error instanceof Error))
14595
+ return false;
14596
+ return error.message.includes("duplicate column name");
14597
+ }
14598
+ function repairSchemaDrift(db) {
14599
+ ensureColumn(db, "recordings", "goal", "TEXT");
14600
+ ensureColumn(db, "recordings", "role", "TEXT");
14601
+ ensureColumn(db, "recordings", "task_list_id", "TEXT");
14602
+ ensureColumn(db, "recordings", "machine_id", "TEXT");
14603
+ ensureColumn(db, "recordings", "metadata", "TEXT DEFAULT '{}'");
14604
+ ensureColumn(db, "agents", "active_project_id", "TEXT REFERENCES projects(id) ON DELETE SET NULL");
14605
+ }
14606
+ function ensureColumn(db, table, column, definition) {
14607
+ const rows = db.query(`PRAGMA table_info(${table})`).all();
14608
+ if (rows.some((row) => row.name === column)) {
14609
+ return;
14610
+ }
14611
+ db.run(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
14538
14612
  }
14539
14613
  function getAdapter() {
14540
14614
  if (!_adapter) {
@@ -14954,388 +15028,404 @@ async function processText(rawText, config, systemPrompt) {
14954
15028
  }
14955
15029
 
14956
15030
  // src/version.ts
14957
- var VERSION = "0.1.11";
15031
+ var VERSION = "0.1.21";
14958
15032
 
14959
15033
  // src/mcp/index.ts
14960
15034
  var config = loadConfig();
14961
15035
  ensureDataDir(config);
14962
15036
  getDatabase(config.db_path);
14963
- var server = new McpServer({
14964
- name: "recordings",
14965
- version: VERSION
14966
- });
14967
- var registerTool = server.tool.bind(server);
14968
- function text(content) {
14969
- return { content: [{ type: "text", text: content }] };
14970
- }
14971
- function errorResult(e) {
14972
- const msg = e instanceof Error ? e.message : String(e);
14973
- return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
14974
- }
14975
- function compact(r) {
14976
- const t = (r.processed_text || r.raw_text).slice(0, 80);
14977
- return `${r.id.slice(0, 8)} | ${r.processing_mode} | ${r.created_at.slice(0, 16)} | ${t}${t.length >= 80 ? "..." : ""}`;
14978
- }
14979
- function full(r) {
14980
- const lines = [`ID: ${r.id}`, `Mode: ${r.processing_mode}`, `Model: ${r.model_used}`];
14981
- if (r.enhancement_model)
14982
- lines.push(`Enhanced by: ${r.enhancement_model}`);
14983
- if (r.duration_ms)
14984
- lines.push(`Duration: ${(r.duration_ms / 1000).toFixed(1)}s`);
14985
- if (r.language)
14986
- lines.push(`Language: ${r.language}`);
14987
- if (r.tags.length > 0)
14988
- lines.push(`Tags: ${r.tags.join(", ")}`);
14989
- if (r.agent_id)
14990
- lines.push(`Agent: ${r.agent_id}`);
14991
- if (r.project_id)
14992
- lines.push(`Project: ${r.project_id}`);
14993
- if (r.session_id)
14994
- lines.push(`Session: ${r.session_id}`);
14995
- lines.push(`Created: ${r.created_at}`);
14996
- lines.push(`Text: ${r.raw_text}`);
14997
- if (r.processed_text && r.processed_text !== r.raw_text) {
14998
- lines.push(`Enhanced: ${r.processed_text}`);
14999
- }
15000
- return lines.join(`
15037
+ function buildServer() {
15038
+ const server = new McpServer({
15039
+ name: "recordings",
15040
+ version: VERSION
15041
+ });
15042
+ const registerTool = server.tool.bind(server);
15043
+ function text(content) {
15044
+ return { content: [{ type: "text", text: content }] };
15045
+ }
15046
+ function errorResult(e) {
15047
+ const msg = e instanceof Error ? e.message : String(e);
15048
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
15049
+ }
15050
+ function compact(r) {
15051
+ const t = (r.processed_text || r.raw_text).slice(0, 80);
15052
+ return `${r.id.slice(0, 8)} | ${r.processing_mode} | ${r.created_at.slice(0, 16)} | ${t}${t.length >= 80 ? "..." : ""}`;
15053
+ }
15054
+ function full(r) {
15055
+ const lines = [`ID: ${r.id}`, `Mode: ${r.processing_mode}`, `Model: ${r.model_used}`];
15056
+ if (r.enhancement_model)
15057
+ lines.push(`Enhanced by: ${r.enhancement_model}`);
15058
+ if (r.duration_ms)
15059
+ lines.push(`Duration: ${(r.duration_ms / 1000).toFixed(1)}s`);
15060
+ if (r.language)
15061
+ lines.push(`Language: ${r.language}`);
15062
+ if (r.tags.length > 0)
15063
+ lines.push(`Tags: ${r.tags.join(", ")}`);
15064
+ if (r.agent_id)
15065
+ lines.push(`Agent: ${r.agent_id}`);
15066
+ if (r.project_id)
15067
+ lines.push(`Project: ${r.project_id}`);
15068
+ if (r.session_id)
15069
+ lines.push(`Session: ${r.session_id}`);
15070
+ lines.push(`Created: ${r.created_at}`);
15071
+ lines.push(`Text: ${r.raw_text}`);
15072
+ if (r.processed_text && r.processed_text !== r.raw_text) {
15073
+ lines.push(`Enhanced: ${r.processed_text}`);
15074
+ }
15075
+ return lines.join(`
15001
15076
  `);
15002
- }
15003
- async function saveRecordingMemento(args) {
15004
- try {
15005
- const proc = Bun.spawn([
15006
- "mementos",
15007
- "save",
15008
- "--scope",
15009
- "shared",
15010
- "--category",
15011
- "history",
15012
- "--importance",
15013
- "5",
15014
- "--tags",
15015
- "recording,transcription",
15016
- "--summary",
15017
- args.summary,
15018
- args.key,
15019
- args.value
15020
- ], {
15021
- stdout: "ignore",
15022
- stderr: "ignore"
15023
- });
15024
- await proc.exited;
15025
- } catch {}
15026
- }
15027
- var toolDocs = {
15028
- transcribe_audio: `Transcribe audio file. Auto-enhances if needed.
15077
+ }
15078
+ async function saveRecordingMemento(args) {
15079
+ try {
15080
+ const proc = Bun.spawn([
15081
+ "mementos",
15082
+ "save",
15083
+ "--scope",
15084
+ "shared",
15085
+ "--category",
15086
+ "history",
15087
+ "--importance",
15088
+ "5",
15089
+ "--tags",
15090
+ "recording,transcription",
15091
+ "--summary",
15092
+ args.summary,
15093
+ args.key,
15094
+ args.value
15095
+ ], {
15096
+ stdout: "ignore",
15097
+ stderr: "ignore"
15098
+ });
15099
+ await proc.exited;
15100
+ } catch {}
15101
+ }
15102
+ const toolDocs = {
15103
+ transcribe_audio: `Transcribe audio file. Auto-enhances if needed.
15029
15104
  Params: audio_path (string, required): path to wav/mp3/m4a/webm | language (string): ISO code e.g. en/es/fr | no_enhance (bool): skip AI enhancement | tags (string[]): tags | agent_id (string) | project_id (string) | session_id (string)`,
15030
- save_recording: `Save text as recording. Auto-enhances if needed.
15105
+ save_recording: `Save text as recording. Auto-enhances if needed.
15031
15106
  Params: text (string, required): text to save | enhance (bool): force enhancement | tags (string[]) | agent_id (string) | project_id (string) | session_id (string) | metadata (object)`,
15032
- get_recording: `Get recording by ID or prefix.
15107
+ get_recording: `Get recording by ID or prefix.
15033
15108
  Params: id (string, required): recording ID or prefix`,
15034
- list_recordings: `List recordings, compact by default, most recent first.
15109
+ list_recordings: `List recordings, compact by default, most recent first.
15035
15110
  Params: limit (number, default 10) | offset (number) | processing_mode ('raw'|'enhanced') | tags (string[]) | search (string): text search | since/until (ISO date) | agent_id | project_id | session_id | full (bool): verbose output`,
15036
- search_recordings: `Search recordings by text content.
15111
+ search_recordings: `Search recordings by text content.
15037
15112
  Params: query (string, required) | limit (number, default 10) | agent_id | project_id | full (bool): verbose output`,
15038
- delete_recording: `Delete recording by ID.
15113
+ delete_recording: `Delete recording by ID.
15039
15114
  Params: id (string, required)`,
15040
- recording_stats: `Recording count, mode breakdown, duration.
15115
+ recording_stats: `Recording count, mode breakdown, duration.
15041
15116
  Params: none`,
15042
- detect_enhancement: `Check if text needs AI enhancement.
15117
+ detect_enhancement: `Check if text needs AI enhancement.
15043
15118
  Params: text (string, required)`,
15044
- register_agent: `Register agent (idempotent). Auto-updates last_seen_at on re-register.
15119
+ register_agent: `Register agent (idempotent). Auto-updates last_seen_at on re-register.
15045
15120
  Params: name (string, required) | description (string) | role (string)`,
15046
- list_agents: `List registered agents.
15121
+ list_agents: `List registered agents.
15047
15122
  Params: none`,
15048
- get_agent: `Get agent by ID or name.
15123
+ get_agent: `Get agent by ID or name.
15049
15124
  Params: id (string, required)`,
15050
- heartbeat: `Update last_seen_at to signal agent is active.
15125
+ heartbeat: `Update last_seen_at to signal agent is active.
15051
15126
  Params: agent_id (string, required): agent ID or name`,
15052
- set_focus: `Set active project context for this agent session.
15127
+ set_focus: `Set active project context for this agent session.
15053
15128
  Params: agent_id (string, required) | project_id (string, nullable): project ID or null to clear`,
15054
- register_project: `Register project (idempotent).
15129
+ register_project: `Register project (idempotent).
15055
15130
  Params: name (string, required) | path (string, required): absolute path | description (string)`,
15056
- list_projects: `List registered projects.
15131
+ list_projects: `List registered projects.
15057
15132
  Params: none`
15058
- };
15059
- registerTool("describe_tool", "Get full param docs for any tool.", { name: exports_external2.string() }, async (args) => {
15060
- const doc = toolDocs[args.name];
15061
- return doc ? text(doc) : text(`Unknown tool: ${args.name}. Available: ${Object.keys(toolDocs).join(", ")}`);
15062
- });
15063
- registerTool("transcribe_audio", "Transcribe audio file. Auto-enhances if needed.", {
15064
- audio_path: exports_external2.string(),
15065
- language: exports_external2.string().optional(),
15066
- no_enhance: exports_external2.boolean().optional(),
15067
- tags: exports_external2.array(exports_external2.string()).optional(),
15068
- agent_id: exports_external2.string().optional(),
15069
- project_id: exports_external2.string().optional(),
15070
- session_id: exports_external2.string().optional()
15071
- }, async (args) => {
15072
- try {
15073
- const cfg = { ...config };
15074
- if (args.language)
15075
- cfg.language = args.language;
15076
- if (args.no_enhance)
15077
- cfg.auto_enhance = false;
15078
- const transcription = await transcribeAudio(args.audio_path, cfg);
15079
- const processed = await processText(transcription.text, cfg);
15080
- const recording = createRecording({
15081
- audio_path: args.audio_path,
15082
- raw_text: transcription.text,
15083
- processed_text: processed.mode === "enhanced" ? processed.text : undefined,
15084
- processing_mode: processed.mode,
15085
- model_used: transcription.model,
15086
- enhancement_model: processed.enhancement_model || undefined,
15087
- duration_ms: transcription.duration_ms,
15088
- language: transcription.language || undefined,
15089
- tags: args.tags,
15090
- agent_id: args.agent_id,
15091
- project_id: args.project_id,
15092
- session_id: args.session_id
15093
- });
15094
- if (args.agent_id) {
15095
- await saveRecordingMemento({
15096
- key: `recording-${recording.id}`,
15097
- value: JSON.stringify({
15098
- recording_id: recording.id,
15099
- text: processed.mode === "enhanced" ? processed.text : transcription.text,
15100
- agent_id: args.agent_id,
15101
- project_id: args.project_id,
15102
- session_id: args.session_id,
15103
- created_at: recording.created_at
15104
- }),
15105
- summary: `Recording ${recording.id.slice(0, 8)} for ${args.agent_id}`
15106
- });
15133
+ };
15134
+ registerTool("describe_tool", "Get full param docs for any tool.", { name: exports_external2.string() }, async (args) => {
15135
+ const doc = toolDocs[args.name];
15136
+ return doc ? text(doc) : text(`Unknown tool: ${args.name}. Available: ${Object.keys(toolDocs).join(", ")}`);
15137
+ });
15138
+ registerTool("transcribe_audio", "Transcribe audio file. Auto-enhances if needed.", {
15139
+ audio_path: exports_external2.string(),
15140
+ language: exports_external2.string().optional(),
15141
+ no_enhance: exports_external2.boolean().optional(),
15142
+ tags: exports_external2.array(exports_external2.string()).optional(),
15143
+ agent_id: exports_external2.string().optional(),
15144
+ project_id: exports_external2.string().optional(),
15145
+ session_id: exports_external2.string().optional()
15146
+ }, async (args) => {
15147
+ try {
15148
+ const cfg = { ...config };
15149
+ if (args.language)
15150
+ cfg.language = args.language;
15151
+ if (args.no_enhance)
15152
+ cfg.auto_enhance = false;
15153
+ const transcription = await transcribeAudio(args.audio_path, cfg);
15154
+ const processed = await processText(transcription.text, cfg);
15155
+ const recording = createRecording({
15156
+ audio_path: args.audio_path,
15157
+ raw_text: transcription.text,
15158
+ processed_text: processed.mode === "enhanced" ? processed.text : undefined,
15159
+ processing_mode: processed.mode,
15160
+ model_used: transcription.model,
15161
+ enhancement_model: processed.enhancement_model || undefined,
15162
+ duration_ms: transcription.duration_ms,
15163
+ language: transcription.language || undefined,
15164
+ tags: args.tags,
15165
+ agent_id: args.agent_id,
15166
+ project_id: args.project_id,
15167
+ session_id: args.session_id
15168
+ });
15169
+ if (args.agent_id) {
15170
+ await saveRecordingMemento({
15171
+ key: `recording-${recording.id}`,
15172
+ value: JSON.stringify({
15173
+ recording_id: recording.id,
15174
+ text: processed.mode === "enhanced" ? processed.text : transcription.text,
15175
+ agent_id: args.agent_id,
15176
+ project_id: args.project_id,
15177
+ session_id: args.session_id,
15178
+ created_at: recording.created_at
15179
+ }),
15180
+ summary: `Recording ${recording.id.slice(0, 8)} for ${args.agent_id}`
15181
+ });
15182
+ }
15183
+ const output = processed.mode === "enhanced" ? processed.text : transcription.text;
15184
+ return text(`${recording.id.slice(0, 8)} | ${processed.mode} | ${output}`);
15185
+ } catch (e) {
15186
+ return errorResult(e);
15107
15187
  }
15108
- const output = processed.mode === "enhanced" ? processed.text : transcription.text;
15109
- return text(`${recording.id.slice(0, 8)} | ${processed.mode} | ${output}`);
15110
- } catch (e) {
15111
- return errorResult(e);
15112
- }
15113
- });
15114
- registerTool("save_recording", "Save text as recording. Auto-enhances if needed.", {
15115
- text: exports_external2.string(),
15116
- enhance: exports_external2.boolean().optional(),
15117
- tags: exports_external2.array(exports_external2.string()).optional(),
15118
- agent_id: exports_external2.string().optional(),
15119
- project_id: exports_external2.string().optional(),
15120
- session_id: exports_external2.string().optional(),
15121
- goal: exports_external2.string().optional().describe("Goal or purpose of this recording session (e.g. 'code review for PR #123')"),
15122
- role: exports_external2.string().optional().describe("Agent role for this session (e.g. 'dev agent for connectdev')"),
15123
- task_list_id: exports_external2.string().optional().describe("Task list ID to bind this recording to"),
15124
- metadata: exports_external2.record(exports_external2.unknown()).optional()
15125
- }, async (args) => {
15126
- try {
15127
- let processedText;
15128
- let mode = "raw";
15129
- let enhModel;
15130
- if (args.enhance !== false) {
15131
- const processed = await processText(args.text, config);
15132
- if (processed.mode === "enhanced") {
15133
- processedText = processed.text;
15134
- mode = "enhanced";
15135
- enhModel = processed.enhancement_model || undefined;
15136
- }
15137
- }
15138
- const recording = createRecording({
15139
- raw_text: args.text,
15140
- processed_text: processedText,
15141
- processing_mode: mode,
15142
- model_used: "direct-input",
15143
- enhancement_model: enhModel,
15144
- tags: args.tags,
15145
- agent_id: args.agent_id,
15146
- project_id: args.project_id,
15147
- session_id: args.session_id,
15148
- goal: args.goal,
15149
- role: args.role,
15150
- task_list_id: args.task_list_id,
15151
- metadata: args.metadata
15152
- });
15153
- const output = processedText || args.text;
15154
- return text(`${recording.id.slice(0, 8)} | ${mode} | ${output}`);
15155
- } catch (e) {
15156
- return errorResult(e);
15157
- }
15158
- });
15159
- registerTool("get_recording", "Get recording by ID or prefix.", { id: exports_external2.string() }, async (args) => {
15160
- try {
15161
- const r = getRecording(args.id);
15162
- if (!r)
15163
- return text(`Not found: ${args.id}`);
15164
- return text(full(r));
15165
- } catch (e) {
15166
- return errorResult(e);
15167
- }
15168
- });
15169
- registerTool("list_recordings", "List recordings. Compact default, recent first.", {
15170
- limit: exports_external2.number().optional(),
15171
- offset: exports_external2.number().optional(),
15172
- processing_mode: exports_external2.enum(["raw", "enhanced"]).optional(),
15173
- tags: exports_external2.array(exports_external2.string()).optional(),
15174
- search: exports_external2.string().optional(),
15175
- since: exports_external2.string().optional(),
15176
- until: exports_external2.string().optional(),
15177
- agent_id: exports_external2.string().optional(),
15178
- project_id: exports_external2.string().optional(),
15179
- session_id: exports_external2.string().optional(),
15180
- full: exports_external2.boolean().optional()
15181
- }, async (args) => {
15182
- try {
15183
- const filter = {
15184
- limit: args.limit || 10,
15185
- offset: args.offset,
15186
- processing_mode: args.processing_mode,
15187
- tags: args.tags,
15188
- search: args.search,
15189
- since: args.since,
15190
- until: args.until,
15191
- agent_id: args.agent_id,
15192
- project_id: args.project_id,
15193
- session_id: args.session_id
15194
- };
15195
- const recordings = listRecordings(filter);
15196
- if (recordings.length === 0)
15197
- return text("No recordings found.");
15198
- const fmt = args.full ? full : compact;
15199
- const sep = args.full ? `
15188
+ });
15189
+ registerTool("save_recording", "Save text as recording. Auto-enhances if needed.", {
15190
+ text: exports_external2.string(),
15191
+ enhance: exports_external2.boolean().optional(),
15192
+ tags: exports_external2.array(exports_external2.string()).optional(),
15193
+ agent_id: exports_external2.string().optional(),
15194
+ project_id: exports_external2.string().optional(),
15195
+ session_id: exports_external2.string().optional(),
15196
+ goal: exports_external2.string().optional().describe("Goal or purpose of this recording session (e.g. 'code review for PR #123')"),
15197
+ role: exports_external2.string().optional().describe("Agent role for this session (e.g. 'dev agent for connectdev')"),
15198
+ task_list_id: exports_external2.string().optional().describe("Task list ID to bind this recording to"),
15199
+ metadata: exports_external2.record(exports_external2.unknown()).optional()
15200
+ }, async (args) => {
15201
+ try {
15202
+ let processedText;
15203
+ let mode = "raw";
15204
+ let enhModel;
15205
+ if (args.enhance !== false) {
15206
+ const processed = await processText(args.text, config);
15207
+ if (processed.mode === "enhanced") {
15208
+ processedText = processed.text;
15209
+ mode = "enhanced";
15210
+ enhModel = processed.enhancement_model || undefined;
15211
+ }
15212
+ }
15213
+ const recording = createRecording({
15214
+ raw_text: args.text,
15215
+ processed_text: processedText,
15216
+ processing_mode: mode,
15217
+ model_used: "direct-input",
15218
+ enhancement_model: enhModel,
15219
+ tags: args.tags,
15220
+ agent_id: args.agent_id,
15221
+ project_id: args.project_id,
15222
+ session_id: args.session_id,
15223
+ goal: args.goal,
15224
+ role: args.role,
15225
+ task_list_id: args.task_list_id,
15226
+ metadata: args.metadata
15227
+ });
15228
+ const output = processedText || args.text;
15229
+ return text(`${recording.id.slice(0, 8)} | ${mode} | ${output}`);
15230
+ } catch (e) {
15231
+ return errorResult(e);
15232
+ }
15233
+ });
15234
+ registerTool("get_recording", "Get recording by ID or prefix.", { id: exports_external2.string() }, async (args) => {
15235
+ try {
15236
+ const r = getRecording(args.id);
15237
+ if (!r)
15238
+ return text(`Not found: ${args.id}`);
15239
+ return text(full(r));
15240
+ } catch (e) {
15241
+ return errorResult(e);
15242
+ }
15243
+ });
15244
+ registerTool("list_recordings", "List recordings. Compact default, recent first.", {
15245
+ limit: exports_external2.number().optional(),
15246
+ offset: exports_external2.number().optional(),
15247
+ processing_mode: exports_external2.enum(["raw", "enhanced"]).optional(),
15248
+ tags: exports_external2.array(exports_external2.string()).optional(),
15249
+ search: exports_external2.string().optional(),
15250
+ since: exports_external2.string().optional(),
15251
+ until: exports_external2.string().optional(),
15252
+ agent_id: exports_external2.string().optional(),
15253
+ project_id: exports_external2.string().optional(),
15254
+ session_id: exports_external2.string().optional(),
15255
+ full: exports_external2.boolean().optional()
15256
+ }, async (args) => {
15257
+ try {
15258
+ const filter = {
15259
+ limit: args.limit || 10,
15260
+ offset: args.offset,
15261
+ processing_mode: args.processing_mode,
15262
+ tags: args.tags,
15263
+ search: args.search,
15264
+ since: args.since,
15265
+ until: args.until,
15266
+ agent_id: args.agent_id,
15267
+ project_id: args.project_id,
15268
+ session_id: args.session_id
15269
+ };
15270
+ const recordings = listRecordings(filter);
15271
+ if (recordings.length === 0)
15272
+ return text("No recordings found.");
15273
+ const fmt = args.full ? full : compact;
15274
+ const sep = args.full ? `
15200
15275
  ---
15201
15276
  ` : `
15202
15277
  `;
15203
- return text(`${recordings.length} recording(s):${sep}${recordings.map(fmt).join(sep)}`);
15204
- } catch (e) {
15205
- return errorResult(e);
15206
- }
15207
- });
15208
- registerTool("search_recordings", "Search recordings by text.", {
15209
- query: exports_external2.string(),
15210
- limit: exports_external2.number().optional(),
15211
- agent_id: exports_external2.string().optional(),
15212
- project_id: exports_external2.string().optional(),
15213
- full: exports_external2.boolean().optional()
15214
- }, async (args) => {
15215
- try {
15216
- const results = searchRecordings(args.query, {
15217
- limit: args.limit || 10,
15218
- agent_id: args.agent_id,
15219
- project_id: args.project_id
15220
- });
15221
- if (results.length === 0)
15222
- return text("No results.");
15223
- const fmt = args.full ? full : compact;
15224
- const sep = args.full ? `
15278
+ return text(`${recordings.length} recording(s):${sep}${recordings.map(fmt).join(sep)}`);
15279
+ } catch (e) {
15280
+ return errorResult(e);
15281
+ }
15282
+ });
15283
+ registerTool("search_recordings", "Search recordings by text.", {
15284
+ query: exports_external2.string(),
15285
+ limit: exports_external2.number().optional(),
15286
+ agent_id: exports_external2.string().optional(),
15287
+ project_id: exports_external2.string().optional(),
15288
+ full: exports_external2.boolean().optional()
15289
+ }, async (args) => {
15290
+ try {
15291
+ const results = searchRecordings(args.query, {
15292
+ limit: args.limit || 10,
15293
+ agent_id: args.agent_id,
15294
+ project_id: args.project_id
15295
+ });
15296
+ if (results.length === 0)
15297
+ return text("No results.");
15298
+ const fmt = args.full ? full : compact;
15299
+ const sep = args.full ? `
15225
15300
  ---
15226
15301
  ` : `
15227
15302
  `;
15228
- return text(`${results.length} result(s):${sep}${results.map(fmt).join(sep)}`);
15229
- } catch (e) {
15230
- return errorResult(e);
15231
- }
15232
- });
15233
- registerTool("delete_recording", "Delete recording by ID.", { id: exports_external2.string() }, async (args) => {
15234
- try {
15235
- return text(deleteRecording(args.id) ? `Deleted ${args.id}` : `Not found: ${args.id}`);
15236
- } catch (e) {
15237
- return errorResult(e);
15238
- }
15239
- });
15240
- registerTool("recording_stats", "Recording stats: count, modes, duration.", {}, async () => {
15241
- try {
15242
- const s = getRecordingStats();
15243
- let out = `Total: ${s.total} | Raw: ${s.raw} | Enhanced: ${s.enhanced} | Duration: ${(s.total_duration_ms / 1000).toFixed(1)}s`;
15244
- if (Object.keys(s.by_model).length > 0) {
15245
- out += `
15303
+ return text(`${results.length} result(s):${sep}${results.map(fmt).join(sep)}`);
15304
+ } catch (e) {
15305
+ return errorResult(e);
15306
+ }
15307
+ });
15308
+ registerTool("delete_recording", "Delete recording by ID.", { id: exports_external2.string() }, async (args) => {
15309
+ try {
15310
+ return text(deleteRecording(args.id) ? `Deleted ${args.id}` : `Not found: ${args.id}`);
15311
+ } catch (e) {
15312
+ return errorResult(e);
15313
+ }
15314
+ });
15315
+ registerTool("recording_stats", "Recording stats: count, modes, duration.", {}, async () => {
15316
+ try {
15317
+ const s = getRecordingStats();
15318
+ let out = `Total: ${s.total} | Raw: ${s.raw} | Enhanced: ${s.enhanced} | Duration: ${(s.total_duration_ms / 1000).toFixed(1)}s`;
15319
+ if (Object.keys(s.by_model).length > 0) {
15320
+ out += `
15246
15321
  ` + Object.entries(s.by_model).map(([m, c]) => `${m}: ${c}`).join(", ");
15322
+ }
15323
+ return text(out);
15324
+ } catch (e) {
15325
+ return errorResult(e);
15247
15326
  }
15248
- return text(out);
15249
- } catch (e) {
15250
- return errorResult(e);
15251
- }
15252
- });
15253
- registerTool("detect_enhancement", "Check if text needs AI enhancement.", { text: exports_external2.string() }, async (args) => {
15254
- try {
15255
- const r = needsEnhancement(args.text, config);
15256
- return text(`${r.needs ? "Yes" : "No"}: ${r.reason}`);
15257
- } catch (e) {
15258
- return errorResult(e);
15259
- }
15260
- });
15261
- registerTool("register_agent", "Register agent (idempotent).", { name: exports_external2.string(), description: exports_external2.string().optional(), role: exports_external2.string().optional() }, async (args) => {
15262
- try {
15263
- const a = registerAgent(args.name, args.description, args.role);
15264
- return text(`${a.id} | ${a.name} | ${a.role}`);
15265
- } catch (e) {
15266
- return errorResult(e);
15267
- }
15268
- });
15269
- registerTool("list_agents", "List registered agents.", {}, async () => {
15270
- try {
15271
- const agents = listAgents();
15272
- if (agents.length === 0)
15273
- return text("None.");
15274
- return text(agents.map((a) => `${a.id} | ${a.name} | ${a.role}`).join(`
15327
+ });
15328
+ registerTool("detect_enhancement", "Check if text needs AI enhancement.", { text: exports_external2.string() }, async (args) => {
15329
+ try {
15330
+ const r = needsEnhancement(args.text, config);
15331
+ return text(`${r.needs ? "Yes" : "No"}: ${r.reason}`);
15332
+ } catch (e) {
15333
+ return errorResult(e);
15334
+ }
15335
+ });
15336
+ registerTool("register_agent", "Register agent (idempotent).", { name: exports_external2.string(), description: exports_external2.string().optional(), role: exports_external2.string().optional() }, async (args) => {
15337
+ try {
15338
+ const a = registerAgent(args.name, args.description, args.role);
15339
+ return text(`${a.id} | ${a.name} | ${a.role}`);
15340
+ } catch (e) {
15341
+ return errorResult(e);
15342
+ }
15343
+ });
15344
+ registerTool("list_agents", "List registered agents.", {}, async () => {
15345
+ try {
15346
+ const agents = listAgents();
15347
+ if (agents.length === 0)
15348
+ return text("None.");
15349
+ return text(agents.map((a) => `${a.id} | ${a.name} | ${a.role}`).join(`
15275
15350
  `));
15276
- } catch (e) {
15277
- return errorResult(e);
15278
- }
15279
- });
15280
- registerTool("get_agent", "Get agent by ID or name.", { id: exports_external2.string() }, async (args) => {
15281
- try {
15282
- const a = getAgent(args.id);
15283
- if (!a)
15284
- return text(`Not found: ${args.id}`);
15285
- return text(`${a.id} | ${a.name} | ${a.role} | ${a.last_seen_at}`);
15286
- } catch (e) {
15287
- return errorResult(e);
15288
- }
15289
- });
15290
- registerTool("register_project", "Register project (idempotent).", { name: exports_external2.string(), path: exports_external2.string(), description: exports_external2.string().optional() }, async (args) => {
15291
- try {
15292
- const p = registerProject(args.name, args.path, args.description);
15293
- return text(`${p.id.slice(0, 8)} | ${p.name} | ${p.path}`);
15294
- } catch (e) {
15295
- return errorResult(e);
15296
- }
15297
- });
15298
- registerTool("list_projects", "List registered projects.", {}, async () => {
15299
- try {
15300
- const projects = listProjects();
15301
- if (projects.length === 0)
15302
- return text("None.");
15303
- return text(projects.map((p) => `${p.id.slice(0, 8)} | ${p.name} | ${p.path}`).join(`
15351
+ } catch (e) {
15352
+ return errorResult(e);
15353
+ }
15354
+ });
15355
+ registerTool("get_agent", "Get agent by ID or name.", { id: exports_external2.string() }, async (args) => {
15356
+ try {
15357
+ const a = getAgent(args.id);
15358
+ if (!a)
15359
+ return text(`Not found: ${args.id}`);
15360
+ return text(`${a.id} | ${a.name} | ${a.role} | ${a.last_seen_at}`);
15361
+ } catch (e) {
15362
+ return errorResult(e);
15363
+ }
15364
+ });
15365
+ registerTool("register_project", "Register project (idempotent).", { name: exports_external2.string(), path: exports_external2.string(), description: exports_external2.string().optional() }, async (args) => {
15366
+ try {
15367
+ const p = registerProject(args.name, args.path, args.description);
15368
+ return text(`${p.id.slice(0, 8)} | ${p.name} | ${p.path}`);
15369
+ } catch (e) {
15370
+ return errorResult(e);
15371
+ }
15372
+ });
15373
+ registerTool("list_projects", "List registered projects.", {}, async () => {
15374
+ try {
15375
+ const projects = listProjects();
15376
+ if (projects.length === 0)
15377
+ return text("None.");
15378
+ return text(projects.map((p) => `${p.id.slice(0, 8)} | ${p.name} | ${p.path}`).join(`
15304
15379
  `));
15305
- } catch (e) {
15306
- return errorResult(e);
15307
- }
15308
- });
15309
- registerTool("heartbeat", "Update last_seen_at to signal agent is active. Call periodically during long tasks.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async (args) => {
15310
- try {
15311
- const agent = heartbeatAgent(args.agent_id);
15312
- if (!agent)
15313
- return text(`Agent not found: ${args.agent_id}`);
15314
- return text(`${agent.id} | ${agent.name} | last_seen: ${agent.last_seen_at}`);
15315
- } catch (e) {
15316
- return errorResult(e);
15317
- }
15318
- });
15319
- registerTool("set_focus", "Set active project context for this agent session.", { agent_id: exports_external2.string().describe("Agent ID or name"), project_id: exports_external2.string().nullable().optional().describe("Project ID to focus on, or null to clear") }, async (args) => {
15320
- try {
15321
- const agent = setAgentFocus(args.agent_id, args.project_id ?? null);
15322
- if (!agent)
15323
- return text(`Agent not found: ${args.agent_id}`);
15324
- return text(args.project_id ? `Focus set: ${args.project_id}` : "Focus cleared");
15325
- } catch (e) {
15326
- return errorResult(e);
15380
+ } catch (e) {
15381
+ return errorResult(e);
15382
+ }
15383
+ });
15384
+ registerTool("heartbeat", "Update last_seen_at to signal agent is active. Call periodically during long tasks.", { agent_id: exports_external2.string().describe("Agent ID or name") }, async (args) => {
15385
+ try {
15386
+ const agent = heartbeatAgent(args.agent_id);
15387
+ if (!agent)
15388
+ return text(`Agent not found: ${args.agent_id}`);
15389
+ return text(`${agent.id} | ${agent.name} | last_seen: ${agent.last_seen_at}`);
15390
+ } catch (e) {
15391
+ return errorResult(e);
15392
+ }
15393
+ });
15394
+ registerTool("set_focus", "Set active project context for this agent session.", { agent_id: exports_external2.string().describe("Agent ID or name"), project_id: exports_external2.string().nullable().optional().describe("Project ID to focus on, or null to clear") }, async (args) => {
15395
+ try {
15396
+ const agent = setAgentFocus(args.agent_id, args.project_id ?? null);
15397
+ if (!agent)
15398
+ return text(`Agent not found: ${args.agent_id}`);
15399
+ return text(args.project_id ? `Focus set: ${args.project_id}` : "Focus cleared");
15400
+ } catch (e) {
15401
+ return errorResult(e);
15402
+ }
15403
+ });
15404
+ registerTool("send_feedback", "Send feedback about this service", {
15405
+ message: exports_external2.string().describe("Feedback message"),
15406
+ email: exports_external2.string().optional().describe("Contact email (optional)"),
15407
+ category: exports_external2.enum(["bug", "feature", "general"]).optional().describe("Feedback category")
15408
+ }, async (params) => {
15409
+ const adapter = getAdapter();
15410
+ const pkg = require_package();
15411
+ adapter.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", params.message, params.email || null, params.category || "general", pkg.version);
15412
+ return text("Feedback saved. Thank you!");
15413
+ });
15414
+ registerCloudTools(server, "recordings");
15415
+ return server;
15416
+ }
15417
+ async function main() {
15418
+ const args = process.argv.slice(2);
15419
+ if (isHttpMode(args)) {
15420
+ startMcpHttpServer({ name: "recordings", port: resolveMcpHttpPort(args), buildServer });
15421
+ return;
15327
15422
  }
15328
- });
15329
- registerTool("send_feedback", "Send feedback about this service", {
15330
- message: exports_external2.string().describe("Feedback message"),
15331
- email: exports_external2.string().optional().describe("Contact email (optional)"),
15332
- category: exports_external2.enum(["bug", "feature", "general"]).optional().describe("Feedback category")
15333
- }, async (params) => {
15334
- const adapter = getAdapter();
15335
- const pkg = require_package();
15336
- adapter.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", params.message, params.email || null, params.category || "general", pkg.version);
15337
- return text("Feedback saved. Thank you!");
15338
- });
15339
- var transport = new StdioServerTransport;
15340
- registerCloudTools(server, "recordings");
15341
- await server.connect(transport);
15423
+ const transport = new StdioServerTransport;
15424
+ await buildServer().connect(transport);
15425
+ }
15426
+ if (import.meta.main) {
15427
+ await main();
15428
+ }
15429
+ export {
15430
+ buildServer
15431
+ };