@retaw106/mmc-pi 2.6.0 → 2.7.1
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/index.js +218 -48
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13915,6 +13915,39 @@ function keywordRelevance(query, content) {
|
|
|
13915
13915
|
const words = new Set(terms(cleanSearchText(content)));
|
|
13916
13916
|
return 0.8 * needles.filter((term) => words.has(term)).length / needles.length;
|
|
13917
13917
|
}
|
|
13918
|
+
var CJK_RUN = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+$/u;
|
|
13919
|
+
var CJK_RUN_SPLIT = /([\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+)/u;
|
|
13920
|
+
function hintTerms(text) {
|
|
13921
|
+
return [
|
|
13922
|
+
...new Set(terms(text).flatMap((term) => term.split(CJK_RUN_SPLIT)).filter((term) => term.length > 0 && !QUERY_STOP_WORDS.has(term)))
|
|
13923
|
+
];
|
|
13924
|
+
}
|
|
13925
|
+
function characterBigrams(run) {
|
|
13926
|
+
const characters = [...run];
|
|
13927
|
+
return characters.length === 1 ? characters : characters.slice(1).map((character, index) => characters[index] + character);
|
|
13928
|
+
}
|
|
13929
|
+
function hintKeywordRelevance(query, content) {
|
|
13930
|
+
const needles = hintTerms(query);
|
|
13931
|
+
if (needles.length === 0)
|
|
13932
|
+
return 0;
|
|
13933
|
+
const words = new Set(hintTerms(cleanSearchText(content)));
|
|
13934
|
+
const runs = [...words].filter((word) => CJK_RUN.test(word));
|
|
13935
|
+
const bigrams = new Set(runs.flatMap(characterBigrams));
|
|
13936
|
+
let matched = 0;
|
|
13937
|
+
for (const needle of needles) {
|
|
13938
|
+
if (!CJK_RUN.test(needle)) {
|
|
13939
|
+
matched += Number(words.has(needle));
|
|
13940
|
+
continue;
|
|
13941
|
+
}
|
|
13942
|
+
const grams = characterBigrams(needle);
|
|
13943
|
+
if ([...needle].length === 1) {
|
|
13944
|
+
matched += Number(runs.some((run) => run.includes(needle)));
|
|
13945
|
+
continue;
|
|
13946
|
+
}
|
|
13947
|
+
matched += Math.min(1, grams.filter((gram) => bigrams.has(gram)).length / Math.ceil(grams.length / 2));
|
|
13948
|
+
}
|
|
13949
|
+
return 0.8 * matched / needles.length;
|
|
13950
|
+
}
|
|
13918
13951
|
function matchedSearchSnippet(content, query, cap = 220) {
|
|
13919
13952
|
const text = cleanSearchText(content);
|
|
13920
13953
|
const needles = new Set(terms(query));
|
|
@@ -21080,7 +21113,7 @@ function registerStandalonePluginMarker(version) {
|
|
|
21080
21113
|
// package.json
|
|
21081
21114
|
var package_default = {
|
|
21082
21115
|
name: "@retaw106/mmc-pi",
|
|
21083
|
-
version: "2.
|
|
21116
|
+
version: "2.7.1",
|
|
21084
21117
|
type: "module",
|
|
21085
21118
|
description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
|
|
21086
21119
|
main: "dist/index.js",
|
|
@@ -29681,6 +29714,38 @@ function buildToolTarget(part, message, tagId) {
|
|
|
29681
29714
|
};
|
|
29682
29715
|
}
|
|
29683
29716
|
|
|
29717
|
+
// ../plugin/src/features/magic-context/auto-search-diagnostics.ts
|
|
29718
|
+
var memoryHintContent = new WeakMap;
|
|
29719
|
+
function withMemoryHintContent(result, fullContent) {
|
|
29720
|
+
memoryHintContent.set(result, fullContent);
|
|
29721
|
+
return result;
|
|
29722
|
+
}
|
|
29723
|
+
function createAutoSearchDiagnostics(threshold) {
|
|
29724
|
+
return {
|
|
29725
|
+
threshold,
|
|
29726
|
+
lanes: {},
|
|
29727
|
+
pool: "unknown",
|
|
29728
|
+
poolRows: null,
|
|
29729
|
+
peek: "not-run",
|
|
29730
|
+
workerStarted: false
|
|
29731
|
+
};
|
|
29732
|
+
}
|
|
29733
|
+
function autoSearchRelevance(query, result) {
|
|
29734
|
+
const content = result.source === "memory" ? memoryHintContent.get(result) ?? result.content : result.content;
|
|
29735
|
+
if (cleanSearchText(content).length === 0)
|
|
29736
|
+
return 0;
|
|
29737
|
+
return result.source === "message" || "matchType" in result ? hintKeywordRelevance(query, content) : result.score;
|
|
29738
|
+
}
|
|
29739
|
+
function recordAutoSearchLane(diagnostics, lane, query, results) {
|
|
29740
|
+
if (!diagnostics)
|
|
29741
|
+
return;
|
|
29742
|
+
const scores = results.map((result) => result.relevanceScore ?? autoSearchRelevance(query, result));
|
|
29743
|
+
diagnostics.lanes[lane] = {
|
|
29744
|
+
top: scores.length ? Math.max(...scores) : null,
|
|
29745
|
+
nearMisses: scores.filter((score) => score >= 0.5 && score < diagnostics.threshold).length
|
|
29746
|
+
};
|
|
29747
|
+
}
|
|
29748
|
+
|
|
29684
29749
|
// ../plugin/src/features/magic-context/literal-probes.ts
|
|
29685
29750
|
var MAX_PROBES = 5;
|
|
29686
29751
|
var MIN_PROBE_LENGTH = 3;
|
|
@@ -30307,7 +30372,7 @@ function mergeMemoryResults(args) {
|
|
|
30307
30372
|
suppressedVisibleIds.push(id);
|
|
30308
30373
|
continue;
|
|
30309
30374
|
}
|
|
30310
|
-
results.push({
|
|
30375
|
+
results.push(withMemoryHintContent({
|
|
30311
30376
|
source: "memory",
|
|
30312
30377
|
content: previewText(memory.content),
|
|
30313
30378
|
score,
|
|
@@ -30315,7 +30380,7 @@ function mergeMemoryResults(args) {
|
|
|
30315
30380
|
category: memory.category,
|
|
30316
30381
|
matchType: "fts",
|
|
30317
30382
|
sourceName: args.sourceNameByMemoryId?.get(memory.id)
|
|
30318
|
-
});
|
|
30383
|
+
}, memory.content));
|
|
30319
30384
|
}
|
|
30320
30385
|
return {
|
|
30321
30386
|
results: results.sort((left, right) => {
|
|
@@ -30912,8 +30977,12 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
30912
30977
|
options.diagnostics.suppressedLiveMessageMatches = outcome.suppressedLiveMessageMatches;
|
|
30913
30978
|
}
|
|
30914
30979
|
messageResults = outcome.results.filter((result) => result.messageId !== options.excludeMessageId);
|
|
30980
|
+
recordAutoSearchLane(options.autoSearchDiagnostics, "messages", trimmedQuery, messageResults);
|
|
30981
|
+
options.onAutoSearchDiagnostics?.();
|
|
30915
30982
|
}
|
|
30916
30983
|
const workspace = resolveSearchWorkspaceContext(db, projectPath);
|
|
30984
|
+
if (options.autoSearchDiagnostics)
|
|
30985
|
+
options.autoSearchDiagnostics.pool = "off";
|
|
30917
30986
|
const [memoryOutcome, primerResults, noteResults] = await Promise.all([
|
|
30918
30987
|
runMemory ? searchMemories({
|
|
30919
30988
|
db,
|
|
@@ -30948,6 +31017,10 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
30948
31017
|
if (options.diagnostics) {
|
|
30949
31018
|
options.diagnostics.suppressedVisibleMemoryIds = memoryOutcome.suppressedVisibleIds;
|
|
30950
31019
|
}
|
|
31020
|
+
recordAutoSearchLane(options.autoSearchDiagnostics, "memory", trimmedQuery, memoryOutcome.results);
|
|
31021
|
+
recordAutoSearchLane(options.autoSearchDiagnostics, "primers", trimmedQuery, primerResults);
|
|
31022
|
+
recordAutoSearchLane(options.autoSearchDiagnostics, "notes", trimmedQuery, noteResults);
|
|
31023
|
+
options.onAutoSearchDiagnostics?.();
|
|
30951
31024
|
const results = [...memoryOutcome.results, ...primerResults, ...messageResults, ...noteResults].sort(compareUnifiedResults).slice(0, limit);
|
|
30952
31025
|
const countRetrievals = options.countRetrievals ?? true;
|
|
30953
31026
|
if (countRetrievals) {
|
|
@@ -30969,7 +31042,7 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
30969
31042
|
if (options.autoHint) {
|
|
30970
31043
|
return results.map((result) => ({
|
|
30971
31044
|
...result,
|
|
30972
|
-
relevanceScore:
|
|
31045
|
+
relevanceScore: autoSearchRelevance(trimmedQuery, result)
|
|
30973
31046
|
}));
|
|
30974
31047
|
}
|
|
30975
31048
|
return results;
|
|
@@ -31215,6 +31288,32 @@ function selectAutoSearchResults(results, threshold) {
|
|
|
31215
31288
|
return results.filter((result) => relevance(result) >= threshold).sort((left, right) => relevance(right) - relevance(left));
|
|
31216
31289
|
}
|
|
31217
31290
|
|
|
31291
|
+
// ../plugin/src/hooks/magic-context/auto-search-observation.ts
|
|
31292
|
+
var disabledTurns = new WeakMap;
|
|
31293
|
+
function logDisabledAutoSearch(db, sessionId, messageId, threshold, startedAt) {
|
|
31294
|
+
const sessions = disabledTurns.get(db) ?? new Map;
|
|
31295
|
+
disabledTurns.set(db, sessions);
|
|
31296
|
+
if (sessions.get(sessionId) === messageId)
|
|
31297
|
+
return;
|
|
31298
|
+
sessions.set(sessionId, messageId);
|
|
31299
|
+
observeAutoSearch(sessionId, threshold, startedAt).finish("disabled");
|
|
31300
|
+
}
|
|
31301
|
+
function observeAutoSearch(sessionId, threshold, startedAt) {
|
|
31302
|
+
const diagnostics = createAutoSearchDiagnostics(threshold);
|
|
31303
|
+
let logged = false;
|
|
31304
|
+
return {
|
|
31305
|
+
diagnostics,
|
|
31306
|
+
finish(outcome, reason) {
|
|
31307
|
+
if (logged)
|
|
31308
|
+
return;
|
|
31309
|
+
logged = true;
|
|
31310
|
+
const lanes = Object.entries(diagnostics.lanes).map(([lane, value]) => `${lane}:${value.top?.toFixed(3) ?? "none"}`).join(",");
|
|
31311
|
+
const nearMisses = Object.values(diagnostics.lanes).reduce((sum, lane) => sum + lane.nearMisses, 0);
|
|
31312
|
+
sessionLog(sessionId, `auto-search: decision outcome=${outcome}${reason ? ` reason=${reason}` : ""} lanes=${lanes || "none"} nearMisses=${nearMisses} threshold=${threshold} pool=${diagnostics.pool} rows=${diagnostics.poolRows ?? "unknown"} workerStarted=${diagnostics.workerStarted ?? "unknown"} peek=${diagnostics.peek} elapsedMs=${Math.round(performance.now() - startedAt)}`);
|
|
31313
|
+
}
|
|
31314
|
+
};
|
|
31315
|
+
}
|
|
31316
|
+
|
|
31218
31317
|
// src/auto-search-pi.ts
|
|
31219
31318
|
var DEFAULT_SCORE_THRESHOLD = 0.55;
|
|
31220
31319
|
var DEFAULT_MIN_PROMPT_CHARS = 20;
|
|
@@ -31351,8 +31450,6 @@ async function runAutoSearchHintForPi(args) {
|
|
|
31351
31450
|
const timestamp = message.timestamp;
|
|
31352
31451
|
return `test-entry-${index}:${typeof timestamp === "number" ? timestamp : "no-ts"}`;
|
|
31353
31452
|
}) : args.entryIds;
|
|
31354
|
-
if (!options.enabled)
|
|
31355
|
-
return messages;
|
|
31356
31453
|
const strictResolutionFailed = entryIds === null;
|
|
31357
31454
|
const effectiveEntryIds = strictResolutionFailed ? messages.map((message) => {
|
|
31358
31455
|
const id = message.id;
|
|
@@ -31362,6 +31459,11 @@ async function runAutoSearchHintForPi(args) {
|
|
|
31362
31459
|
if (found === null)
|
|
31363
31460
|
return messages;
|
|
31364
31461
|
const { message: userMsg, messageId: userMsgId } = found;
|
|
31462
|
+
if (!options.enabled) {
|
|
31463
|
+
if (found.index === messages.length - 1)
|
|
31464
|
+
logDisabledAutoSearch(db, sessionId, userMsgId, options.scoreThreshold ?? DEFAULT_SCORE_THRESHOLD, startedAt);
|
|
31465
|
+
return messages;
|
|
31466
|
+
}
|
|
31365
31467
|
const replayServedDecision = () => {
|
|
31366
31468
|
const decision = getAutoSearchHintDecisions(db, sessionId).find((entry) => entry.messageId === userMsgId);
|
|
31367
31469
|
if (decision?.decision === "hint") {
|
|
@@ -31390,21 +31492,24 @@ async function runAutoSearchHintForPi(args) {
|
|
|
31390
31492
|
return messages;
|
|
31391
31493
|
if (!isUsableProjectIdentity(options.projectPath))
|
|
31392
31494
|
return messages;
|
|
31495
|
+
const observation = observeAutoSearch(sessionId, options.scoreThreshold ?? DEFAULT_SCORE_THRESHOLD, startedAt);
|
|
31393
31496
|
const writeNoHintAndReconcile = (reason) => {
|
|
31394
31497
|
const outcome = appendAutoSearchHintDecision(db, sessionId, {
|
|
31395
31498
|
messageId: userMsgId,
|
|
31396
31499
|
decision: "no-hint",
|
|
31397
31500
|
reason
|
|
31398
31501
|
});
|
|
31399
|
-
if (!outcome.ok)
|
|
31502
|
+
if (!outcome.ok) {
|
|
31503
|
+
observation.finish("error", "cas-exhausted");
|
|
31400
31504
|
return;
|
|
31505
|
+
}
|
|
31401
31506
|
if (outcome.kind === "already-present" && outcome.decision.decision === "hint") {
|
|
31402
31507
|
appendHintToUserMessage(userMsg, outcome.decision.text);
|
|
31403
31508
|
}
|
|
31509
|
+
observation.finish(outcome.decision.decision === "hint" ? "attached" : reason === "below-threshold" ? "below-threshold" : reason === "error" ? "error" : "no-candidates", reason);
|
|
31404
31510
|
};
|
|
31405
31511
|
const rawPartsText = collectUserPromptParts(userMsg);
|
|
31406
31512
|
if (hasStackedAugmentation(rawPartsText)) {
|
|
31407
|
-
sessionLog(sessionId, "auto-search: skipping — user message already carries augmentation/hint");
|
|
31408
31513
|
writeNoHintAndReconcile("stacked");
|
|
31409
31514
|
return messages;
|
|
31410
31515
|
}
|
|
@@ -31417,6 +31522,7 @@ async function runAutoSearchHintForPi(args) {
|
|
|
31417
31522
|
let results;
|
|
31418
31523
|
try {
|
|
31419
31524
|
const searchOptions = {
|
|
31525
|
+
autoSearchDiagnostics: observation.diagnostics,
|
|
31420
31526
|
limit: 10,
|
|
31421
31527
|
autoHint: true,
|
|
31422
31528
|
maxMessageOrdinal: Math.max(0, getLastCompartmentEndMessage(db, sessionId)),
|
|
@@ -31432,13 +31538,13 @@ async function runAutoSearchHintForPi(args) {
|
|
|
31432
31538
|
countRetrievals: false
|
|
31433
31539
|
});
|
|
31434
31540
|
}, startedAt);
|
|
31435
|
-
} catch
|
|
31436
|
-
|
|
31541
|
+
} catch {
|
|
31542
|
+
observation.finish("error");
|
|
31437
31543
|
return messages;
|
|
31438
31544
|
}
|
|
31439
31545
|
if (results === null) {
|
|
31440
31546
|
persistAutoSearchSkip(db, sessionId, userMsgId, "timeout");
|
|
31441
|
-
|
|
31547
|
+
observation.finish("timeout");
|
|
31442
31548
|
return messages;
|
|
31443
31549
|
}
|
|
31444
31550
|
if (results.length === 0) {
|
|
@@ -31448,7 +31554,6 @@ async function runAutoSearchHintForPi(args) {
|
|
|
31448
31554
|
const scoreThreshold = options.scoreThreshold ?? DEFAULT_SCORE_THRESHOLD;
|
|
31449
31555
|
results = selectAutoSearchResults(results, scoreThreshold);
|
|
31450
31556
|
if (results.length === 0) {
|
|
31451
|
-
sessionLog(sessionId, `auto-search: no fragment meets relevance threshold ${scoreThreshold}`);
|
|
31452
31557
|
writeNoHintAndReconcile("below-threshold");
|
|
31453
31558
|
return messages;
|
|
31454
31559
|
}
|
|
@@ -31468,12 +31573,14 @@ ${hintText}`;
|
|
|
31468
31573
|
decision: "hint",
|
|
31469
31574
|
text: payload
|
|
31470
31575
|
});
|
|
31471
|
-
if (!outcome.ok)
|
|
31576
|
+
if (!outcome.ok) {
|
|
31577
|
+
observation.finish("error", "cas-exhausted");
|
|
31472
31578
|
return messages;
|
|
31579
|
+
}
|
|
31473
31580
|
if (outcome.decision.decision === "hint") {
|
|
31474
31581
|
appendHintToUserMessage(userMsg, outcome.decision.text);
|
|
31475
31582
|
}
|
|
31476
|
-
|
|
31583
|
+
observation.finish(outcome.decision.decision === "hint" ? "attached" : "no-candidates");
|
|
31477
31584
|
return messages;
|
|
31478
31585
|
}, replayServedDecision));
|
|
31479
31586
|
}
|
|
@@ -42489,16 +42596,6 @@ function registerPiGuardedContext(pi, handler, options = {}) {
|
|
|
42489
42596
|
refused: false,
|
|
42490
42597
|
ended: false
|
|
42491
42598
|
});
|
|
42492
|
-
const lifecycle = new Map;
|
|
42493
|
-
receiptMaps.add(lifecycle);
|
|
42494
|
-
const lifecycleCounts = (key) => {
|
|
42495
|
-
let counts = lifecycle.get(key);
|
|
42496
|
-
if (!counts) {
|
|
42497
|
-
counts = { starts: 0, ends: 0 };
|
|
42498
|
-
lifecycle.set(key, counts);
|
|
42499
|
-
}
|
|
42500
|
-
return counts;
|
|
42501
|
-
};
|
|
42502
42599
|
const fenceEnabled = resolvePiHarnessKind() === "omp";
|
|
42503
42600
|
const unsendableNonces = new Map;
|
|
42504
42601
|
receiptMaps.add(unsendableNonces);
|
|
@@ -59480,9 +59577,9 @@ var FULL_PARAMETER_DESCRIPTIONS = {
|
|
|
59480
59577
|
},
|
|
59481
59578
|
mmc_memory: {
|
|
59482
59579
|
action: "write | update | archive | merge | get",
|
|
59483
|
-
content: "The memory text — one standalone fact (write, update, merge).",
|
|
59484
|
-
category: "Kind of fact
|
|
59485
|
-
ids: "Memory ids from <project-memory>: one for update, one or more for archive, two or more for merge, 1–20 for get.",
|
|
59580
|
+
content: "The memory text — one standalone fact (write, update, merge). Archive and get take an empty string.",
|
|
59581
|
+
category: "Kind of fact: the new fact's kind for write; on update/merge, the memory's current category to keep it. Archive and get ignore it.",
|
|
59582
|
+
ids: "Memory ids from <project-memory>: one for update, one or more for archive, two or more for merge, 1–20 for get. Write takes an empty list.",
|
|
59486
59583
|
reason: "Why it is being archived (optional)."
|
|
59487
59584
|
},
|
|
59488
59585
|
mmc_search: {
|
|
@@ -59514,9 +59611,9 @@ var LIGHT_PARAMETER_DESCRIPTIONS = {
|
|
|
59514
59611
|
},
|
|
59515
59612
|
mmc_memory: {
|
|
59516
59613
|
action: "write | update | archive | merge | get",
|
|
59517
|
-
content: "One standalone fact (write, update, merge).",
|
|
59518
|
-
category: "Kind of fact (
|
|
59519
|
-
ids: "Ids from <project-memory>: one for update, 1+ for archive, 2+ for merge, 1–20 for get.",
|
|
59614
|
+
content: "One standalone fact (write, update, merge). Archive and get: empty string.",
|
|
59615
|
+
category: "Kind of fact (write; on update/merge pass the current category to keep it). Archive and get ignore it.",
|
|
59616
|
+
ids: "Ids from <project-memory>: one for update, 1+ for archive, 2+ for merge, 1–20 for get. Write: empty list.",
|
|
59520
59617
|
reason: "Why it is archived (optional)."
|
|
59521
59618
|
},
|
|
59522
59619
|
mmc_search: {
|
|
@@ -59631,6 +59728,17 @@ function resolveCtxExpandMode(args, domain) {
|
|
|
59631
59728
|
|
|
59632
59729
|
// ../plugin/src/tools/ctx-expand/render.ts
|
|
59633
59730
|
import { createHash as createHash25 } from "node:crypto";
|
|
59731
|
+
function recoveryParts(sessionId, message, attachments) {
|
|
59732
|
+
return attachments ? [...message.parts, ...attachments(sessionId, message.id)] : message.parts;
|
|
59733
|
+
}
|
|
59734
|
+
function attachmentNotice(part) {
|
|
59735
|
+
const field = (value) => typeof value === "string" ? value.trim().replace(/[\r\n]+/g, " ") : "";
|
|
59736
|
+
const source = isRecord7(part.source) ? part.source : {};
|
|
59737
|
+
const location = [part.url, part.path, source.uri, source.url, source.path].map(field).find((value) => value && !/^(data|blob):/i.test(value));
|
|
59738
|
+
const name = field(part.filename) || field(part.name);
|
|
59739
|
+
const mime = field(part.mime) || field(part.mimeType) || field(part.mediaType);
|
|
59740
|
+
return `[file]${name ? ` ${name}` : ""}${mime ? ` (${mime})` : ""}` + `${location ? ` — stored location: ${location}` : ""}` + "; content not recovered. If needed, ask the user to attach it again.";
|
|
59741
|
+
}
|
|
59634
59742
|
function isRecord7(value) {
|
|
59635
59743
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
59636
59744
|
}
|
|
@@ -59737,8 +59845,8 @@ function renderPartPreview(part, expandTools) {
|
|
|
59737
59845
|
if (reasoning !== null)
|
|
59738
59846
|
return ` • [reasoning] ${truncate2(reasoning, 120)}`;
|
|
59739
59847
|
const type = typeof part.type === "string" ? part.type : "part";
|
|
59740
|
-
if (type === "file")
|
|
59741
|
-
return
|
|
59848
|
+
if (type === "file" || type === "image")
|
|
59849
|
+
return ` • ${attachmentNotice(part)}`;
|
|
59742
59850
|
if (type === "step-start" || type === "step-finish")
|
|
59743
59851
|
return null;
|
|
59744
59852
|
return ` • [${type}]`;
|
|
@@ -59768,13 +59876,11 @@ ${tool.output}`);
|
|
|
59768
59876
|
`);
|
|
59769
59877
|
}
|
|
59770
59878
|
const type = typeof part.type === "string" ? part.type : "part";
|
|
59771
|
-
if (type === "file")
|
|
59772
|
-
|
|
59773
|
-
return ` [file]${name ? ` ${name}` : ""}`;
|
|
59774
|
-
}
|
|
59879
|
+
if (type === "file" || type === "image")
|
|
59880
|
+
return ` ${attachmentNotice(part)}`;
|
|
59775
59881
|
return null;
|
|
59776
59882
|
}
|
|
59777
|
-
function renderMessageByOrdinal(sessionId, ordinal) {
|
|
59883
|
+
function renderMessageByOrdinal(sessionId, ordinal, attachments) {
|
|
59778
59884
|
const found = [];
|
|
59779
59885
|
visitRawSessionMessages2(sessionId, ordinal, ordinal, (m) => {
|
|
59780
59886
|
if (m.ordinal === ordinal)
|
|
@@ -59785,7 +59891,7 @@ function renderMessageByOrdinal(sessionId, ordinal) {
|
|
|
59785
59891
|
if (!msg) {
|
|
59786
59892
|
return `No message at ordinal ${ordinal} is currently in this session's stored history — ` + `it may have been removed by a session prune/revert, or the ordinal may be wrong. ` + `Re-run the tool if you still need the data.`;
|
|
59787
59893
|
}
|
|
59788
|
-
const rendered = msg.
|
|
59894
|
+
const rendered = recoveryParts(sessionId, msg, attachments).map(renderPartFull).filter((l) => l !== null);
|
|
59789
59895
|
const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
|
|
59790
59896
|
if (rendered.length === 0) {
|
|
59791
59897
|
lines.push(" (no recoverable content — message had only structural/reasoning parts)");
|
|
@@ -59795,14 +59901,14 @@ function renderMessageByOrdinal(sessionId, ordinal) {
|
|
|
59795
59901
|
return lines.join(`
|
|
59796
59902
|
`);
|
|
59797
59903
|
}
|
|
59798
|
-
function renderVerboseRange(sessionId, start, end, tokenBudget, expandTools) {
|
|
59904
|
+
function renderVerboseRange(sessionId, start, end, tokenBudget, expandTools, attachments) {
|
|
59799
59905
|
const out = [];
|
|
59800
59906
|
let usedTokens = 0;
|
|
59801
59907
|
let lastOrdinal = start - 1;
|
|
59802
59908
|
let truncated = false;
|
|
59803
59909
|
visitRawSessionMessages2(sessionId, start, end, (msg) => {
|
|
59804
59910
|
const header = `[${msg.ordinal}] ${verboseRoleLabel(msg)}`;
|
|
59805
|
-
const partLines = msg.
|
|
59911
|
+
const partLines = recoveryParts(sessionId, msg, attachments).map((part) => renderPartPreview(part, expandTools)).filter((l) => l !== null);
|
|
59806
59912
|
const block = partLines.length > 0 ? `${header}
|
|
59807
59913
|
${partLines.join(`
|
|
59808
59914
|
`)}` : header;
|
|
@@ -59820,7 +59926,7 @@ ${partLines.join(`
|
|
|
59820
59926
|
|
|
59821
59927
|
`), lastOrdinal, truncated };
|
|
59822
59928
|
}
|
|
59823
|
-
function renderItemByTag(db, sessionId, number, textIndexDomain = "part") {
|
|
59929
|
+
function renderItemByTag(db, sessionId, number, textIndexDomain = "part", attachments) {
|
|
59824
59930
|
const tag = getTagById(db, sessionId, number);
|
|
59825
59931
|
if (!tag)
|
|
59826
59932
|
return `no tag ${number} in this session; if ${number} came from a <session-history> heading or a ctx_search hit, it is an ordinal: use message=${number}`;
|
|
@@ -59845,17 +59951,19 @@ function renderItemByTag(db, sessionId, number, textIndexDomain = "part") {
|
|
|
59845
59951
|
return rendered.length ? rendered.join(`
|
|
59846
59952
|
`) : `Tag ${number}'s original tool call is no longer in stored history.`;
|
|
59847
59953
|
}
|
|
59848
|
-
const scoped = /^(.*):p(\d+)$/.exec(tag.messageId);
|
|
59954
|
+
const scoped = /^(.*):(?:p|file)(\d+)$/.exec(tag.messageId);
|
|
59849
59955
|
const derived = /^(.*):mc-text-v1:([a-f0-9]+):([a-f0-9]+):o(\d+)$/.exec(tag.messageId);
|
|
59850
59956
|
const owner = scoped?.[1] ?? derived?.[1] ?? tag.messageId;
|
|
59851
59957
|
const message = readRawSessionMessageById2(sessionId, owner);
|
|
59852
59958
|
if (!message)
|
|
59853
59959
|
return `Tag ${number}'s original text is no longer in stored history.`;
|
|
59854
59960
|
const index = scoped ? Number(scoped[2]) : 0;
|
|
59855
|
-
const
|
|
59856
|
-
const
|
|
59961
|
+
const attached = attachments?.(sessionId, message.id) ?? [];
|
|
59962
|
+
const parts = [...message.parts, ...attached];
|
|
59963
|
+
const piText = parts.filter((part) => isRecord7(part) && part.type === "text");
|
|
59964
|
+
const part = tag.type === "file" ? attached.find((part) => isRecord7(part) && part.partIndex === index) ?? message.parts[index] : parts[index];
|
|
59857
59965
|
const matching = derived ? piText.filter((part) => isRecord7(part) && typeof part.text === "string" && createHash25("sha256").update(part.text).digest("hex") === derived[3]) : [];
|
|
59858
|
-
const selected = derived ? matching[Number(derived[4])] : textIndexDomain === "text" ? piText[index] : part;
|
|
59966
|
+
const selected = derived ? matching[Number(derived[4])] : textIndexDomain === "text" && tag.type !== "file" ? piText[index] : part;
|
|
59859
59967
|
if (!selected)
|
|
59860
59968
|
return `Tag ${number}'s original text part is no longer in stored history.`;
|
|
59861
59969
|
const renderedPart = renderPartFull(selected);
|
|
@@ -60298,6 +60406,34 @@ var integerKeyPattern = new RegExp("^(?:0|[1-9][0-9]*)$");
|
|
|
60298
60406
|
|
|
60299
60407
|
// ../../node_modules/.bun/typebox@1.3.27/node_modules/typebox/build/type/engine/indexed/from_object.mjs
|
|
60300
60408
|
var NumericKeyPattern = new RegExp(IntegerKey);
|
|
60409
|
+
// src/tools/expand-attachments.ts
|
|
60410
|
+
function piRecoveryAttachments(entries) {
|
|
60411
|
+
return (_sessionId, messageId) => {
|
|
60412
|
+
const entry = entries.find((entry) => entry !== null && typeof entry === "object" && entry.id === messageId);
|
|
60413
|
+
if (entry?.message?.role !== "user" || !Array.isArray(entry.message.content))
|
|
60414
|
+
return [];
|
|
60415
|
+
return entry.message.content.flatMap((part, partIndex) => {
|
|
60416
|
+
if (!part || part.type !== "image" && part.type !== "file")
|
|
60417
|
+
return [];
|
|
60418
|
+
return [
|
|
60419
|
+
{
|
|
60420
|
+
type: part.type,
|
|
60421
|
+
filename: part.filename ?? part.name,
|
|
60422
|
+
mime: part.mimeType ?? part.mime,
|
|
60423
|
+
url: [
|
|
60424
|
+
part.url,
|
|
60425
|
+
part.source?.uri,
|
|
60426
|
+
part.source?.url,
|
|
60427
|
+
part.providerFile?.uri
|
|
60428
|
+
].find((value) => typeof value === "string" && value.trim() && !/^(data|blob):/i.test(value.trim())),
|
|
60429
|
+
path: part.path ?? part.source?.path,
|
|
60430
|
+
partIndex
|
|
60431
|
+
}
|
|
60432
|
+
];
|
|
60433
|
+
});
|
|
60434
|
+
};
|
|
60435
|
+
}
|
|
60436
|
+
|
|
60301
60437
|
// src/tools/ctx-expand.ts
|
|
60302
60438
|
var ParamsSchema = _Object_({
|
|
60303
60439
|
tag: Optional(Union([Number2(), String2()], {
|
|
@@ -60347,12 +60483,13 @@ function createCtxExpandTool(deps) {
|
|
|
60347
60483
|
readMessages: () => readPiSessionMessages(ctx, piRawOrdinalOffsetSource(deps.db, sessionId))
|
|
60348
60484
|
});
|
|
60349
60485
|
try {
|
|
60486
|
+
const attachments = piRecoveryAttachments(ctx.sessionManager.getBranch());
|
|
60350
60487
|
const mode = resolveCtxExpandMode(params, "positive");
|
|
60351
60488
|
if (mode.kind === "error") {
|
|
60352
60489
|
return err(mode.message);
|
|
60353
60490
|
}
|
|
60354
60491
|
if (mode.kind === "tag") {
|
|
60355
|
-
return ok(renderItemByTag(deps.db, sessionId, mode.tag, "text"));
|
|
60492
|
+
return ok(renderItemByTag(deps.db, sessionId, mode.tag, "text", attachments));
|
|
60356
60493
|
}
|
|
60357
60494
|
const alignment = resolvePiOrdinalAlignmentForContext(deps.db, sessionId, ctx);
|
|
60358
60495
|
if (isPiOrdinalAlignmentUnanchored(alignment)) {
|
|
@@ -60370,7 +60507,7 @@ function createCtxExpandTool(deps) {
|
|
|
60370
60507
|
if (mode.message <= lostThrough) {
|
|
60371
60508
|
return ok(`No message at ordinal ${mode.message}: it is no longer in this session's history, which now starts at message ${firstAvailable}.`);
|
|
60372
60509
|
}
|
|
60373
|
-
return ok(renderMessageByOrdinal(sessionId, mode.message));
|
|
60510
|
+
return ok(renderMessageByOrdinal(sessionId, mode.message, attachments));
|
|
60374
60511
|
}
|
|
60375
60512
|
const { end, verbose } = mode;
|
|
60376
60513
|
const requestedStart = mode.start;
|
|
@@ -60390,7 +60527,7 @@ function createCtxExpandTool(deps) {
|
|
|
60390
60527
|
|
|
60391
60528
|
` : "";
|
|
60392
60529
|
if (verbose) {
|
|
60393
|
-
const v = renderVerboseRange(sessionId, start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET, deps.expandTools);
|
|
60530
|
+
const v = renderVerboseRange(sessionId, start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET, deps.expandTools, attachments);
|
|
60394
60531
|
if (!v.text) {
|
|
60395
60532
|
return ok(`No messages found in range ${start}-${effectiveEnd}. The range may be outside this session's history.`);
|
|
60396
60533
|
}
|
|
@@ -60480,6 +60617,20 @@ var ParamsShape = {
|
|
|
60480
60617
|
var PrimaryParamsSchema = _Object_(ParamsShape, {
|
|
60481
60618
|
additionalProperties: true
|
|
60482
60619
|
});
|
|
60620
|
+
function rejectInvalidMemoryArguments(args) {
|
|
60621
|
+
if (args === null || typeof args !== "object")
|
|
60622
|
+
return;
|
|
60623
|
+
const record = args;
|
|
60624
|
+
if (record.content !== undefined && typeof record.content !== "string") {
|
|
60625
|
+
throw new Error("Error: 'content' must be a string.");
|
|
60626
|
+
}
|
|
60627
|
+
if (record.reason !== undefined && typeof record.reason !== "string") {
|
|
60628
|
+
throw new Error("Error: 'reason' must be a string.");
|
|
60629
|
+
}
|
|
60630
|
+
if (record.ids !== undefined && (!Array.isArray(record.ids) || record.ids.some((id) => typeof id !== "number" || !Number.isSafeInteger(id) || id < 1))) {
|
|
60631
|
+
throw new Error("Error: 'ids' must be an array of positive memory IDs.");
|
|
60632
|
+
}
|
|
60633
|
+
}
|
|
60483
60634
|
function ok2(text) {
|
|
60484
60635
|
return { content: [{ type: "text", text }], details: undefined };
|
|
60485
60636
|
}
|
|
@@ -60578,6 +60729,10 @@ function createCtxMemoryTool(deps) {
|
|
|
60578
60729
|
label: "Magic Context: Memory",
|
|
60579
60730
|
description: CTX_MEMORY_DESCRIPTION,
|
|
60580
60731
|
parameters: PrimaryParamsSchema,
|
|
60732
|
+
prepareArguments(args) {
|
|
60733
|
+
rejectInvalidMemoryArguments(args);
|
|
60734
|
+
return args;
|
|
60735
|
+
},
|
|
60581
60736
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
60582
60737
|
params = unwrapImitatedReducedArgs(params, ["action"], {
|
|
60583
60738
|
action: { type: "enum", values: PRIMARY_ACTIONS },
|
|
@@ -60965,6 +61120,17 @@ var ParamsSchema2 = _Object_({
|
|
|
60965
61120
|
description: "Skip this many newest rows (default 0)."
|
|
60966
61121
|
}))
|
|
60967
61122
|
}, { additionalProperties: true });
|
|
61123
|
+
function rejectInvalidNoteArguments(args) {
|
|
61124
|
+
if (args === null || typeof args !== "object")
|
|
61125
|
+
return;
|
|
61126
|
+
const record = args;
|
|
61127
|
+
if (record.content !== undefined && typeof record.content !== "string") {
|
|
61128
|
+
throw new Error("Error: 'content' must be a string.");
|
|
61129
|
+
}
|
|
61130
|
+
if (record.note_ids !== undefined && (!Array.isArray(record.note_ids) || record.note_ids.some((id) => typeof id !== "number" || !Number.isSafeInteger(id) || id < 1))) {
|
|
61131
|
+
throw new Error("Error: 'note_ids' must be an array of positive note IDs.");
|
|
61132
|
+
}
|
|
61133
|
+
}
|
|
60968
61134
|
function ok3(text) {
|
|
60969
61135
|
return { content: [{ type: "text", text }], details: undefined };
|
|
60970
61136
|
}
|
|
@@ -61021,6 +61187,10 @@ function createCtxNoteTool(deps) {
|
|
|
61021
61187
|
label: "Magic Context: Notes",
|
|
61022
61188
|
description: CTX_NOTE_DESCRIPTION,
|
|
61023
61189
|
parameters: ParamsSchema2,
|
|
61190
|
+
prepareArguments(args) {
|
|
61191
|
+
rejectInvalidNoteArguments(args);
|
|
61192
|
+
return args;
|
|
61193
|
+
},
|
|
61024
61194
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
61025
61195
|
params = unwrapImitatedReducedArgs(params, ["action", "content"], {
|
|
61026
61196
|
action: {
|
package/package.json
CHANGED