@xopcai/xopc 0.0.104 → 0.0.106
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/adapters/gateway-setup.js +2 -2
- package/dist/extensions/feishu/src/adapters/gateway-setup.js.map +1 -1
- package/dist/extensions/feishu/src/directory/directory-adapter.js +1 -1
- package/dist/extensions/feishu/src/directory/directory-adapter.js.map +1 -1
- package/dist/extensions/feishu/src/outbound/outbound-adapter.js +4 -3
- package/dist/extensions/feishu/src/outbound/outbound-adapter.js.map +1 -1
- package/dist/extensions/feishu/src/plugin.d.ts +6 -3
- package/dist/extensions/feishu/src/plugin.js +59 -40
- package/dist/extensions/feishu/src/plugin.js.map +1 -1
- package/dist/extensions/feishu/src/streaming/streaming-adapter.js +20 -16
- package/dist/extensions/feishu/src/streaming/streaming-adapter.js.map +1 -1
- package/dist/extensions/feishu/src/transport/socket-mode/monitor.js +1 -1
- package/dist/extensions/feishu/src/transport/webhook/monitor.js +1 -1
- package/dist/extensions/feishu/src/workflow-progress.d.ts +1 -1
- package/dist/extensions/feishu/src/workflow-progress.js +2 -2
- package/dist/extensions/feishu/src/workflow-progress.js.map +1 -1
- package/dist/extensions/telegram/src/adapters/gateway-setup.js +8 -6
- package/dist/extensions/telegram/src/adapters/gateway-setup.js.map +1 -1
- package/dist/extensions/telegram/src/command-handler.d.ts +1 -1
- package/dist/extensions/telegram/src/command-handler.js +11 -9
- package/dist/extensions/telegram/src/command-handler.js.map +1 -1
- package/dist/extensions/telegram/src/inbound-processor.js +1 -2
- package/dist/extensions/telegram/src/inbound-processor.js.map +1 -1
- package/dist/extensions/telegram/src/plugin.d.ts +1 -0
- package/dist/extensions/telegram/src/plugin.js +6 -6
- package/dist/extensions/telegram/src/plugin.js.map +1 -1
- package/dist/extensions/telegram/src/workflow-progress.d.ts +1 -1
- package/dist/extensions/telegram/src/workflow-progress.js.map +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/cli/gateway-qr-login.d.ts +4 -0
- package/dist/extensions/weixin/src/cli/gateway-qr-login.js +34 -5
- package/dist/extensions/weixin/src/cli/gateway-qr-login.js.map +1 -1
- package/dist/extensions/weixin/src/cli/qr-login.js +4 -3
- package/dist/extensions/weixin/src/cli/qr-login.js.map +1 -1
- package/dist/extensions/weixin/src/plugin.d.ts +1 -0
- package/dist/extensions/weixin/src/plugin.js +7 -6
- package/dist/extensions/weixin/src/plugin.js.map +1 -1
- package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
- package/dist/gateway/static/root/assets/agents-3eP0vqFO.js +396 -0
- package/dist/gateway/static/root/assets/{apps-page-L-IwQTe0.js → apps-page-CZ2ZKrko.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-BbETa93K.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-DCtUkHMu.js +1 -0
- package/dist/gateway/static/root/assets/{dist-C6vclgEL.js → dist-OM9qGfm5.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-D4JYAbsX.js → extension-debug-page-D-WHrr_w.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-_T62gi6e.js → extension-page-CCgjSoKn.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CkJbjyM-.js → extension-settings-page-BJH5a-Gn.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-B5aiIguv.js → fetch-CkvRO30i.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DqmLXhs6.js → gateway-startup-retry-BkKyRDrF.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-HwJV1K_Y.js → heartbeat-config-api-CVyP6pFj.js} +1 -1
- package/dist/gateway/static/root/assets/index-CEZxETzw.js +4845 -0
- package/dist/gateway/static/root/assets/{logs-page-DsjtTjBy.js → logs-page-R6Vv_Ja6.js} +2 -2
- package/dist/gateway/static/root/assets/{note-detail-page-B2V7RhgD.js → note-detail-page-BUDarLhh.js} +54 -54
- package/dist/gateway/static/root/assets/{note-time-BG8GCTr4.js → note-time-Cc2-T21g.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-xrcVBQ6Z.js → notes-page-EAHl16bq.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-S-QUvhul.js → schema-form-CrlOmEZt.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-Ce_xuuSB.js +1 -0
- package/dist/gateway/static/root/assets/{settings-advanced-gate-DujQl9c7.js → settings-advanced-gate-BBAQxxew.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-r1V7dRV6.js → settings-form-section-DjPD-ErC.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-KhQ9QgH0.js +3 -0
- package/dist/gateway/static/root/assets/{share-preview-page-DOFy0FNt.js → share-preview-page-BjvPht3V.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page-C0laq7l1.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-Bo3j5Ovv.js → theme-store-DYgVdRB4.js} +1 -1
- package/dist/gateway/static/root/assets/url-DPEStU4M.js +3 -0
- package/dist/gateway/static/root/assets/{voice-api-key-field-AjmUAlEQ.js → voice-api-key-field-DeGckWkl.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-page.utils-DLEJ92gK.js → workflow-page.utils-D4sqR0yX.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-DFjM95g5.js +27 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/context/workspace-seed.js +1 -1
- package/dist/src/agent/inbound/turn-dispatcher.d.ts +2 -0
- package/dist/src/agent/inbound/turn-dispatcher.js +1 -0
- package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-config.js +16 -8
- package/dist/src/agent/mcp/bundle-mcp-config.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-policy.js +2 -2
- package/dist/src/agent/mcp/bundle-mcp-policy.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
- package/dist/src/agent/mcp/mcp-http.js +1 -1
- package/dist/src/agent/mcp/mcp-http.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +2 -6
- package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
- package/dist/src/agent/prompt/memory/index.js +30 -2
- package/dist/src/agent/prompt/memory/index.js.map +1 -1
- package/dist/src/agent/service/direct-turn-helpers.d.ts +2 -0
- package/dist/src/agent/service/direct-turn-helpers.js +5 -2
- package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.d.ts +2 -0
- package/dist/src/agent/service/process-direct-streaming.js +15 -1
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.js +2 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +3 -0
- package/dist/src/agent/skills/hub-pull.js +1 -1
- package/dist/src/agent/source-context/index.d.ts +2 -0
- package/dist/src/agent/source-context/index.js +3 -0
- package/dist/src/agent/source-context/injector.d.ts +3 -0
- package/dist/src/agent/source-context/injector.js +36 -0
- package/dist/src/agent/source-context/injector.js.map +1 -0
- package/dist/src/agent/source-context/types.d.ts +19 -0
- package/dist/src/agent/source-context/types.js +10 -0
- package/dist/src/agent/source-context/types.js.map +1 -0
- package/dist/src/agent/tools/factory.js +5 -5
- package/dist/src/agent/tools/index.js +3 -3
- package/dist/src/agent/tools/session-search-tool.js +1 -1
- package/dist/src/agent/tools/shell.js +1 -1
- package/dist/src/agent/tools/web-extract.js +2 -2
- package/dist/src/agent/tools/web.js +1 -1
- package/dist/src/agent/workflow/index.js +2 -1
- package/dist/src/agent/workflow/runtime.d.ts +2 -2
- package/dist/src/agent/workflow/runtime.js +1 -14
- package/dist/src/agent/workflow/runtime.js.map +1 -1
- package/dist/src/agent/workflow/snapshot-empty.d.ts +2 -0
- package/dist/src/agent/workflow/snapshot-empty.js +19 -0
- package/dist/src/agent/workflow/snapshot-empty.js.map +1 -0
- package/dist/src/agent/workflow/snapshot.js +1 -1
- package/dist/src/agent/workflow/snapshot.js.map +1 -1
- package/dist/src/browser/actions/extension-registry-bridge.js +1 -1
- package/dist/src/browser/actions/registry.js +2 -2
- package/dist/src/browser/cdp-local-launcher.js +1 -1
- package/dist/src/browser/index.js +3 -3
- package/dist/src/browser/providers/browser-ext-install.js +1 -1
- package/dist/src/browser/providers/cloakbrowser.js +1 -1
- package/dist/src/cli/commands/browser-cli-helpers.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/config/paths.d.ts +1 -0
- package/dist/src/config/paths.js +5 -1
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/connectors/health.js +1 -1
- package/dist/src/connectors/install.js +1 -1
- package/dist/src/document-understanding/extract.d.ts +13 -0
- package/dist/src/document-understanding/extract.js +63 -0
- package/dist/src/document-understanding/extract.js.map +1 -0
- package/dist/src/gateway/hono/routes/mcp.js +1 -1
- package/dist/src/gateway/hono/routes/notes.js +46 -27
- package/dist/src/gateway/hono/routes/notes.js.map +1 -1
- package/dist/src/gateway/service.js +11 -0
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/notes/agent-context.d.ts +40 -0
- package/dist/src/notes/agent-context.js +383 -0
- package/dist/src/notes/agent-context.js.map +1 -0
- package/dist/src/notes/index.d.ts +2 -0
- package/dist/src/notes/index.js +2 -1
- package/dist/src/notes/service.d.ts +5 -0
- package/dist/src/notes/service.js +22 -0
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/notes/store.js +2 -0
- package/dist/src/notes/store.js.map +1 -1
- package/dist/src/share/share-thumbnail.js +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -0
- package/dist/src/storage/sqlite/index.js +2 -1
- package/dist/src/storage/sqlite/migrations/005_add_note_agent_contexts.sql +10 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/note-agent-context-repository.d.ts +10 -0
- package/dist/src/storage/sqlite/note-agent-context-repository.js +35 -0
- package/dist/src/storage/sqlite/note-agent-context-repository.js.map +1 -0
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/tui-startup-resources.js +1 -1
- package/dist/src/voice/stt/availability.d.ts +6 -0
- package/dist/src/voice/stt/availability.js +14 -6
- package/dist/src/voice/stt/availability.js.map +1 -1
- package/dist/src/voice/stt/index.js +2 -1
- package/dist/src/voice/stt/index.js.map +1 -1
- package/dist/src/voice/stt/mention.d.ts +2 -0
- package/dist/src/voice/stt/mention.js +20 -0
- package/dist/src/voice/stt/mention.js.map +1 -0
- package/dist/src/voice/stt/preflight.d.ts +1 -4
- package/dist/src/voice/stt/preflight.js +1 -18
- package/dist/src/voice/stt/preflight.js.map +1 -1
- package/package.json +4 -4
- package/dist/gateway/static/root/assets/agents-CvsBrBfi.js +0 -396
- package/dist/gateway/static/root/assets/channels-settings-D95KuPeP.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-CfkWXDAI.js +0 -1
- package/dist/gateway/static/root/assets/index-DvTb6wNR.js +0 -4845
- package/dist/gateway/static/root/assets/sessions-page-CrQer6Au.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-C9nrONQ2.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-DmTsrs_a.js +0 -2
- package/dist/gateway/static/root/assets/url-WgQIjFZu.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-BnjjKrM1.js +0 -27
package/dist/src/notes/store.js
CHANGED
|
@@ -4,6 +4,7 @@ import { init_logger } from "../utils/logger.js";
|
|
|
4
4
|
import { requireXopcDatabase } from "../storage/sqlite/connection.js";
|
|
5
5
|
import { buildNoteIndexMeta } from "./note-index-meta.js";
|
|
6
6
|
import { deleteNoteRecord, getNoteRecord, listNoteRecords, upsertNoteRecord } from "../storage/sqlite/notes-repository.js";
|
|
7
|
+
import { deleteNoteAgentContextRecord } from "../storage/sqlite/note-agent-context-repository.js";
|
|
7
8
|
import "../storage/sqlite/index.js";
|
|
8
9
|
import { resolveNoteHistoryDir, resolveNoteMediaDir } from "./paths.js";
|
|
9
10
|
import { randomUUID } from "node:crypto";
|
|
@@ -46,6 +47,7 @@ var NotesStore = class {
|
|
|
46
47
|
async deleteNote(id) {
|
|
47
48
|
requireXopcDatabase();
|
|
48
49
|
if (!deleteNoteRecord(id)) return false;
|
|
50
|
+
deleteNoteAgentContextRecord(id);
|
|
49
51
|
await rm(resolveNoteMediaDir(id), {
|
|
50
52
|
recursive: true,
|
|
51
53
|
force: true
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","names":[],"sources":["../../../src/notes/store.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { readFile, mkdir, writeFile, rm, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { writeTextAtomic } from '../infra/write-file-atomic.js';\nimport { createLogger } from '../utils/logger.js';\nimport {\n deleteNoteRecord,\n getNoteRecord,\n listNoteRecords,\n requireXopcDatabase,\n upsertNoteRecord,\n} from '../storage/sqlite/index.js';\nimport { buildNoteIndexMeta } from './note-index-meta.js';\nimport { resolveNoteMediaDir, resolveNoteHistoryDir } from './paths.js';\nimport type {\n Note,\n NoteIndexEntry,\n NoteSnapshot,\n NoteSnapshotEntry,\n NotesListQuery,\n SnapshotTrigger,\n} from './types.js';\n\nconst log = createLogger('NotesStore');\n\nexport class NotesStore {\n private initialized = false;\n\n async initialize(): Promise<void> {\n if (this.initialized) return;\n requireXopcDatabase();\n this.initialized = true;\n log.debug('NotesStore initialized');\n }\n\n async addNote(note: Note): Promise<void> {\n requireXopcDatabase();\n upsertNoteRecord(note);\n }\n\n async getNote(id: string): Promise<Note | null> {\n requireXopcDatabase();\n return getNoteRecord(id);\n }\n\n async updateNote(id: string, patch: Partial<Note>): Promise<Note | null> {\n const existing = await this.getNote(id);\n if (!existing) return null;\n\n const updated: Note = {\n ...existing,\n ...patch,\n id: existing.id,\n createdAt: existing.createdAt,\n updatedAt: Date.now(),\n };\n\n requireXopcDatabase();\n upsertNoteRecord(updated);\n return updated;\n }\n\n async deleteNote(id: string): Promise<boolean> {\n requireXopcDatabase();\n const deleted = deleteNoteRecord(id);\n if (!deleted) {\n return false;\n }\n\n const mediaDir = resolveNoteMediaDir(id);\n await rm(mediaDir, { recursive: true, force: true }).catch(() => undefined);\n return true;\n }\n\n async listNotes(query: NotesListQuery = {}): Promise<{\n items: NoteIndexEntry[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n }> {\n requireXopcDatabase();\n return listNoteRecords(query);\n }\n\n async saveAttachment(\n noteId: string,\n fileName: string,\n buffer: Buffer,\n ): Promise<{ relativePath: string; size: number }> {\n const mediaDir = resolveNoteMediaDir(noteId);\n await mkdir(mediaDir, { recursive: true });\n const safeName = `${randomUUID().slice(0, 8)}_${fileName.replace(/[^a-zA-Z0-9._-]/g, '_')}`;\n const filePath = join(mediaDir, safeName);\n await writeFile(filePath, buffer);\n return { relativePath: safeName, size: buffer.length };\n }\n\n resolveAttachmentPath(noteId: string, relativePath: string): string {\n return join(resolveNoteMediaDir(noteId), relativePath);\n }\n\n async deleteAttachmentFile(noteId: string, relativePath: string): Promise<void> {\n const filePath = this.resolveAttachmentPath(noteId, relativePath);\n await rm(filePath, { force: true }).catch((err) => {\n log.warn({ err, noteId, relativePath }, 'Failed to remove note attachment file');\n });\n }\n\n async saveSnapshot(note: Note, trigger: SnapshotTrigger): Promise<void> {\n const historyDir = resolveNoteHistoryDir(note.id);\n await mkdir(historyDir, { recursive: true });\n const snapshot: NoteSnapshot = {\n noteId: note.id,\n timestamp: Date.now(),\n trigger,\n title: note.title,\n markdown: note.markdown,\n tags: note.tags,\n kind: note.kind,\n status: note.status,\n };\n const filePath = join(historyDir, `${snapshot.timestamp}.json`);\n await writeTextAtomic(filePath, JSON.stringify(snapshot, null, 2));\n log.debug({ noteId: note.id, trigger, timestamp: snapshot.timestamp }, 'Snapshot saved');\n }\n\n async listSnapshots(noteId: string): Promise<NoteSnapshotEntry[]> {\n const historyDir = resolveNoteHistoryDir(noteId);\n let files: string[];\n try {\n files = await readdir(historyDir);\n } catch {\n return [];\n }\n const entries: NoteSnapshotEntry[] = [];\n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n const timestamp = parseInt(file.slice(0, -'.json'.length), 10);\n if (!Number.isFinite(timestamp)) continue;\n try {\n const content = await readFile(join(historyDir, file), 'utf-8');\n const snapshot = JSON.parse(content) as NoteSnapshot;\n const rawText = snapshot.markdown ?? '';\n entries.push({\n timestamp: snapshot.timestamp,\n trigger: snapshot.trigger,\n snippet: rawText.slice(0, 80) || undefined,\n });\n } catch {\n log.debug({ noteId, file }, 'Skipped unreadable snapshot');\n }\n }\n entries.sort((a, b) => b.timestamp - a.timestamp);\n return entries;\n }\n\n async getSnapshot(noteId: string, timestamp: number): Promise<NoteSnapshot | null> {\n const filePath = join(resolveNoteHistoryDir(noteId), `${timestamp}.json`);\n try {\n const content = await readFile(filePath, 'utf-8');\n return JSON.parse(content) as NoteSnapshot;\n } catch {\n return null;\n }\n }\n\n async pruneSnapshots(noteId: string, maxCount: number): Promise<void> {\n const historyDir = resolveNoteHistoryDir(noteId);\n let files: string[];\n try {\n files = await readdir(historyDir);\n } catch {\n return;\n }\n const jsonFiles = files.filter((f) => f.endsWith('.json')).sort();\n if (jsonFiles.length <= maxCount) return;\n const toDelete = jsonFiles.slice(0, jsonFiles.length - maxCount);\n for (const file of toDelete) {\n await rm(join(historyDir, file), { force: true }).catch(() => undefined);\n }\n log.debug({ noteId, deleted: toDelete.length }, 'Pruned old snapshots');\n }\n\n async deleteAllSnapshots(noteId: string): Promise<void> {\n const historyDir = resolveNoteHistoryDir(noteId);\n await rm(historyDir, { recursive: true, force: true }).catch(() => undefined);\n }\n\n /** No-op: SQLite writes are synchronous. Kept for API compatibility with tests. */\n async flush(): Promise<void> {}\n}\n\nexport { buildNoteIndexMeta };\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.js","names":[],"sources":["../../../src/notes/store.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { readFile, mkdir, writeFile, rm, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport { writeTextAtomic } from '../infra/write-file-atomic.js';\nimport { createLogger } from '../utils/logger.js';\nimport {\n deleteNoteAgentContextRecord,\n deleteNoteRecord,\n getNoteRecord,\n listNoteRecords,\n requireXopcDatabase,\n upsertNoteRecord,\n} from '../storage/sqlite/index.js';\nimport { buildNoteIndexMeta } from './note-index-meta.js';\nimport { resolveNoteMediaDir, resolveNoteHistoryDir } from './paths.js';\nimport type {\n Note,\n NoteIndexEntry,\n NoteSnapshot,\n NoteSnapshotEntry,\n NotesListQuery,\n SnapshotTrigger,\n} from './types.js';\n\nconst log = createLogger('NotesStore');\n\nexport class NotesStore {\n private initialized = false;\n\n async initialize(): Promise<void> {\n if (this.initialized) return;\n requireXopcDatabase();\n this.initialized = true;\n log.debug('NotesStore initialized');\n }\n\n async addNote(note: Note): Promise<void> {\n requireXopcDatabase();\n upsertNoteRecord(note);\n }\n\n async getNote(id: string): Promise<Note | null> {\n requireXopcDatabase();\n return getNoteRecord(id);\n }\n\n async updateNote(id: string, patch: Partial<Note>): Promise<Note | null> {\n const existing = await this.getNote(id);\n if (!existing) return null;\n\n const updated: Note = {\n ...existing,\n ...patch,\n id: existing.id,\n createdAt: existing.createdAt,\n updatedAt: Date.now(),\n };\n\n requireXopcDatabase();\n upsertNoteRecord(updated);\n return updated;\n }\n\n async deleteNote(id: string): Promise<boolean> {\n requireXopcDatabase();\n const deleted = deleteNoteRecord(id);\n if (!deleted) {\n return false;\n }\n\n deleteNoteAgentContextRecord(id);\n const mediaDir = resolveNoteMediaDir(id);\n await rm(mediaDir, { recursive: true, force: true }).catch(() => undefined);\n return true;\n }\n\n async listNotes(query: NotesListQuery = {}): Promise<{\n items: NoteIndexEntry[];\n total: number;\n limit: number;\n offset: number;\n hasMore: boolean;\n }> {\n requireXopcDatabase();\n return listNoteRecords(query);\n }\n\n async saveAttachment(\n noteId: string,\n fileName: string,\n buffer: Buffer,\n ): Promise<{ relativePath: string; size: number }> {\n const mediaDir = resolveNoteMediaDir(noteId);\n await mkdir(mediaDir, { recursive: true });\n const safeName = `${randomUUID().slice(0, 8)}_${fileName.replace(/[^a-zA-Z0-9._-]/g, '_')}`;\n const filePath = join(mediaDir, safeName);\n await writeFile(filePath, buffer);\n return { relativePath: safeName, size: buffer.length };\n }\n\n resolveAttachmentPath(noteId: string, relativePath: string): string {\n return join(resolveNoteMediaDir(noteId), relativePath);\n }\n\n async deleteAttachmentFile(noteId: string, relativePath: string): Promise<void> {\n const filePath = this.resolveAttachmentPath(noteId, relativePath);\n await rm(filePath, { force: true }).catch((err) => {\n log.warn({ err, noteId, relativePath }, 'Failed to remove note attachment file');\n });\n }\n\n async saveSnapshot(note: Note, trigger: SnapshotTrigger): Promise<void> {\n const historyDir = resolveNoteHistoryDir(note.id);\n await mkdir(historyDir, { recursive: true });\n const snapshot: NoteSnapshot = {\n noteId: note.id,\n timestamp: Date.now(),\n trigger,\n title: note.title,\n markdown: note.markdown,\n tags: note.tags,\n kind: note.kind,\n status: note.status,\n };\n const filePath = join(historyDir, `${snapshot.timestamp}.json`);\n await writeTextAtomic(filePath, JSON.stringify(snapshot, null, 2));\n log.debug({ noteId: note.id, trigger, timestamp: snapshot.timestamp }, 'Snapshot saved');\n }\n\n async listSnapshots(noteId: string): Promise<NoteSnapshotEntry[]> {\n const historyDir = resolveNoteHistoryDir(noteId);\n let files: string[];\n try {\n files = await readdir(historyDir);\n } catch {\n return [];\n }\n const entries: NoteSnapshotEntry[] = [];\n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n const timestamp = parseInt(file.slice(0, -'.json'.length), 10);\n if (!Number.isFinite(timestamp)) continue;\n try {\n const content = await readFile(join(historyDir, file), 'utf-8');\n const snapshot = JSON.parse(content) as NoteSnapshot;\n const rawText = snapshot.markdown ?? '';\n entries.push({\n timestamp: snapshot.timestamp,\n trigger: snapshot.trigger,\n snippet: rawText.slice(0, 80) || undefined,\n });\n } catch {\n log.debug({ noteId, file }, 'Skipped unreadable snapshot');\n }\n }\n entries.sort((a, b) => b.timestamp - a.timestamp);\n return entries;\n }\n\n async getSnapshot(noteId: string, timestamp: number): Promise<NoteSnapshot | null> {\n const filePath = join(resolveNoteHistoryDir(noteId), `${timestamp}.json`);\n try {\n const content = await readFile(filePath, 'utf-8');\n return JSON.parse(content) as NoteSnapshot;\n } catch {\n return null;\n }\n }\n\n async pruneSnapshots(noteId: string, maxCount: number): Promise<void> {\n const historyDir = resolveNoteHistoryDir(noteId);\n let files: string[];\n try {\n files = await readdir(historyDir);\n } catch {\n return;\n }\n const jsonFiles = files.filter((f) => f.endsWith('.json')).sort();\n if (jsonFiles.length <= maxCount) return;\n const toDelete = jsonFiles.slice(0, jsonFiles.length - maxCount);\n for (const file of toDelete) {\n await rm(join(historyDir, file), { force: true }).catch(() => undefined);\n }\n log.debug({ noteId, deleted: toDelete.length }, 'Pruned old snapshots');\n }\n\n async deleteAllSnapshots(noteId: string): Promise<void> {\n const historyDir = resolveNoteHistoryDir(noteId);\n await rm(historyDir, { recursive: true, force: true }).catch(() => undefined);\n }\n\n /** No-op: SQLite writes are synchronous. Kept for API compatibility with tests. */\n async flush(): Promise<void> {}\n}\n\nexport { buildNoteIndexMeta };\n"],"mappings":";;;;;;;;;;;;;wBAIgE;aACd;AAoBlD,MAAM,MAAM,aAAa,aAAa;AAEtC,IAAa,aAAb,MAAwB;CACtB,cAAsB;CAEtB,MAAM,aAA4B;AAChC,MAAI,KAAK,YAAa;AACtB,uBAAqB;AACrB,OAAK,cAAc;AACnB,MAAI,MAAM,yBAAyB;;CAGrC,MAAM,QAAQ,MAA2B;AACvC,uBAAqB;AACrB,mBAAiB,KAAK;;CAGxB,MAAM,QAAQ,IAAkC;AAC9C,uBAAqB;AACrB,SAAO,cAAc,GAAG;;CAG1B,MAAM,WAAW,IAAY,OAA4C;EACvE,MAAM,WAAW,MAAM,KAAK,QAAQ,GAAG;AACvC,MAAI,CAAC,SAAU,QAAO;EAEtB,MAAM,UAAgB;GACpB,GAAG;GACH,GAAG;GACH,IAAI,SAAS;GACb,WAAW,SAAS;GACpB,WAAW,KAAK,KAAK;GACtB;AAED,uBAAqB;AACrB,mBAAiB,QAAQ;AACzB,SAAO;;CAGT,MAAM,WAAW,IAA8B;AAC7C,uBAAqB;AAErB,MAAI,CADY,iBAAiB,GACrB,CACV,QAAO;AAGT,+BAA6B,GAAG;AAEhC,QAAM,GADW,oBAAoB,GACpB,EAAE;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC,CAAC,YAAY,KAAA,EAAU;AAC3E,SAAO;;CAGT,MAAM,UAAU,QAAwB,EAAE,EAMvC;AACD,uBAAqB;AACrB,SAAO,gBAAgB,MAAM;;CAG/B,MAAM,eACJ,QACA,UACA,QACiD;EACjD,MAAM,WAAW,oBAAoB,OAAO;AAC5C,QAAM,MAAM,UAAU,EAAE,WAAW,MAAM,CAAC;EAC1C,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,SAAS,QAAQ,oBAAoB,IAAI;AAEzF,QAAM,UADW,KAAK,UAAU,SACR,EAAE,OAAO;AACjC,SAAO;GAAE,cAAc;GAAU,MAAM,OAAO;GAAQ;;CAGxD,sBAAsB,QAAgB,cAA8B;AAClE,SAAO,KAAK,oBAAoB,OAAO,EAAE,aAAa;;CAGxD,MAAM,qBAAqB,QAAgB,cAAqC;AAE9E,QAAM,GADW,KAAK,sBAAsB,QAAQ,aACnC,EAAE,EAAE,OAAO,MAAM,CAAC,CAAC,OAAO,QAAQ;AACjD,OAAI,KAAK;IAAE;IAAK;IAAQ;IAAc,EAAE,wCAAwC;IAChF;;CAGJ,MAAM,aAAa,MAAY,SAAyC;EACtE,MAAM,aAAa,sBAAsB,KAAK,GAAG;AACjD,QAAM,MAAM,YAAY,EAAE,WAAW,MAAM,CAAC;EAC5C,MAAM,WAAyB;GAC7B,QAAQ,KAAK;GACb,WAAW,KAAK,KAAK;GACrB;GACA,OAAO,KAAK;GACZ,UAAU,KAAK;GACf,MAAM,KAAK;GACX,MAAM,KAAK;GACX,QAAQ,KAAK;GACd;AAED,QAAM,gBADW,KAAK,YAAY,GAAG,SAAS,UAAU,OAC1B,EAAE,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC;AAClE,MAAI,MAAM;GAAE,QAAQ,KAAK;GAAI;GAAS,WAAW,SAAS;GAAW,EAAE,iBAAiB;;CAG1F,MAAM,cAAc,QAA8C;EAChE,MAAM,aAAa,sBAAsB,OAAO;EAChD,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,QAAQ,WAAW;UAC3B;AACN,UAAO,EAAE;;EAEX,MAAM,UAA+B,EAAE;AACvC,OAAK,MAAM,QAAQ,OAAO;AACxB,OAAI,CAAC,KAAK,SAAS,QAAQ,CAAE;GAC7B,MAAM,YAAY,SAAS,KAAK,MAAM,GAAG,GAAgB,EAAE,GAAG;AAC9D,OAAI,CAAC,OAAO,SAAS,UAAU,CAAE;AACjC,OAAI;IACF,MAAM,UAAU,MAAM,SAAS,KAAK,YAAY,KAAK,EAAE,QAAQ;IAC/D,MAAM,WAAW,KAAK,MAAM,QAAQ;IACpC,MAAM,UAAU,SAAS,YAAY;AACrC,YAAQ,KAAK;KACX,WAAW,SAAS;KACpB,SAAS,SAAS;KAClB,SAAS,QAAQ,MAAM,GAAG,GAAG,IAAI,KAAA;KAClC,CAAC;WACI;AACN,QAAI,MAAM;KAAE;KAAQ;KAAM,EAAE,8BAA8B;;;AAG9D,UAAQ,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,UAAU;AACjD,SAAO;;CAGT,MAAM,YAAY,QAAgB,WAAiD;EACjF,MAAM,WAAW,KAAK,sBAAsB,OAAO,EAAE,GAAG,UAAU,OAAO;AACzE,MAAI;GACF,MAAM,UAAU,MAAM,SAAS,UAAU,QAAQ;AACjD,UAAO,KAAK,MAAM,QAAQ;UACpB;AACN,UAAO;;;CAIX,MAAM,eAAe,QAAgB,UAAiC;EACpE,MAAM,aAAa,sBAAsB,OAAO;EAChD,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,QAAQ,WAAW;UAC3B;AACN;;EAEF,MAAM,YAAY,MAAM,QAAQ,MAAM,EAAE,SAAS,QAAQ,CAAC,CAAC,MAAM;AACjE,MAAI,UAAU,UAAU,SAAU;EAClC,MAAM,WAAW,UAAU,MAAM,GAAG,UAAU,SAAS,SAAS;AAChE,OAAK,MAAM,QAAQ,SACjB,OAAM,GAAG,KAAK,YAAY,KAAK,EAAE,EAAE,OAAO,MAAM,CAAC,CAAC,YAAY,KAAA,EAAU;AAE1E,MAAI,MAAM;GAAE;GAAQ,SAAS,SAAS;GAAQ,EAAE,uBAAuB;;CAGzE,MAAM,mBAAmB,QAA+B;AAEtD,QAAM,GADa,sBAAsB,OACtB,EAAE;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC,CAAC,YAAY,KAAA,EAAU;;;CAI/E,MAAM,QAAuB"}
|
|
@@ -2,9 +2,9 @@ import { createLogger } from "../utils/logger/index.js";
|
|
|
2
2
|
import { init_logger } from "../utils/logger.js";
|
|
3
3
|
import { resolveStateDir } from "../config/paths-state.js";
|
|
4
4
|
import { init_paths } from "../config/paths.js";
|
|
5
|
-
import { loadPlaywrightCoreModule } from "../browser/providers/playwright-doctor.js";
|
|
6
5
|
import { getShareStore } from "./share-store.js";
|
|
7
6
|
import { getSiteShareStore } from "./site-share-store.js";
|
|
7
|
+
import { loadPlaywrightCoreModule } from "../browser/providers/playwright-doctor.js";
|
|
8
8
|
import { mkdir, readFile, stat, unlink, writeFile } from "node:fs/promises";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
//#region src/share/share-thumbnail.ts
|
|
@@ -11,5 +11,6 @@ export { deleteSessionRecord, ensureSessionRecord, getCurrentTranscriptId, getGl
|
|
|
11
11
|
export { appendTranscriptEntry, captureCompactionCheckpoint, getCompactionCheckpointDetail, listCompactionCheckpoints, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRows, loadTranscriptRowsForSession, paginateTranscriptMessages, replaceTranscriptRows, restoreCompactionCheckpoint, } from './transcript-repository.js';
|
|
12
12
|
export { appendCronRun, deleteCronRunsForJob, readAllCronRuns, readCronJobHistory, } from './cron-run-repository.js';
|
|
13
13
|
export { deleteNoteRecord, getNoteRecord, listNoteRecords, upsertNoteRecord, } from './notes-repository.js';
|
|
14
|
+
export { deleteNoteAgentContextRecord, getNoteAgentContextRecord, upsertNoteAgentContextRecord, type NoteAgentContextRecord, } from './note-agent-context-repository.js';
|
|
14
15
|
export { resolveAgentIdFromMemoriesDir, searchMemoryIndex, syncMemoryIndex, type MemorySearchHit, } from './memory-index-repository.js';
|
|
15
16
|
export { deleteSessionConfig, getSessionConfig, hasSessionConfig, setSessionConfig, updateSessionConfig, } from './config-repository.js';
|
|
@@ -12,6 +12,7 @@ import { deleteSessionRecord, ensureSessionRecord, findSessionKeyByTranscriptId,
|
|
|
12
12
|
import { appendTranscriptEntry, captureCompactionCheckpoint, getCompactionCheckpointDetail, listCompactionCheckpoints, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRows, loadTranscriptRowsForSession, paginateTranscriptMessages, replaceTranscriptRows, restoreCompactionCheckpoint } from "./transcript-repository.js";
|
|
13
13
|
import { appendCronRun, deleteCronRunsForJob, readAllCronRuns, readCronJobHistory } from "./cron-run-repository.js";
|
|
14
14
|
import { deleteNoteRecord, getNoteRecord, listNoteRecords, upsertNoteRecord } from "./notes-repository.js";
|
|
15
|
+
import { deleteNoteAgentContextRecord, getNoteAgentContextRecord, upsertNoteAgentContextRecord } from "./note-agent-context-repository.js";
|
|
15
16
|
import { resolveAgentIdFromMemoriesDir, searchMemoryIndex, syncMemoryIndex } from "./memory-index-repository.js";
|
|
16
17
|
import { deleteSessionConfig, getSessionConfig, hasSessionConfig, setSessionConfig, updateSessionConfig } from "./config-repository.js";
|
|
17
|
-
export { DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError, SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_FILENAME, XOPC_DB_SCHEMA_VERSION, appendCronRun, appendTranscriptEntry, applyPendingMigrations, buildDefaultSessionMetadata, buildGlobalSessionStats, captureCompactionCheckpoint, classifyStoredRow, closeXopcDatabase, deleteCronRunsForJob, deleteNoteRecord, deleteSessionConfig, deleteSessionRecord, discoverSqlMigrations, ensureSessionRecord, ensureXopcDatabaseSchema, estimateTokensFromMessages, extractFtsContent, extractRoutingFromSessionKey, findSessionKeyByTranscriptId, getCompactionCheckpointDetail, getCurrentTranscriptId, getGlobalSessionStats, getNoteRecord, getSessionConfig, getSessionMetadata, getSessionPersistedLevels, getSqliteDatabase, getXopcDatabase, hasSessionConfig, incrementSessionStatsOnAppend, inspectSchemaMigrationStatus, isXopcDatabaseOpen, listCompactionCheckpoints, listNoteRecords, listRegisteredMigrationTargets, listSessionMetadata, listSessionsByAgent, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRows, loadTranscriptRowsForSession, metadataToSessionInsert, openXopcDatabase, paginateTranscriptMessages, parseSessionKeySource, patchSessionMetadata, readAllCronRuns, readCronJobHistory, readSchemaVersion, readSchemaVersionForTest, replaceTranscriptRows, requireXopcDatabase, resetSessionRecord, resetXopcDatabaseSingletonForTest, resolveAgentIdFromMemoriesDir, resolveAgentIdFromSessionKey, resolveMigrationsDir, resolveSessionAgentId, resolveXopcDatabasePath, restoreCompactionCheckpoint, runSqliteWriteTransaction, searchMemoryIndex, sessionConfigRowToConfig, sessionRowToMetadata, setSessionConfig, syncMemoryIndex, transcriptEntryRowToStoredRow, updateSessionConfig, updateSessionStats, upsertNoteRecord };
|
|
18
|
+
export { DatabaseSchemaMigrationGapError, DatabaseSchemaTooNewError, SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_FILENAME, XOPC_DB_SCHEMA_VERSION, appendCronRun, appendTranscriptEntry, applyPendingMigrations, buildDefaultSessionMetadata, buildGlobalSessionStats, captureCompactionCheckpoint, classifyStoredRow, closeXopcDatabase, deleteCronRunsForJob, deleteNoteAgentContextRecord, deleteNoteRecord, deleteSessionConfig, deleteSessionRecord, discoverSqlMigrations, ensureSessionRecord, ensureXopcDatabaseSchema, estimateTokensFromMessages, extractFtsContent, extractRoutingFromSessionKey, findSessionKeyByTranscriptId, getCompactionCheckpointDetail, getCurrentTranscriptId, getGlobalSessionStats, getNoteAgentContextRecord, getNoteRecord, getSessionConfig, getSessionMetadata, getSessionPersistedLevels, getSqliteDatabase, getXopcDatabase, hasSessionConfig, incrementSessionStatsOnAppend, inspectSchemaMigrationStatus, isXopcDatabaseOpen, listCompactionCheckpoints, listNoteRecords, listRegisteredMigrationTargets, listSessionMetadata, listSessionsByAgent, loadCheckpointRows, loadLlmMessagesForSession, loadTranscriptRows, loadTranscriptRowsForSession, metadataToSessionInsert, openXopcDatabase, paginateTranscriptMessages, parseSessionKeySource, patchSessionMetadata, readAllCronRuns, readCronJobHistory, readSchemaVersion, readSchemaVersionForTest, replaceTranscriptRows, requireXopcDatabase, resetSessionRecord, resetXopcDatabaseSingletonForTest, resolveAgentIdFromMemoriesDir, resolveAgentIdFromSessionKey, resolveMigrationsDir, resolveSessionAgentId, resolveXopcDatabasePath, restoreCompactionCheckpoint, runSqliteWriteTransaction, searchMemoryIndex, sessionConfigRowToConfig, sessionRowToMetadata, setSessionConfig, syncMemoryIndex, transcriptEntryRowToStoredRow, updateSessionConfig, updateSessionStats, upsertNoteAgentContextRecord, upsertNoteRecord };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS note_agent_contexts (
|
|
2
|
+
note_id TEXT PRIMARY KEY,
|
|
3
|
+
note_updated_at INTEGER NOT NULL,
|
|
4
|
+
context_version TEXT NOT NULL,
|
|
5
|
+
generated_at INTEGER NOT NULL,
|
|
6
|
+
payload_json TEXT NOT NULL
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
CREATE INDEX IF NOT EXISTS idx_note_agent_contexts_generated
|
|
10
|
+
ON note_agent_contexts(generated_at DESC);
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 3;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 5;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -12,7 +12,7 @@ const log = createLogger("Sqlite:Migrations");
|
|
|
12
12
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
13
13
|
const XOPC_DB_BASELINE_SCHEMA_VERSION = 3;
|
|
14
14
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
15
|
-
const XOPC_DB_SCHEMA_VERSION =
|
|
15
|
+
const XOPC_DB_SCHEMA_VERSION = 5;
|
|
16
16
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
function resolveMigrationsDir(override) {
|
|
18
18
|
if (override) return override;
|
|
@@ -46,7 +46,7 @@ function applySingleMigration(db, migration) {
|
|
|
46
46
|
* Each step runs in its own transaction and updates schema_meta on success.
|
|
47
47
|
*/
|
|
48
48
|
function applyPendingMigrations(db, options = {}) {
|
|
49
|
-
const targetVersion = options.targetVersion ??
|
|
49
|
+
const targetVersion = options.targetVersion ?? 5;
|
|
50
50
|
let currentVersion = readSchemaVersion(db);
|
|
51
51
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
52
52
|
if (currentVersion >= targetVersion) return currentVersion;
|
|
@@ -62,7 +62,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
62
62
|
}
|
|
63
63
|
/** Inspect schema version without mutating the database. */
|
|
64
64
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
65
|
-
const appVersion = options.targetVersion ??
|
|
65
|
+
const appVersion = options.targetVersion ?? 5;
|
|
66
66
|
const dbVersion = readSchemaVersion(db);
|
|
67
67
|
if (dbVersion > appVersion) return {
|
|
68
68
|
dbVersion,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { readSchemaVersion, setSchemaVersion } from '../schema-version.js';\nimport { discoverSqlMigrations } from './discover.js';\nimport {\n DatabaseSchemaMigrationGapError,\n DatabaseSchemaTooNewError,\n} from './errors.js';\nimport type { ApplyMigrationsOptions, SqlMigration } from './types.js';\n\nconst log = createLogger('Sqlite:Migrations');\n\n/** Baseline schema version applied from schema.sql on first open. */\nexport const XOPC_DB_BASELINE_SCHEMA_VERSION = 3;\n\n/** Latest schema version this release supports (increment when adding migrations). */\nexport const XOPC_DB_SCHEMA_VERSION =
|
|
1
|
+
{"version":3,"file":"runner.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { readSchemaVersion, setSchemaVersion } from '../schema-version.js';\nimport { discoverSqlMigrations } from './discover.js';\nimport {\n DatabaseSchemaMigrationGapError,\n DatabaseSchemaTooNewError,\n} from './errors.js';\nimport type { ApplyMigrationsOptions, SqlMigration } from './types.js';\n\nconst log = createLogger('Sqlite:Migrations');\n\n/** Baseline schema version applied from schema.sql on first open. */\nexport const XOPC_DB_BASELINE_SCHEMA_VERSION = 3;\n\n/** Latest schema version this release supports (increment when adding migrations). */\nexport const XOPC_DB_SCHEMA_VERSION = 5;\n\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\n\nexport function resolveMigrationsDir(override?: string): string {\n if (override) {\n return override;\n }\n // Packaged Electron gateway bundle: `out/server/index.js` + `out/server/migrations/`.\n const siblingDir = join(MODULE_DIR, 'migrations');\n if (existsSync(siblingDir)) {\n return siblingDir;\n }\n // Dev / dist: SQL files live next to `migrations/runner.js`.\n return MODULE_DIR;\n}\n\nfunction migrationByTarget(\n migrations: SqlMigration[],\n targetVersion: number,\n): SqlMigration | undefined {\n return migrations.find((migration) => migration.targetVersion === targetVersion);\n}\n\nfunction applySingleMigration(db: DatabaseSync, migration: SqlMigration): void {\n log.info({ targetVersion: migration.targetVersion, file: migration.filename }, 'Applying SQLite migration');\n db.exec('BEGIN IMMEDIATE');\n try {\n db.exec(migration.sql);\n setSchemaVersion(db, migration.targetVersion);\n db.exec('COMMIT');\n } catch (error) {\n try {\n db.exec('ROLLBACK');\n } catch {\n /* preserve original error */\n }\n const em = error instanceof Error ? error.message : String(error);\n throw new Error(\n `SQLite migration to v${migration.targetVersion} (${migration.filename}) failed: ${em}`,\n { cause: error },\n );\n }\n}\n\n/**\n * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.\n * Each step runs in its own transaction and updates schema_meta on success.\n */\nexport function applyPendingMigrations(\n db: DatabaseSync,\n options: ApplyMigrationsOptions = {},\n): number {\n const targetVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n let currentVersion = readSchemaVersion(db);\n\n if (currentVersion > targetVersion) {\n throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);\n }\n\n if (currentVersion >= targetVersion) {\n return currentVersion;\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n\n while (currentVersion < targetVersion) {\n const nextVersion = currentVersion + 1;\n const migration = migrationByTarget(migrations, nextVersion);\n if (!migration) {\n throw new DatabaseSchemaMigrationGapError(currentVersion, targetVersion, nextVersion);\n }\n applySingleMigration(db, migration);\n currentVersion = nextVersion;\n }\n\n return currentVersion;\n}\n\nexport type SchemaMigrationStatus = {\n dbVersion: number;\n appVersion: number;\n pendingVersions: number[];\n isTooNew: boolean;\n hasMigrationGap: boolean;\n missingVersion: number | null;\n};\n\n/** Inspect schema version without mutating the database. */\nexport function inspectSchemaMigrationStatus(\n db: DatabaseSync,\n options: Pick<ApplyMigrationsOptions, 'migrationsDir' | 'targetVersion'> = {},\n): SchemaMigrationStatus {\n const appVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n const dbVersion = readSchemaVersion(db);\n const isTooNew = dbVersion > appVersion;\n\n if (isTooNew) {\n return {\n dbVersion,\n appVersion,\n pendingVersions: [],\n isTooNew: true,\n hasMigrationGap: false,\n missingVersion: null,\n };\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n const pendingVersions: number[] = [];\n let hasMigrationGap = false;\n let missingVersion: number | null = null;\n\n for (let version = dbVersion + 1; version <= appVersion; version++) {\n if (migrationByTarget(migrations, version)) {\n pendingVersions.push(version);\n } else {\n hasMigrationGap = true;\n missingVersion = version;\n break;\n }\n }\n\n return {\n dbVersion,\n appVersion,\n pendingVersions,\n isTooNew: false,\n hasMigrationGap,\n missingVersion,\n };\n}\n"],"mappings":";;;;;;;;;aAMwD;AASxD,MAAM,MAAM,aAAa,oBAAoB;;AAG7C,MAAa,kCAAkC;;AAG/C,MAAa,yBAAyB;AAEtC,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE1D,SAAgB,qBAAqB,UAA2B;AAC9D,KAAI,SACF,QAAO;CAGT,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,KAAI,WAAW,WAAW,CACxB,QAAO;AAGT,QAAO;;AAGT,SAAS,kBACP,YACA,eAC0B;AAC1B,QAAO,WAAW,MAAM,cAAc,UAAU,kBAAkB,cAAc;;AAGlF,SAAS,qBAAqB,IAAkB,WAA+B;AAC7E,KAAI,KAAK;EAAE,eAAe,UAAU;EAAe,MAAM,UAAU;EAAU,EAAE,4BAA4B;AAC3G,IAAG,KAAK,kBAAkB;AAC1B,KAAI;AACF,KAAG,KAAK,UAAU,IAAI;AACtB,mBAAiB,IAAI,UAAU,cAAc;AAC7C,KAAG,KAAK,SAAS;UACV,OAAO;AACd,MAAI;AACF,MAAG,KAAK,WAAW;UACb;EAGR,MAAM,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACjE,QAAM,IAAI,MACR,wBAAwB,UAAU,cAAc,IAAI,UAAU,SAAS,YAAY,MACnF,EAAE,OAAO,OAAO,CACjB;;;;;;;AAQL,SAAgB,uBACd,IACA,UAAkC,EAAE,EAC5B;CACR,MAAM,gBAAgB,QAAQ,iBAAA;CAC9B,IAAI,iBAAiB,kBAAkB,GAAG;AAE1C,KAAI,iBAAiB,cACnB,OAAM,IAAI,0BAA0B,gBAAgB,cAAc;AAGpE,KAAI,kBAAkB,cACpB,QAAO;CAGT,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;AAErF,QAAO,iBAAiB,eAAe;EACrC,MAAM,cAAc,iBAAiB;EACrC,MAAM,YAAY,kBAAkB,YAAY,YAAY;AAC5D,MAAI,CAAC,UACH,OAAM,IAAI,gCAAgC,gBAAgB,eAAe,YAAY;AAEvF,uBAAqB,IAAI,UAAU;AACnC,mBAAiB;;AAGnB,QAAO;;;AAaT,SAAgB,6BACd,IACA,UAA2E,EAAE,EACtD;CACvB,MAAM,aAAa,QAAQ,iBAAA;CAC3B,MAAM,YAAY,kBAAkB,GAAG;AAGvC,KAFiB,YAAY,WAG3B,QAAO;EACL;EACA;EACA,iBAAiB,EAAE;EACnB,UAAU;EACV,iBAAiB;EACjB,gBAAgB;EACjB;CAGH,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;CACrF,MAAM,kBAA4B,EAAE;CACpC,IAAI,kBAAkB;CACtB,IAAI,iBAAgC;AAEpC,MAAK,IAAI,UAAU,YAAY,GAAG,WAAW,YAAY,UACvD,KAAI,kBAAkB,YAAY,QAAQ,CACxC,iBAAgB,KAAK,QAAQ;MACxB;AACL,oBAAkB;AAClB,mBAAiB;AACjB;;AAIJ,QAAO;EACL;EACA;EACA;EACA,UAAU;EACV;EACA;EACD"}
|
|
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
|
|
|
19
19
|
export const XOPC_DB_BASELINE_SCHEMA_VERSION = 3;
|
|
20
20
|
|
|
21
21
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
22
|
-
export const XOPC_DB_SCHEMA_VERSION =
|
|
22
|
+
export const XOPC_DB_SCHEMA_VERSION = 5;
|
|
23
23
|
|
|
24
24
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface NoteAgentContextRecord<T = unknown> {
|
|
2
|
+
noteId: string;
|
|
3
|
+
noteUpdatedAt: number;
|
|
4
|
+
contextVersion: string;
|
|
5
|
+
generatedAt: number;
|
|
6
|
+
payload: T;
|
|
7
|
+
}
|
|
8
|
+
export declare function getNoteAgentContextRecord<T = unknown>(noteId: string): NoteAgentContextRecord<T> | null;
|
|
9
|
+
export declare function upsertNoteAgentContextRecord(record: NoteAgentContextRecord): void;
|
|
10
|
+
export declare function deleteNoteAgentContextRecord(noteId: string): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { requireXopcDatabase } from "./connection.js";
|
|
2
|
+
//#region src/storage/sqlite/note-agent-context-repository.ts
|
|
3
|
+
function getNoteAgentContextRecord(noteId) {
|
|
4
|
+
const row = requireXopcDatabase().db.prepare(`
|
|
5
|
+
SELECT note_id, note_updated_at, context_version, generated_at, payload_json
|
|
6
|
+
FROM note_agent_contexts
|
|
7
|
+
WHERE note_id = ?
|
|
8
|
+
`).get(noteId);
|
|
9
|
+
if (!row) return null;
|
|
10
|
+
return {
|
|
11
|
+
noteId: row.note_id,
|
|
12
|
+
noteUpdatedAt: row.note_updated_at,
|
|
13
|
+
contextVersion: row.context_version,
|
|
14
|
+
generatedAt: row.generated_at,
|
|
15
|
+
payload: JSON.parse(row.payload_json)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function upsertNoteAgentContextRecord(record) {
|
|
19
|
+
requireXopcDatabase().db.prepare(`
|
|
20
|
+
INSERT INTO note_agent_contexts (note_id, note_updated_at, context_version, generated_at, payload_json)
|
|
21
|
+
VALUES (?, ?, ?, ?, ?)
|
|
22
|
+
ON CONFLICT(note_id) DO UPDATE SET
|
|
23
|
+
note_updated_at = excluded.note_updated_at,
|
|
24
|
+
context_version = excluded.context_version,
|
|
25
|
+
generated_at = excluded.generated_at,
|
|
26
|
+
payload_json = excluded.payload_json
|
|
27
|
+
`).run(record.noteId, record.noteUpdatedAt, record.contextVersion, record.generatedAt, JSON.stringify(record.payload));
|
|
28
|
+
}
|
|
29
|
+
function deleteNoteAgentContextRecord(noteId) {
|
|
30
|
+
requireXopcDatabase().db.prepare(`DELETE FROM note_agent_contexts WHERE note_id = ?`).run(noteId);
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { deleteNoteAgentContextRecord, getNoteAgentContextRecord, upsertNoteAgentContextRecord };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=note-agent-context-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"note-agent-context-repository.js","names":[],"sources":["../../../../src/storage/sqlite/note-agent-context-repository.ts"],"sourcesContent":["import { requireXopcDatabase } from './connection.js';\n\nexport interface NoteAgentContextRecord<T = unknown> {\n noteId: string;\n noteUpdatedAt: number;\n contextVersion: string;\n generatedAt: number;\n payload: T;\n}\n\nexport function getNoteAgentContextRecord<T = unknown>(noteId: string): NoteAgentContextRecord<T> | null {\n const db = requireXopcDatabase().db;\n const row = db.prepare(`\n SELECT note_id, note_updated_at, context_version, generated_at, payload_json\n FROM note_agent_contexts\n WHERE note_id = ?\n `).get(noteId) as { note_id: string; note_updated_at: number; context_version: string; generated_at: number; payload_json: string } | undefined;\n if (!row) return null;\n return {\n noteId: row.note_id,\n noteUpdatedAt: row.note_updated_at,\n contextVersion: row.context_version,\n generatedAt: row.generated_at,\n payload: JSON.parse(row.payload_json) as T,\n };\n}\n\nexport function upsertNoteAgentContextRecord(record: NoteAgentContextRecord): void {\n const db = requireXopcDatabase().db;\n db.prepare(`\n INSERT INTO note_agent_contexts (note_id, note_updated_at, context_version, generated_at, payload_json)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(note_id) DO UPDATE SET\n note_updated_at = excluded.note_updated_at,\n context_version = excluded.context_version,\n generated_at = excluded.generated_at,\n payload_json = excluded.payload_json\n `).run(\n record.noteId,\n record.noteUpdatedAt,\n record.contextVersion,\n record.generatedAt,\n JSON.stringify(record.payload),\n );\n}\n\nexport function deleteNoteAgentContextRecord(noteId: string): void {\n const db = requireXopcDatabase().db;\n db.prepare(`DELETE FROM note_agent_contexts WHERE note_id = ?`).run(noteId);\n}\n"],"mappings":";;AAUA,SAAgB,0BAAuC,QAAkD;CAEvG,MAAM,MADK,qBAAqB,CAAC,GAClB,QAAQ;;;;IAIrB,CAAC,IAAI,OAAO;AACd,KAAI,CAAC,IAAK,QAAO;AACjB,QAAO;EACL,QAAQ,IAAI;EACZ,eAAe,IAAI;EACnB,gBAAgB,IAAI;EACpB,aAAa,IAAI;EACjB,SAAS,KAAK,MAAM,IAAI,aAAa;EACtC;;AAGH,SAAgB,6BAA6B,QAAsC;AACtE,sBAAqB,CAAC,GAC9B,QAAQ;;;;;;;;IAQT,CAAC,IACD,OAAO,QACP,OAAO,eACP,OAAO,gBACP,OAAO,aACP,KAAK,UAAU,OAAO,QAAQ,CAC/B;;AAGH,SAAgB,6BAA6B,QAAsB;AACtD,sBAAqB,CAAC,GAC9B,QAAQ,oDAAoD,CAAC,IAAI,OAAO"}
|
|
@@ -31,7 +31,7 @@ function ensureXopcDatabaseSchema(db) {
|
|
|
31
31
|
bootstrapFreshDatabase(db);
|
|
32
32
|
currentVersion = 3;
|
|
33
33
|
}
|
|
34
|
-
applyPendingMigrations(db, { targetVersion:
|
|
34
|
+
applyPendingMigrations(db, { targetVersion: 5 });
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
37
|
export { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, ensureSchemaMetaTable, ensureXopcDatabaseSchema, readSchemaVersion, readSchemaVersionForTest, setSchemaVersion };
|
|
@@ -4,10 +4,10 @@ import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.
|
|
|
4
4
|
import { resolveBootstrapFilesSync } from "../agent/bootstrap/bootstrap-files.js";
|
|
5
5
|
import { createWorkflowCatalog } from "../agent/workflow/catalog.js";
|
|
6
6
|
import { createSkillConfigManager, isSkillEnabled } from "../agent/skills/config.js";
|
|
7
|
-
import { listConnectorInstances } from "../connectors/instances.js";
|
|
8
7
|
import { loadSkills } from "../agent/skills/index.js";
|
|
9
8
|
import { getWorkspacePath } from "../config/workspace-path-helpers.js";
|
|
10
9
|
import "../config/index.js";
|
|
10
|
+
import { listConnectorInstances } from "../connectors/instances.js";
|
|
11
11
|
import { basename } from "node:path";
|
|
12
12
|
//#region src/tui/tui-startup-resources.ts
|
|
13
13
|
init_paths();
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import type { STTConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Lightweight availability check for channel routing. Do not import the STT
|
|
4
|
+
* factory/provider registry here; channel plugins call this at startup and the
|
|
5
|
+
* provider registry pulls optional SDKs (e.g. openai) into Electron's dynamic
|
|
6
|
+
* extension runtime path.
|
|
7
|
+
*/
|
|
2
8
|
export declare function isSTTAvailable(config?: STTConfig): boolean;
|
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import { resolveSTTProviderChain } from "./factory.js";
|
|
2
1
|
//#region src/voice/stt/availability.ts
|
|
2
|
+
function providerHasConfig(config, providerId) {
|
|
3
|
+
const provider = config.providers?.[providerId];
|
|
4
|
+
if (provider && Object.keys(provider).length > 0) return true;
|
|
5
|
+
return [...config.models ?? [], ...config.sharedModels ?? []].some((entry) => entry.provider === providerId && entry.capabilities?.includes("audio"));
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Lightweight availability check for channel routing. Do not import the STT
|
|
9
|
+
* factory/provider registry here; channel plugins call this at startup and the
|
|
10
|
+
* provider registry pulls optional SDKs (e.g. openai) into Electron's dynamic
|
|
11
|
+
* extension runtime path.
|
|
12
|
+
*/
|
|
3
13
|
function isSTTAvailable(config) {
|
|
4
14
|
if (!config?.enabled) return false;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
15
|
+
if (providerHasConfig(config, config.provider)) return true;
|
|
16
|
+
if (config.fallback?.enabled) return config.fallback.order.some((providerId) => providerHasConfig(config, providerId));
|
|
17
|
+
return false;
|
|
10
18
|
}
|
|
11
19
|
//#endregion
|
|
12
20
|
export { isSTTAvailable };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"availability.js","names":[],"sources":["../../../../src/voice/stt/availability.ts"],"sourcesContent":["import type { STTConfig } from './types.js';\
|
|
1
|
+
{"version":3,"file":"availability.js","names":[],"sources":["../../../../src/voice/stt/availability.ts"],"sourcesContent":["import type { STTConfig } from './types.js';\n\nfunction providerHasConfig(config: STTConfig, providerId: string): boolean {\n const provider = config.providers?.[providerId];\n if (provider && Object.keys(provider).length > 0) return true;\n return [...(config.models ?? []), ...(config.sharedModels ?? [])].some(\n (entry) => entry.provider === providerId && entry.capabilities?.includes('audio'),\n );\n}\n\n/**\n * Lightweight availability check for channel routing. Do not import the STT\n * factory/provider registry here; channel plugins call this at startup and the\n * provider registry pulls optional SDKs (e.g. openai) into Electron's dynamic\n * extension runtime path.\n */\nexport function isSTTAvailable(config?: STTConfig): boolean {\n if (!config?.enabled) return false;\n if (providerHasConfig(config, config.provider)) return true;\n if (config.fallback?.enabled) {\n return config.fallback.order.some((providerId) => providerHasConfig(config, providerId));\n }\n return false;\n}\n"],"mappings":";AAEA,SAAS,kBAAkB,QAAmB,YAA6B;CACzE,MAAM,WAAW,OAAO,YAAY;AACpC,KAAI,YAAY,OAAO,KAAK,SAAS,CAAC,SAAS,EAAG,QAAO;AACzD,QAAO,CAAC,GAAI,OAAO,UAAU,EAAE,EAAG,GAAI,OAAO,gBAAgB,EAAE,CAAE,CAAC,MAC/D,UAAU,MAAM,aAAa,cAAc,MAAM,cAAc,SAAS,QAAQ,CAClF;;;;;;;;AASH,SAAgB,eAAe,QAA6B;AAC1D,KAAI,CAAC,QAAQ,QAAS,QAAO;AAC7B,KAAI,kBAAkB,QAAQ,OAAO,SAAS,CAAE,QAAO;AACvD,KAAI,OAAO,UAAU,QACnB,QAAO,OAAO,SAAS,MAAM,MAAM,eAAe,kBAAkB,QAAQ,WAAW,CAAC;AAE1F,QAAO"}
|
|
@@ -4,7 +4,8 @@ import "./providers/index.js";
|
|
|
4
4
|
import { resolveSTTProviderChain, resolveSTTProviderConfig, resolveSTTProviderOrder } from "./factory.js";
|
|
5
5
|
import { transcribe } from "./transcribe-core.js";
|
|
6
6
|
import { isSTTAvailable } from "./availability.js";
|
|
7
|
-
import {
|
|
7
|
+
import { checkMentionInTranscription } from "./mention.js";
|
|
8
|
+
import { audioPreflightTranscribe } from "./preflight.js";
|
|
8
9
|
//#region src/voice/stt/index.ts
|
|
9
10
|
/**
|
|
10
11
|
* Single-provider transcription helper. Used by chat-commands /stt so a user
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/voice/stt/index.ts"],"sourcesContent":["/**\n * STT (Speech-to-Text) Module\n *\n * v2.0: providers implement MediaUnderstandingProvider; orchestration goes\n * through the media-understanding runner. The legacy `transcribe()` /\n * `isSTTAvailable()` API surface is preserved for channel adapters.\n */\n\nimport './providers/index.js'; // side-effect: register built-in STT providers\n\nimport type { STTConfig, STTOptions, STTResult } from './types.js';\nimport { transcribe } from './transcribe-core.js';\n\nexport { transcribe } from './transcribe-core.js';\nexport { isSTTAvailable } from './availability.js';\n\n/**\n * Single-provider transcription helper. Used by chat-commands /stt so a user\n * can force a specific provider regardless of fallback config.\n */\nexport async function transcribeWithProvider(\n audioBuffer: Buffer,\n providerName: string,\n config: STTConfig,\n options?: STTOptions,\n): Promise<STTResult> {\n // Build a per-call config that pins primary to the requested provider and\n // disables fallback. This routes through the same runner machinery without\n // needing a separate single-provider code path.\n const pinnedConfig: STTConfig = {\n ...config,\n provider: providerName,\n fallback: { enabled: false, order: [providerName] },\n };\n const result = await transcribe(audioBuffer, pinnedConfig, options);\n return {\n text: result.text,\n provider: result.provider,\n ...(result.duration !== undefined ? { duration: result.duration } : {}),\n ...(result.language ? { language: result.language } : {}),\n };\n}\n\nexport type {\n STTResult,\n STTOptions,\n STTConfig,\n STTResultWithTracking,\n STTProviderAttempt,\n STTProviderFailureReason,\n} from './types.js';\n\nexport { openAiTranscriptionProvider, alibabaTranscriptionProvider } from './providers/index.js';\n\nexport {\n resolveSTTProviderConfig,\n resolveSTTProviderChain,\n resolveSTTProviderOrder,\n} from './factory.js';\n\nexport { audioPreflightTranscribe, checkMentionInTranscription } from './preflight.js';\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/voice/stt/index.ts"],"sourcesContent":["/**\n * STT (Speech-to-Text) Module\n *\n * v2.0: providers implement MediaUnderstandingProvider; orchestration goes\n * through the media-understanding runner. The legacy `transcribe()` /\n * `isSTTAvailable()` API surface is preserved for channel adapters.\n */\n\nimport './providers/index.js'; // side-effect: register built-in STT providers\n\nimport type { STTConfig, STTOptions, STTResult } from './types.js';\nimport { transcribe } from './transcribe-core.js';\n\nexport { transcribe } from './transcribe-core.js';\nexport { isSTTAvailable } from './availability.js';\n\n/**\n * Single-provider transcription helper. Used by chat-commands /stt so a user\n * can force a specific provider regardless of fallback config.\n */\nexport async function transcribeWithProvider(\n audioBuffer: Buffer,\n providerName: string,\n config: STTConfig,\n options?: STTOptions,\n): Promise<STTResult> {\n // Build a per-call config that pins primary to the requested provider and\n // disables fallback. This routes through the same runner machinery without\n // needing a separate single-provider code path.\n const pinnedConfig: STTConfig = {\n ...config,\n provider: providerName,\n fallback: { enabled: false, order: [providerName] },\n };\n const result = await transcribe(audioBuffer, pinnedConfig, options);\n return {\n text: result.text,\n provider: result.provider,\n ...(result.duration !== undefined ? { duration: result.duration } : {}),\n ...(result.language ? { language: result.language } : {}),\n };\n}\n\nexport type {\n STTResult,\n STTOptions,\n STTConfig,\n STTResultWithTracking,\n STTProviderAttempt,\n STTProviderFailureReason,\n} from './types.js';\n\nexport { openAiTranscriptionProvider, alibabaTranscriptionProvider } from './providers/index.js';\n\nexport {\n resolveSTTProviderConfig,\n resolveSTTProviderChain,\n resolveSTTProviderOrder,\n} from './factory.js';\n\nexport { audioPreflightTranscribe, checkMentionInTranscription } from './preflight.js';\n"],"mappings":";;;;;;;;;;;;;AAoBA,eAAsB,uBACpB,aACA,cACA,QACA,SACoB;CASpB,MAAM,SAAS,MAAM,WAAW,aAAa;EAJ3C,GAAG;EACH,UAAU;EACV,UAAU;GAAE,SAAS;GAAO,OAAO,CAAC,aAAa;GAAE;EAEI,EAAE,QAAQ;AACnE,QAAO;EACL,MAAM,OAAO;EACb,UAAU,OAAO;EACjB,GAAI,OAAO,aAAa,KAAA,IAAY,EAAE,UAAU,OAAO,UAAU,GAAG,EAAE;EACtE,GAAI,OAAO,WAAW,EAAE,UAAU,OAAO,UAAU,GAAG,EAAE;EACzD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/voice/stt/mention.ts
|
|
2
|
+
/** Spoken @bot variants ("at botname", "hey botname") for STT text. */
|
|
3
|
+
function checkMentionInTranscription(transcribedText, botNames) {
|
|
4
|
+
const normalizedText = transcribedText.toLowerCase().trim();
|
|
5
|
+
for (const name of botNames) {
|
|
6
|
+
const normalizedName = name.toLowerCase().trim();
|
|
7
|
+
if (!normalizedName) continue;
|
|
8
|
+
if (normalizedText.includes(normalizedName)) return true;
|
|
9
|
+
if ([
|
|
10
|
+
`at ${normalizedName}`,
|
|
11
|
+
`hey ${normalizedName}`,
|
|
12
|
+
`hi ${normalizedName}`
|
|
13
|
+
].some((pattern) => normalizedText.includes(pattern))) return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { checkMentionInTranscription };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=mention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention.js","names":[],"sources":["../../../../src/voice/stt/mention.ts"],"sourcesContent":["/** Spoken @bot variants (\"at botname\", \"hey botname\") for STT text. */\nexport function checkMentionInTranscription(transcribedText: string, botNames: string[]): boolean {\n const normalizedText = transcribedText.toLowerCase().trim();\n\n for (const name of botNames) {\n const normalizedName = name.toLowerCase().trim();\n if (!normalizedName) continue;\n\n if (normalizedText.includes(normalizedName)) return true;\n\n const fuzzyPatterns = [`at ${normalizedName}`, `hey ${normalizedName}`, `hi ${normalizedName}`];\n if (fuzzyPatterns.some((pattern) => normalizedText.includes(pattern))) return true;\n }\n\n return false;\n}\n"],"mappings":";;AACA,SAAgB,4BAA4B,iBAAyB,UAA6B;CAChG,MAAM,iBAAiB,gBAAgB,aAAa,CAAC,MAAM;AAE3D,MAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,iBAAiB,KAAK,aAAa,CAAC,MAAM;AAChD,MAAI,CAAC,eAAgB;AAErB,MAAI,eAAe,SAAS,eAAe,CAAE,QAAO;AAGpD,MAAI;GADmB,MAAM;GAAkB,OAAO;GAAkB,MAAM;GAC7D,CAAC,MAAM,YAAY,eAAe,SAAS,QAAQ,CAAC,CAAE,QAAO;;AAGhF,QAAO"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { STTConfig } from './types.js';
|
|
2
|
+
export { checkMentionInTranscription } from './mention.js';
|
|
2
3
|
export interface AudioPreflightInput {
|
|
3
4
|
audioBuffer: Buffer;
|
|
4
5
|
mimeType?: string;
|
|
@@ -16,7 +17,3 @@ export interface AudioPreflightResult {
|
|
|
16
17
|
* Transcribe audio before mention routing (e.g. group voice → detect spoken @bot).
|
|
17
18
|
*/
|
|
18
19
|
export declare function audioPreflightTranscribe(input: AudioPreflightInput): Promise<AudioPreflightResult>;
|
|
19
|
-
/**
|
|
20
|
-
* Spoken @bot variants ("at botname", "hey botname") for STT text.
|
|
21
|
-
*/
|
|
22
|
-
export declare function checkMentionInTranscription(transcribedText: string, botNames: string[]): boolean;
|
|
@@ -2,6 +2,7 @@ import { createLogger } from "../../utils/logger/index.js";
|
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
3
|
import { transcribe } from "./transcribe-core.js";
|
|
4
4
|
import { isSTTAvailable } from "./availability.js";
|
|
5
|
+
import { checkMentionInTranscription } from "./mention.js";
|
|
5
6
|
//#region src/voice/stt/preflight.ts
|
|
6
7
|
init_logger();
|
|
7
8
|
const log = createLogger("STT:Preflight");
|
|
@@ -59,24 +60,6 @@ async function audioPreflightTranscribe(input) {
|
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
|
-
/**
|
|
63
|
-
* Spoken @bot variants ("at botname", "hey botname") for STT text.
|
|
64
|
-
*/
|
|
65
|
-
function checkMentionInTranscription(transcribedText, botNames) {
|
|
66
|
-
const normalizedText = transcribedText.toLowerCase().trim();
|
|
67
|
-
for (const name of botNames) {
|
|
68
|
-
const normalizedName = name.toLowerCase().trim();
|
|
69
|
-
if (!normalizedName) continue;
|
|
70
|
-
if (normalizedText.includes(normalizedName)) return true;
|
|
71
|
-
const fuzzyPatterns = [
|
|
72
|
-
`at ${normalizedName}`,
|
|
73
|
-
`hey ${normalizedName}`,
|
|
74
|
-
`hi ${normalizedName}`
|
|
75
|
-
];
|
|
76
|
-
for (const pattern of fuzzyPatterns) if (normalizedText.includes(pattern)) return true;
|
|
77
|
-
}
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
63
|
//#endregion
|
|
81
64
|
export { audioPreflightTranscribe, checkMentionInTranscription };
|
|
82
65
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preflight.js","names":[],"sources":["../../../../src/voice/stt/preflight.ts"],"sourcesContent":["import type { STTConfig, STTResult } from './types.js';\nimport { transcribe } from './transcribe-core.js';\nimport { isSTTAvailable } from './availability.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('STT:Preflight');\n\nexport interface AudioPreflightInput {\n audioBuffer: Buffer;\n mimeType?: string;\n sttConfig: STTConfig;\n language?: string;\n}\n\nexport interface AudioPreflightResult {\n success: boolean;\n transcribedText?: string;\n provider?: string;\n duration?: number;\n error?: string;\n}\n\n/**\n * Transcribe audio before mention routing (e.g. group voice → detect spoken @bot).\n */\nexport async function audioPreflightTranscribe(\n input: AudioPreflightInput,\n): Promise<AudioPreflightResult> {\n const { audioBuffer, sttConfig, language } = input;\n\n if (!isSTTAvailable(sttConfig)) {\n log.debug('STT not available, skipping preflight');\n return { success: false, error: 'STT not available' };\n }\n\n if (!audioBuffer || audioBuffer.length === 0) {\n return { success: false, error: 'Empty audio buffer' };\n }\n\n const startTime = Date.now();\n\n try {\n const result: STTResult = await transcribe(audioBuffer, sttConfig, {\n language,\n });\n\n const duration = (Date.now() - startTime) / 1000;\n\n if (!result.text?.trim()) {\n log.debug({ duration }, 'Preflight transcription returned empty text');\n return {\n success: false,\n provider: result.provider,\n duration,\n error: 'Empty transcription result',\n };\n }\n\n log.info(\n {\n provider: result.provider,\n textLength: result.text.length,\n duration,\n },\n 'Audio preflight transcription completed',\n );\n\n return {\n success: true,\n transcribedText: result.text,\n provider: result.provider,\n duration,\n };\n } catch (error) {\n const duration = (Date.now() - startTime) / 1000;\n const errorMsg = error instanceof Error ? error.message : String(error);\n\n log.warn({ errorMessage: errorMsg, duration }, 'Audio preflight transcription failed');\n\n return {\n success: false,\n duration,\n error: errorMsg,\n };\n }\n}\n\n
|
|
1
|
+
{"version":3,"file":"preflight.js","names":[],"sources":["../../../../src/voice/stt/preflight.ts"],"sourcesContent":["import type { STTConfig, STTResult } from './types.js';\nimport { transcribe } from './transcribe-core.js';\nimport { isSTTAvailable } from './availability.js';\nexport { checkMentionInTranscription } from './mention.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('STT:Preflight');\n\nexport interface AudioPreflightInput {\n audioBuffer: Buffer;\n mimeType?: string;\n sttConfig: STTConfig;\n language?: string;\n}\n\nexport interface AudioPreflightResult {\n success: boolean;\n transcribedText?: string;\n provider?: string;\n duration?: number;\n error?: string;\n}\n\n/**\n * Transcribe audio before mention routing (e.g. group voice → detect spoken @bot).\n */\nexport async function audioPreflightTranscribe(\n input: AudioPreflightInput,\n): Promise<AudioPreflightResult> {\n const { audioBuffer, sttConfig, language } = input;\n\n if (!isSTTAvailable(sttConfig)) {\n log.debug('STT not available, skipping preflight');\n return { success: false, error: 'STT not available' };\n }\n\n if (!audioBuffer || audioBuffer.length === 0) {\n return { success: false, error: 'Empty audio buffer' };\n }\n\n const startTime = Date.now();\n\n try {\n const result: STTResult = await transcribe(audioBuffer, sttConfig, {\n language,\n });\n\n const duration = (Date.now() - startTime) / 1000;\n\n if (!result.text?.trim()) {\n log.debug({ duration }, 'Preflight transcription returned empty text');\n return {\n success: false,\n provider: result.provider,\n duration,\n error: 'Empty transcription result',\n };\n }\n\n log.info(\n {\n provider: result.provider,\n textLength: result.text.length,\n duration,\n },\n 'Audio preflight transcription completed',\n );\n\n return {\n success: true,\n transcribedText: result.text,\n provider: result.provider,\n duration,\n };\n } catch (error) {\n const duration = (Date.now() - startTime) / 1000;\n const errorMsg = error instanceof Error ? error.message : String(error);\n\n log.warn({ errorMessage: errorMsg, duration }, 'Audio preflight transcription failed');\n\n return {\n success: false,\n duration,\n error: errorMsg,\n };\n }\n}\n\n"],"mappings":";;;;;;aAIqD;AAErD,MAAM,MAAM,aAAa,gBAAgB;;;;AAoBzC,eAAsB,yBACpB,OAC+B;CAC/B,MAAM,EAAE,aAAa,WAAW,aAAa;AAE7C,KAAI,CAAC,eAAe,UAAU,EAAE;AAC9B,MAAI,MAAM,wCAAwC;AAClD,SAAO;GAAE,SAAS;GAAO,OAAO;GAAqB;;AAGvD,KAAI,CAAC,eAAe,YAAY,WAAW,EACzC,QAAO;EAAE,SAAS;EAAO,OAAO;EAAsB;CAGxD,MAAM,YAAY,KAAK,KAAK;AAE5B,KAAI;EACF,MAAM,SAAoB,MAAM,WAAW,aAAa,WAAW,EACjE,UACD,CAAC;EAEF,MAAM,YAAY,KAAK,KAAK,GAAG,aAAa;AAE5C,MAAI,CAAC,OAAO,MAAM,MAAM,EAAE;AACxB,OAAI,MAAM,EAAE,UAAU,EAAE,8CAA8C;AACtE,UAAO;IACL,SAAS;IACT,UAAU,OAAO;IACjB;IACA,OAAO;IACR;;AAGH,MAAI,KACF;GACE,UAAU,OAAO;GACjB,YAAY,OAAO,KAAK;GACxB;GACD,EACD,0CACD;AAED,SAAO;GACL,SAAS;GACT,iBAAiB,OAAO;GACxB,UAAU,OAAO;GACjB;GACD;UACM,OAAO;EACd,MAAM,YAAY,KAAK,KAAK,GAAG,aAAa;EAC5C,MAAM,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAEvE,MAAI,KAAK;GAAE,cAAc;GAAU;GAAU,EAAE,uCAAuC;AAEtF,SAAO;GACL,SAAS;GACT;GACA,OAAO;GACR"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xopcai/xopc",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.106",
|
|
4
4
|
"description": "Goal Loop OS for long-term AI work: a local-first multi-surface agent system with workflow automation, multi-agent routing, and extensibility via extensions/skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"semver": "^7.8.1",
|
|
101
101
|
"silk-wasm": "^3.7.1",
|
|
102
102
|
"thread-stream": "^4.2.0",
|
|
103
|
-
"undici": "^8.
|
|
103
|
+
"undici": "^8.5.0",
|
|
104
104
|
"uuid": "^14.0.0",
|
|
105
105
|
"ws": "^8.21.0",
|
|
106
106
|
"zod": "^4.4.3"
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"@vitejs/plugin-react-swc": "^4.3.1",
|
|
119
119
|
"cross-env": "^10.1.0",
|
|
120
120
|
"dependency-cruiser": "^17.4.3",
|
|
121
|
-
"electron": "^
|
|
121
|
+
"electron": "^42.4.1",
|
|
122
122
|
"electron-builder": "^26.12.1",
|
|
123
123
|
"electron-vite": "^6.0.0-beta.1",
|
|
124
124
|
"esbuild": "^0.28.1",
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
"electron:server:build": "node scripts/build-electron-server.mjs",
|
|
183
183
|
"electron:frpc:download": "node --import tsx/esm scripts/download-frpc-binaries.mjs",
|
|
184
184
|
"electron:frpc:download:all": "node --import tsx/esm scripts/download-frpc-binaries.mjs --all",
|
|
185
|
-
"electron:build": "pnpm run electron:vite:build:package && pnpm run electron:server:build && pnpm run electron:package",
|
|
185
|
+
"electron:build": "pnpm run build:web && pnpm run electron:vite:build:package && pnpm run electron:server:build && pnpm run electron:package",
|
|
186
186
|
"electron:preview": "cross-env ELECTRON_ENTRY=out/main/index.js electron-vite preview"
|
|
187
187
|
}
|
|
188
188
|
}
|