negotium 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/chunk-0ay6cjdy.js +1331 -0
- package/dist/chunk-0ay6cjdy.js.map +16 -0
- package/dist/chunk-1k7t7bnx.js +97 -0
- package/dist/chunk-1k7t7bnx.js.map +10 -0
- package/dist/chunk-5ahkew9c.js +806 -0
- package/dist/chunk-5ahkew9c.js.map +17 -0
- package/dist/chunk-6cpn7ht2.js +287 -0
- package/dist/chunk-6cpn7ht2.js.map +13 -0
- package/dist/chunk-6kctn20e.js +3529 -0
- package/dist/chunk-6kctn20e.js.map +20 -0
- package/dist/chunk-7xqj7aex.js +77 -0
- package/dist/chunk-7xqj7aex.js.map +11 -0
- package/dist/chunk-9p0ww3tk.js +41 -0
- package/dist/chunk-9p0ww3tk.js.map +10 -0
- package/dist/chunk-a92a7nrk.js +252 -0
- package/dist/chunk-a92a7nrk.js.map +10 -0
- package/dist/chunk-c500xe9z.js +33 -0
- package/dist/chunk-c500xe9z.js.map +10 -0
- package/dist/chunk-cds1qxsf.js +1644 -0
- package/dist/chunk-cds1qxsf.js.map +17 -0
- package/dist/chunk-e4407g8x.js +274 -0
- package/dist/chunk-e4407g8x.js.map +10 -0
- package/dist/chunk-e5pbggsh.js +363 -0
- package/dist/chunk-e5pbggsh.js.map +11 -0
- package/dist/chunk-edb07sjp.js +567 -0
- package/dist/chunk-edb07sjp.js.map +14 -0
- package/dist/chunk-exd0v74a.js +3012 -0
- package/dist/chunk-exd0v74a.js.map +32 -0
- package/dist/chunk-fgrme9tr.js +4398 -0
- package/dist/chunk-fgrme9tr.js.map +47 -0
- package/dist/chunk-fka9eqe1.js +581 -0
- package/dist/chunk-fka9eqe1.js.map +11 -0
- package/dist/chunk-g82jygez.js +108 -0
- package/dist/chunk-g82jygez.js.map +10 -0
- package/dist/chunk-ghs2j1fr.js +312 -0
- package/dist/chunk-ghs2j1fr.js.map +10 -0
- package/dist/chunk-gmy11efb.js +125 -0
- package/dist/chunk-gmy11efb.js.map +10 -0
- package/dist/chunk-gxma4c70.js +110 -0
- package/dist/chunk-gxma4c70.js.map +10 -0
- package/dist/chunk-hkdqhm34.js +61 -0
- package/dist/chunk-hkdqhm34.js.map +10 -0
- package/dist/chunk-hpa0cpzh.js +134 -0
- package/dist/chunk-hpa0cpzh.js.map +10 -0
- package/dist/chunk-hwy3n31w.js +21 -0
- package/dist/chunk-hwy3n31w.js.map +9 -0
- package/dist/chunk-j2behpy7.js +214 -0
- package/dist/chunk-j2behpy7.js.map +10 -0
- package/dist/chunk-jc0at0tc.js +102 -0
- package/dist/chunk-jc0at0tc.js.map +10 -0
- package/dist/chunk-jkqq5ef6.js +243 -0
- package/dist/chunk-jkqq5ef6.js.map +11 -0
- package/dist/chunk-jzkw2f01.js +51 -0
- package/dist/chunk-jzkw2f01.js.map +10 -0
- package/dist/chunk-jzz91jwh.js +70 -0
- package/dist/chunk-jzz91jwh.js.map +10 -0
- package/dist/chunk-k9z47mab.js +247 -0
- package/dist/chunk-k9z47mab.js.map +12 -0
- package/dist/chunk-n17yxpa8.js +23 -0
- package/dist/chunk-n17yxpa8.js.map +10 -0
- package/dist/chunk-naa09ags.js +2143 -0
- package/dist/chunk-naa09ags.js.map +15 -0
- package/dist/chunk-ns3ke88n.js +142 -0
- package/dist/chunk-ns3ke88n.js.map +10 -0
- package/dist/chunk-p3cr2ejg.js +1626 -0
- package/dist/chunk-p3cr2ejg.js.map +15 -0
- package/dist/chunk-q5bcpf23.js +2925 -0
- package/dist/chunk-q5bcpf23.js.map +20 -0
- package/dist/chunk-rafc55bs.js +424 -0
- package/dist/chunk-rafc55bs.js.map +11 -0
- package/dist/chunk-rbrxjvbj.js +2830 -0
- package/dist/chunk-rbrxjvbj.js.map +23 -0
- package/dist/chunk-sa874j5c.js +9 -0
- package/dist/chunk-sa874j5c.js.map +10 -0
- package/dist/chunk-sgsz2fpq.js +289 -0
- package/dist/chunk-sgsz2fpq.js.map +10 -0
- package/dist/chunk-sm6fh5c5.js +569 -0
- package/dist/chunk-sm6fh5c5.js.map +14 -0
- package/dist/chunk-t9zcg8f1.js +499 -0
- package/dist/chunk-t9zcg8f1.js.map +13 -0
- package/dist/chunk-vp2db42d.js +121 -0
- package/dist/chunk-vp2db42d.js.map +10 -0
- package/dist/chunk-w13jjf4a.js +46 -0
- package/dist/chunk-w13jjf4a.js.map +10 -0
- package/dist/chunk-x1arr7ze.js +63 -0
- package/dist/chunk-x1arr7ze.js.map +10 -0
- package/dist/chunk-xfq68rch.js +583 -0
- package/dist/chunk-xfq68rch.js.map +11 -0
- package/dist/main.js +132 -20
- package/dist/main.js.map +4 -4
- package/dist/runtime/scripts/faster-whisper-wrapper.py +87 -0
- package/dist/runtime/scripts/mcp-patchright-http.mjs +349 -0
- package/dist/runtime/src/agents/api-topic-agent-switch.ts +178 -0
- package/dist/runtime/src/agents/archiver.ts +396 -0
- package/dist/runtime/src/agents/auth-check.ts +64 -0
- package/dist/runtime/src/agents/claude-provider.ts +737 -0
- package/dist/runtime/src/agents/claude-registry.ts +115 -0
- package/dist/runtime/src/agents/codex-native-multi-agent.ts +177 -0
- package/dist/runtime/src/agents/codex-provider.ts +785 -0
- package/dist/runtime/src/agents/codex-registry.ts +115 -0
- package/dist/runtime/src/agents/codex-tree-kill.ts +365 -0
- package/dist/runtime/src/agents/contracts.ts +69 -0
- package/dist/runtime/src/agents/deep-map.ts +13 -0
- package/dist/runtime/src/agents/execution-host.ts +91 -0
- package/dist/runtime/src/agents/fixtures/claude-attachments.jsonl +2 -0
- package/dist/runtime/src/agents/fixtures/codex-shell.jsonl +5 -0
- package/dist/runtime/src/agents/fork.ts +65 -0
- package/dist/runtime/src/agents/hosted-agent.ts +48 -0
- package/dist/runtime/src/agents/idle-archiver.ts +128 -0
- package/dist/runtime/src/agents/index.ts +205 -0
- package/dist/runtime/src/agents/maestro-provider.ts +206 -0
- package/dist/runtime/src/agents/maestro-registry.ts +31 -0
- package/dist/runtime/src/agents/mcp-tools/ask-user.ts +280 -0
- package/dist/runtime/src/agents/mcp-tools/common.ts +21 -0
- package/dist/runtime/src/agents/mcp-tools/self-config.ts +220 -0
- package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +525 -0
- package/dist/runtime/src/agents/mcp-tools/visuals.ts +88 -0
- package/dist/runtime/src/agents/model-catalog.ts +151 -0
- package/dist/runtime/src/agents/registry.ts +24 -0
- package/dist/runtime/src/agents/rollout/claude.ts +331 -0
- package/dist/runtime/src/agents/rollout/codex.ts +569 -0
- package/dist/runtime/src/agents/rollout/shared.ts +205 -0
- package/dist/runtime/src/agents/self-config-core.ts +415 -0
- package/dist/runtime/src/agents/task-events.ts +44 -0
- package/dist/runtime/src/agents/tool-format.ts +352 -0
- package/dist/runtime/src/agents/topic-agent-switch.ts +193 -0
- package/dist/runtime/src/agents/topic-cleanup.ts +231 -0
- package/dist/runtime/src/agents/vault-tool-policy.ts +33 -0
- package/dist/runtime/src/application/execute-external-user-turn.ts +23 -0
- package/dist/runtime/src/application/submit-user-message.ts +47 -0
- package/dist/runtime/src/application/switch-topic-effort.ts +48 -0
- package/dist/runtime/src/application/switch-topic-model.ts +79 -0
- package/dist/runtime/src/application/topic-service.ts +139 -0
- package/dist/runtime/src/application/vault-command.ts +170 -0
- package/dist/runtime/src/bus.ts +319 -0
- package/dist/runtime/src/index.ts +333 -0
- package/dist/runtime/src/mcp/agent-health-server.ts +309 -0
- package/dist/runtime/src/mcp/background-bash-server.ts +346 -0
- package/dist/runtime/src/mcp/canonical-bridge-config.ts +84 -0
- package/dist/runtime/src/mcp/canonical-proxy-server.ts +171 -0
- package/dist/runtime/src/mcp/dm-ipc.ts +60 -0
- package/dist/runtime/src/mcp/mcp-helpers.ts +40 -0
- package/dist/runtime/src/mcp/paddleocr-server.ts +105 -0
- package/dist/runtime/src/mcp/peer-bridge.ts +114 -0
- package/dist/runtime/src/mcp/runtime-spec.ts +134 -0
- package/dist/runtime/src/mcp/session-comm/bridge-ipc-config.ts +27 -0
- package/dist/runtime/src/mcp/session-comm/peer-forward.ts +165 -0
- package/dist/runtime/src/mcp/session-comm/runtime.ts +115 -0
- package/dist/runtime/src/mcp/session-comm/server.ts +649 -0
- package/dist/runtime/src/mcp/session-comm/topic-config.ts +131 -0
- package/dist/runtime/src/mcp/session-comm/topics.ts +229 -0
- package/dist/runtime/src/mcp/stdio-protect.ts +84 -0
- package/dist/runtime/src/mcp/system-health-server.ts +130 -0
- package/dist/runtime/src/mcp/task-server.ts +173 -0
- package/dist/runtime/src/mcp/token-stats-server.ts +98 -0
- package/dist/runtime/src/mcp/vault-http.ts +219 -0
- package/dist/runtime/src/mcp/vault-run.ts +153 -0
- package/dist/runtime/src/mcp/vault-server.ts +104 -0
- package/dist/runtime/src/mcp/wiki-server.ts +755 -0
- package/dist/runtime/src/media/file-events.ts +33 -0
- package/dist/runtime/src/media/text-extractor.ts +312 -0
- package/dist/runtime/src/media/video.ts +92 -0
- package/dist/runtime/src/outbox/file-ops.ts +210 -0
- package/dist/runtime/src/outbox/index.ts +8 -0
- package/dist/runtime/src/outbox/utils.ts +51 -0
- package/dist/runtime/src/platform/background-bash/manager.ts +279 -0
- package/dist/runtime/src/platform/config.ts +371 -0
- package/dist/runtime/src/platform/constants.ts +18 -0
- package/dist/runtime/src/platform/debug-users.ts +14 -0
- package/dist/runtime/src/platform/delay.ts +1 -0
- package/dist/runtime/src/platform/error.ts +9 -0
- package/dist/runtime/src/platform/file-utils.ts +16 -0
- package/dist/runtime/src/platform/jsonl.ts +202 -0
- package/dist/runtime/src/platform/lifecycle.ts +174 -0
- package/dist/runtime/src/platform/logger.ts +31 -0
- package/dist/runtime/src/platform/maestro-bootstrap-env.ts +3 -0
- package/dist/runtime/src/platform/mcp-config.ts +865 -0
- package/dist/runtime/src/platform/modules.ts +145 -0
- package/dist/runtime/src/platform/node-plugins.ts +46 -0
- package/dist/runtime/src/platform/playwright/manager.ts +1233 -0
- package/dist/runtime/src/prompts/agents/wiki-archiver.md +261 -0
- package/dist/runtime/src/prompts/builders.ts +357 -0
- package/dist/runtime/src/prompts/sessions/channel-system.md +48 -0
- package/dist/runtime/src/prompts/sessions/manager-system.md +37 -0
- package/dist/runtime/src/prompts/sessions/topic-system.md +47 -0
- package/dist/runtime/src/prompts/sessions/visual-design.md +86 -0
- package/dist/runtime/src/query/active-rooms.ts +532 -0
- package/dist/runtime/src/query/control.ts +313 -0
- package/dist/runtime/src/query/index.ts +24 -0
- package/dist/runtime/src/query/session-inbox-cleanup.ts +44 -0
- package/dist/runtime/src/query/session-inbox-path.ts +42 -0
- package/dist/runtime/src/query/state.ts +94 -0
- package/dist/runtime/src/query/types.ts +83 -0
- package/dist/runtime/src/runtime/ask-callbacks.ts +153 -0
- package/dist/runtime/src/runtime/attachments.ts +151 -0
- package/dist/runtime/src/runtime/background-sessions.ts +83 -0
- package/dist/runtime/src/runtime/channel-context.ts +107 -0
- package/dist/runtime/src/runtime/errors.ts +69 -0
- package/dist/runtime/src/runtime/event-heartbeat.ts +77 -0
- package/dist/runtime/src/runtime/file-hooks.ts +81 -0
- package/dist/runtime/src/runtime/footer.ts +23 -0
- package/dist/runtime/src/runtime/inbox.ts +946 -0
- package/dist/runtime/src/runtime/tasks.ts +56 -0
- package/dist/runtime/src/runtime/topic-config.ts +29 -0
- package/dist/runtime/src/runtime/turn-runner.ts +2286 -0
- package/dist/runtime/src/runtime/usage-alert.ts +84 -0
- package/dist/runtime/src/runtime/visual-html.ts +109 -0
- package/dist/runtime/src/runtime/visual-store.ts +430 -0
- package/dist/runtime/src/runtime/visuals.ts +171 -0
- package/dist/runtime/src/security/sanitize.ts +27 -0
- package/dist/runtime/src/security/sensitive-path.ts +34 -0
- package/dist/runtime/src/storage/activity-log.ts +152 -0
- package/dist/runtime/src/storage/api-messages.ts +617 -0
- package/dist/runtime/src/storage/api-topic-brief.ts +127 -0
- package/dist/runtime/src/storage/api-topic-config.ts +192 -0
- package/dist/runtime/src/storage/api-topics.ts +844 -0
- package/dist/runtime/src/storage/app-settings.ts +44 -0
- package/dist/runtime/src/storage/browser-profiles.ts +133 -0
- package/dist/runtime/src/storage/conversations.ts +268 -0
- package/dist/runtime/src/storage/forum/agent-settings.ts +20 -0
- package/dist/runtime/src/storage/forum/index.ts +4 -0
- package/dist/runtime/src/storage/forum/repository.ts +283 -0
- package/dist/runtime/src/storage/forum/schema.ts +300 -0
- package/dist/runtime/src/storage/forum-db.ts +23 -0
- package/dist/runtime/src/storage/runtime-events.ts +130 -0
- package/dist/runtime/src/storage/runtime-leases.ts +189 -0
- package/dist/runtime/src/storage/runtime-process-leases.ts +216 -0
- package/dist/runtime/src/storage/runtime-topic-state.ts +148 -0
- package/dist/runtime/src/storage/runtime-turn-requests.ts +300 -0
- package/dist/runtime/src/storage/self-schedules.ts +298 -0
- package/dist/runtime/src/storage/session-asks.ts +347 -0
- package/dist/runtime/src/storage/sqlite.ts +129 -0
- package/dist/runtime/src/storage/tasks.ts +179 -0
- package/dist/runtime/src/storage/token-stats.ts +144 -0
- package/dist/runtime/src/storage/topic-archive-state.ts +59 -0
- package/dist/runtime/src/storage/topic-archive.ts +74 -0
- package/dist/runtime/src/storage/topic-settings.ts +67 -0
- package/dist/runtime/src/storage/topic-transcript.ts +98 -0
- package/dist/runtime/src/storage/vault-crypto.ts +65 -0
- package/dist/runtime/src/storage/vault.ts +225 -0
- package/dist/runtime/src/storage/wiki-summary-names.ts +33 -0
- package/dist/runtime/src/storage/wiki.ts +85 -0
- package/dist/runtime/src/topics/create.ts +123 -0
- package/dist/runtime/src/topics/derive.ts +398 -0
- package/dist/runtime/src/topics/lifecycle.ts +311 -0
- package/dist/runtime/src/topics/links.ts +7 -0
- package/dist/runtime/src/topics/personal-general.ts +65 -0
- package/dist/runtime/src/topics/session.ts +381 -0
- package/dist/runtime/src/types/api.ts +497 -0
- package/dist/runtime/src/types.ts +255 -0
- package/dist/runtime/src/version.ts +1 -0
- package/dist/runtime/tsconfig.json +13 -0
- package/package.json +16 -3
- package/dist/main.d.ts +0 -3
- package/dist/main.d.ts.map +0 -1
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
db
|
|
4
|
+
} from "./chunk-7xqj7aex.js";
|
|
5
|
+
import {
|
|
6
|
+
logger
|
|
7
|
+
} from "./chunk-k9z47mab.js";
|
|
8
|
+
import"./chunk-hwy3n31w.js";
|
|
9
|
+
|
|
10
|
+
// ../../packages/core/src/platform/constants.ts
|
|
11
|
+
var FROM_AUTO_CONTINUE = "auto-continue";
|
|
12
|
+
var FROM_SELF_SCHEDULE = "self-schedule";
|
|
13
|
+
var RESERVED_TOPIC_NAMES = new Set([
|
|
14
|
+
FROM_AUTO_CONTINUE,
|
|
15
|
+
FROM_SELF_SCHEDULE,
|
|
16
|
+
"general"
|
|
17
|
+
]);
|
|
18
|
+
var GENERAL_TOPIC_ID = "general";
|
|
19
|
+
|
|
20
|
+
// ../../packages/core/src/storage/api-topics.ts
|
|
21
|
+
var DEFAULT_AGENT_ROOM_AGENT = "maestro";
|
|
22
|
+
function tableColumns(table) {
|
|
23
|
+
const rows = db.query(`PRAGMA table_info(${table})`).all();
|
|
24
|
+
return new Set(rows.map((row) => row.name));
|
|
25
|
+
}
|
|
26
|
+
function setApiTopicAgent(topicId, agent, defaults) {
|
|
27
|
+
if (!defaults) {
|
|
28
|
+
db.query("UPDATE api_topics SET agent = ? WHERE id = ?").run(agent, topicId);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
db.query("UPDATE api_topics SET agent = ?, base_model = ?, base_effort = ? WHERE id = ?").run(agent, defaults.model, defaults.effort ?? null, topicId);
|
|
32
|
+
}
|
|
33
|
+
db.exec(`
|
|
34
|
+
CREATE TABLE IF NOT EXISTS api_topics (
|
|
35
|
+
id TEXT PRIMARY KEY,
|
|
36
|
+
title TEXT NOT NULL,
|
|
37
|
+
kind TEXT NOT NULL DEFAULT 'channel',
|
|
38
|
+
description TEXT,
|
|
39
|
+
agent TEXT,
|
|
40
|
+
default_model TEXT,
|
|
41
|
+
default_effort TEXT,
|
|
42
|
+
participants TEXT,
|
|
43
|
+
created_at TEXT NOT NULL,
|
|
44
|
+
last_message_at TEXT,
|
|
45
|
+
is_archived INTEGER NOT NULL DEFAULT 0,
|
|
46
|
+
ai_mention INTEGER NOT NULL DEFAULT 0,
|
|
47
|
+
ai_mode TEXT
|
|
48
|
+
)
|
|
49
|
+
`);
|
|
50
|
+
var initialTopicColumns = tableColumns("api_topics");
|
|
51
|
+
var legacyTopicSchema = !initialTopicColumns.has("response_policy");
|
|
52
|
+
var needsCanonicalTopicRebuild = legacyTopicSchema || !initialTopicColumns.has("agent");
|
|
53
|
+
if (legacyTopicSchema) {
|
|
54
|
+
try {
|
|
55
|
+
db.exec("ALTER TABLE api_topics RENAME COLUMN default_agent TO agent");
|
|
56
|
+
} catch {}
|
|
57
|
+
try {
|
|
58
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN ai_mention INTEGER NOT NULL DEFAULT 0");
|
|
59
|
+
} catch {}
|
|
60
|
+
try {
|
|
61
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN parent_topic_id TEXT");
|
|
62
|
+
} catch {}
|
|
63
|
+
try {
|
|
64
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN is_fork INTEGER NOT NULL DEFAULT 0");
|
|
65
|
+
} catch {}
|
|
66
|
+
try {
|
|
67
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN is_subagent INTEGER NOT NULL DEFAULT 0");
|
|
68
|
+
} catch {}
|
|
69
|
+
try {
|
|
70
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN session_id TEXT");
|
|
71
|
+
} catch {}
|
|
72
|
+
try {
|
|
73
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN kind TEXT NOT NULL DEFAULT 'channel'");
|
|
74
|
+
} catch {}
|
|
75
|
+
try {
|
|
76
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN ai_mode TEXT");
|
|
77
|
+
} catch {}
|
|
78
|
+
db.exec(`
|
|
79
|
+
CREATE TABLE IF NOT EXISTS api_schema_migrations (
|
|
80
|
+
key TEXT PRIMARY KEY,
|
|
81
|
+
applied_at TEXT NOT NULL
|
|
82
|
+
)
|
|
83
|
+
`);
|
|
84
|
+
const ALWAYS_RESPOND_MIGRATION = "api_topics_ai_invited_default_always_respond_20260623";
|
|
85
|
+
const alwaysRespondMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(ALWAYS_RESPOND_MIGRATION);
|
|
86
|
+
if (!alwaysRespondMigration) {
|
|
87
|
+
db.transaction(() => {
|
|
88
|
+
db.query("UPDATE api_topics SET ai_mention = 0 WHERE agent IS NOT NULL").run();
|
|
89
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(ALWAYS_RESPOND_MIGRATION, new Date().toISOString());
|
|
90
|
+
})();
|
|
91
|
+
}
|
|
92
|
+
const GENERAL_AGENT_KIND_MIGRATION = "api_topics_general_agent_kind_20260704";
|
|
93
|
+
const generalAgentKindMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(GENERAL_AGENT_KIND_MIGRATION);
|
|
94
|
+
if (!generalAgentKindMigration) {
|
|
95
|
+
db.transaction(() => {
|
|
96
|
+
db.query("UPDATE api_topics SET kind = 'agent', ai_mention = 0 WHERE id = ?").run(GENERAL_TOPIC_ID);
|
|
97
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(GENERAL_AGENT_KIND_MIGRATION, new Date().toISOString());
|
|
98
|
+
})();
|
|
99
|
+
}
|
|
100
|
+
const AI_MODE_MIGRATION = "api_topics_ai_mode_20260704";
|
|
101
|
+
const aiModeMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(AI_MODE_MIGRATION);
|
|
102
|
+
if (!aiModeMigration) {
|
|
103
|
+
db.transaction(() => {
|
|
104
|
+
db.query(`UPDATE api_topics
|
|
105
|
+
SET
|
|
106
|
+
kind = CASE
|
|
107
|
+
WHEN id = ? THEN 'agent'
|
|
108
|
+
WHEN kind = 'channel' AND agent IS NOT NULL AND ai_mention = 0 THEN 'agent'
|
|
109
|
+
ELSE kind
|
|
110
|
+
END,
|
|
111
|
+
ai_mode = CASE
|
|
112
|
+
WHEN id = ? THEN 'always'
|
|
113
|
+
WHEN agent IS NULL THEN 'off'
|
|
114
|
+
WHEN kind = 'agent' THEN 'always'
|
|
115
|
+
WHEN kind = 'channel' AND ai_mention != 0 THEN 'mention'
|
|
116
|
+
WHEN kind = 'channel' AND ai_mention = 0 THEN 'always'
|
|
117
|
+
WHEN ai_mention != 0 THEN 'mention'
|
|
118
|
+
ELSE 'always'
|
|
119
|
+
END`).run(GENERAL_TOPIC_ID, GENERAL_TOPIC_ID);
|
|
120
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(AI_MODE_MIGRATION, new Date().toISOString());
|
|
121
|
+
})();
|
|
122
|
+
}
|
|
123
|
+
const GENERAL_MANAGER_KIND_MIGRATION = "api_topics_general_manager_kind_20260704";
|
|
124
|
+
const generalManagerKindMigration = db.query("SELECT key FROM api_schema_migrations WHERE key = ?").get(GENERAL_MANAGER_KIND_MIGRATION);
|
|
125
|
+
if (!generalManagerKindMigration) {
|
|
126
|
+
db.transaction(() => {
|
|
127
|
+
db.query("UPDATE api_topics SET kind = 'manager', ai_mention = 0, ai_mode = 'always' WHERE id = ?").run(GENERAL_TOPIC_ID);
|
|
128
|
+
db.query("INSERT INTO api_schema_migrations (key, applied_at) VALUES (?, ?)").run(GENERAL_MANAGER_KIND_MIGRATION, new Date().toISOString());
|
|
129
|
+
})();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function createCanonicalTopicsTable(name) {
|
|
133
|
+
db.exec(`
|
|
134
|
+
CREATE TABLE IF NOT EXISTS ${name} (
|
|
135
|
+
id TEXT PRIMARY KEY,
|
|
136
|
+
title TEXT NOT NULL,
|
|
137
|
+
kind TEXT NOT NULL CHECK (kind IN ('channel','agent','manager')),
|
|
138
|
+
description TEXT,
|
|
139
|
+
agent TEXT CHECK (agent IS NULL OR agent IN ('maestro','claude','codex')),
|
|
140
|
+
base_model TEXT,
|
|
141
|
+
base_effort TEXT CHECK (base_effort IS NULL OR base_effort IN ('low','medium','high','xhigh','max')),
|
|
142
|
+
response_policy TEXT NOT NULL CHECK (response_policy IN ('off','mention','always')),
|
|
143
|
+
created_at TEXT NOT NULL,
|
|
144
|
+
last_message_at TEXT,
|
|
145
|
+
parent_topic_id TEXT,
|
|
146
|
+
is_fork INTEGER NOT NULL DEFAULT 0 CHECK (is_fork IN (0,1)),
|
|
147
|
+
is_subagent INTEGER NOT NULL DEFAULT 0 CHECK (is_subagent IN (0,1)),
|
|
148
|
+
visibility TEXT NOT NULL DEFAULT 'visible' CHECK (visibility IN ('visible','hidden')),
|
|
149
|
+
access_mode TEXT NOT NULL DEFAULT 'private' CHECK (access_mode IN ('private','shared')),
|
|
150
|
+
browser_profile TEXT NOT NULL DEFAULT 'default',
|
|
151
|
+
browser_profile_owner TEXT,
|
|
152
|
+
session_id TEXT,
|
|
153
|
+
CHECK (
|
|
154
|
+
(kind = 'channel' AND response_policy = 'off' AND agent IS NULL) OR
|
|
155
|
+
(kind = 'channel' AND response_policy = 'mention' AND agent IS NOT NULL) OR
|
|
156
|
+
(kind IN ('agent','manager') AND response_policy = 'always' AND agent IS NOT NULL)
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
`);
|
|
160
|
+
}
|
|
161
|
+
function createTopicMembersTable() {
|
|
162
|
+
db.exec(`
|
|
163
|
+
CREATE TABLE IF NOT EXISTS topic_members (
|
|
164
|
+
topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,
|
|
165
|
+
user_id TEXT NOT NULL,
|
|
166
|
+
role TEXT NOT NULL CHECK (role IN ('owner','member')),
|
|
167
|
+
PRIMARY KEY (topic_id, user_id)
|
|
168
|
+
)
|
|
169
|
+
`);
|
|
170
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_topic_members_user ON topic_members(user_id)");
|
|
171
|
+
}
|
|
172
|
+
if (needsCanonicalTopicRebuild) {
|
|
173
|
+
const legacyRows = db.query("SELECT * FROM api_topics").all();
|
|
174
|
+
const existingMemberRows = tableColumns("topic_members").has("topic_id") ? db.query("SELECT topic_id, user_id, role FROM topic_members").all() : [];
|
|
175
|
+
const configColumns = tableColumns("api_topic_config");
|
|
176
|
+
const legacyAgentOverrides = configColumns.has("agent") ? new Map(db.query("SELECT topic_id, agent FROM api_topic_config WHERE agent IS NOT NULL").all().map((row) => [row.topic_id, row.agent])) : new Map;
|
|
177
|
+
const previousForeignKeys = db.query("PRAGMA foreign_keys").get()?.foreign_keys;
|
|
178
|
+
db.exec("PRAGMA foreign_keys = OFF");
|
|
179
|
+
try {
|
|
180
|
+
db.transaction(() => {
|
|
181
|
+
createCanonicalTopicsTable("api_topics_next");
|
|
182
|
+
for (const row of legacyRows) {
|
|
183
|
+
const selectedAgent = legacyAgentOverrides.get(String(row.id)) ?? row.agent ?? row.runtime_agent ?? row.default_agent ?? undefined;
|
|
184
|
+
const normalized = normalizeTopicState({
|
|
185
|
+
id: String(row.id),
|
|
186
|
+
kind: normalizeTopicKind(row.kind),
|
|
187
|
+
agent: selectedAgent,
|
|
188
|
+
aiMode: normalizeAiMode(row.response_policy ?? row.ai_mode),
|
|
189
|
+
aiMention: Number(row.ai_mention ?? 0) !== 0
|
|
190
|
+
});
|
|
191
|
+
const legacyBaseModel = row.base_model ?? row.default_model;
|
|
192
|
+
const legacyBaseEffort = row.base_effort ?? row.default_effort;
|
|
193
|
+
db.query(`INSERT INTO api_topics_next
|
|
194
|
+
(id,title,kind,description,agent,base_model,base_effort,response_policy,
|
|
195
|
+
created_at,last_message_at,parent_topic_id,is_fork,is_subagent,visibility,access_mode,session_id)
|
|
196
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(String(row.id), String(row.title), normalized.kind, typeof row.description === "string" ? row.description : null, normalized.agent ?? null, typeof legacyBaseModel === "string" ? legacyBaseModel : null, typeof legacyBaseEffort === "string" ? legacyBaseEffort : null, normalized.aiMode, String(row.created_at), typeof row.last_message_at === "string" ? row.last_message_at : null, typeof row.parent_topic_id === "string" ? row.parent_topic_id : null, Number(row.is_fork ?? 0) !== 0 ? 1 : 0, Number(row.is_subagent ?? 0) !== 0 ? 1 : 0, row.visibility === "hidden" ? "hidden" : "visible", row.access_mode === "shared" ? "shared" : "private", typeof row.session_id === "string" ? row.session_id : null);
|
|
197
|
+
}
|
|
198
|
+
db.exec("DROP TABLE IF EXISTS topic_members");
|
|
199
|
+
db.exec("DROP TABLE api_topics");
|
|
200
|
+
db.exec("ALTER TABLE api_topics_next RENAME TO api_topics");
|
|
201
|
+
createTopicMembersTable();
|
|
202
|
+
if (existingMemberRows.length > 0) {
|
|
203
|
+
for (const member of existingMemberRows) {
|
|
204
|
+
db.query("INSERT OR REPLACE INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(member.topic_id, member.user_id, member.role === "owner" ? "owner" : "member");
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
for (const row of legacyRows) {
|
|
208
|
+
let participants = [];
|
|
209
|
+
try {
|
|
210
|
+
const parsed = JSON.parse(String(row.participants ?? "[]"));
|
|
211
|
+
if (Array.isArray(parsed))
|
|
212
|
+
participants = parsed;
|
|
213
|
+
} catch {
|
|
214
|
+
participants = [];
|
|
215
|
+
}
|
|
216
|
+
for (const participant of participants) {
|
|
217
|
+
if (!participant?.userId)
|
|
218
|
+
continue;
|
|
219
|
+
db.query("INSERT OR REPLACE INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(String(row.id), participant.userId, participant.role === "owner" ? "owner" : "member");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
})();
|
|
224
|
+
} finally {
|
|
225
|
+
db.exec(`PRAGMA foreign_keys = ${previousForeignKeys ? "ON" : "OFF"}`);
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
createCanonicalTopicsTable("api_topics");
|
|
229
|
+
createTopicMembersTable();
|
|
230
|
+
}
|
|
231
|
+
if (!tableColumns("api_topics").has("visibility")) {
|
|
232
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN visibility TEXT NOT NULL DEFAULT 'visible'");
|
|
233
|
+
}
|
|
234
|
+
if (!tableColumns("api_topics").has("access_mode")) {
|
|
235
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN access_mode TEXT NOT NULL DEFAULT 'private'");
|
|
236
|
+
}
|
|
237
|
+
if (!tableColumns("api_topics").has("browser_profile")) {
|
|
238
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile TEXT NOT NULL DEFAULT 'default'");
|
|
239
|
+
}
|
|
240
|
+
if (!tableColumns("api_topics").has("browser_profile_owner")) {
|
|
241
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN browser_profile_owner TEXT");
|
|
242
|
+
}
|
|
243
|
+
db.exec(`
|
|
244
|
+
UPDATE api_topics
|
|
245
|
+
SET browser_profile_owner = (
|
|
246
|
+
SELECT m.user_id FROM topic_members m
|
|
247
|
+
WHERE m.topic_id = api_topics.id AND m.role = 'owner'
|
|
248
|
+
ORDER BY m.rowid
|
|
249
|
+
LIMIT 1
|
|
250
|
+
)
|
|
251
|
+
WHERE browser_profile_owner IS NULL
|
|
252
|
+
`);
|
|
253
|
+
function shortSessionId(sessionId) {
|
|
254
|
+
return sessionId ? sessionId.slice(0, 8) : null;
|
|
255
|
+
}
|
|
256
|
+
function getTopicParticipants(topicId) {
|
|
257
|
+
return db.query("SELECT user_id, role FROM topic_members WHERE topic_id = ? ORDER BY rowid").all(topicId).map((row) => ({ userId: row.user_id, role: row.role }));
|
|
258
|
+
}
|
|
259
|
+
function getAllTopicParticipants() {
|
|
260
|
+
const grouped = new Map;
|
|
261
|
+
const rows = db.query("SELECT topic_id, user_id, role FROM topic_members ORDER BY rowid").all();
|
|
262
|
+
for (const row of rows) {
|
|
263
|
+
const participants = grouped.get(row.topic_id) ?? [];
|
|
264
|
+
participants.push({ userId: row.user_id, role: row.role });
|
|
265
|
+
grouped.set(row.topic_id, participants);
|
|
266
|
+
}
|
|
267
|
+
return grouped;
|
|
268
|
+
}
|
|
269
|
+
function rowToDto(r, participants = getTopicParticipants(r.id)) {
|
|
270
|
+
const normalized = normalizeTopicState({
|
|
271
|
+
id: r.id,
|
|
272
|
+
kind: normalizeTopicKind(r.kind),
|
|
273
|
+
agent: r.agent ?? undefined,
|
|
274
|
+
aiMode: normalizeAiMode(r.response_policy)
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
id: r.id,
|
|
278
|
+
title: r.title,
|
|
279
|
+
kind: normalized.kind,
|
|
280
|
+
description: r.description ?? undefined,
|
|
281
|
+
agent: normalized.agent,
|
|
282
|
+
defaultModel: r.base_model ?? "deepseek-pro",
|
|
283
|
+
defaultEffort: r.base_effort ?? undefined,
|
|
284
|
+
aiMode: normalized.aiMode,
|
|
285
|
+
aiMention: aiMentionFromMode(normalized.aiMode),
|
|
286
|
+
participants,
|
|
287
|
+
createdAt: r.created_at,
|
|
288
|
+
lastMessageAt: r.last_message_at ?? new Date().toISOString(),
|
|
289
|
+
parentTopicId: r.parent_topic_id ?? undefined,
|
|
290
|
+
isFork: r.is_fork !== 0,
|
|
291
|
+
...r.is_subagent !== 0 ? { isSubagent: true } : {},
|
|
292
|
+
visibility: normalizeTopicVisibility(r.visibility),
|
|
293
|
+
accessMode: normalizeTopicAccessMode(r.access_mode)
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function normalizeTopicAccessMode(value) {
|
|
297
|
+
return value === "shared" ? "shared" : "private";
|
|
298
|
+
}
|
|
299
|
+
function isTopicShared(topic) {
|
|
300
|
+
return topic.accessMode === "shared";
|
|
301
|
+
}
|
|
302
|
+
function normalizeTopicVisibility(value) {
|
|
303
|
+
return value === "hidden" ? "hidden" : "visible";
|
|
304
|
+
}
|
|
305
|
+
function isTopicVisible(topic) {
|
|
306
|
+
return topic.visibility !== "hidden";
|
|
307
|
+
}
|
|
308
|
+
function normalizeTopicKind(value) {
|
|
309
|
+
return value === "channel" || value === "agent" || value === "manager" ? value : null;
|
|
310
|
+
}
|
|
311
|
+
function normalizeAiMode(value) {
|
|
312
|
+
return value === "off" || value === "mention" || value === "always" ? value : null;
|
|
313
|
+
}
|
|
314
|
+
function aiMentionFromMode(mode) {
|
|
315
|
+
return mode === "mention";
|
|
316
|
+
}
|
|
317
|
+
function inferTopicKind(input) {
|
|
318
|
+
return input.agent && input.aiMention !== true ? "agent" : "channel";
|
|
319
|
+
}
|
|
320
|
+
function inferAiMode(input) {
|
|
321
|
+
if (input.kind === "manager")
|
|
322
|
+
return "always";
|
|
323
|
+
if (input.kind === "agent")
|
|
324
|
+
return "always";
|
|
325
|
+
if (input.kind === "channel") {
|
|
326
|
+
if (!input.agent)
|
|
327
|
+
return "off";
|
|
328
|
+
if (input.aiMode === "mention")
|
|
329
|
+
return "mention";
|
|
330
|
+
if (input.aiMention === true)
|
|
331
|
+
return "mention";
|
|
332
|
+
return "mention";
|
|
333
|
+
}
|
|
334
|
+
if (!input.agent)
|
|
335
|
+
return "off";
|
|
336
|
+
if (input.aiMode)
|
|
337
|
+
return input.aiMode;
|
|
338
|
+
return input.aiMention === true ? "mention" : "always";
|
|
339
|
+
}
|
|
340
|
+
function normalizeTopicState(input) {
|
|
341
|
+
if (input.kind === "manager" || input.id === GENERAL_TOPIC_ID) {
|
|
342
|
+
return {
|
|
343
|
+
kind: "manager",
|
|
344
|
+
aiMode: "always",
|
|
345
|
+
agent: input.agent ?? DEFAULT_AGENT_ROOM_AGENT
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
const requestedKind = input.kind;
|
|
349
|
+
const kind = requestedKind ?? (input.aiMode === "always" ? "agent" : input.aiMode === "mention" || input.aiMode === "off" ? "channel" : input.agent && input.aiMention !== true ? "agent" : "channel");
|
|
350
|
+
if (kind === "agent") {
|
|
351
|
+
return {
|
|
352
|
+
kind: "agent",
|
|
353
|
+
aiMode: "always",
|
|
354
|
+
agent: input.agent ?? DEFAULT_AGENT_ROOM_AGENT
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
const agent = input.aiMode === "off" ? undefined : input.agent ?? undefined;
|
|
358
|
+
return {
|
|
359
|
+
kind: "channel",
|
|
360
|
+
aiMode: agent ? "mention" : "off",
|
|
361
|
+
agent
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function normalizedTitle(title) {
|
|
365
|
+
return title.trim().toLowerCase();
|
|
366
|
+
}
|
|
367
|
+
function upsertTopic(t) {
|
|
368
|
+
const normalized = normalizeTopicState({
|
|
369
|
+
id: t.id,
|
|
370
|
+
kind: normalizeTopicKind(t.kind),
|
|
371
|
+
agent: t.agent ?? undefined,
|
|
372
|
+
aiMode: normalizeAiMode(t.aiMode),
|
|
373
|
+
aiMention: t.aiMention
|
|
374
|
+
});
|
|
375
|
+
db.transaction(() => {
|
|
376
|
+
db.query(`INSERT INTO api_topics
|
|
377
|
+
(id,title,kind,description,agent,base_model,base_effort,response_policy,
|
|
378
|
+
created_at,last_message_at,parent_topic_id,is_fork,is_subagent,visibility,access_mode)
|
|
379
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
380
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
381
|
+
title = excluded.title,
|
|
382
|
+
kind = excluded.kind,
|
|
383
|
+
description = excluded.description,
|
|
384
|
+
agent = excluded.agent,
|
|
385
|
+
base_model = excluded.base_model,
|
|
386
|
+
base_effort = excluded.base_effort,
|
|
387
|
+
response_policy = excluded.response_policy,
|
|
388
|
+
created_at = excluded.created_at,
|
|
389
|
+
last_message_at = excluded.last_message_at,
|
|
390
|
+
parent_topic_id = excluded.parent_topic_id,
|
|
391
|
+
is_fork = excluded.is_fork,
|
|
392
|
+
is_subagent = excluded.is_subagent,
|
|
393
|
+
visibility = excluded.visibility,
|
|
394
|
+
access_mode = excluded.access_mode`).run(t.id, t.title, normalized.kind, t.description ?? null, normalized.agent ?? null, t.defaultModel ?? null, t.defaultEffort ?? null, normalized.aiMode, t.createdAt, t.lastMessageAt ?? null, t.parentTopicId ?? null, t.isFork ? 1 : 0, t.isSubagent ? 1 : 0, normalizeTopicVisibility(t.visibility), normalizeTopicAccessMode(t.accessMode));
|
|
395
|
+
db.query("DELETE FROM topic_members WHERE topic_id = ?").run(t.id);
|
|
396
|
+
for (const participant of t.participants) {
|
|
397
|
+
db.query("INSERT INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(t.id, participant.userId, participant.role);
|
|
398
|
+
}
|
|
399
|
+
const initialBrowserProfileOwner = t.participants.find((participant) => participant.role === "owner")?.userId;
|
|
400
|
+
if (initialBrowserProfileOwner) {
|
|
401
|
+
db.query(`UPDATE api_topics
|
|
402
|
+
SET browser_profile_owner = COALESCE(browser_profile_owner, ?)
|
|
403
|
+
WHERE id = ?`).run(initialBrowserProfileOwner, t.id);
|
|
404
|
+
}
|
|
405
|
+
})();
|
|
406
|
+
}
|
|
407
|
+
function listTopics() {
|
|
408
|
+
const rows = db.query("SELECT * FROM api_topics ORDER BY last_message_at DESC").all();
|
|
409
|
+
const participants = getAllTopicParticipants();
|
|
410
|
+
return rows.map((row) => rowToDto(row, participants.get(row.id) ?? []));
|
|
411
|
+
}
|
|
412
|
+
function getTopic(id) {
|
|
413
|
+
const r = db.query("SELECT * FROM api_topics WHERE id = ?").get(id);
|
|
414
|
+
return r ? rowToDto(r) : null;
|
|
415
|
+
}
|
|
416
|
+
function getManagerTopicForUser(userId) {
|
|
417
|
+
const row = db.query(`SELECT t.* FROM api_topics t
|
|
418
|
+
JOIN topic_members m ON m.topic_id = t.id
|
|
419
|
+
WHERE t.kind = 'manager'
|
|
420
|
+
AND t.id != ?
|
|
421
|
+
AND m.user_id = ?
|
|
422
|
+
AND m.role = 'owner'
|
|
423
|
+
ORDER BY t.created_at ASC
|
|
424
|
+
LIMIT 1`).get(GENERAL_TOPIC_ID, userId);
|
|
425
|
+
return row ? rowToDto(row) : null;
|
|
426
|
+
}
|
|
427
|
+
function getTopicMemoryOrigin(id) {
|
|
428
|
+
let current = getTopic(id);
|
|
429
|
+
if (!current)
|
|
430
|
+
return null;
|
|
431
|
+
const seen = new Set([current.id]);
|
|
432
|
+
while (current.parentTopicId && !seen.has(current.parentTopicId)) {
|
|
433
|
+
const parent = getTopic(current.parentTopicId);
|
|
434
|
+
if (!parent)
|
|
435
|
+
break;
|
|
436
|
+
current = parent;
|
|
437
|
+
seen.add(current.id);
|
|
438
|
+
}
|
|
439
|
+
return current;
|
|
440
|
+
}
|
|
441
|
+
function getTopicByName(title) {
|
|
442
|
+
const r = db.query("SELECT * FROM api_topics WHERE LOWER(title) = LOWER(?)").get(title);
|
|
443
|
+
return r ? rowToDto(r) : null;
|
|
444
|
+
}
|
|
445
|
+
function getTopicByNameAndKind(title, kind) {
|
|
446
|
+
const r = db.query("SELECT * FROM api_topics WHERE LOWER(title) = LOWER(?) AND kind = ?").get(title, kind);
|
|
447
|
+
return r ? rowToDto(r) : null;
|
|
448
|
+
}
|
|
449
|
+
function findTopicTitleConflict(title, kind, opts = {}) {
|
|
450
|
+
const wanted = normalizedTitle(title);
|
|
451
|
+
const generalTitleRequested = wanted === normalizedTitle(GENERAL_TOPIC_ID);
|
|
452
|
+
if (generalTitleRequested && opts.excludeTopicId !== GENERAL_TOPIC_ID) {
|
|
453
|
+
const general = db.query("SELECT * FROM api_topics WHERE id = ?").get(GENERAL_TOPIC_ID);
|
|
454
|
+
if (general)
|
|
455
|
+
return rowToDto(general);
|
|
456
|
+
}
|
|
457
|
+
const params = [wanted];
|
|
458
|
+
let sql = "SELECT * FROM api_topics WHERE LOWER(TRIM(title)) = ?";
|
|
459
|
+
if (kind !== "manager") {
|
|
460
|
+
sql += " AND (kind = ? OR id = ?)";
|
|
461
|
+
params.push(kind, GENERAL_TOPIC_ID);
|
|
462
|
+
}
|
|
463
|
+
if (opts.excludeTopicId) {
|
|
464
|
+
sql += " AND id != ?";
|
|
465
|
+
params.push(opts.excludeTopicId);
|
|
466
|
+
}
|
|
467
|
+
sql += " LIMIT 1";
|
|
468
|
+
const row = db.query(sql).get(...params);
|
|
469
|
+
return row ? rowToDto(row) : null;
|
|
470
|
+
}
|
|
471
|
+
function getTopicByNameForUser(title, userId) {
|
|
472
|
+
const trimmed = title.trim();
|
|
473
|
+
const qualified = /^(agent|channel|manager):(.+)$/i.exec(trimmed);
|
|
474
|
+
const requestedKind = qualified ? normalizeTopicKind(qualified[1]?.toLowerCase()) : null;
|
|
475
|
+
const requestedTitle = qualified ? qualified[2].trim() : trimmed;
|
|
476
|
+
const rows = db.query(`SELECT t.* FROM api_topics t
|
|
477
|
+
WHERE LOWER(t.title) = LOWER(?)
|
|
478
|
+
AND t.id != ?
|
|
479
|
+
AND t.visibility != 'hidden'
|
|
480
|
+
AND EXISTS (
|
|
481
|
+
SELECT 1 FROM topic_members m WHERE m.topic_id = t.id AND m.user_id = ?
|
|
482
|
+
)`).all(requestedTitle, GENERAL_TOPIC_ID, userId);
|
|
483
|
+
const matches = requestedKind ? rows.filter((row) => row.kind === requestedKind) : rows;
|
|
484
|
+
return matches.length === 1 ? rowToDto(matches[0]) : null;
|
|
485
|
+
}
|
|
486
|
+
function setTopicSessionId(topicId, sessionId, context = {}) {
|
|
487
|
+
const previous = getTopicSessionId(topicId);
|
|
488
|
+
const result = db.query("UPDATE api_topics SET session_id = ? WHERE id = ?").run(sessionId, topicId);
|
|
489
|
+
const changes = Number(result.changes ?? 0);
|
|
490
|
+
const logContext = {
|
|
491
|
+
topicId,
|
|
492
|
+
previousSessionId: shortSessionId(previous),
|
|
493
|
+
sessionId: shortSessionId(sessionId),
|
|
494
|
+
reason: context.reason,
|
|
495
|
+
queryId: context.queryId,
|
|
496
|
+
agent: context.agent
|
|
497
|
+
};
|
|
498
|
+
if (changes === 0) {
|
|
499
|
+
logger.warn(logContext, "api-topic session_id update missed topic");
|
|
500
|
+
} else if (previous !== sessionId) {
|
|
501
|
+
logger.debug(logContext, "api-topic session_id updated");
|
|
502
|
+
} else {
|
|
503
|
+
logger.debug(logContext, "api-topic session_id unchanged");
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function clearTopicSessionId(topicId, reason = "unspecified") {
|
|
507
|
+
const previous = getTopicSessionId(topicId);
|
|
508
|
+
const result = db.query("UPDATE api_topics SET session_id = NULL WHERE id = ?").run(topicId);
|
|
509
|
+
const changes = Number(result.changes ?? 0);
|
|
510
|
+
const logContext = { topicId, previousSessionId: shortSessionId(previous), reason };
|
|
511
|
+
if (changes === 0) {
|
|
512
|
+
logger.warn(logContext, "api-topic session_id clear missed topic");
|
|
513
|
+
} else if (previous) {
|
|
514
|
+
logger.info(logContext, "api-topic session_id cleared");
|
|
515
|
+
} else {
|
|
516
|
+
logger.debug(logContext, "api-topic session_id already clear");
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function getTopicSessionId(topicId) {
|
|
520
|
+
const r = db.query("SELECT session_id FROM api_topics WHERE id = ?").get(topicId);
|
|
521
|
+
return r?.session_id ?? null;
|
|
522
|
+
}
|
|
523
|
+
function deleteTopic(id, options = {}) {
|
|
524
|
+
const r = db.query("SELECT id, kind FROM api_topics WHERE id = ?").get(id);
|
|
525
|
+
if (!r)
|
|
526
|
+
return false;
|
|
527
|
+
if (id === GENERAL_TOPIC_ID || r.kind === "manager" && !options.allowManager)
|
|
528
|
+
return false;
|
|
529
|
+
db.query("DELETE FROM api_topics WHERE id = ?").run(id);
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
function reparentTopicChildren(deletedTopicId, replacementParentTopicId) {
|
|
533
|
+
const rows = db.query("SELECT id FROM api_topics WHERE parent_topic_id = ?").all(deletedTopicId);
|
|
534
|
+
if (rows.length === 0)
|
|
535
|
+
return [];
|
|
536
|
+
db.query("UPDATE api_topics SET parent_topic_id = ? WHERE parent_topic_id = ?").run(replacementParentTopicId, deletedTopicId);
|
|
537
|
+
return rows.map((row) => row.id);
|
|
538
|
+
}
|
|
539
|
+
function addParticipantToDB(topicId, userId, role) {
|
|
540
|
+
if (!db.query("SELECT 1 FROM api_topics WHERE id = ?").get(topicId))
|
|
541
|
+
return false;
|
|
542
|
+
db.query("INSERT OR IGNORE INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(topicId, userId, role);
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
function removeParticipantFromDB(topicId, userId) {
|
|
546
|
+
if (!db.query("SELECT 1 FROM api_topics WHERE id = ?").get(topicId))
|
|
547
|
+
return false;
|
|
548
|
+
db.query("DELETE FROM topic_members WHERE topic_id = ? AND user_id = ?").run(topicId, userId);
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
export {
|
|
552
|
+
upsertTopic,
|
|
553
|
+
setTopicSessionId,
|
|
554
|
+
setApiTopicAgent,
|
|
555
|
+
reparentTopicChildren,
|
|
556
|
+
removeParticipantFromDB,
|
|
557
|
+
normalizeTopicVisibility,
|
|
558
|
+
normalizeTopicState,
|
|
559
|
+
normalizeTopicKind,
|
|
560
|
+
normalizeTopicAccessMode,
|
|
561
|
+
normalizeAiMode,
|
|
562
|
+
listTopics,
|
|
563
|
+
isTopicVisible,
|
|
564
|
+
isTopicShared,
|
|
565
|
+
inferTopicKind,
|
|
566
|
+
inferAiMode,
|
|
567
|
+
getTopicSessionId,
|
|
568
|
+
getTopicMemoryOrigin,
|
|
569
|
+
getTopicByNameForUser,
|
|
570
|
+
getTopicByNameAndKind,
|
|
571
|
+
getTopicByName,
|
|
572
|
+
getTopic,
|
|
573
|
+
getManagerTopicForUser,
|
|
574
|
+
findTopicTitleConflict,
|
|
575
|
+
deleteTopic,
|
|
576
|
+
clearTopicSessionId,
|
|
577
|
+
aiMentionFromMode,
|
|
578
|
+
addParticipantToDB
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
export { FROM_AUTO_CONTINUE, FROM_SELF_SCHEDULE, RESERVED_TOPIC_NAMES, GENERAL_TOPIC_ID, setApiTopicAgent, isTopicShared, isTopicVisible, normalizeTopicKind, inferTopicKind, normalizeTopicState, upsertTopic, listTopics, getTopic, getManagerTopicForUser, getTopicMemoryOrigin, findTopicTitleConflict, getTopicByNameForUser, setTopicSessionId, clearTopicSessionId, getTopicSessionId, deleteTopic, reparentTopicChildren };
|
|
582
|
+
|
|
583
|
+
//# debugId=3A5DC61522A52BE064756E2164756E21
|