negotium 0.2.16 → 0.2.18
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/agent-helpers.js +83 -38
- package/dist/agent-helpers.js.map +13 -13
- package/dist/background-bash.js.map +1 -1
- package/dist/browser-runtime.js.map +1 -1
- package/dist/{chunk-5cpdbbeh.js → chunk-fc58cqry.js} +3 -1
- package/dist/{chunk-5cpdbbeh.js.map → chunk-fc58cqry.js.map} +4 -4
- package/dist/hosted-agent.js +5 -2
- package/dist/hosted-agent.js.map +6 -6
- package/dist/main.js +1228 -486
- package/dist/main.js.map +23 -22
- package/dist/mcp-factories.js +701 -97
- package/dist/mcp-factories.js.map +14 -14
- package/dist/prompts.js +8 -8
- package/dist/prompts.js.map +4 -4
- package/dist/query-runtime.js.map +1 -1
- package/dist/registry.js +3 -3
- package/dist/registry.js.map +2 -2
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/compaction-support.ts +1 -1
- package/dist/runtime/src/agents/idle-archiver.ts +3 -2
- package/dist/runtime/src/agents/maestro-provider.ts +3 -0
- package/dist/runtime/src/index.ts +1 -0
- package/dist/runtime/src/mcp/canonical-proxy-server.ts +18 -1
- package/dist/runtime/src/mcp/wiki-server.ts +801 -59
- package/dist/runtime/src/platform/mcp-config.ts +1 -0
- package/dist/runtime/src/prompts/agents/wiki-archiver.md +16 -6
- package/dist/runtime/src/prompts/builders.ts +17 -34
- package/dist/runtime/src/runtime/turn-event-stream.ts +26 -0
- package/dist/runtime/src/runtime/turn-runner.ts +34 -26
- package/dist/runtime/src/storage/api-topics.ts +13 -4
- package/dist/runtime/src/topics/lifecycle.ts +3 -2
- package/dist/runtime/src/types/api.ts +6 -0
- package/dist/runtime/src/types.ts +6 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js.map +1 -1
- package/dist/storage.js +12 -6
- package/dist/storage.js.map +4 -4
- package/dist/types/packages/core/src/agents/compaction-support.d.ts +1 -0
- package/dist/types/packages/core/src/mcp/wiki-server.d.ts +4 -0
- package/dist/types/packages/core/src/prompts/builders.d.ts +2 -4
- package/dist/types/packages/core/src/runtime/turn-runner.d.ts +4 -0
- package/dist/types/packages/core/src/storage/api-topics.d.ts +1 -0
- package/dist/types/packages/core/src/types/api.d.ts +6 -0
- package/dist/types/packages/core/src/types.d.ts +6 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js.map +1 -1
- package/package.json +2 -2
package/dist/mcp-factories.js
CHANGED
|
@@ -952,7 +952,7 @@ var init_claude_registry = __esm(() => {
|
|
|
952
952
|
});
|
|
953
953
|
|
|
954
954
|
// ../../packages/core/src/version.ts
|
|
955
|
-
var NEGOTIUM_VERSION = "0.2.
|
|
955
|
+
var NEGOTIUM_VERSION = "0.2.18";
|
|
956
956
|
|
|
957
957
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
958
958
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -2128,6 +2128,8 @@ var init_mcp_config = __esm(() => {
|
|
|
2128
2128
|
return env ? buildStdioMcpServer(agent, CANONICAL_MCP_PROXY_SERVER, ["--surface=wiki"], env) : null;
|
|
2129
2129
|
}
|
|
2130
2130
|
const args = [`--user-id=${userId}`];
|
|
2131
|
+
if (topicId)
|
|
2132
|
+
args.push(`--current-topic-id=${topicId}`);
|
|
2131
2133
|
const resolvedWikiTopicId = wikiTopicId ?? topicId ?? (session !== "dm" ? session : undefined);
|
|
2132
2134
|
if (resolvedWikiTopicId)
|
|
2133
2135
|
args.push(`--topic-id=${resolvedWikiTopicId}`);
|
|
@@ -4140,6 +4142,7 @@ function initializeApiTopicsSchema() {
|
|
|
4140
4142
|
last_message_at TEXT,
|
|
4141
4143
|
parent_topic_id TEXT,
|
|
4142
4144
|
memory_topic_id TEXT,
|
|
4145
|
+
memory_key TEXT,
|
|
4143
4146
|
is_fork INTEGER NOT NULL DEFAULT 0 CHECK (is_fork IN (0,1)),
|
|
4144
4147
|
is_subagent INTEGER NOT NULL DEFAULT 0 CHECK (is_subagent IN (0,1)),
|
|
4145
4148
|
visibility TEXT NOT NULL DEFAULT 'visible' CHECK (visibility IN ('visible','hidden')),
|
|
@@ -4189,8 +4192,8 @@ function initializeApiTopicsSchema() {
|
|
|
4189
4192
|
const legacyBaseEffort = row.base_effort ?? row.default_effort;
|
|
4190
4193
|
db.query(`INSERT INTO api_topics_next
|
|
4191
4194
|
(id,title,kind,description,agent,base_model,base_effort,response_policy,
|
|
4192
|
-
created_at,last_message_at,parent_topic_id,memory_topic_id,is_fork,is_subagent,visibility,access_mode,session_id)
|
|
4193
|
-
VALUES (
|
|
4195
|
+
created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,access_mode,session_id)
|
|
4196
|
+
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, typeof row.memory_topic_id === "string" ? row.memory_topic_id : null, typeof row.memory_key === "string" ? row.memory_key : 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);
|
|
4194
4197
|
}
|
|
4195
4198
|
db.exec("DROP TABLE IF EXISTS topic_members");
|
|
4196
4199
|
db.exec("DROP TABLE api_topics");
|
|
@@ -4259,6 +4262,9 @@ function initializeApiTopicsSchema() {
|
|
|
4259
4262
|
if (!tableColumns("api_topics").has("memory_topic_id")) {
|
|
4260
4263
|
db.exec("ALTER TABLE api_topics ADD COLUMN memory_topic_id TEXT");
|
|
4261
4264
|
}
|
|
4265
|
+
if (!tableColumns("api_topics").has("memory_key")) {
|
|
4266
|
+
db.exec("ALTER TABLE api_topics ADD COLUMN memory_key TEXT");
|
|
4267
|
+
}
|
|
4262
4268
|
db.exec(`
|
|
4263
4269
|
UPDATE api_topics
|
|
4264
4270
|
SET browser_profile_owner = (
|
|
@@ -4319,6 +4325,7 @@ function rowToDto2(r, participants = getTopicParticipants(r.id), tellTargets) {
|
|
|
4319
4325
|
lastMessageAt: r.last_message_at ?? new Date().toISOString(),
|
|
4320
4326
|
parentTopicId: r.parent_topic_id ?? undefined,
|
|
4321
4327
|
memoryTopicId: r.memory_topic_id ?? undefined,
|
|
4328
|
+
memoryKey: r.memory_key ?? undefined,
|
|
4322
4329
|
isFork: r.is_fork !== 0,
|
|
4323
4330
|
...r.is_subagent !== 0 ? { isSubagent: true } : {},
|
|
4324
4331
|
...r.is_subagent !== 0 ? {
|
|
@@ -4390,9 +4397,9 @@ function upsertTopic(t) {
|
|
|
4390
4397
|
db.transaction(() => {
|
|
4391
4398
|
db.query(`INSERT INTO api_topics
|
|
4392
4399
|
(id,title,kind,description,agent,base_model,base_effort,response_policy,
|
|
4393
|
-
created_at,last_message_at,parent_topic_id,memory_topic_id,is_fork,is_subagent,visibility,access_mode,
|
|
4400
|
+
created_at,last_message_at,parent_topic_id,memory_topic_id,memory_key,is_fork,is_subagent,visibility,access_mode,
|
|
4394
4401
|
subagent_report_mode)
|
|
4395
|
-
VALUES (
|
|
4402
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
4396
4403
|
ON CONFLICT(id) DO UPDATE SET
|
|
4397
4404
|
title = excluded.title,
|
|
4398
4405
|
kind = excluded.kind,
|
|
@@ -4405,11 +4412,12 @@ function upsertTopic(t) {
|
|
|
4405
4412
|
last_message_at = excluded.last_message_at,
|
|
4406
4413
|
parent_topic_id = excluded.parent_topic_id,
|
|
4407
4414
|
memory_topic_id = excluded.memory_topic_id,
|
|
4415
|
+
memory_key = excluded.memory_key,
|
|
4408
4416
|
is_fork = excluded.is_fork,
|
|
4409
4417
|
is_subagent = excluded.is_subagent,
|
|
4410
4418
|
visibility = excluded.visibility,
|
|
4411
4419
|
access_mode = excluded.access_mode,
|
|
4412
|
-
subagent_report_mode = excluded.subagent_report_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.memoryTopicId ?? null, t.isFork ? 1 : 0, t.isSubagent ? 1 : 0, normalizeTopicVisibility(t.visibility), normalizeTopicAccessMode(t.accessMode), t.subagentReportMode ?? "auto");
|
|
4420
|
+
subagent_report_mode = excluded.subagent_report_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.memoryTopicId ?? null, t.memoryKey ?? null, t.isFork ? 1 : 0, t.isSubagent ? 1 : 0, normalizeTopicVisibility(t.visibility), normalizeTopicAccessMode(t.accessMode), t.subagentReportMode ?? "auto");
|
|
4413
4421
|
db.query("DELETE FROM topic_members WHERE topic_id = ?").run(t.id);
|
|
4414
4422
|
for (const participant of t.participants) {
|
|
4415
4423
|
db.query("INSERT INTO topic_members (topic_id,user_id,role) VALUES (?,?,?)").run(t.id, participant.userId, participant.role);
|
|
@@ -9006,7 +9014,7 @@ __export(exports_codex_provider, {
|
|
|
9006
9014
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
9007
9015
|
import { existsSync as existsSync15, readFileSync as readFileSync14, realpathSync as realpathSync4, statSync as statSync6 } from "fs";
|
|
9008
9016
|
import { homedir as homedir6 } from "os";
|
|
9009
|
-
import { dirname as dirname11, isAbsolute as isAbsolute3, join as join16, relative, resolve as resolve12 } from "path";
|
|
9017
|
+
import { dirname as dirname11, isAbsolute as isAbsolute3, join as join16, relative as relative2, resolve as resolve12 } from "path";
|
|
9010
9018
|
import { Codex } from "@openai/codex-sdk";
|
|
9011
9019
|
function sameCodexUsage(usage, total) {
|
|
9012
9020
|
return usage.input_tokens === total.inputTokens && usage.output_tokens === total.outputTokens && (usage.cached_input_tokens ?? 0) === total.cachedInputTokens && (usage.cache_write_input_tokens ?? 0) === total.cacheWriteInputTokens;
|
|
@@ -9190,7 +9198,7 @@ class CodexFilePreviewTracker {
|
|
|
9190
9198
|
if (!this.#root || !this.#baselineAvailable || !succeeded)
|
|
9191
9199
|
return {};
|
|
9192
9200
|
const absolute = canonicalPath(isAbsolute3(path) ? path : resolve12(this.#cwd, path));
|
|
9193
|
-
const relativePath =
|
|
9201
|
+
const relativePath = relative2(this.#root, absolute);
|
|
9194
9202
|
if (!relativePath || relativePath.startsWith("..") || isAbsolute3(relativePath))
|
|
9195
9203
|
return {};
|
|
9196
9204
|
const before = this.#baseline.has(relativePath) ? this.#baseline.get(relativePath) : this.#headFile(relativePath);
|
|
@@ -9787,6 +9795,7 @@ function maestroProvider(opts) {
|
|
|
9787
9795
|
...opts,
|
|
9788
9796
|
enableToolSearch: !opts.toolPolicy && opts.enableToolSearch !== false,
|
|
9789
9797
|
toolResultTruncation: buildMaestroToolResultTruncation(opts),
|
|
9798
|
+
ephemeralSystemPrompt: opts.ephemeralSystemPrompt,
|
|
9790
9799
|
apiKeyOverrides: resolveMaestroApiKeyOverrides(userId),
|
|
9791
9800
|
agent: "maestro",
|
|
9792
9801
|
disallowedTools: buildMaestroDisallowedTools(callerDisallowedTools, opts.toolPolicy),
|
|
@@ -10281,14 +10290,14 @@ function buildMemoryPromptSection(opts) {
|
|
|
10281
10290
|
const parts = [`
|
|
10282
10291
|
|
|
10283
10292
|
## Memory`];
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
if (opts.
|
|
10287
|
-
parts.push(`
|
|
10288
|
-
}
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
parts.push("
|
|
10293
|
+
if (opts.isManager) {
|
|
10294
|
+
parts.push("Use `wiki_query` for past workspace decisions or cross-topic context, then `wiki_read` only for the relevant result.");
|
|
10295
|
+
} else if (opts.memoryKey) {
|
|
10296
|
+
parts.push(`This topic uses the canonical memory key \`${opts.memoryKey}\`. At the start of a new session, read it with \`wiki_read(kind: "topic", key: "${opts.memoryKey}")\`.`);
|
|
10297
|
+
} else {
|
|
10298
|
+
parts.push(`At the start of a new session, search topic memories with \`wiki_query(question: "${opts.topicTitle}", kind: "topic", limit: 5)\` and read the most relevant candidate with \`wiki_read\`.`, "If a candidate is clearly the same continuing topic/persona, read it with `adopt: true`; otherwise keep this topic's own name as its memory key. Do not merge weak or ambiguous matches.");
|
|
10299
|
+
}
|
|
10300
|
+
parts.push("Keep the lookup quiet. Mention prior context naturally in one short line only when it helps the user.", 'Use `wiki_query(kind: "article")` for reusable knowledge and `wiki_query(kind: "summary")` for historical session details.');
|
|
10292
10301
|
if (opts.hasArchive) {
|
|
10293
10302
|
parts.push("", "If you need the actual conversation from an earlier session, use the `wiki_last_conversation` MCP tool.");
|
|
10294
10303
|
}
|
|
@@ -11244,11 +11253,12 @@ function archiveActiveTopicForMemory(topicId, userId, options) {
|
|
|
11244
11253
|
}, "topic-memory-archiver: raw snapshot preserved below exchange threshold");
|
|
11245
11254
|
return "below-threshold";
|
|
11246
11255
|
}
|
|
11247
|
-
const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
|
|
11256
|
+
const memoryTopic = topic.memoryKey ? topic : getTopicMemoryOrigin(topicId) ?? topic;
|
|
11257
|
+
const memoryTitle = memoryTopic.memoryKey?.trim() || memoryTopic.title;
|
|
11248
11258
|
const launched = (options.launchArchiver ?? runArchiverTurn)({
|
|
11249
11259
|
userId,
|
|
11250
11260
|
topicId: memoryTopic.id,
|
|
11251
|
-
topicTitle:
|
|
11261
|
+
topicTitle: memoryTitle,
|
|
11252
11262
|
archivePath: job.archivePath,
|
|
11253
11263
|
...options.rawArchivePaths ? { rawArchivePaths: options.rawArchivePaths } : {},
|
|
11254
11264
|
messageCount: job.messageCount,
|
|
@@ -13356,11 +13366,12 @@ async function deleteTopicCascadeImpl(topic, userId, options, deletingAncestorId
|
|
|
13356
13366
|
}
|
|
13357
13367
|
if (archived && meetsMemoryArchiveExchangeThreshold(archived.exchangeCount, MIN_MEMORY_ARCHIVE_EXCHANGES, topic)) {
|
|
13358
13368
|
try {
|
|
13359
|
-
const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
|
|
13369
|
+
const memoryTopic = topic.memoryKey ? topic : getTopicMemoryOrigin(topicId) ?? topic;
|
|
13370
|
+
const memoryTitle = memoryTopic.memoryKey?.trim() || memoryTopic.title;
|
|
13360
13371
|
runArchiverTurn({
|
|
13361
13372
|
userId,
|
|
13362
13373
|
...memoryTopic.id !== topicId && !deletingTopicIds.has(memoryTopic.id) ? { topicId: memoryTopic.id } : {},
|
|
13363
|
-
topicTitle:
|
|
13374
|
+
topicTitle: memoryTitle,
|
|
13364
13375
|
archivePath: archived.path,
|
|
13365
13376
|
...rawArchives.length > 0 ? { rawArchivePaths: rawArchives } : {},
|
|
13366
13377
|
messageCount: archived.messageCount
|
|
@@ -14883,6 +14894,9 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
14883
14894
|
let pendingSawDelta = false;
|
|
14884
14895
|
let accumulatedText = "";
|
|
14885
14896
|
let pendingText = "";
|
|
14897
|
+
let assistantContextTokens = 0;
|
|
14898
|
+
let toolContextTokens = 0;
|
|
14899
|
+
let lastContextProgressAt = 0;
|
|
14886
14900
|
let lastVisibleMessageId = null;
|
|
14887
14901
|
const visibleMessageIds = [];
|
|
14888
14902
|
let lastTaskPanelText = null;
|
|
@@ -14901,6 +14915,20 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
14901
14915
|
model
|
|
14902
14916
|
});
|
|
14903
14917
|
};
|
|
14918
|
+
const broadcastContextProgress = (force = false) => {
|
|
14919
|
+
if (silent)
|
|
14920
|
+
return;
|
|
14921
|
+
const now = Date.now();
|
|
14922
|
+
if (!force && lastContextProgressAt > 0 && now - lastContextProgressAt < 250)
|
|
14923
|
+
return;
|
|
14924
|
+
lastContextProgressAt = now;
|
|
14925
|
+
hub.broadcastAiStatus(topicId, {
|
|
14926
|
+
kind: "context_progress",
|
|
14927
|
+
queryId,
|
|
14928
|
+
assistantTokens: assistantContextTokens,
|
|
14929
|
+
toolTokens: toolContextTokens
|
|
14930
|
+
});
|
|
14931
|
+
};
|
|
14904
14932
|
const nextSyntheticToolUseId = () => `tool-${queryId}-${++syntheticToolCounter}`;
|
|
14905
14933
|
const bindToolUseId = (providerToolUseId, fallback) => {
|
|
14906
14934
|
const providerId = normalizeToolUseId(providerToolUseId);
|
|
@@ -15010,6 +15038,8 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
15010
15038
|
const incoming = event.content;
|
|
15011
15039
|
accumulatedText += incoming;
|
|
15012
15040
|
pendingText += incoming;
|
|
15041
|
+
assistantContextTokens = estimateTextTokens(accumulatedText);
|
|
15042
|
+
broadcastContextProgress();
|
|
15013
15043
|
break;
|
|
15014
15044
|
}
|
|
15015
15045
|
case "text":
|
|
@@ -15017,10 +15047,15 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
15017
15047
|
const incoming = event.content;
|
|
15018
15048
|
accumulatedText += incoming;
|
|
15019
15049
|
pendingText += incoming;
|
|
15050
|
+
assistantContextTokens = estimateTextTokens(accumulatedText);
|
|
15051
|
+
broadcastContextProgress();
|
|
15020
15052
|
}
|
|
15021
15053
|
break;
|
|
15022
15054
|
case "tool_use":
|
|
15023
15055
|
emitPendingAssistantMessage();
|
|
15056
|
+
toolContextTokens += estimateTextTokens(`${event.name}
|
|
15057
|
+
${JSON.stringify(event.input ?? {})}`);
|
|
15058
|
+
broadcastContextProgress(true);
|
|
15024
15059
|
if (isVisualsShowHtmlTool(event.name)) {
|
|
15025
15060
|
const input = event.input;
|
|
15026
15061
|
if (typeof input.html !== "string" || input.html.trim().length === 0) {
|
|
@@ -15174,6 +15209,8 @@ async function runTurnEventStream(topicId, topicTitle, queryId, events, control,
|
|
|
15174
15209
|
}
|
|
15175
15210
|
break;
|
|
15176
15211
|
case "tool_result":
|
|
15212
|
+
toolContextTokens += estimateTextTokens(event.content);
|
|
15213
|
+
broadcastContextProgress(true);
|
|
15177
15214
|
if (isVisualToolResultHandled(event.toolUseId))
|
|
15178
15215
|
break;
|
|
15179
15216
|
if (!silent) {
|
|
@@ -15559,6 +15596,7 @@ __export(exports_turn_runner, {
|
|
|
15559
15596
|
channelTranscriptSpeaker: () => channelTranscriptSpeaker,
|
|
15560
15597
|
canonicalModelId: () => canonicalModelId,
|
|
15561
15598
|
buildVideoHtml: () => buildVideoHtml,
|
|
15599
|
+
buildTurnReminderQuery: () => buildTurnReminderQuery,
|
|
15562
15600
|
buildMermaidHtml: () => buildMermaidHtml,
|
|
15563
15601
|
buildMentionOnlyChannelPrompt: () => buildMentionOnlyChannelPrompt,
|
|
15564
15602
|
buildImageHtml: () => buildImageHtml,
|
|
@@ -15584,6 +15622,18 @@ function withDefaultPlaywright(configuredMcp, isManager) {
|
|
|
15584
15622
|
enabled.add("background-bash");
|
|
15585
15623
|
return [...enabled];
|
|
15586
15624
|
}
|
|
15625
|
+
function buildTurnReminderQuery(agent, prompt, reminders) {
|
|
15626
|
+
const reminderPrompt = reminders.join(`
|
|
15627
|
+
|
|
15628
|
+
`);
|
|
15629
|
+
if (!reminderPrompt)
|
|
15630
|
+
return { prompt };
|
|
15631
|
+
if (agent === "maestro")
|
|
15632
|
+
return { prompt, ephemeralSystemPrompt: reminderPrompt };
|
|
15633
|
+
return { prompt: `${prompt}
|
|
15634
|
+
|
|
15635
|
+
${reminderPrompt}` };
|
|
15636
|
+
}
|
|
15587
15637
|
function appendSystemMessage(topicId, text2) {
|
|
15588
15638
|
const message = {
|
|
15589
15639
|
id: randomUUID16(),
|
|
@@ -16399,25 +16449,24 @@ function startAiTurn(params) {
|
|
|
16399
16449
|
const isManager = topicRecord?.kind === "manager";
|
|
16400
16450
|
const isMentionOnlyChannel = topic.aiMode === "mention" && !isManager;
|
|
16401
16451
|
let systemPrompt = isManager ? buildManagerSystemPrompt(systemPromptOpts) : isMentionOnlyChannel ? buildChannelSystemPrompt(systemPromptOpts) : buildTopicSystemPrompt(systemPromptOpts);
|
|
16402
|
-
const memoryTopic = isMentionOnlyChannel || isManager ? topic : getTopicMemoryOrigin(topicId) ?? topic;
|
|
16452
|
+
const memoryTopic = isMentionOnlyChannel || isManager || topic.memoryKey ? topic : getTopicMemoryOrigin(topicId) ?? topic;
|
|
16453
|
+
const memoryTitle = memoryTopic.memoryKey?.trim() || memoryTopic.title;
|
|
16403
16454
|
if (!isMentionOnlyChannel) {
|
|
16455
|
+
let hasMemoryArchive = false;
|
|
16404
16456
|
try {
|
|
16405
|
-
const resolvedBrief = resolveTopicBrief(memoryTopic.id,
|
|
16457
|
+
const resolvedBrief = resolveTopicBrief(memoryTopic.id, memoryTitle);
|
|
16406
16458
|
const wikiDir = getSharedWikiDir();
|
|
16407
|
-
const {
|
|
16408
|
-
|
|
16409
|
-
systemPrompt += buildMemoryPromptSection({
|
|
16410
|
-
briefFile,
|
|
16411
|
-
wikiDir,
|
|
16412
|
-
hasFiles: hasBriefFile,
|
|
16413
|
-
latestSummaryFile,
|
|
16414
|
-
hasArchive: Boolean(resolvedBrief?.brief.latestSummaryMd || latestSummaryFile),
|
|
16415
|
-
isManager
|
|
16416
|
-
});
|
|
16417
|
-
}
|
|
16459
|
+
const { latestSummaryFile } = resolveWikiMemoryMirror(wikiDir, memoryTopic.id, memoryTitle);
|
|
16460
|
+
hasMemoryArchive = Boolean(resolvedBrief?.brief.latestSummaryMd || latestSummaryFile);
|
|
16418
16461
|
} catch (err) {
|
|
16419
16462
|
logger.warn({ topicId, err }, "ai: failed to inject topic brief");
|
|
16420
16463
|
}
|
|
16464
|
+
systemPrompt += buildMemoryPromptSection({
|
|
16465
|
+
topicTitle: topic.title,
|
|
16466
|
+
...memoryTopic.memoryKey ? { memoryKey: memoryTopic.memoryKey } : {},
|
|
16467
|
+
hasArchive: hasMemoryArchive,
|
|
16468
|
+
isManager
|
|
16469
|
+
});
|
|
16421
16470
|
}
|
|
16422
16471
|
const activeVisual = visualTools ? getActiveVisualForPrompt(topicId, userId) : null;
|
|
16423
16472
|
if (activeVisual) {
|
|
@@ -16531,15 +16580,11 @@ function startAiTurn(params) {
|
|
|
16531
16580
|
if (consumePlaywrightUnavailable(userId, topic.title)) {
|
|
16532
16581
|
turnReminders.push("<system-reminder>Playwright browser tools are UNAVAILABLE this turn. The `mcp__playwright__*` tools have been removed from this turn's catalog because the long-lived browser MCP could not be prepared. Do not attempt to call browser tools. If browser interaction is required, ask the user to retry shortly or use a non-browser alternative.</system-reminder>");
|
|
16533
16582
|
}
|
|
16534
|
-
const
|
|
16535
|
-
|
|
16536
|
-
${turnReminders.join(`
|
|
16537
|
-
|
|
16538
|
-
`)}` : agentPrompt;
|
|
16583
|
+
const reminderQuery = buildTurnReminderQuery(agentKind, agentPrompt, turnReminders);
|
|
16539
16584
|
try {
|
|
16540
16585
|
yield* runAgent({
|
|
16541
16586
|
agent: agentKind,
|
|
16542
|
-
|
|
16587
|
+
...reminderQuery,
|
|
16543
16588
|
attachments: promptAttachments,
|
|
16544
16589
|
cwd: workspaceCwd,
|
|
16545
16590
|
systemPrompt,
|
|
@@ -18161,7 +18206,7 @@ import {
|
|
|
18161
18206
|
unlinkSync,
|
|
18162
18207
|
writeFileSync as writeFileSync2
|
|
18163
18208
|
} from "fs";
|
|
18164
|
-
import { basename, dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
18209
|
+
import { basename, dirname as dirname2, join as join2, relative, resolve as resolve2 } from "path";
|
|
18165
18210
|
init_config();
|
|
18166
18211
|
init_wiki_summary_names();
|
|
18167
18212
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -18295,9 +18340,314 @@ function topicNameFrom(topicIdStr) {
|
|
|
18295
18340
|
function slugify(topic) {
|
|
18296
18341
|
return wikiSummarySlug(topic);
|
|
18297
18342
|
}
|
|
18343
|
+
function normalizeSearchText(value2) {
|
|
18344
|
+
return value2.toLowerCase().replaceAll(/[^a-z0-9\uAC00-\uD7A3]+/g, " ").trim().replaceAll(/\s+/g, " ");
|
|
18345
|
+
}
|
|
18346
|
+
function compactSearchText(value2) {
|
|
18347
|
+
return normalizeSearchText(value2).replaceAll(" ", "");
|
|
18348
|
+
}
|
|
18349
|
+
function trigrams(value2) {
|
|
18350
|
+
const compact = compactSearchText(value2);
|
|
18351
|
+
if (compact.length < 3)
|
|
18352
|
+
return new Set(compact ? [compact] : []);
|
|
18353
|
+
const result = new Set;
|
|
18354
|
+
for (let i = 0;i <= compact.length - 3; i += 1)
|
|
18355
|
+
result.add(compact.slice(i, i + 3));
|
|
18356
|
+
return result;
|
|
18357
|
+
}
|
|
18358
|
+
function fuzzySearchScore(query, candidate, maximum) {
|
|
18359
|
+
const compactQuery = compactSearchText(query);
|
|
18360
|
+
const compactCandidate = compactSearchText(candidate);
|
|
18361
|
+
if (compactQuery.length < 3 || compactCandidate.length < 3)
|
|
18362
|
+
return 0;
|
|
18363
|
+
const similarity = trigramSimilarity(compactQuery, compactCandidate);
|
|
18364
|
+
const minimumSimilarity = compactQuery.length <= 4 ? 0.6 : 0.45;
|
|
18365
|
+
return similarity >= minimumSimilarity ? similarity * maximum : 0;
|
|
18366
|
+
}
|
|
18367
|
+
var SEARCH_STOP_WORDS = new Set([
|
|
18368
|
+
"a",
|
|
18369
|
+
"an",
|
|
18370
|
+
"and",
|
|
18371
|
+
"are",
|
|
18372
|
+
"as",
|
|
18373
|
+
"at",
|
|
18374
|
+
"be",
|
|
18375
|
+
"by",
|
|
18376
|
+
"for",
|
|
18377
|
+
"from",
|
|
18378
|
+
"in",
|
|
18379
|
+
"is",
|
|
18380
|
+
"it",
|
|
18381
|
+
"of",
|
|
18382
|
+
"on",
|
|
18383
|
+
"or",
|
|
18384
|
+
"that",
|
|
18385
|
+
"the",
|
|
18386
|
+
"to",
|
|
18387
|
+
"with"
|
|
18388
|
+
]);
|
|
18389
|
+
var SEARCH_MONTHS = new Map([
|
|
18390
|
+
["january", "01"],
|
|
18391
|
+
["jan", "01"],
|
|
18392
|
+
["1\uC6D4", "01"],
|
|
18393
|
+
["february", "02"],
|
|
18394
|
+
["feb", "02"],
|
|
18395
|
+
["2\uC6D4", "02"],
|
|
18396
|
+
["march", "03"],
|
|
18397
|
+
["mar", "03"],
|
|
18398
|
+
["3\uC6D4", "03"],
|
|
18399
|
+
["april", "04"],
|
|
18400
|
+
["apr", "04"],
|
|
18401
|
+
["4\uC6D4", "04"],
|
|
18402
|
+
["may", "05"],
|
|
18403
|
+
["5\uC6D4", "05"],
|
|
18404
|
+
["june", "06"],
|
|
18405
|
+
["jun", "06"],
|
|
18406
|
+
["6\uC6D4", "06"],
|
|
18407
|
+
["july", "07"],
|
|
18408
|
+
["jul", "07"],
|
|
18409
|
+
["7\uC6D4", "07"],
|
|
18410
|
+
["august", "08"],
|
|
18411
|
+
["aug", "08"],
|
|
18412
|
+
["8\uC6D4", "08"],
|
|
18413
|
+
["september", "09"],
|
|
18414
|
+
["sep", "09"],
|
|
18415
|
+
["sept", "09"],
|
|
18416
|
+
["9\uC6D4", "09"],
|
|
18417
|
+
["october", "10"],
|
|
18418
|
+
["oct", "10"],
|
|
18419
|
+
["10\uC6D4", "10"],
|
|
18420
|
+
["november", "11"],
|
|
18421
|
+
["nov", "11"],
|
|
18422
|
+
["11\uC6D4", "11"],
|
|
18423
|
+
["december", "12"],
|
|
18424
|
+
["dec", "12"],
|
|
18425
|
+
["12\uC6D4", "12"]
|
|
18426
|
+
]);
|
|
18427
|
+
var SEARCH_RECENCY_TERMS = new Set([
|
|
18428
|
+
"latest",
|
|
18429
|
+
"newest",
|
|
18430
|
+
"recent",
|
|
18431
|
+
"earliest",
|
|
18432
|
+
"oldest",
|
|
18433
|
+
"\uCD5C\uADFC",
|
|
18434
|
+
"\uCD5C\uC2E0",
|
|
18435
|
+
"\uCD5C\uCD08",
|
|
18436
|
+
"\uC624\uB798\uB41C"
|
|
18437
|
+
]);
|
|
18438
|
+
function meaningfulSearchTokens(value2) {
|
|
18439
|
+
return normalizeSearchText(value2).split(" ").filter((token) => (token.length >= 2 || /^\d+$/.test(token)) && !SEARCH_STOP_WORDS.has(token));
|
|
18440
|
+
}
|
|
18441
|
+
function canonicalSearchToken(token) {
|
|
18442
|
+
return token.length > 4 && token.endsWith("s") ? token.slice(0, -1) : token;
|
|
18443
|
+
}
|
|
18444
|
+
function canonicalSearchTokens(value2) {
|
|
18445
|
+
return meaningfulSearchTokens(value2).map(canonicalSearchToken);
|
|
18446
|
+
}
|
|
18447
|
+
function countToken(tokens, token) {
|
|
18448
|
+
let count = 0;
|
|
18449
|
+
for (const candidate of tokens)
|
|
18450
|
+
if (candidate === token)
|
|
18451
|
+
count += 1;
|
|
18452
|
+
return count;
|
|
18453
|
+
}
|
|
18454
|
+
function countPhrase(text, phrase) {
|
|
18455
|
+
if (!phrase)
|
|
18456
|
+
return 0;
|
|
18457
|
+
let count = 0;
|
|
18458
|
+
let offset = 0;
|
|
18459
|
+
let match = text.indexOf(phrase, offset);
|
|
18460
|
+
while (match >= 0) {
|
|
18461
|
+
count += 1;
|
|
18462
|
+
offset = match + phrase.length;
|
|
18463
|
+
match = text.indexOf(phrase, offset);
|
|
18464
|
+
}
|
|
18465
|
+
return count;
|
|
18466
|
+
}
|
|
18467
|
+
function summaryTopicIdentity(key) {
|
|
18468
|
+
return normalizeSearchText(key.replace(/^\d{4}-\d{2}-\d{2}-/, ""));
|
|
18469
|
+
}
|
|
18470
|
+
function scoreSummaryTime(query, key, title) {
|
|
18471
|
+
const date = key.match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
18472
|
+
if (!date)
|
|
18473
|
+
return 0;
|
|
18474
|
+
const tokens = meaningfulSearchTokens(query);
|
|
18475
|
+
const documentDate = `${date[1]}-${date[2]}-${date[3]}`;
|
|
18476
|
+
const requestedDateMatch = query.match(/\b(\d{4}) (\d{2}) (\d{2})\b/);
|
|
18477
|
+
const requestedDate = requestedDateMatch ? `${requestedDateMatch[1]}-${requestedDateMatch[2]}-${requestedDateMatch[3]}` : undefined;
|
|
18478
|
+
const beforeDate = /\bbefore\b/.test(query);
|
|
18479
|
+
const afterDate = /\bafter\b/.test(query);
|
|
18480
|
+
if (requestedDate && beforeDate && documentDate >= requestedDate)
|
|
18481
|
+
return null;
|
|
18482
|
+
if (requestedDate && afterDate && documentDate <= requestedDate)
|
|
18483
|
+
return null;
|
|
18484
|
+
const queryYear = tokens.find((token) => /^\d{4}(?:\uB144)?$/.test(token))?.replace(/\uB144$/, "");
|
|
18485
|
+
const queryMonth = tokens.map((token) => SEARCH_MONTHS.get(token)).find(Boolean);
|
|
18486
|
+
const hasRecencyTerm = tokens.some((token) => SEARCH_RECENCY_TERMS.has(token));
|
|
18487
|
+
const yearMatches = !queryYear || queryYear === date[1];
|
|
18488
|
+
const monthMatches = !queryMonth || queryMonth === date[2];
|
|
18489
|
+
if (!queryYear && !queryMonth && !hasRecencyTerm || !yearMatches || !monthMatches)
|
|
18490
|
+
return 0;
|
|
18491
|
+
const normalizedMetadata = `${normalizeSearchText(key)} ${normalizeSearchText(title)}`;
|
|
18492
|
+
const contentTokens = tokens.filter((token) => !/^\d{4}(?:\uB144)?$/.test(token) && !SEARCH_MONTHS.has(token) && !SEARCH_RECENCY_TERMS.has(token));
|
|
18493
|
+
const contentMatches = contentTokens.filter((token) => normalizedMetadata.includes(token)).length;
|
|
18494
|
+
const relationScore = requestedDate && (beforeDate || afterDate) ? 60 : 0;
|
|
18495
|
+
const exactDateScore = requestedDate === documentDate ? 90 : 0;
|
|
18496
|
+
return 30 + relationScore + exactDateScore + contentMatches * 15;
|
|
18497
|
+
}
|
|
18498
|
+
function trigramSimilarity(left, right) {
|
|
18499
|
+
const a = trigrams(left);
|
|
18500
|
+
const b = trigrams(right);
|
|
18501
|
+
if (a.size === 0 || b.size === 0)
|
|
18502
|
+
return 0;
|
|
18503
|
+
let overlap = 0;
|
|
18504
|
+
for (const gram of a)
|
|
18505
|
+
if (b.has(gram))
|
|
18506
|
+
overlap += 1;
|
|
18507
|
+
return 2 * overlap / (a.size + b.size);
|
|
18508
|
+
}
|
|
18509
|
+
function parseWikiIndex(path) {
|
|
18510
|
+
let text;
|
|
18511
|
+
try {
|
|
18512
|
+
text = readFileSync2(path, "utf-8");
|
|
18513
|
+
} catch {
|
|
18514
|
+
return [];
|
|
18515
|
+
}
|
|
18516
|
+
const candidates = [];
|
|
18517
|
+
const entryPattern = /^\s*-\s*\[\[(topic|articles|summaries)\/([^\]]+)\]\]\s*(.*)$/;
|
|
18518
|
+
for (const line of text.split(`
|
|
18519
|
+
`)) {
|
|
18520
|
+
const match = line.match(entryPattern);
|
|
18521
|
+
if (!match)
|
|
18522
|
+
continue;
|
|
18523
|
+
const namespace = match[1];
|
|
18524
|
+
const key = match[2].replace(/\.md$/i, "");
|
|
18525
|
+
const tail = match[3].trim();
|
|
18526
|
+
const dateMatch = tail.match(/\((\d{4}-\d{2}-\d{2})(?:[^)]*)\)\s*$/);
|
|
18527
|
+
const description = dateMatch ? tail.slice(0, dateMatch.index).trim() : tail;
|
|
18528
|
+
candidates.push({
|
|
18529
|
+
kind: namespace === "topic" ? "topic" : namespace === "articles" ? "article" : "summary",
|
|
18530
|
+
key,
|
|
18531
|
+
description,
|
|
18532
|
+
...dateMatch ? { date: dateMatch[1] } : {}
|
|
18533
|
+
});
|
|
18534
|
+
}
|
|
18535
|
+
return candidates;
|
|
18536
|
+
}
|
|
18537
|
+
function indexCandidateExists(candidate) {
|
|
18538
|
+
if (candidate.kind === "topic")
|
|
18539
|
+
return true;
|
|
18540
|
+
const root = candidate.kind === "article" ? runtime().articlesDir : runtime().summariesDir;
|
|
18541
|
+
const filePath = resolve2(root, `${candidate.key}.md`);
|
|
18542
|
+
const relativePath = relative(root, filePath);
|
|
18543
|
+
if (relativePath.startsWith("..") || relativePath.startsWith("/"))
|
|
18544
|
+
return false;
|
|
18545
|
+
try {
|
|
18546
|
+
return statSync(filePath).isFile();
|
|
18547
|
+
} catch {
|
|
18548
|
+
return false;
|
|
18549
|
+
}
|
|
18550
|
+
}
|
|
18551
|
+
function scoreIndexKey(query, key) {
|
|
18552
|
+
const normalizedQuery = normalizeSearchText(query);
|
|
18553
|
+
const compactQuery = compactSearchText(normalizedQuery);
|
|
18554
|
+
const normalizedKey = normalizeSearchText(key);
|
|
18555
|
+
const compactKey = compactSearchText(normalizedKey);
|
|
18556
|
+
const tokens = meaningfulSearchTokens(normalizedQuery);
|
|
18557
|
+
let score = 0;
|
|
18558
|
+
if (normalizedKey === normalizedQuery)
|
|
18559
|
+
score += 120;
|
|
18560
|
+
else if (compactKey === compactQuery)
|
|
18561
|
+
score += 110;
|
|
18562
|
+
else {
|
|
18563
|
+
if (compactQuery.length >= 2 && (compactKey.startsWith(compactQuery) || compactQuery.startsWith(compactKey))) {
|
|
18564
|
+
score += 65;
|
|
18565
|
+
}
|
|
18566
|
+
if (tokens.length > 0 && compactQuery.length >= 3 && compactKey.includes(compactQuery)) {
|
|
18567
|
+
score += 45;
|
|
18568
|
+
}
|
|
18569
|
+
score += fuzzySearchScore(compactQuery, compactKey, 55);
|
|
18570
|
+
}
|
|
18571
|
+
for (const token of tokens) {
|
|
18572
|
+
if (normalizedKey.includes(token))
|
|
18573
|
+
score += 10;
|
|
18574
|
+
}
|
|
18575
|
+
return score;
|
|
18576
|
+
}
|
|
18577
|
+
function scoreIndexCandidate(query, candidate) {
|
|
18578
|
+
const normalizedQuery = normalizeSearchText(query);
|
|
18579
|
+
if (!normalizedQuery)
|
|
18580
|
+
return 0;
|
|
18581
|
+
const normalizedDescription = normalizeSearchText(candidate.description);
|
|
18582
|
+
const tokens = canonicalSearchTokens(normalizedQuery);
|
|
18583
|
+
const descriptionTokens = new Set(canonicalSearchTokens(normalizedDescription));
|
|
18584
|
+
let score = scoreIndexKey(normalizedQuery, candidate.key);
|
|
18585
|
+
if (tokens.length > 0 && normalizedDescription.includes(normalizedQuery))
|
|
18586
|
+
score += 30;
|
|
18587
|
+
let descriptionMatches = 0;
|
|
18588
|
+
for (const token of tokens) {
|
|
18589
|
+
if (descriptionTokens.has(token)) {
|
|
18590
|
+
score += 5;
|
|
18591
|
+
descriptionMatches += 1;
|
|
18592
|
+
}
|
|
18593
|
+
}
|
|
18594
|
+
if (tokens.length > 1 && descriptionMatches === tokens.length)
|
|
18595
|
+
score += 25;
|
|
18596
|
+
if (candidate.kind === "topic") {
|
|
18597
|
+
const requestsCurrent = /\b(?:current|active|ongoing|open)\b|\uD604\uC7AC|\uC9C4\uD589 \uC911/.test(normalizedQuery);
|
|
18598
|
+
const archivedCandidate = /\b(?:archive|archived|historical|closed)\b|\uBCF4\uAD00|\uC885\uB8CC/.test(`${normalizeSearchText(candidate.key)} ${normalizedDescription}`);
|
|
18599
|
+
if (requestsCurrent && archivedCandidate)
|
|
18600
|
+
score -= 40;
|
|
18601
|
+
}
|
|
18602
|
+
return score;
|
|
18603
|
+
}
|
|
18298
18604
|
function wikiQuery(args) {
|
|
18299
|
-
const question = safeExt(args, ["question", "query", "q", "text"], "");
|
|
18300
|
-
const
|
|
18605
|
+
const question = String(safeExt(args, ["question", "query", "q", "text"], "")).trim();
|
|
18606
|
+
const requestedKind = String(safeExt(args, ["kind", "type"], "all"));
|
|
18607
|
+
const kind = requestedKind === "topic" || requestedKind === "article" || requestedKind === "summary" ? requestedKind : "all";
|
|
18608
|
+
const requestedLimit = Number(safeExt(args, ["limit", "maxResults"], 8));
|
|
18609
|
+
const limit = Math.min(Math.max(Number.isFinite(requestedLimit) ? requestedLimit : 8, 1), 20);
|
|
18610
|
+
if (!question) {
|
|
18611
|
+
return {
|
|
18612
|
+
content: [{ type: "text", text: "A non-empty wiki query is required." }],
|
|
18613
|
+
isError: true
|
|
18614
|
+
};
|
|
18615
|
+
}
|
|
18616
|
+
const normalizedQuery = normalizeSearchText(question);
|
|
18617
|
+
if (!normalizedQuery) {
|
|
18618
|
+
return { content: [{ type: "text", text: "No matching wiki articles found." }] };
|
|
18619
|
+
}
|
|
18620
|
+
const canReadTopicMemory = runtime().host.canReadTopicMemory;
|
|
18621
|
+
const temporalDirection = /\b(?:latest|newest|recent)\b|\uCD5C\uADFC|\uCD5C\uC2E0/.test(normalizedQuery) ? "newest" : /\b(?:earliest|oldest)\b|\uAC00\uC7A5 \uC624\uB798\uB41C|\uCD5C\uCD08/.test(normalizedQuery) ? "oldest" : undefined;
|
|
18622
|
+
const visibleIndexCandidates = [
|
|
18623
|
+
...parseWikiIndex(resolve2(runtime().wikiDir, "topic-index.md")),
|
|
18624
|
+
...parseWikiIndex(resolve2(runtime().wikiDir, "article-index.md"))
|
|
18625
|
+
].filter((candidate) => kind === "all" || candidate.kind === kind).filter((candidate) => candidate.kind !== "topic" || !canReadTopicMemory || canReadTopicMemory(candidate.key, runtime().userId));
|
|
18626
|
+
const hasStaleExactIndexMatch = visibleIndexCandidates.some((candidate) => !indexCandidateExists(candidate) && scoreIndexKey(question, candidate.key) >= 100);
|
|
18627
|
+
const indexCandidates = visibleIndexCandidates.filter(indexCandidateExists).map((candidate) => ({
|
|
18628
|
+
candidate,
|
|
18629
|
+
keyScore: scoreIndexKey(question, candidate.key),
|
|
18630
|
+
score: scoreIndexCandidate(question, candidate)
|
|
18631
|
+
})).filter(({ keyScore, score }) => keyScore >= 25 || score >= 40).sort((left, right) => right.score - left.score || left.candidate.key.localeCompare(right.candidate.key));
|
|
18632
|
+
const strongestIndex = indexCandidates[0];
|
|
18633
|
+
if (kind === "topic" && strongestIndex && (strongestIndex.keyScore >= 25 || strongestIndex.score >= 40)) {
|
|
18634
|
+
const runnerUp = indexCandidates[1];
|
|
18635
|
+
const ambiguous = strongestIndex.keyScore < 100 && runnerUp !== undefined && runnerUp.score >= strongestIndex.score * 0.9;
|
|
18636
|
+
if (ambiguous) {
|
|
18637
|
+
const lines2 = ["Found 2 ambiguous wiki candidates:", ""];
|
|
18638
|
+
for (const { candidate, score } of [strongestIndex, runnerUp]) {
|
|
18639
|
+
lines2.push(`- kind: ${candidate.kind}`, ` key: ${candidate.key}`, ` score: ${score.toFixed(2)}`, ` description: ${candidate.description || "(none)"}`, ...candidate.date ? [` date: ${candidate.date}`] : []);
|
|
18640
|
+
}
|
|
18641
|
+
return { content: [{ type: "text", text: lines2.join(`
|
|
18642
|
+
`) }] };
|
|
18643
|
+
}
|
|
18644
|
+
const lines = ["Found 1 wiki candidate:", ""];
|
|
18645
|
+
for (const { candidate, score } of [strongestIndex]) {
|
|
18646
|
+
lines.push(`- kind: ${candidate.kind}`, ` key: ${candidate.key}`, ` score: ${score.toFixed(2)}`, ` description: ${candidate.description || "(none)"}`, ...candidate.date ? [` date: ${candidate.date}`] : []);
|
|
18647
|
+
}
|
|
18648
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
18649
|
+
`) }] };
|
|
18650
|
+
}
|
|
18301
18651
|
const results = [];
|
|
18302
18652
|
const scored = [];
|
|
18303
18653
|
function scan(dir, label) {
|
|
@@ -18314,67 +18664,234 @@ function wikiQuery(args) {
|
|
|
18314
18664
|
const fp = join2(sub, f.name);
|
|
18315
18665
|
try {
|
|
18316
18666
|
const text = readFileSync2(fp, "utf-8");
|
|
18667
|
+
const key = `${entry.name}/${f.name.replace(/\.md$/i, "")}`;
|
|
18317
18668
|
scored.push({
|
|
18318
|
-
|
|
18669
|
+
kind: label === "articles" ? "article" : "skill",
|
|
18670
|
+
key,
|
|
18671
|
+
keyScore: scoreIndexKey(question, key),
|
|
18672
|
+
score: scoreMatch(text, key, label === "articles" ? "article" : "skill"),
|
|
18319
18673
|
path: `${label}/${entry.name}/${f.name}`,
|
|
18320
|
-
text
|
|
18674
|
+
text,
|
|
18675
|
+
title: titleFromDocument(text, key)
|
|
18321
18676
|
});
|
|
18322
18677
|
} catch {}
|
|
18323
18678
|
}
|
|
18324
18679
|
} catch {}
|
|
18325
18680
|
}
|
|
18326
18681
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
18682
|
+
if (label === "topic" && canReadTopicMemory && !canReadTopicMemory(entry.name.replace(/\.md$/i, ""), runtime().userId)) {
|
|
18683
|
+
continue;
|
|
18684
|
+
}
|
|
18327
18685
|
const fp = join2(dir, entry.name);
|
|
18328
18686
|
try {
|
|
18329
18687
|
const text = readFileSync2(fp, "utf-8");
|
|
18688
|
+
const key = entry.name.replace(/\.md$/i, "");
|
|
18689
|
+
const resultKind = label === "articles" ? "article" : label === "topic" ? "topic" : label === "summaries" ? "summary" : "skill";
|
|
18330
18690
|
scored.push({
|
|
18331
|
-
|
|
18691
|
+
kind: resultKind,
|
|
18692
|
+
key,
|
|
18693
|
+
keyScore: scoreIndexKey(question, key),
|
|
18694
|
+
score: scoreMatch(text, key, resultKind),
|
|
18332
18695
|
path: `${label}/${entry.name}`,
|
|
18333
|
-
text
|
|
18696
|
+
text,
|
|
18697
|
+
title: titleFromDocument(text, key),
|
|
18698
|
+
...label === "summaries" && /^\d{4}-\d{2}-\d{2}/.test(key) ? { date: key.slice(0, 10) } : {}
|
|
18334
18699
|
});
|
|
18335
18700
|
} catch {}
|
|
18336
18701
|
}
|
|
18337
18702
|
}
|
|
18338
18703
|
} catch {}
|
|
18339
18704
|
}
|
|
18340
|
-
function
|
|
18341
|
-
const
|
|
18705
|
+
function titleFromDocument(text, key) {
|
|
18706
|
+
const titleLine = text.split(`
|
|
18707
|
+
`).find((line) => /^#+\s*/.test(line));
|
|
18708
|
+
return titleLine ? titleLine.replace(/^#+\s*/, "") : basename(key);
|
|
18709
|
+
}
|
|
18710
|
+
function scoreMatch(text, key, resultKind) {
|
|
18711
|
+
const titleLine = text.split(`
|
|
18712
|
+
`).find((line) => /^#+\s*/.test(line)) ?? "";
|
|
18713
|
+
const title = titleLine.replace(/^#+\s*/, "");
|
|
18714
|
+
const normalizedKey = normalizeSearchText(key);
|
|
18715
|
+
const normalizedTitle = normalizeSearchText(title);
|
|
18716
|
+
const normalizedBody = normalizeSearchText(text.replace(titleLine, ""));
|
|
18717
|
+
const compactQuery = compactSearchText(normalizedQuery);
|
|
18718
|
+
const compactKey = compactSearchText(normalizedKey);
|
|
18719
|
+
const compactTitle = compactSearchText(normalizedTitle);
|
|
18720
|
+
const tokens = canonicalSearchTokens(normalizedQuery);
|
|
18721
|
+
const keyTokens = new Set(canonicalSearchTokens(normalizedKey));
|
|
18722
|
+
const titleTokens = new Set(canonicalSearchTokens(normalizedTitle));
|
|
18723
|
+
const bodyTokens = canonicalSearchTokens(normalizedBody);
|
|
18342
18724
|
let score = 0;
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18346
|
-
|
|
18347
|
-
|
|
18348
|
-
|
|
18349
|
-
|
|
18350
|
-
|
|
18725
|
+
let strongMetadataEvidence = false;
|
|
18726
|
+
if (normalizedKey === normalizedQuery) {
|
|
18727
|
+
score += 120;
|
|
18728
|
+
strongMetadataEvidence = true;
|
|
18729
|
+
} else if (compactKey === compactQuery) {
|
|
18730
|
+
score += 110;
|
|
18731
|
+
strongMetadataEvidence = true;
|
|
18732
|
+
} else if (compactQuery.length >= 2 && (compactKey.startsWith(compactQuery) || compactQuery.startsWith(compactKey))) {
|
|
18733
|
+
score += 65;
|
|
18734
|
+
strongMetadataEvidence = true;
|
|
18735
|
+
}
|
|
18736
|
+
if (tokens.length > 0 && compactQuery.length >= 3 && compactKey.includes(compactQuery)) {
|
|
18737
|
+
score += 45;
|
|
18738
|
+
strongMetadataEvidence = true;
|
|
18739
|
+
}
|
|
18740
|
+
const fuzzyKeyScore = fuzzySearchScore(compactQuery, compactKey, 55);
|
|
18741
|
+
score += fuzzyKeyScore;
|
|
18742
|
+
if (fuzzyKeyScore > 0)
|
|
18743
|
+
strongMetadataEvidence = true;
|
|
18744
|
+
if (normalizedTitle === normalizedQuery) {
|
|
18745
|
+
score += 115;
|
|
18746
|
+
strongMetadataEvidence = true;
|
|
18747
|
+
} else if (compactTitle === compactQuery) {
|
|
18748
|
+
score += 105;
|
|
18749
|
+
strongMetadataEvidence = true;
|
|
18750
|
+
} else if (compactQuery.length >= 2 && compactTitle.startsWith(compactQuery)) {
|
|
18751
|
+
score += 60;
|
|
18752
|
+
strongMetadataEvidence = true;
|
|
18753
|
+
}
|
|
18754
|
+
const fuzzyTitleScore = fuzzySearchScore(compactQuery, compactTitle, 50);
|
|
18755
|
+
score += fuzzyTitleScore;
|
|
18756
|
+
if (fuzzyTitleScore > 0)
|
|
18757
|
+
strongMetadataEvidence = true;
|
|
18758
|
+
if (resultKind === "summary") {
|
|
18759
|
+
const temporalScore = scoreSummaryTime(normalizedQuery, key, title);
|
|
18760
|
+
if (temporalScore === null)
|
|
18761
|
+
return 0;
|
|
18762
|
+
score += temporalScore;
|
|
18763
|
+
if (temporalScore > 0)
|
|
18764
|
+
strongMetadataEvidence = true;
|
|
18765
|
+
}
|
|
18766
|
+
const isMultiToken = tokens.length > 1;
|
|
18767
|
+
if (tokens.length > 0 && normalizedTitle.includes(normalizedQuery)) {
|
|
18768
|
+
score += 35;
|
|
18769
|
+
strongMetadataEvidence = true;
|
|
18770
|
+
}
|
|
18771
|
+
const phraseMatches = isMultiToken ? countPhrase(normalizedBody, normalizedQuery) : 0;
|
|
18772
|
+
if (phraseMatches > 0)
|
|
18773
|
+
score += phraseMatches * 12;
|
|
18774
|
+
else if (tokens.length === 1 && tokens[0].length >= 5 && normalizedBody.includes(tokens[0])) {
|
|
18775
|
+
score += 16;
|
|
18776
|
+
}
|
|
18777
|
+
let bodyMatches = 0;
|
|
18778
|
+
let minimumBodyFrequency = Number.POSITIVE_INFINITY;
|
|
18779
|
+
for (const token of tokens) {
|
|
18780
|
+
if (keyTokens.has(token))
|
|
18781
|
+
score += 10;
|
|
18782
|
+
if (titleTokens.has(token))
|
|
18783
|
+
score += 12;
|
|
18784
|
+
const bodyFrequency = countToken(bodyTokens, token);
|
|
18785
|
+
if (bodyFrequency > 0) {
|
|
18786
|
+
score += Math.min(bodyFrequency, 3) * 4;
|
|
18787
|
+
bodyMatches += 1;
|
|
18788
|
+
minimumBodyFrequency = Math.min(minimumBodyFrequency, bodyFrequency);
|
|
18351
18789
|
}
|
|
18352
18790
|
}
|
|
18791
|
+
if (isMultiToken && bodyMatches === tokens.length) {
|
|
18792
|
+
score += 8 * Math.min(minimumBodyFrequency, 3);
|
|
18793
|
+
}
|
|
18794
|
+
const minimumBodyTokens = hasStaleExactIndexMatch ? 3 : 2;
|
|
18795
|
+
const partialArticleEvidence = resultKind === "article" && bodyMatches >= 3 && bodyMatches / tokens.length >= 0.6;
|
|
18796
|
+
const strongBodyEvidence = tokens.length >= minimumBodyTokens && (bodyMatches === tokens.length && (phraseMatches > 0 || minimumBodyFrequency >= 1) || partialArticleEvidence);
|
|
18797
|
+
if (!strongMetadataEvidence && !strongBodyEvidence)
|
|
18798
|
+
return 0;
|
|
18353
18799
|
return score;
|
|
18354
18800
|
}
|
|
18355
|
-
|
|
18356
|
-
|
|
18801
|
+
if (kind === "all" || kind === "article")
|
|
18802
|
+
scan(runtime().articlesDir, "articles");
|
|
18803
|
+
if (kind === "all" && runtime().surface === "all")
|
|
18357
18804
|
scan(runtime().skillsDir, "skills");
|
|
18358
|
-
|
|
18359
|
-
|
|
18360
|
-
|
|
18361
|
-
|
|
18362
|
-
|
|
18805
|
+
if (kind === "all" || kind === "topic")
|
|
18806
|
+
scan(runtime().topicsDir, "topic");
|
|
18807
|
+
if (kind === "all" || kind === "summary")
|
|
18808
|
+
scan(runtime().summariesDir, "summaries");
|
|
18809
|
+
const merged = new Map;
|
|
18810
|
+
for (const { candidate, keyScore, score } of indexCandidates) {
|
|
18811
|
+
merged.set(`${candidate.kind}:${candidate.key}`, {
|
|
18812
|
+
kind: candidate.kind,
|
|
18813
|
+
key: candidate.key,
|
|
18814
|
+
keyScore,
|
|
18815
|
+
score,
|
|
18816
|
+
description: candidate.description,
|
|
18817
|
+
date: candidate.date
|
|
18818
|
+
});
|
|
18819
|
+
}
|
|
18820
|
+
const documentThreshold = kind === "topic" ? 24 : 16;
|
|
18821
|
+
for (const document of scored) {
|
|
18822
|
+
if (document.score < documentThreshold)
|
|
18823
|
+
continue;
|
|
18824
|
+
const id = `${document.kind}:${document.key}`;
|
|
18825
|
+
const indexed = merged.get(id);
|
|
18826
|
+
merged.set(id, indexed ? { ...indexed, ...document, score: Math.max(indexed.score, document.score) } : document);
|
|
18827
|
+
}
|
|
18828
|
+
let ranked = [...merged.values()].sort((left, right) => right.score - left.score || (left.kind === "summary" && right.kind === "summary" ? temporalDirection === "oldest" ? (left.date ?? "").localeCompare(right.date ?? "") : (right.date ?? "").localeCompare(left.date ?? "") : 0) || left.key.localeCompare(right.key));
|
|
18829
|
+
if (kind === "summary") {
|
|
18830
|
+
const sameTopic = ranked.filter((result) => {
|
|
18831
|
+
const identity = summaryTopicIdentity(result.key);
|
|
18832
|
+
return identity.length >= 3 && normalizedQuery.includes(identity);
|
|
18833
|
+
});
|
|
18834
|
+
if (sameTopic.length > 0)
|
|
18835
|
+
ranked = sameTopic;
|
|
18836
|
+
const requestedDateMatch = normalizedQuery.match(/\b(\d{4}) (\d{2}) (\d{2})\b/);
|
|
18837
|
+
const requestedDate = requestedDateMatch ? `${requestedDateMatch[1]}-${requestedDateMatch[2]}-${requestedDateMatch[3]}` : undefined;
|
|
18838
|
+
const beforeDate = /\bbefore\b/.test(normalizedQuery);
|
|
18839
|
+
const afterDate = /\bafter\b/.test(normalizedQuery);
|
|
18840
|
+
if (requestedDate && beforeDate) {
|
|
18841
|
+
ranked = ranked.filter((result) => !result.date || result.date < requestedDate);
|
|
18842
|
+
} else if (requestedDate && afterDate) {
|
|
18843
|
+
ranked = ranked.filter((result) => !result.date || result.date > requestedDate);
|
|
18844
|
+
}
|
|
18845
|
+
ranked.sort((left, right) => {
|
|
18846
|
+
if (beforeDate) {
|
|
18847
|
+
return (right.date ?? "").localeCompare(left.date ?? "") || right.score - left.score;
|
|
18848
|
+
}
|
|
18849
|
+
if (afterDate) {
|
|
18850
|
+
return (left.date ?? "").localeCompare(right.date ?? "") || right.score - left.score;
|
|
18851
|
+
}
|
|
18852
|
+
if (requestedDate) {
|
|
18853
|
+
const dateDifference = Number(right.date === requestedDate) - Number(left.date === requestedDate);
|
|
18854
|
+
if (dateDifference !== 0)
|
|
18855
|
+
return dateDifference;
|
|
18856
|
+
}
|
|
18857
|
+
if (temporalDirection) {
|
|
18858
|
+
const dateDifference = temporalDirection === "oldest" ? (left.date ?? "").localeCompare(right.date ?? "") : (right.date ?? "").localeCompare(left.date ?? "");
|
|
18859
|
+
if (dateDifference !== 0)
|
|
18860
|
+
return dateDifference;
|
|
18861
|
+
}
|
|
18862
|
+
return right.score - left.score || (right.date ?? "").localeCompare(left.date ?? "") || left.key.localeCompare(right.key);
|
|
18863
|
+
});
|
|
18864
|
+
}
|
|
18865
|
+
if (kind === "topic" && ranked[0]) {
|
|
18866
|
+
const runnerUp = ranked[1];
|
|
18867
|
+
const ambiguous = ranked[0].keyScore < 100 && runnerUp !== undefined && runnerUp.score >= ranked[0].score * 0.9;
|
|
18868
|
+
if (ambiguous) {
|
|
18869
|
+
ranked = ranked.slice(0, 2);
|
|
18870
|
+
} else {
|
|
18871
|
+
ranked = ranked.slice(0, 1);
|
|
18872
|
+
}
|
|
18873
|
+
} else {
|
|
18874
|
+
ranked = ranked.slice(0, limit);
|
|
18875
|
+
}
|
|
18876
|
+
if (ranked.length === 0) {
|
|
18363
18877
|
results.push("No matching wiki articles found.");
|
|
18364
18878
|
} else {
|
|
18365
|
-
results.push(`Found ${
|
|
18879
|
+
results.push(`Found ${ranked.length} matching result(s):
|
|
18366
18880
|
`);
|
|
18367
|
-
for (const
|
|
18368
|
-
|
|
18369
|
-
|
|
18370
|
-
|
|
18371
|
-
|
|
18372
|
-
results.push(`## ${title}
|
|
18373
|
-
Path: ${path}
|
|
18881
|
+
for (const result of ranked) {
|
|
18882
|
+
if (result.kind === "skill") {
|
|
18883
|
+
const body = (result.text ?? "").trim().slice(0, 400);
|
|
18884
|
+
results.push(`## ${result.title ?? result.key}
|
|
18885
|
+
Path: ${result.path}
|
|
18374
18886
|
|
|
18375
|
-
${body}${text
|
|
18887
|
+
${body}${(result.text?.length ?? 0) > 400 ? `
|
|
18376
18888
|
...(truncated)` : ""}
|
|
18377
18889
|
`);
|
|
18890
|
+
continue;
|
|
18891
|
+
}
|
|
18892
|
+
results.push(`- kind: ${result.kind}`, ` key: ${result.key}`, ` score: ${result.score.toFixed(2)}`, ...result.title ? [` title: ${result.title}`] : [], ` description: ${result.description || "(none)"}`, ...result.date ? [` date: ${result.date}`] : [], ...result.path ? [` path: ${result.path}`, ` Path: ${result.path}`] : [], ...result.text ? [
|
|
18893
|
+
` excerpt: ${result.text.replace(/^---[\s\S]*?---\n?/, "").replace(/^#.*$/m, "").trim().slice(0, 400)}`
|
|
18894
|
+
] : []);
|
|
18378
18895
|
}
|
|
18379
18896
|
}
|
|
18380
18897
|
return { content: [{ type: "text", text: results.join(`
|
|
@@ -18393,36 +18910,94 @@ function wikiTopicBrief(args) {
|
|
|
18393
18910
|
};
|
|
18394
18911
|
}
|
|
18395
18912
|
const { getTopicBrief, resolveTopicBrief } = runtime().host;
|
|
18396
|
-
|
|
18913
|
+
const hasAuthorizedBridge = Boolean(runtime().topicId && (getTopicBrief || resolveTopicBrief));
|
|
18914
|
+
if (hasAuthorizedBridge) {
|
|
18397
18915
|
try {
|
|
18398
18916
|
const brief = resolveTopicBrief ? resolveTopicBrief(rawTopic, runtime().userId) : getTopicBrief(rawTopic);
|
|
18399
|
-
if (
|
|
18400
|
-
|
|
18401
|
-
|
|
18402
|
-
|
|
18403
|
-
|
|
18404
|
-
|
|
18405
|
-
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
18409
|
-
|
|
18410
|
-
|
|
18411
|
-
|
|
18412
|
-
if (brief.summaryDate) {
|
|
18413
|
-
lines.push("", `Summary date: ${brief.summaryDate}`);
|
|
18414
|
-
}
|
|
18415
|
-
return { content: [{ type: "text", text: lines.join(`
|
|
18917
|
+
if (brief) {
|
|
18918
|
+
const lines = [
|
|
18919
|
+
`# Topic Brief: ${topicNameFrom(rawTopic)}`,
|
|
18920
|
+
"",
|
|
18921
|
+
brief.briefMd || "(empty brief)"
|
|
18922
|
+
];
|
|
18923
|
+
if (brief.latestSummaryMd) {
|
|
18924
|
+
lines.push("", "## Latest Summary", "", brief.latestSummaryMd);
|
|
18925
|
+
}
|
|
18926
|
+
if (brief.summaryDate) {
|
|
18927
|
+
lines.push("", `Summary date: ${brief.summaryDate}`);
|
|
18928
|
+
}
|
|
18929
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
18416
18930
|
`) }] };
|
|
18417
|
-
|
|
18931
|
+
}
|
|
18932
|
+
} catch {}
|
|
18933
|
+
return {
|
|
18934
|
+
content: [{ type: "text", text: `No brief found for topic: ${rawTopic}` }]
|
|
18935
|
+
};
|
|
18936
|
+
}
|
|
18937
|
+
const topicKey = wikiSummarySlug(rawTopic.replace(/^topic\//, "").replace(/\.md$/i, ""));
|
|
18938
|
+
const filePath = resolve2(runtime().topicsDir, `${topicKey}.md`);
|
|
18939
|
+
try {
|
|
18940
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
18941
|
+
return {
|
|
18942
|
+
content: [{ type: "text", text: `# Topic Brief: ${topicKey}
|
|
18943
|
+
|
|
18944
|
+
${content}` }]
|
|
18945
|
+
};
|
|
18946
|
+
} catch {}
|
|
18947
|
+
return {
|
|
18948
|
+
content: [{ type: "text", text: `No brief found for topic: ${rawTopic}` }]
|
|
18949
|
+
};
|
|
18950
|
+
}
|
|
18951
|
+
function wikiRead(args) {
|
|
18952
|
+
const rawKind = String(safeExt(args, ["kind", "type"], "article"));
|
|
18953
|
+
const kind = rawKind === "topic" || rawKind === "summary" ? rawKind : "article";
|
|
18954
|
+
const rawKey = String(safeExt(args, ["key", "slug", "topic"], "")).trim();
|
|
18955
|
+
if (!rawKey) {
|
|
18956
|
+
return { content: [{ type: "text", text: "A wiki document key is required." }], isError: true };
|
|
18957
|
+
}
|
|
18958
|
+
if (kind === "topic") {
|
|
18959
|
+
const result = wikiTopicBrief({ topic: rawKey });
|
|
18960
|
+
const resultText = result.content.map((entry) => entry.type === "text" ? entry.text : "").join(`
|
|
18961
|
+
`);
|
|
18962
|
+
const adopt = safeExt(args, ["adopt", "use", "select"], false);
|
|
18963
|
+
if (!adopt || resultText.startsWith("No brief found for topic"))
|
|
18964
|
+
return result;
|
|
18965
|
+
const currentTopicId = runtime().currentTopicId;
|
|
18966
|
+
const memoryKey = wikiSummarySlug(rawKey.replace(/^topic\//, "").replace(/\.md$/i, ""));
|
|
18967
|
+
if (!currentTopicId || !runtime().host.adoptTopicMemory?.(currentTopicId, runtime().userId, memoryKey)) {
|
|
18418
18968
|
return {
|
|
18419
|
-
content: [
|
|
18969
|
+
content: [
|
|
18970
|
+
...result.content,
|
|
18971
|
+
{ type: "text", text: `
|
|
18972
|
+
Memory was read but could not be adopted by this topic.` }
|
|
18973
|
+
]
|
|
18420
18974
|
};
|
|
18421
18975
|
}
|
|
18976
|
+
return {
|
|
18977
|
+
content: [...result.content, { type: "text", text: `
|
|
18978
|
+
Adopted topic memory: ${memoryKey}` }]
|
|
18979
|
+
};
|
|
18980
|
+
}
|
|
18981
|
+
if (rawKey.includes("\\") || rawKey.split("/").some((part) => part === "..") || rawKey.startsWith("/")) {
|
|
18982
|
+
return { content: [{ type: "text", text: "Invalid wiki document key." }], isError: true };
|
|
18983
|
+
}
|
|
18984
|
+
const root = kind === "summary" ? runtime().summariesDir : runtime().articlesDir;
|
|
18985
|
+
const key = rawKey.replace(new RegExp(`^(?:${kind === "summary" ? "summaries" : "articles"})/`), "").replace(/\.md$/i, "");
|
|
18986
|
+
const filePath = resolve2(root, `${key}.md`);
|
|
18987
|
+
const relativePath = relative(root, filePath);
|
|
18988
|
+
if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
|
|
18989
|
+
return { content: [{ type: "text", text: "Invalid wiki document key." }], isError: true };
|
|
18990
|
+
}
|
|
18991
|
+
try {
|
|
18992
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
18993
|
+
return {
|
|
18994
|
+
content: [{ type: "text", text: `# Wiki ${kind}: ${key}
|
|
18995
|
+
|
|
18996
|
+
${content}` }]
|
|
18997
|
+
};
|
|
18998
|
+
} catch {
|
|
18999
|
+
return { content: [{ type: "text", text: `No ${kind} found for: ${rawKey}` }] };
|
|
18422
19000
|
}
|
|
18423
|
-
return {
|
|
18424
|
-
content: [{ type: "text", text: `No topic brief found for: ${rawTopic}` }]
|
|
18425
|
-
};
|
|
18426
19001
|
}
|
|
18427
19002
|
function wikiLastConversation(args) {
|
|
18428
19003
|
const rawTopic = safeExt(args, ["topic", "topicName", "topicId"], runtime().topicId);
|
|
@@ -18789,7 +19364,7 @@ function indexUpsert(args) {
|
|
|
18789
19364
|
var WIKI_TOOLS = [
|
|
18790
19365
|
{
|
|
18791
19366
|
name: "wiki_query",
|
|
18792
|
-
description: "Search
|
|
19367
|
+
description: "Search topic, article, or summary indexes and return relevant candidates.",
|
|
18793
19368
|
inputSchema: {
|
|
18794
19369
|
type: "object",
|
|
18795
19370
|
properties: {
|
|
@@ -18800,11 +19375,38 @@ var WIKI_TOOLS = [
|
|
|
18800
19375
|
topic: {
|
|
18801
19376
|
type: "string",
|
|
18802
19377
|
description: "Optional topic name to narrow the search"
|
|
19378
|
+
},
|
|
19379
|
+
kind: {
|
|
19380
|
+
type: "string",
|
|
19381
|
+
enum: ["all", "topic", "article", "summary"],
|
|
19382
|
+
description: "Document kind to search; defaults to all"
|
|
19383
|
+
},
|
|
19384
|
+
limit: {
|
|
19385
|
+
type: "number",
|
|
19386
|
+
minimum: 1,
|
|
19387
|
+
maximum: 20,
|
|
19388
|
+
description: "Maximum number of candidates; defaults to 8"
|
|
18803
19389
|
}
|
|
18804
19390
|
},
|
|
18805
19391
|
required: ["question"]
|
|
18806
19392
|
}
|
|
18807
19393
|
},
|
|
19394
|
+
{
|
|
19395
|
+
name: "wiki_read",
|
|
19396
|
+
description: "Read one topic, article, or summary returned by wiki_query. For a clearly matching continuing persona, set adopt=true when reading a topic.",
|
|
19397
|
+
inputSchema: {
|
|
19398
|
+
type: "object",
|
|
19399
|
+
properties: {
|
|
19400
|
+
kind: { type: "string", enum: ["topic", "article", "summary"] },
|
|
19401
|
+
key: { type: "string", description: "Canonical key returned by wiki_query" },
|
|
19402
|
+
adopt: {
|
|
19403
|
+
type: "boolean",
|
|
19404
|
+
description: "For kind=topic, persist this canonical memory key for the current room"
|
|
19405
|
+
}
|
|
19406
|
+
},
|
|
19407
|
+
required: ["kind", "key"]
|
|
19408
|
+
}
|
|
19409
|
+
},
|
|
18808
19410
|
{
|
|
18809
19411
|
name: "wiki_topic_brief",
|
|
18810
19412
|
description: "Get the lightweight topic brief for a specific topic.",
|
|
@@ -18937,6 +19539,7 @@ function createWikiMcpServer(context, host) {
|
|
|
18937
19539
|
const wikiDir = resolve2(host.wikiRoot);
|
|
18938
19540
|
const current = {
|
|
18939
19541
|
userId: context.userId,
|
|
19542
|
+
currentTopicId: context.currentTopicId ?? context.topicId,
|
|
18940
19543
|
topicId: context.topicId,
|
|
18941
19544
|
surface,
|
|
18942
19545
|
host,
|
|
@@ -18966,6 +19569,7 @@ function createWikiMcpServer(context, host) {
|
|
|
18966
19569
|
const { name, arguments: args } = request.params;
|
|
18967
19570
|
const handlers = {
|
|
18968
19571
|
wiki_query: wikiQuery,
|
|
19572
|
+
wiki_read: wikiRead,
|
|
18969
19573
|
wiki_topic_brief: wikiTopicBrief,
|
|
18970
19574
|
wiki_last_conversation: wikiLastConversation,
|
|
18971
19575
|
skill_query: skillQuery,
|
|
@@ -19907,4 +20511,4 @@ export {
|
|
|
19907
20511
|
createAgentHealthMcpServer
|
|
19908
20512
|
};
|
|
19909
20513
|
|
|
19910
|
-
//# debugId=
|
|
20514
|
+
//# debugId=AE60D4AF576C043E64756E2164756E21
|