@cortexkit/pi-magic-context 0.31.3 → 0.31.5
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 +1064 -317
- package/dist/subagent-entry.js +657 -178
- package/package.json +1 -1
package/dist/subagent-entry.js
CHANGED
|
@@ -8198,6 +8198,7 @@ function healNullTextColumns(db) {
|
|
|
8198
8198
|
["cache_ttl", ""],
|
|
8199
8199
|
["last_nudge_band", ""],
|
|
8200
8200
|
["last_nudge_level", ""],
|
|
8201
|
+
["channel2_nudge_claim_token", ""],
|
|
8201
8202
|
["last_transform_error", ""],
|
|
8202
8203
|
["nudge_anchor_message_id", ""],
|
|
8203
8204
|
["nudge_anchor_text", ""],
|
|
@@ -8261,6 +8262,7 @@ function healNullIntegerColumns(db) {
|
|
|
8261
8262
|
}
|
|
8262
8263
|
|
|
8263
8264
|
// ../plugin/src/features/magic-context/workspaces.ts
|
|
8265
|
+
init_logger();
|
|
8264
8266
|
import { createHash as createHash2 } from "node:crypto";
|
|
8265
8267
|
|
|
8266
8268
|
// ../plugin/src/features/magic-context/memory/constants.ts
|
|
@@ -8615,6 +8617,7 @@ function storedPathBelongsToIdentity(storedProjectPath, projectIdentity) {
|
|
|
8615
8617
|
}
|
|
8616
8618
|
// ../plugin/src/features/magic-context/workspaces.ts
|
|
8617
8619
|
var VALID_SHARE_CATEGORIES = new Set(V2_MEMORY_CATEGORIES);
|
|
8620
|
+
var DEFAULT_WORKSPACE_SHARE_CATEGORIES = ["CONSTRAINTS"];
|
|
8618
8621
|
function tableExists(db, tableName) {
|
|
8619
8622
|
const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ? LIMIT 1").get(tableName);
|
|
8620
8623
|
return Boolean(row);
|
|
@@ -8629,23 +8632,39 @@ function uniqueSorted(values) {
|
|
|
8629
8632
|
function placeholders(values) {
|
|
8630
8633
|
return values.map(() => "?").join(", ");
|
|
8631
8634
|
}
|
|
8635
|
+
function defaultWorkspaceShareCategories() {
|
|
8636
|
+
return [...DEFAULT_WORKSPACE_SHARE_CATEGORIES];
|
|
8637
|
+
}
|
|
8638
|
+
function warnInvalidShareCategories(reason, raw) {
|
|
8639
|
+
log("[magic-context] WARN: invalid workspace share_categories; sharing no foreign memory categories", {
|
|
8640
|
+
reason,
|
|
8641
|
+
raw
|
|
8642
|
+
});
|
|
8643
|
+
}
|
|
8632
8644
|
function normalizeShareCategories(raw) {
|
|
8633
|
-
if (raw === null || raw === undefined)
|
|
8634
|
-
return
|
|
8635
|
-
|
|
8636
|
-
|
|
8645
|
+
if (raw === null || raw === undefined) {
|
|
8646
|
+
return defaultWorkspaceShareCategories();
|
|
8647
|
+
}
|
|
8648
|
+
if (typeof raw !== "string") {
|
|
8649
|
+
warnInvalidShareCategories("not a string", raw);
|
|
8650
|
+
return [];
|
|
8651
|
+
}
|
|
8637
8652
|
let parsed;
|
|
8638
8653
|
try {
|
|
8639
8654
|
parsed = JSON.parse(raw);
|
|
8640
8655
|
} catch {
|
|
8641
|
-
|
|
8656
|
+
warnInvalidShareCategories("malformed JSON", raw);
|
|
8657
|
+
return [];
|
|
8658
|
+
}
|
|
8659
|
+
if (!Array.isArray(parsed)) {
|
|
8660
|
+
warnInvalidShareCategories("not a JSON array", raw);
|
|
8661
|
+
return [];
|
|
8642
8662
|
}
|
|
8643
|
-
if (!Array.isArray(parsed))
|
|
8644
|
-
return null;
|
|
8645
8663
|
const categories = [];
|
|
8646
8664
|
for (const value of parsed) {
|
|
8647
8665
|
if (typeof value !== "string" || !VALID_SHARE_CATEGORIES.has(value)) {
|
|
8648
|
-
|
|
8666
|
+
warnInvalidShareCategories("unknown category", raw);
|
|
8667
|
+
return [];
|
|
8649
8668
|
}
|
|
8650
8669
|
if (!categories.includes(value))
|
|
8651
8670
|
categories.push(value);
|
|
@@ -8654,8 +8673,21 @@ function normalizeShareCategories(raw) {
|
|
|
8654
8673
|
}
|
|
8655
8674
|
function selectWorkspaceShareCategories(db, identities) {
|
|
8656
8675
|
const candidates = uniqueSorted(identities.filter((identity) => identity.length > 0));
|
|
8657
|
-
if (candidates.length === 0 || !tableExists(db, "workspace_members")
|
|
8676
|
+
if (candidates.length === 0 || !tableExists(db, "workspace_members")) {
|
|
8677
|
+
return null;
|
|
8678
|
+
}
|
|
8679
|
+
const hasMembership = Boolean(db.prepare(`SELECT 1
|
|
8680
|
+
FROM workspace_members
|
|
8681
|
+
WHERE project_path IN (${placeholders(candidates)})
|
|
8682
|
+
LIMIT 1`).get(...candidates));
|
|
8683
|
+
if (!hasMembership)
|
|
8658
8684
|
return null;
|
|
8685
|
+
if (!tableExists(db, "workspaces")) {
|
|
8686
|
+
log("[magic-context] WARN: workspace member has no workspaces table; sharing no foreign memory categories");
|
|
8687
|
+
return [];
|
|
8688
|
+
}
|
|
8689
|
+
if (!columnExists(db, "workspaces", "share_categories")) {
|
|
8690
|
+
return defaultWorkspaceShareCategories();
|
|
8659
8691
|
}
|
|
8660
8692
|
const row = db.prepare(`SELECT workspace.share_categories AS shareCategories
|
|
8661
8693
|
FROM workspace_members AS member
|
|
@@ -8663,7 +8695,11 @@ function selectWorkspaceShareCategories(db, identities) {
|
|
|
8663
8695
|
WHERE member.project_path IN (${placeholders(candidates)})
|
|
8664
8696
|
ORDER BY workspace.id ASC
|
|
8665
8697
|
LIMIT 1`).get(...candidates);
|
|
8666
|
-
|
|
8698
|
+
if (!row) {
|
|
8699
|
+
log("[magic-context] WARN: workspace member has no workspace share_categories row; sharing no foreign memory categories");
|
|
8700
|
+
return [];
|
|
8701
|
+
}
|
|
8702
|
+
return normalizeShareCategories(row.shareCategories);
|
|
8667
8703
|
}
|
|
8668
8704
|
function resolveWorkspaceShareCategories(db, projectIdentity) {
|
|
8669
8705
|
return selectWorkspaceShareCategories(db, [projectIdentity]);
|
|
@@ -8768,7 +8804,7 @@ function computeWorkspaceEpochFingerprint(db, identities) {
|
|
|
8768
8804
|
const hash = createHash2("sha256");
|
|
8769
8805
|
hash.update("share_categories", "utf8");
|
|
8770
8806
|
hash.update("\x00");
|
|
8771
|
-
hash.update(shareCategories === null ? "
|
|
8807
|
+
hash.update(shareCategories === null ? "NO_WORKSPACE" : JSON.stringify(shareCategories), "utf8");
|
|
8772
8808
|
hash.update(`
|
|
8773
8809
|
`);
|
|
8774
8810
|
for (const identity of canonical) {
|
|
@@ -143735,6 +143771,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143735
143771
|
CREATE TABLE IF NOT EXISTS message_history_index (
|
|
143736
143772
|
session_id TEXT PRIMARY KEY,
|
|
143737
143773
|
last_indexed_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
143774
|
+
dirty_floor_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
143738
143775
|
updated_at INTEGER NOT NULL,
|
|
143739
143776
|
harness TEXT NOT NULL DEFAULT 'opencode'
|
|
143740
143777
|
);
|
|
@@ -143764,6 +143801,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143764
143801
|
last_nudge_level TEXT DEFAULT '',
|
|
143765
143802
|
channel2_nudge_state TEXT DEFAULT '',
|
|
143766
143803
|
channel2_nudge_claimed_at INTEGER DEFAULT 0,
|
|
143804
|
+
channel2_nudge_claim_token TEXT DEFAULT '',
|
|
143767
143805
|
last_emergency_input_sample INTEGER DEFAULT 0,
|
|
143768
143806
|
last_transform_error TEXT DEFAULT '',
|
|
143769
143807
|
nudge_anchor_message_id TEXT DEFAULT '',
|
|
@@ -144010,6 +144048,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
144010
144048
|
ensureColumn(db, "session_meta", "last_nudge_level", "TEXT DEFAULT ''");
|
|
144011
144049
|
ensureColumn(db, "session_meta", "channel2_nudge_state", "TEXT DEFAULT ''");
|
|
144012
144050
|
ensureColumn(db, "session_meta", "channel2_nudge_claimed_at", "INTEGER DEFAULT 0");
|
|
144051
|
+
ensureColumn(db, "session_meta", "channel2_nudge_claim_token", "TEXT DEFAULT ''");
|
|
144013
144052
|
ensureColumn(db, "session_meta", "last_emergency_input_sample", "INTEGER DEFAULT 0");
|
|
144014
144053
|
ensureColumn(db, "session_meta", "last_transform_error", "TEXT DEFAULT ''");
|
|
144015
144054
|
ensureColumn(db, "session_meta", "nudge_anchor_message_id", "TEXT DEFAULT ''");
|
|
@@ -144222,6 +144261,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
144222
144261
|
ON transform_decisions(session_id, harness);
|
|
144223
144262
|
`);
|
|
144224
144263
|
ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
144264
|
+
ensureColumn(db, "message_history_index", "dirty_floor_ordinal", "INTEGER NOT NULL DEFAULT 0");
|
|
144225
144265
|
ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
144226
144266
|
ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
144227
144267
|
ensureColumn(db, "compartments", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
@@ -144237,7 +144277,7 @@ var CHANNEL2_CLAIM_TTL_MS = 120000;
|
|
|
144237
144277
|
function healWedgedChannel2Claims(db) {
|
|
144238
144278
|
try {
|
|
144239
144279
|
const staleBefore = Date.now() - CHANNEL2_CLAIM_TTL_MS;
|
|
144240
|
-
db.prepare("UPDATE session_meta SET channel2_nudge_state = 'pending', channel2_nudge_claimed_at = 0 WHERE channel2_nudge_state = 'claimed' AND (channel2_nudge_claimed_at IS NULL OR channel2_nudge_claimed_at = 0 OR channel2_nudge_claimed_at <= ?)").run(staleBefore);
|
|
144280
|
+
db.prepare("UPDATE session_meta SET channel2_nudge_state = 'pending', channel2_nudge_claimed_at = 0, channel2_nudge_claim_token = '' WHERE channel2_nudge_state = 'claimed' AND (channel2_nudge_claimed_at IS NULL OR channel2_nudge_claimed_at = 0 OR channel2_nudge_claimed_at <= ?)").run(staleBefore);
|
|
144241
144281
|
} catch {}
|
|
144242
144282
|
}
|
|
144243
144283
|
function openDatabase(dbPathOrOptions) {
|
|
@@ -145054,92 +145094,6 @@ function migrateLegacyExperimental(rawConfig, warnings) {
|
|
|
145054
145094
|
return patched;
|
|
145055
145095
|
}
|
|
145056
145096
|
|
|
145057
|
-
// ../plugin/src/config/project-security.ts
|
|
145058
|
-
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
145059
|
-
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
145060
|
-
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider"];
|
|
145061
|
-
function isPlainObject2(value) {
|
|
145062
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
145063
|
-
}
|
|
145064
|
-
function stripUnsafeProjectConfigFields(projectRaw) {
|
|
145065
|
-
const warnings = [];
|
|
145066
|
-
if ("auto_update" in projectRaw) {
|
|
145067
|
-
delete projectRaw.auto_update;
|
|
145068
|
-
warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
|
|
145069
|
-
}
|
|
145070
|
-
if ("language" in projectRaw) {
|
|
145071
|
-
delete projectRaw.language;
|
|
145072
|
-
warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
|
|
145073
|
-
}
|
|
145074
|
-
if ("sqlite" in projectRaw) {
|
|
145075
|
-
delete projectRaw.sqlite;
|
|
145076
|
-
warnings.push("Ignoring sqlite.* from project config (security: SQLite cache/mmap PRAGMAs apply to the " + "process-global shared database handle; only user-level config may set them).");
|
|
145077
|
-
}
|
|
145078
|
-
const embedding = projectRaw.embedding;
|
|
145079
|
-
if (isPlainObject2(embedding)) {
|
|
145080
|
-
const removed = [];
|
|
145081
|
-
for (const field of EMBEDDING_DESTINATION_FIELDS) {
|
|
145082
|
-
if (field in embedding) {
|
|
145083
|
-
delete embedding[field];
|
|
145084
|
-
removed.push(field);
|
|
145085
|
-
}
|
|
145086
|
-
}
|
|
145087
|
-
if (removed.length > 0) {
|
|
145088
|
-
warnings.push(`Ignoring embedding.${removed.join("/")} from project config ` + "(security: a repository cannot choose where private text is embedded).");
|
|
145089
|
-
}
|
|
145090
|
-
}
|
|
145091
|
-
for (const agentKey of HIDDEN_AGENT_KEYS) {
|
|
145092
|
-
const block = projectRaw[agentKey];
|
|
145093
|
-
if (!isPlainObject2(block))
|
|
145094
|
-
continue;
|
|
145095
|
-
const removed = [];
|
|
145096
|
-
for (const field of AGENT_ESCALATION_FIELDS) {
|
|
145097
|
-
if (field in block) {
|
|
145098
|
-
delete block[field];
|
|
145099
|
-
removed.push(field);
|
|
145100
|
-
}
|
|
145101
|
-
}
|
|
145102
|
-
if (removed.length > 0) {
|
|
145103
|
-
warnings.push(`Ignoring ${agentKey}.${removed.join("/")} from project config ` + "(security: a repository cannot reprogram or re-permission hidden agents).");
|
|
145104
|
-
}
|
|
145105
|
-
}
|
|
145106
|
-
return warnings;
|
|
145107
|
-
}
|
|
145108
|
-
function normalizeEndpoint(value) {
|
|
145109
|
-
if (typeof value !== "string")
|
|
145110
|
-
return;
|
|
145111
|
-
const trimmed = value.trim().replace(/\/+$/, "");
|
|
145112
|
-
return trimmed.length > 0 ? trimmed.toLowerCase() : undefined;
|
|
145113
|
-
}
|
|
145114
|
-
function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
145115
|
-
const projectEmbedding = projectRaw.embedding;
|
|
145116
|
-
if (!isPlainObject2(projectEmbedding))
|
|
145117
|
-
return [];
|
|
145118
|
-
const redirectsEndpoint = "endpoint" in projectEmbedding;
|
|
145119
|
-
if (!redirectsEndpoint)
|
|
145120
|
-
return [];
|
|
145121
|
-
const userEmbedding = userRaw?.embedding;
|
|
145122
|
-
if (isPlainObject2(userEmbedding)) {
|
|
145123
|
-
const projectEndpoint = normalizeEndpoint(projectEmbedding.endpoint);
|
|
145124
|
-
const userEndpoint = normalizeEndpoint(userEmbedding.endpoint);
|
|
145125
|
-
if (projectEndpoint !== undefined && projectEndpoint === userEndpoint) {
|
|
145126
|
-
return [];
|
|
145127
|
-
}
|
|
145128
|
-
}
|
|
145129
|
-
const providesOwnKey = typeof projectEmbedding.api_key === "string" && projectEmbedding.api_key.length > 0;
|
|
145130
|
-
if (providesOwnKey)
|
|
145131
|
-
return [];
|
|
145132
|
-
const mergedEmbedding = mergedRaw.embedding;
|
|
145133
|
-
if (!isPlainObject2(mergedEmbedding))
|
|
145134
|
-
return [];
|
|
145135
|
-
if (!("api_key" in mergedEmbedding))
|
|
145136
|
-
return [];
|
|
145137
|
-
delete mergedEmbedding.api_key;
|
|
145138
|
-
return [
|
|
145139
|
-
"Dropped inherited user embedding api_key because project config redirected " + "embedding.endpoint without supplying its own key (security: prevents key " + "exfiltration to a repository-chosen endpoint)."
|
|
145140
|
-
];
|
|
145141
|
-
}
|
|
145142
|
-
|
|
145143
145097
|
// ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
145144
145098
|
var exports_external = {};
|
|
145145
145099
|
__export(exports_external, {
|
|
@@ -145771,7 +145725,7 @@ __export(exports_util, {
|
|
|
145771
145725
|
jsonStringifyReplacer: () => jsonStringifyReplacer,
|
|
145772
145726
|
joinValues: () => joinValues,
|
|
145773
145727
|
issue: () => issue,
|
|
145774
|
-
isPlainObject: () =>
|
|
145728
|
+
isPlainObject: () => isPlainObject2,
|
|
145775
145729
|
isObject: () => isObject,
|
|
145776
145730
|
hexToUint8Array: () => hexToUint8Array,
|
|
145777
145731
|
getSizableOrigin: () => getSizableOrigin,
|
|
@@ -145953,7 +145907,7 @@ var allowsEval = /* @__PURE__ */ cached(() => {
|
|
|
145953
145907
|
return false;
|
|
145954
145908
|
}
|
|
145955
145909
|
});
|
|
145956
|
-
function
|
|
145910
|
+
function isPlainObject2(o) {
|
|
145957
145911
|
if (isObject(o) === false)
|
|
145958
145912
|
return false;
|
|
145959
145913
|
const ctor = o.constructor;
|
|
@@ -145970,7 +145924,7 @@ function isPlainObject3(o) {
|
|
|
145970
145924
|
return true;
|
|
145971
145925
|
}
|
|
145972
145926
|
function shallowClone(o) {
|
|
145973
|
-
if (
|
|
145927
|
+
if (isPlainObject2(o))
|
|
145974
145928
|
return { ...o };
|
|
145975
145929
|
if (Array.isArray(o))
|
|
145976
145930
|
return [...o];
|
|
@@ -146174,7 +146128,7 @@ function omit(schema, mask) {
|
|
|
146174
146128
|
return clone(schema, def);
|
|
146175
146129
|
}
|
|
146176
146130
|
function extend(schema, shape) {
|
|
146177
|
-
if (!
|
|
146131
|
+
if (!isPlainObject2(shape)) {
|
|
146178
146132
|
throw new Error("Invalid input to extend: expected a plain object");
|
|
146179
146133
|
}
|
|
146180
146134
|
const checks = schema._zod.def.checks;
|
|
@@ -146197,7 +146151,7 @@ function extend(schema, shape) {
|
|
|
146197
146151
|
return clone(schema, def);
|
|
146198
146152
|
}
|
|
146199
146153
|
function safeExtend(schema, shape) {
|
|
146200
|
-
if (!
|
|
146154
|
+
if (!isPlainObject2(shape)) {
|
|
146201
146155
|
throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
146202
146156
|
}
|
|
146203
146157
|
const def = mergeDefs(schema._zod.def, {
|
|
@@ -148540,7 +148494,7 @@ function mergeValues(a, b) {
|
|
|
148540
148494
|
if (a instanceof Date && b instanceof Date && +a === +b) {
|
|
148541
148495
|
return { valid: true, data: a };
|
|
148542
148496
|
}
|
|
148543
|
-
if (
|
|
148497
|
+
if (isPlainObject2(a) && isPlainObject2(b)) {
|
|
148544
148498
|
const bKeys = Object.keys(b);
|
|
148545
148499
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
148546
148500
|
const newObj = { ...a, ...b };
|
|
@@ -148726,7 +148680,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
148726
148680
|
$ZodType.init(inst, def);
|
|
148727
148681
|
inst._zod.parse = (payload, ctx) => {
|
|
148728
148682
|
const input = payload.value;
|
|
148729
|
-
if (!
|
|
148683
|
+
if (!isPlainObject2(input)) {
|
|
148730
148684
|
payload.issues.push({
|
|
148731
148685
|
expected: "record",
|
|
148732
148686
|
code: "invalid_type",
|
|
@@ -159937,6 +159891,295 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
159937
159891
|
};
|
|
159938
159892
|
});
|
|
159939
159893
|
|
|
159894
|
+
// ../plugin/src/config/project-security.ts
|
|
159895
|
+
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
159896
|
+
var HISTORIAN_USER_ONLY_FIELDS = ["model", "fallback_models"];
|
|
159897
|
+
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
159898
|
+
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider"];
|
|
159899
|
+
var PERCENTAGE_THRESHOLD_REASON = "security: a repository may only raise compaction thresholds above the user's effective value; it cannot force earlier historian work or cloned-repo cost escalation.";
|
|
159900
|
+
var TOKEN_THRESHOLD_REASON = "security: a repository may only raise execute_threshold_tokens above the user's trusted token threshold; it cannot force earlier historian work or cloned-repo cost escalation.";
|
|
159901
|
+
var TOKEN_THRESHOLD_INTRODUCTION_REASON = "security: a repository cannot introduce a new execute_threshold_tokens override when the user has no trusted token threshold for that key; that could force earlier historian work or cloned-repo cost escalation.";
|
|
159902
|
+
function isPlainObject3(value) {
|
|
159903
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
159904
|
+
}
|
|
159905
|
+
function isValidPercentageThreshold(value) {
|
|
159906
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 20 && value <= 80;
|
|
159907
|
+
}
|
|
159908
|
+
function isValidTokenThreshold(value) {
|
|
159909
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 5000 && value <= 2000000;
|
|
159910
|
+
}
|
|
159911
|
+
function normalizeTrustedPercentageThresholds(value) {
|
|
159912
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
159913
|
+
return { defaultValue: value, overrides: new Map };
|
|
159914
|
+
}
|
|
159915
|
+
if (isPlainObject3(value) && typeof value.default === "number" && Number.isFinite(value.default)) {
|
|
159916
|
+
const overrides = new Map;
|
|
159917
|
+
for (const [key, child] of Object.entries(value)) {
|
|
159918
|
+
if (key === "default")
|
|
159919
|
+
continue;
|
|
159920
|
+
if (typeof child === "number" && Number.isFinite(child)) {
|
|
159921
|
+
overrides.set(key, child);
|
|
159922
|
+
}
|
|
159923
|
+
}
|
|
159924
|
+
return { defaultValue: value.default, overrides };
|
|
159925
|
+
}
|
|
159926
|
+
return { defaultValue: DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, overrides: new Map };
|
|
159927
|
+
}
|
|
159928
|
+
function normalizeTrustedTokenThresholds(value) {
|
|
159929
|
+
if (!isPlainObject3(value)) {
|
|
159930
|
+
return { defaultValue: undefined, overrides: new Map };
|
|
159931
|
+
}
|
|
159932
|
+
const overrides = new Map;
|
|
159933
|
+
for (const [key, child] of Object.entries(value)) {
|
|
159934
|
+
if (key === "default")
|
|
159935
|
+
continue;
|
|
159936
|
+
if (typeof child === "number" && Number.isFinite(child)) {
|
|
159937
|
+
overrides.set(key, child);
|
|
159938
|
+
}
|
|
159939
|
+
}
|
|
159940
|
+
return {
|
|
159941
|
+
defaultValue: typeof value.default === "number" && Number.isFinite(value.default) ? value.default : undefined,
|
|
159942
|
+
overrides
|
|
159943
|
+
};
|
|
159944
|
+
}
|
|
159945
|
+
function clonePercentageThresholds(value) {
|
|
159946
|
+
return {
|
|
159947
|
+
defaultValue: value.defaultValue,
|
|
159948
|
+
overrides: new Map(value.overrides)
|
|
159949
|
+
};
|
|
159950
|
+
}
|
|
159951
|
+
function cloneTokenThresholds(value) {
|
|
159952
|
+
return {
|
|
159953
|
+
defaultValue: value.defaultValue,
|
|
159954
|
+
overrides: new Map(value.overrides)
|
|
159955
|
+
};
|
|
159956
|
+
}
|
|
159957
|
+
function percentageThresholdsEqual(left, right) {
|
|
159958
|
+
if (left.defaultValue !== right.defaultValue)
|
|
159959
|
+
return false;
|
|
159960
|
+
if (left.overrides.size !== right.overrides.size)
|
|
159961
|
+
return false;
|
|
159962
|
+
for (const [key, value] of left.overrides) {
|
|
159963
|
+
if (right.overrides.get(key) !== value)
|
|
159964
|
+
return false;
|
|
159965
|
+
}
|
|
159966
|
+
return true;
|
|
159967
|
+
}
|
|
159968
|
+
function setMergedPercentageThreshold(mergedRaw, value) {
|
|
159969
|
+
if (value.overrides.size === 0) {
|
|
159970
|
+
mergedRaw.execute_threshold_percentage = value.defaultValue;
|
|
159971
|
+
return;
|
|
159972
|
+
}
|
|
159973
|
+
const serialized = { default: value.defaultValue };
|
|
159974
|
+
for (const [key, threshold] of value.overrides) {
|
|
159975
|
+
serialized[key] = threshold;
|
|
159976
|
+
}
|
|
159977
|
+
mergedRaw.execute_threshold_percentage = serialized;
|
|
159978
|
+
}
|
|
159979
|
+
function setMergedTokenThreshold(mergedRaw, value) {
|
|
159980
|
+
if (value.defaultValue === undefined && value.overrides.size === 0) {
|
|
159981
|
+
delete mergedRaw.execute_threshold_tokens;
|
|
159982
|
+
return;
|
|
159983
|
+
}
|
|
159984
|
+
const serialized = {};
|
|
159985
|
+
if (value.defaultValue !== undefined) {
|
|
159986
|
+
serialized.default = value.defaultValue;
|
|
159987
|
+
}
|
|
159988
|
+
for (const [key, threshold] of value.overrides) {
|
|
159989
|
+
serialized[key] = threshold;
|
|
159990
|
+
}
|
|
159991
|
+
mergedRaw.execute_threshold_tokens = serialized;
|
|
159992
|
+
}
|
|
159993
|
+
function makeProjectThresholdWarning(field, reason) {
|
|
159994
|
+
return `Ignoring ${field} from project config (${reason})`;
|
|
159995
|
+
}
|
|
159996
|
+
function stripUnsafeProjectConfigFields(projectRaw) {
|
|
159997
|
+
const warnings = [];
|
|
159998
|
+
if ("auto_update" in projectRaw) {
|
|
159999
|
+
delete projectRaw.auto_update;
|
|
160000
|
+
warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
|
|
160001
|
+
}
|
|
160002
|
+
if ("language" in projectRaw) {
|
|
160003
|
+
delete projectRaw.language;
|
|
160004
|
+
warnings.push("Ignoring language from project config (security: output language is a user-level setting).");
|
|
160005
|
+
}
|
|
160006
|
+
if ("sqlite" in projectRaw) {
|
|
160007
|
+
delete projectRaw.sqlite;
|
|
160008
|
+
warnings.push("Ignoring sqlite.* from project config (security: SQLite cache/mmap PRAGMAs apply to the " + "process-global shared database handle; only user-level config may set them).");
|
|
160009
|
+
}
|
|
160010
|
+
const embedding = projectRaw.embedding;
|
|
160011
|
+
if (isPlainObject3(embedding)) {
|
|
160012
|
+
const removed = [];
|
|
160013
|
+
for (const field of EMBEDDING_DESTINATION_FIELDS) {
|
|
160014
|
+
if (field in embedding) {
|
|
160015
|
+
delete embedding[field];
|
|
160016
|
+
removed.push(field);
|
|
160017
|
+
}
|
|
160018
|
+
}
|
|
160019
|
+
if (removed.length > 0) {
|
|
160020
|
+
warnings.push(`Ignoring embedding.${removed.join("/")} from project config ` + "(security: a repository cannot choose where private text is embedded).");
|
|
160021
|
+
}
|
|
160022
|
+
}
|
|
160023
|
+
const historian = projectRaw.historian;
|
|
160024
|
+
if (isPlainObject3(historian)) {
|
|
160025
|
+
const removed = [];
|
|
160026
|
+
for (const field of HISTORIAN_USER_ONLY_FIELDS) {
|
|
160027
|
+
if (field in historian) {
|
|
160028
|
+
delete historian[field];
|
|
160029
|
+
removed.push(field);
|
|
160030
|
+
}
|
|
160031
|
+
}
|
|
160032
|
+
if (removed.length > 0) {
|
|
160033
|
+
warnings.push(`Ignoring historian.${removed.join("/")} from project config ` + "(security: historian model selection is user-level only; a repository cannot force extra compaction cost).");
|
|
160034
|
+
}
|
|
160035
|
+
}
|
|
160036
|
+
for (const agentKey of HIDDEN_AGENT_KEYS) {
|
|
160037
|
+
const block = projectRaw[agentKey];
|
|
160038
|
+
if (!isPlainObject3(block))
|
|
160039
|
+
continue;
|
|
160040
|
+
const removed = [];
|
|
160041
|
+
for (const field of AGENT_ESCALATION_FIELDS) {
|
|
160042
|
+
if (field in block) {
|
|
160043
|
+
delete block[field];
|
|
160044
|
+
removed.push(field);
|
|
160045
|
+
}
|
|
160046
|
+
}
|
|
160047
|
+
if (removed.length > 0) {
|
|
160048
|
+
warnings.push(`Ignoring ${agentKey}.${removed.join("/")} from project config ` + "(security: a repository cannot reprogram or re-permission hidden agents).");
|
|
160049
|
+
}
|
|
160050
|
+
}
|
|
160051
|
+
return warnings;
|
|
160052
|
+
}
|
|
160053
|
+
function constrainProjectThresholdOverrides(args) {
|
|
160054
|
+
const warnings = [];
|
|
160055
|
+
const basePercentage = normalizeTrustedPercentageThresholds(args.trustedBaseConfig.execute_threshold_percentage);
|
|
160056
|
+
const baseTokens = normalizeTrustedTokenThresholds(args.trustedBaseConfig.execute_threshold_tokens);
|
|
160057
|
+
if ("execute_threshold_percentage" in args.projectRaw) {
|
|
160058
|
+
const projectValue = args.projectRaw.execute_threshold_percentage;
|
|
160059
|
+
if (isValidPercentageThreshold(projectValue)) {
|
|
160060
|
+
const constrained = clonePercentageThresholds(basePercentage);
|
|
160061
|
+
constrained.defaultValue = Math.max(basePercentage.defaultValue, projectValue);
|
|
160062
|
+
for (const [modelKey, threshold] of basePercentage.overrides) {
|
|
160063
|
+
const raisedThreshold = Math.max(threshold, projectValue);
|
|
160064
|
+
if (raisedThreshold === constrained.defaultValue) {
|
|
160065
|
+
constrained.overrides.delete(modelKey);
|
|
160066
|
+
} else {
|
|
160067
|
+
constrained.overrides.set(modelKey, raisedThreshold);
|
|
160068
|
+
}
|
|
160069
|
+
}
|
|
160070
|
+
setMergedPercentageThreshold(args.mergedRaw, constrained);
|
|
160071
|
+
if (percentageThresholdsEqual(constrained, basePercentage)) {
|
|
160072
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_percentage", PERCENTAGE_THRESHOLD_REASON));
|
|
160073
|
+
}
|
|
160074
|
+
} else if (isPlainObject3(projectValue)) {
|
|
160075
|
+
const constrained = clonePercentageThresholds(basePercentage);
|
|
160076
|
+
let touchedValidEntry = false;
|
|
160077
|
+
if (isValidPercentageThreshold(projectValue.default)) {
|
|
160078
|
+
touchedValidEntry = true;
|
|
160079
|
+
if (projectValue.default > basePercentage.defaultValue) {
|
|
160080
|
+
constrained.defaultValue = projectValue.default;
|
|
160081
|
+
} else {
|
|
160082
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_percentage.default", PERCENTAGE_THRESHOLD_REASON));
|
|
160083
|
+
}
|
|
160084
|
+
}
|
|
160085
|
+
for (const [modelKey, rawValue] of Object.entries(projectValue)) {
|
|
160086
|
+
if (modelKey === "default")
|
|
160087
|
+
continue;
|
|
160088
|
+
if (!isValidPercentageThreshold(rawValue))
|
|
160089
|
+
continue;
|
|
160090
|
+
touchedValidEntry = true;
|
|
160091
|
+
const baseValue = basePercentage.overrides.get(modelKey) ?? basePercentage.defaultValue;
|
|
160092
|
+
if (rawValue > baseValue) {
|
|
160093
|
+
if (rawValue === constrained.defaultValue) {
|
|
160094
|
+
constrained.overrides.delete(modelKey);
|
|
160095
|
+
} else {
|
|
160096
|
+
constrained.overrides.set(modelKey, rawValue);
|
|
160097
|
+
}
|
|
160098
|
+
} else {
|
|
160099
|
+
warnings.push(makeProjectThresholdWarning(`execute_threshold_percentage.${modelKey}`, PERCENTAGE_THRESHOLD_REASON));
|
|
160100
|
+
}
|
|
160101
|
+
}
|
|
160102
|
+
if (touchedValidEntry) {
|
|
160103
|
+
setMergedPercentageThreshold(args.mergedRaw, constrained);
|
|
160104
|
+
}
|
|
160105
|
+
}
|
|
160106
|
+
}
|
|
160107
|
+
if ("execute_threshold_tokens" in args.projectRaw && isPlainObject3(args.projectRaw.execute_threshold_tokens)) {
|
|
160108
|
+
const projectValue = args.projectRaw.execute_threshold_tokens;
|
|
160109
|
+
const constrained = cloneTokenThresholds(baseTokens);
|
|
160110
|
+
let touchedValidEntry = false;
|
|
160111
|
+
if (isValidTokenThreshold(projectValue.default)) {
|
|
160112
|
+
touchedValidEntry = true;
|
|
160113
|
+
if (baseTokens.defaultValue === undefined) {
|
|
160114
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_INTRODUCTION_REASON));
|
|
160115
|
+
} else if (projectValue.default > baseTokens.defaultValue) {
|
|
160116
|
+
constrained.defaultValue = projectValue.default;
|
|
160117
|
+
} else {
|
|
160118
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_REASON));
|
|
160119
|
+
}
|
|
160120
|
+
}
|
|
160121
|
+
for (const [modelKey, rawValue] of Object.entries(projectValue)) {
|
|
160122
|
+
if (modelKey === "default")
|
|
160123
|
+
continue;
|
|
160124
|
+
if (!isValidTokenThreshold(rawValue))
|
|
160125
|
+
continue;
|
|
160126
|
+
touchedValidEntry = true;
|
|
160127
|
+
const baseValue = baseTokens.overrides.get(modelKey) ?? baseTokens.defaultValue;
|
|
160128
|
+
if (baseValue === undefined) {
|
|
160129
|
+
warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_INTRODUCTION_REASON));
|
|
160130
|
+
continue;
|
|
160131
|
+
}
|
|
160132
|
+
if (rawValue > baseValue) {
|
|
160133
|
+
if (rawValue === constrained.defaultValue) {
|
|
160134
|
+
constrained.overrides.delete(modelKey);
|
|
160135
|
+
} else {
|
|
160136
|
+
constrained.overrides.set(modelKey, rawValue);
|
|
160137
|
+
}
|
|
160138
|
+
} else {
|
|
160139
|
+
warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_REASON));
|
|
160140
|
+
}
|
|
160141
|
+
}
|
|
160142
|
+
if (touchedValidEntry) {
|
|
160143
|
+
setMergedTokenThreshold(args.mergedRaw, constrained);
|
|
160144
|
+
}
|
|
160145
|
+
}
|
|
160146
|
+
return warnings;
|
|
160147
|
+
}
|
|
160148
|
+
function normalizeEndpoint(value) {
|
|
160149
|
+
if (typeof value !== "string")
|
|
160150
|
+
return;
|
|
160151
|
+
const trimmed = value.trim().replace(/\/+$/, "");
|
|
160152
|
+
return trimmed.length > 0 ? trimmed.toLowerCase() : undefined;
|
|
160153
|
+
}
|
|
160154
|
+
function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
160155
|
+
const projectEmbedding = projectRaw.embedding;
|
|
160156
|
+
if (!isPlainObject3(projectEmbedding))
|
|
160157
|
+
return [];
|
|
160158
|
+
const redirectsEndpoint = "endpoint" in projectEmbedding;
|
|
160159
|
+
if (!redirectsEndpoint)
|
|
160160
|
+
return [];
|
|
160161
|
+
const userEmbedding = userRaw?.embedding;
|
|
160162
|
+
if (isPlainObject3(userEmbedding)) {
|
|
160163
|
+
const projectEndpoint = normalizeEndpoint(projectEmbedding.endpoint);
|
|
160164
|
+
const userEndpoint = normalizeEndpoint(userEmbedding.endpoint);
|
|
160165
|
+
if (projectEndpoint !== undefined && projectEndpoint === userEndpoint) {
|
|
160166
|
+
return [];
|
|
160167
|
+
}
|
|
160168
|
+
}
|
|
160169
|
+
const providesOwnKey = typeof projectEmbedding.api_key === "string" && projectEmbedding.api_key.length > 0;
|
|
160170
|
+
if (providesOwnKey)
|
|
160171
|
+
return [];
|
|
160172
|
+
const mergedEmbedding = mergedRaw.embedding;
|
|
160173
|
+
if (!isPlainObject3(mergedEmbedding))
|
|
160174
|
+
return [];
|
|
160175
|
+
if (!("api_key" in mergedEmbedding))
|
|
160176
|
+
return [];
|
|
160177
|
+
delete mergedEmbedding.api_key;
|
|
160178
|
+
return [
|
|
160179
|
+
"Dropped inherited user embedding api_key because project config redirected " + "embedding.endpoint without supplying its own key (security: prevents key " + "exfiltration to a repository-chosen endpoint)."
|
|
160180
|
+
];
|
|
160181
|
+
}
|
|
160182
|
+
|
|
159940
160183
|
// ../plugin/src/config/variable.ts
|
|
159941
160184
|
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "node:fs";
|
|
159942
160185
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -160269,6 +160512,7 @@ function loadPiConfig(opts = {}) {
|
|
|
160269
160512
|
return a.scope === "user" ? -1 : 1;
|
|
160270
160513
|
});
|
|
160271
160514
|
const userRaw = mergeFiles.find((f) => f.scope === "user")?.config;
|
|
160515
|
+
const trustedBaseConfig = parsePiConfig(userRaw ?? {}).config;
|
|
160272
160516
|
for (const loaded of mergeFiles) {
|
|
160273
160517
|
const prefix = loaded.scope === "user" ? "[user config]" : "[project config]";
|
|
160274
160518
|
warnings.push(...loaded.warnings.map((warning) => `${prefix} ${warning}`));
|
|
@@ -160281,6 +160525,13 @@ function loadPiConfig(opts = {}) {
|
|
|
160281
160525
|
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, userRaw)) {
|
|
160282
160526
|
warnings.push(`${prefix} ${warning}`);
|
|
160283
160527
|
}
|
|
160528
|
+
for (const warning of constrainProjectThresholdOverrides({
|
|
160529
|
+
mergedRaw: rawConfig,
|
|
160530
|
+
projectRaw,
|
|
160531
|
+
trustedBaseConfig
|
|
160532
|
+
})) {
|
|
160533
|
+
warnings.push(`${prefix} ${warning}`);
|
|
160534
|
+
}
|
|
160284
160535
|
} else {
|
|
160285
160536
|
rawConfig = mergeRawConfigs(rawConfig, loaded.config);
|
|
160286
160537
|
}
|
|
@@ -160379,6 +160630,7 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
160379
160630
|
return a.scope === "user" ? -1 : 1;
|
|
160380
160631
|
});
|
|
160381
160632
|
const userRaw = mergeFiles.find((f) => f.scope === "user")?.config;
|
|
160633
|
+
const trustedBaseConfig = parsePiConfig(userRaw ?? {}).config;
|
|
160382
160634
|
for (const loaded of mergeFiles) {
|
|
160383
160635
|
const prefix = loaded.scope === "user" ? "[user config]" : "[project config]";
|
|
160384
160636
|
warnings.push(...loaded.warnings.map((warning) => `${prefix} ${warning}`));
|
|
@@ -160391,6 +160643,13 @@ function loadPiConfigDetailed(opts = {}) {
|
|
|
160391
160643
|
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, rawConfig, userRaw)) {
|
|
160392
160644
|
warnings.push(`${prefix} ${warning}`);
|
|
160393
160645
|
}
|
|
160646
|
+
for (const warning of constrainProjectThresholdOverrides({
|
|
160647
|
+
mergedRaw: rawConfig,
|
|
160648
|
+
projectRaw,
|
|
160649
|
+
trustedBaseConfig
|
|
160650
|
+
})) {
|
|
160651
|
+
warnings.push(`${prefix} ${warning}`);
|
|
160652
|
+
}
|
|
160394
160653
|
} else {
|
|
160395
160654
|
rawConfig = mergeRawConfigs(rawConfig, loaded.config);
|
|
160396
160655
|
}
|
|
@@ -161775,6 +162034,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
161775
162034
|
|
|
161776
162035
|
// ../plugin/src/features/magic-context/memory/storage-memory-embeddings.ts
|
|
161777
162036
|
var saveEmbeddingStatements = new WeakMap;
|
|
162037
|
+
var saveEmbeddingIfHashMatchesStatements = new WeakMap;
|
|
161778
162038
|
var loadAllEmbeddingsStatements = new WeakMap;
|
|
161779
162039
|
var deleteEmbeddingStatements = new WeakMap;
|
|
161780
162040
|
var getStoredModelIdStatements = new WeakMap;
|
|
@@ -161805,6 +162065,14 @@ function getSaveEmbeddingStatement(db) {
|
|
|
161805
162065
|
}
|
|
161806
162066
|
return stmt;
|
|
161807
162067
|
}
|
|
162068
|
+
function getSaveEmbeddingIfHashMatchesStatement(db) {
|
|
162069
|
+
let stmt = saveEmbeddingIfHashMatchesStatements.get(db);
|
|
162070
|
+
if (!stmt) {
|
|
162071
|
+
stmt = db.prepare("INSERT INTO memory_embeddings (memory_id, embedding, model_id) SELECT ?, ?, ? FROM memories WHERE id = ? AND normalized_hash = ? ON CONFLICT(memory_id, model_id) DO UPDATE SET embedding = excluded.embedding");
|
|
162072
|
+
saveEmbeddingIfHashMatchesStatements.set(db, stmt);
|
|
162073
|
+
}
|
|
162074
|
+
return stmt;
|
|
162075
|
+
}
|
|
161808
162076
|
function getLoadAllEmbeddingsStatement(db) {
|
|
161809
162077
|
let stmt = loadAllEmbeddingsStatements.get(db);
|
|
161810
162078
|
if (!stmt) {
|
|
@@ -161833,6 +162101,10 @@ function saveEmbedding(db, memoryId, embedding, modelId) {
|
|
|
161833
162101
|
const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
161834
162102
|
getSaveEmbeddingStatement(db).run(memoryId, blob, modelId);
|
|
161835
162103
|
}
|
|
162104
|
+
function saveEmbeddingIfHashMatches(db, memoryId, embedding, modelId, normalizedHash) {
|
|
162105
|
+
const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
162106
|
+
return getSaveEmbeddingIfHashMatchesStatement(db).run(memoryId, blob, modelId, memoryId, normalizedHash).changes > 0;
|
|
162107
|
+
}
|
|
161836
162108
|
function loadAllEmbeddings(db, projectPath, modelId) {
|
|
161837
162109
|
const rows = getLoadAllEmbeddingsStatement(db).all(projectPath, modelId).filter(isEmbeddingRow);
|
|
161838
162110
|
const embeddings = new Map;
|
|
@@ -162764,12 +163036,12 @@ function isUnembeddedMemoryRow(row) {
|
|
|
162764
163036
|
if (row === null || typeof row !== "object")
|
|
162765
163037
|
return false;
|
|
162766
163038
|
const candidate = row;
|
|
162767
|
-
return typeof candidate.id === "number" && typeof candidate.content === "string";
|
|
163039
|
+
return typeof candidate.id === "number" && typeof candidate.content === "string" && typeof candidate.normalized_hash === "string";
|
|
162768
163040
|
}
|
|
162769
163041
|
function getLoadUnembeddedMemoriesStatement(db) {
|
|
162770
163042
|
let stmt = loadUnembeddedMemoriesStatements.get(db);
|
|
162771
163043
|
if (!stmt) {
|
|
162772
|
-
stmt = db.prepare("SELECT m.id AS id, m.content AS content FROM memories m LEFT JOIN memory_embeddings me ON m.id = me.memory_id AND me.model_id = ? WHERE m.project_path = ? AND m.status = 'active' AND me.memory_id IS NULL LIMIT ?");
|
|
163044
|
+
stmt = db.prepare("SELECT m.id AS id, m.content AS content, m.normalized_hash AS normalized_hash FROM memories m LEFT JOIN memory_embeddings me ON m.id = me.memory_id AND me.model_id = ? WHERE m.project_path = ? AND m.status = 'active' AND me.memory_id IS NULL LIMIT ?");
|
|
162773
163045
|
loadUnembeddedMemoriesStatements.set(db, stmt);
|
|
162774
163046
|
}
|
|
162775
163047
|
return stmt;
|
|
@@ -162792,8 +163064,9 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
162792
163064
|
const embedding = result.vectors[index];
|
|
162793
163065
|
if (!embedding)
|
|
162794
163066
|
continue;
|
|
162795
|
-
|
|
162796
|
-
|
|
163067
|
+
if (saveEmbeddingIfHashMatches(db, memory.id, embedding, result.modelId, memory.normalized_hash)) {
|
|
163068
|
+
embeddedCount += 1;
|
|
163069
|
+
}
|
|
162797
163070
|
}
|
|
162798
163071
|
})();
|
|
162799
163072
|
return embeddedCount;
|
|
@@ -164299,12 +164572,19 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
164299
164572
|
let totalTokens = 0;
|
|
164300
164573
|
let messagesProcessed = 0;
|
|
164301
164574
|
let lastOrdinal = startOrdinal - 1;
|
|
164575
|
+
let highestScannedOrdinal = startOrdinal - 1;
|
|
164302
164576
|
let lastMessageId = "";
|
|
164303
164577
|
let firstMessageId = "";
|
|
164304
164578
|
let currentBlock = null;
|
|
164305
164579
|
let pendingNoiseMeta = [];
|
|
164306
164580
|
let commitClusters = 0;
|
|
164307
164581
|
let lastFlushedRole = "";
|
|
164582
|
+
function recordFilteredNoise(meta3) {
|
|
164583
|
+
pendingNoiseMeta.push(meta3);
|
|
164584
|
+
if (!currentBlock) {
|
|
164585
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, meta3.ordinal);
|
|
164586
|
+
}
|
|
164587
|
+
}
|
|
164308
164588
|
function flushCurrentBlock() {
|
|
164309
164589
|
if (!currentBlock)
|
|
164310
164590
|
return true;
|
|
@@ -164320,6 +164600,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
164320
164600
|
if (!firstMessageId)
|
|
164321
164601
|
firstMessageId = currentBlock.meta[0]?.messageId ?? "";
|
|
164322
164602
|
lastOrdinal = currentBlock.meta[currentBlock.meta.length - 1]?.ordinal ?? currentBlock.endOrdinal;
|
|
164603
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, lastOrdinal);
|
|
164323
164604
|
lastMessageId = currentBlock.meta[currentBlock.meta.length - 1]?.messageId ?? "";
|
|
164324
164605
|
messagesProcessed += currentBlock.meta.length;
|
|
164325
164606
|
lines.push(blockText);
|
|
@@ -164343,7 +164624,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
164343
164624
|
if (msg.role === "user" && !hasMeaningfulUserText(msg.parts)) {
|
|
164344
164625
|
const tcSummaries = extractToolCallSummaries(msg.parts);
|
|
164345
164626
|
if (tcSummaries.length === 0) {
|
|
164346
|
-
|
|
164627
|
+
recordFilteredNoise(meta3);
|
|
164347
164628
|
continue;
|
|
164348
164629
|
}
|
|
164349
164630
|
const tcText = tcSummaries.join(" / ");
|
|
@@ -164375,7 +164656,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
164375
164656
|
const compacted = compactTextForSummary(allParts.join(" / "), msg.role);
|
|
164376
164657
|
const text = compacted.text;
|
|
164377
164658
|
if (!text) {
|
|
164378
|
-
|
|
164659
|
+
recordFilteredNoise(meta3);
|
|
164379
164660
|
continue;
|
|
164380
164661
|
}
|
|
164381
164662
|
const msgHasNarrative = textParts.length > 0;
|
|
@@ -164402,7 +164683,9 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
164402
164683
|
};
|
|
164403
164684
|
pendingNoiseMeta = [];
|
|
164404
164685
|
}
|
|
164405
|
-
flushCurrentBlock()
|
|
164686
|
+
if (flushCurrentBlock() && pendingNoiseMeta.length > 0) {
|
|
164687
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, pendingNoiseMeta[pendingNoiseMeta.length - 1]?.ordinal ?? highestScannedOrdinal);
|
|
164688
|
+
}
|
|
164406
164689
|
const toolOnlyRanges = [];
|
|
164407
164690
|
for (const range of flushedToolOnlyBlocks) {
|
|
164408
164691
|
const last = toolOnlyRanges[toolOnlyRanges.length - 1];
|
|
@@ -164419,7 +164702,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
164419
164702
|
endMessageId: lastMessageId,
|
|
164420
164703
|
messageCount: messagesProcessed,
|
|
164421
164704
|
tokenEstimate: totalTokens,
|
|
164422
|
-
hasMore: lastOrdinal < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, totalMessageCount) : totalMessageCount),
|
|
164705
|
+
hasMore: Math.max(lastOrdinal, highestScannedOrdinal) < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, totalMessageCount) : totalMessageCount),
|
|
164423
164706
|
text: lines.join(`
|
|
164424
164707
|
`),
|
|
164425
164708
|
lines: lineMeta,
|
|
@@ -168864,6 +169147,8 @@ function isMidTurnPi(event, _sessionId) {
|
|
|
168864
169147
|
}
|
|
168865
169148
|
if (latestAssistant === null)
|
|
168866
169149
|
return false;
|
|
169150
|
+
if (hasRealUserAfter(messages, latestAssistantIndex))
|
|
169151
|
+
return false;
|
|
168867
169152
|
if (latestAssistant.stopReason === "toolUse")
|
|
168868
169153
|
return true;
|
|
168869
169154
|
const toolCallIds = getToolCallIds(latestAssistant.content);
|
|
@@ -168886,6 +169171,15 @@ function isMidTurnPi(event, _sessionId) {
|
|
|
168886
169171
|
}
|
|
168887
169172
|
return false;
|
|
168888
169173
|
}
|
|
169174
|
+
function hasRealUserAfter(messages, latestAssistantIndex) {
|
|
169175
|
+
for (const msg of messages.slice(latestAssistantIndex + 1)) {
|
|
169176
|
+
if (msg === null || typeof msg !== "object")
|
|
169177
|
+
continue;
|
|
169178
|
+
if (msg.role === "user")
|
|
169179
|
+
return true;
|
|
169180
|
+
}
|
|
169181
|
+
return false;
|
|
169182
|
+
}
|
|
168889
169183
|
function getToolCallIds(content) {
|
|
168890
169184
|
const ids = new Set;
|
|
168891
169185
|
if (!Array.isArray(content))
|
|
@@ -169610,25 +169904,33 @@ function getMemoryCountsByStatusStatement(db) {
|
|
|
169610
169904
|
}
|
|
169611
169905
|
return stmt;
|
|
169612
169906
|
}
|
|
169613
|
-
function
|
|
169614
|
-
const now = Date.now();
|
|
169615
|
-
const normalizedHash = computeNormalizedHash(input.content);
|
|
169907
|
+
function buildInsertMemoryValues(input, normalizedHash, now, includeImportance) {
|
|
169616
169908
|
const insertValues = [
|
|
169617
169909
|
input.projectPath,
|
|
169618
169910
|
input.category,
|
|
169619
169911
|
input.content,
|
|
169620
169912
|
normalizedHash
|
|
169621
169913
|
];
|
|
169622
|
-
if (
|
|
169914
|
+
if (includeImportance) {
|
|
169623
169915
|
insertValues.push(input.importance ?? 50);
|
|
169624
169916
|
}
|
|
169625
169917
|
insertValues.push(input.sourceSessionId ?? null, input.sourceType ?? "historian", 1, 0, now, now, now, now, null, "active", input.expiresAt ?? null, "unverified", null, null, null, input.metadataJson ?? null);
|
|
169626
|
-
|
|
169627
|
-
|
|
169628
|
-
|
|
169918
|
+
return insertValues;
|
|
169919
|
+
}
|
|
169920
|
+
function loadInsertedMemory(db, rowid) {
|
|
169921
|
+
const inserted = getMemoryById(db, Number(rowid));
|
|
169629
169922
|
if (!inserted) {
|
|
169630
169923
|
throw new Error("Failed to load inserted memory row");
|
|
169631
169924
|
}
|
|
169925
|
+
return inserted;
|
|
169926
|
+
}
|
|
169927
|
+
function insertMemory(db, input) {
|
|
169928
|
+
const now = Date.now();
|
|
169929
|
+
const normalizedHash = computeNormalizedHash(input.content);
|
|
169930
|
+
const insertValues = buildInsertMemoryValues(input, normalizedHash, now, hasMemoryImportanceColumn(db));
|
|
169931
|
+
const result = getInsertMemoryStatement(db).run(...insertValues);
|
|
169932
|
+
const insertedResult = result;
|
|
169933
|
+
const inserted = loadInsertedMemory(db, insertedResult.lastInsertRowid);
|
|
169632
169934
|
invalidateProject(input.projectPath);
|
|
169633
169935
|
return inserted;
|
|
169634
169936
|
}
|
|
@@ -170297,7 +170599,10 @@ async function normalizeVerificationFiles(args) {
|
|
|
170297
170599
|
var lastIndexedStatements = new WeakMap;
|
|
170298
170600
|
var insertMessageStatements = new WeakMap;
|
|
170299
170601
|
var upsertIndexStatements = new WeakMap;
|
|
170602
|
+
var upsertCleanIndexStatements = new WeakMap;
|
|
170603
|
+
var upsertDirtyFloorStatements = new WeakMap;
|
|
170300
170604
|
var deleteFtsStatements = new WeakMap;
|
|
170605
|
+
var deleteFtsFromOrdinalStatements = new WeakMap;
|
|
170301
170606
|
var deleteIndexStatements = new WeakMap;
|
|
170302
170607
|
var countIndexedMessageStatements = new WeakMap;
|
|
170303
170608
|
function normalizeIndexText(text) {
|
|
@@ -170306,7 +170611,7 @@ function normalizeIndexText(text) {
|
|
|
170306
170611
|
function getLastIndexedStatement(db) {
|
|
170307
170612
|
let stmt = lastIndexedStatements.get(db);
|
|
170308
170613
|
if (!stmt) {
|
|
170309
|
-
stmt = db.prepare("SELECT last_indexed_ordinal FROM message_history_index WHERE session_id = ?");
|
|
170614
|
+
stmt = db.prepare("SELECT last_indexed_ordinal, dirty_floor_ordinal FROM message_history_index WHERE session_id = ?");
|
|
170310
170615
|
lastIndexedStatements.set(db, stmt);
|
|
170311
170616
|
}
|
|
170312
170617
|
return stmt;
|
|
@@ -170327,6 +170632,22 @@ function getUpsertIndexStatement(db) {
|
|
|
170327
170632
|
}
|
|
170328
170633
|
return stmt;
|
|
170329
170634
|
}
|
|
170635
|
+
function getUpsertCleanIndexStatement(db) {
|
|
170636
|
+
let stmt = upsertCleanIndexStatements.get(db);
|
|
170637
|
+
if (!stmt) {
|
|
170638
|
+
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?, 0, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, dirty_floor_ordinal = 0, updated_at = excluded.updated_at");
|
|
170639
|
+
upsertCleanIndexStatements.set(db, stmt);
|
|
170640
|
+
}
|
|
170641
|
+
return stmt;
|
|
170642
|
+
}
|
|
170643
|
+
function getUpsertDirtyFloorStatement(db) {
|
|
170644
|
+
let stmt = upsertDirtyFloorStatements.get(db);
|
|
170645
|
+
if (!stmt) {
|
|
170646
|
+
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?, ?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = MAX(message_history_index.last_indexed_ordinal, excluded.last_indexed_ordinal), dirty_floor_ordinal = CASE WHEN message_history_index.dirty_floor_ordinal <= 0 THEN excluded.dirty_floor_ordinal WHEN excluded.dirty_floor_ordinal <= 0 THEN message_history_index.dirty_floor_ordinal ELSE MIN(message_history_index.dirty_floor_ordinal, excluded.dirty_floor_ordinal) END, updated_at = excluded.updated_at");
|
|
170647
|
+
upsertDirtyFloorStatements.set(db, stmt);
|
|
170648
|
+
}
|
|
170649
|
+
return stmt;
|
|
170650
|
+
}
|
|
170330
170651
|
function getDeleteFtsStatement(db) {
|
|
170331
170652
|
let stmt = deleteFtsStatements.get(db);
|
|
170332
170653
|
if (!stmt) {
|
|
@@ -170335,6 +170656,14 @@ function getDeleteFtsStatement(db) {
|
|
|
170335
170656
|
}
|
|
170336
170657
|
return stmt;
|
|
170337
170658
|
}
|
|
170659
|
+
function getDeleteFtsFromOrdinalStatement(db) {
|
|
170660
|
+
let stmt = deleteFtsFromOrdinalStatements.get(db);
|
|
170661
|
+
if (!stmt) {
|
|
170662
|
+
stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER) >= ?");
|
|
170663
|
+
deleteFtsFromOrdinalStatements.set(db, stmt);
|
|
170664
|
+
}
|
|
170665
|
+
return stmt;
|
|
170666
|
+
}
|
|
170338
170667
|
function getDeleteIndexStatement(db) {
|
|
170339
170668
|
let stmt = deleteIndexStatements.get(db);
|
|
170340
170669
|
if (!stmt) {
|
|
@@ -170355,6 +170684,14 @@ function getLastIndexedOrdinal(db, sessionId) {
|
|
|
170355
170684
|
const row = getLastIndexedStatement(db).get(sessionId);
|
|
170356
170685
|
return typeof row?.last_indexed_ordinal === "number" ? row.last_indexed_ordinal : 0;
|
|
170357
170686
|
}
|
|
170687
|
+
function getDirtyIndexFloor(db, sessionId) {
|
|
170688
|
+
const row = getLastIndexedStatement(db).get(sessionId);
|
|
170689
|
+
return typeof row?.dirty_floor_ordinal === "number" && row.dirty_floor_ordinal > 0 ? row.dirty_floor_ordinal : null;
|
|
170690
|
+
}
|
|
170691
|
+
function markMessageIndexDirty(db, sessionId, floorOrdinal) {
|
|
170692
|
+
const dirtyFloor = Math.max(1, Math.floor(floorOrdinal));
|
|
170693
|
+
getUpsertDirtyFloorStatement(db).run(sessionId, getLastIndexedOrdinal(db, sessionId), dirtyFloor, Date.now(), getHarness());
|
|
170694
|
+
}
|
|
170358
170695
|
function isMessageAlreadyIndexed(db, sessionId, messageId) {
|
|
170359
170696
|
const row = getCountIndexedMessageStatement(db).get(sessionId, messageId);
|
|
170360
170697
|
return (typeof row?.count === "number" ? row.count : 0) > 0;
|
|
@@ -170422,7 +170759,15 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
170422
170759
|
db.exec("BEGIN IMMEDIATE");
|
|
170423
170760
|
let committed = false;
|
|
170424
170761
|
try {
|
|
170425
|
-
|
|
170762
|
+
let effectiveWatermark = Math.max(lastIndexedOrdinal, getLastIndexedOrdinal(db, sessionId));
|
|
170763
|
+
const dirtyFloor = getDirtyIndexFloor(db, sessionId);
|
|
170764
|
+
if (dirtyFloor !== null) {
|
|
170765
|
+
const rewindOrdinal = Math.max(1, Math.min(dirtyFloor, finalWatermark + 1));
|
|
170766
|
+
if (rewindOrdinal <= finalWatermark) {
|
|
170767
|
+
getDeleteFtsFromOrdinalStatement(db).run(sessionId, rewindOrdinal);
|
|
170768
|
+
}
|
|
170769
|
+
effectiveWatermark = Math.min(effectiveWatermark, rewindOrdinal - 1);
|
|
170770
|
+
}
|
|
170426
170771
|
const insertMessage = getInsertMessageStatement(db);
|
|
170427
170772
|
for (const message of messages) {
|
|
170428
170773
|
if (message.ordinal <= effectiveWatermark) {
|
|
@@ -170439,7 +170784,7 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
170439
170784
|
inserted++;
|
|
170440
170785
|
}
|
|
170441
170786
|
const newWatermark = Math.max(effectiveWatermark, finalWatermark);
|
|
170442
|
-
|
|
170787
|
+
getUpsertCleanIndexStatement(db).run(sessionId, newWatermark, now, getHarness());
|
|
170443
170788
|
db.exec("COMMIT");
|
|
170444
170789
|
committed = true;
|
|
170445
170790
|
} finally {
|
|
@@ -171210,7 +171555,7 @@ function setChannel2NudgeState(db, sessionId, state) {
|
|
|
171210
171555
|
db.transaction(() => {
|
|
171211
171556
|
ensureSessionMetaRow(db, sessionId);
|
|
171212
171557
|
const claimedAt = state === "claimed" ? Date.now() : 0;
|
|
171213
|
-
db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at =
|
|
171558
|
+
db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at = ?, channel2_nudge_claim_token = '' WHERE session_id = ?").run(state, claimedAt, sessionId);
|
|
171214
171559
|
})();
|
|
171215
171560
|
}
|
|
171216
171561
|
function casChannel2NudgeState(db, sessionId, from, to) {
|
|
@@ -171218,7 +171563,7 @@ function casChannel2NudgeState(db, sessionId, from, to) {
|
|
|
171218
171563
|
db.transaction(() => {
|
|
171219
171564
|
ensureSessionMetaRow(db, sessionId);
|
|
171220
171565
|
const claimedAt = to === "claimed" ? Date.now() : 0;
|
|
171221
|
-
const result = db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at =
|
|
171566
|
+
const result = db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at = ?, channel2_nudge_claim_token = '' WHERE session_id = ? AND channel2_nudge_state = ?").run(to, claimedAt, sessionId, from);
|
|
171222
171567
|
changed = (result.changes ?? 0) > 0;
|
|
171223
171568
|
})();
|
|
171224
171569
|
return changed;
|
|
@@ -173062,8 +173407,9 @@ function createCtxMemoryTool(deps) {
|
|
|
173062
173407
|
const isOwn = targetIdentityForStoredPath(memory2.projectPath) === projectIdentity;
|
|
173063
173408
|
if (isOwn)
|
|
173064
173409
|
return true;
|
|
173065
|
-
return toolShareCategories
|
|
173410
|
+
return toolShareCategories?.includes(memory2.category) ?? false;
|
|
173066
173411
|
};
|
|
173412
|
+
const memoryOwnedByTool = (memory2) => workspaceIdentitySet.identities.length > 1 ? targetIdentityForStoredPath(memory2.projectPath) === projectIdentity : storedPathBelongsToIdentity(memory2.projectPath, projectIdentity);
|
|
173067
173413
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
173068
173414
|
if (snapshot ? !snapshot.features.memoryEnabled : deps.memoryEnabled === false) {
|
|
173069
173415
|
return err2("Cross-session memory is disabled for this project.");
|
|
@@ -173110,7 +173456,8 @@ function createCtxMemoryTool(deps) {
|
|
|
173110
173456
|
return err2("Error: 'content' is required when action is 'update'.");
|
|
173111
173457
|
}
|
|
173112
173458
|
const memory2 = getMemoryById(deps.db, updateId);
|
|
173113
|
-
|
|
173459
|
+
const updateAllowed = memory2 ? dreamerAllowed ? memoryVisibleToTool(memory2) : memoryOwnedByTool(memory2) : false;
|
|
173460
|
+
if (!memory2 || !updateAllowed) {
|
|
173114
173461
|
return err2(`Error: Memory with ID ${updateId} was not found.`);
|
|
173115
173462
|
}
|
|
173116
173463
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
@@ -173157,7 +173504,7 @@ function createCtxMemoryTool(deps) {
|
|
|
173157
173504
|
return err2("Error: One or more source memories were not found.");
|
|
173158
173505
|
}
|
|
173159
173506
|
if (!dreamerAllowed) {
|
|
173160
|
-
const foreign = sourceMemories.find((memory2) => !
|
|
173507
|
+
const foreign = sourceMemories.find((memory2) => !memoryOwnedByTool(memory2));
|
|
173161
173508
|
if (foreign) {
|
|
173162
173509
|
return err2(`Error: Memory with ID ${foreign.id} was not found.`);
|
|
173163
173510
|
}
|
|
@@ -173259,7 +173606,8 @@ function createCtxMemoryTool(deps) {
|
|
|
173259
173606
|
const archiveIds = [...new Set(rawArchiveIds)];
|
|
173260
173607
|
for (const memoryId of archiveIds) {
|
|
173261
173608
|
const memory2 = getMemoryById(deps.db, memoryId);
|
|
173262
|
-
|
|
173609
|
+
const archiveAllowed = memory2 ? dreamerAllowed ? memoryVisibleToTool(memory2) : memoryOwnedByTool(memory2) : false;
|
|
173610
|
+
if (!memory2 || !archiveAllowed) {
|
|
173263
173611
|
return err2(`Error: Memory with ID ${memoryId} was not found.`);
|
|
173264
173612
|
}
|
|
173265
173613
|
if (!dreamerAllowed && !isPrimaryMutableMemory(memory2)) {
|
|
@@ -174212,24 +174560,46 @@ function getMessageSearchStatementWithCutoff(db) {
|
|
|
174212
174560
|
return stmt;
|
|
174213
174561
|
}
|
|
174214
174562
|
var ftsRowCountStatements = new WeakMap;
|
|
174563
|
+
var ftsRowCountStatementsWithCutoff = new WeakMap;
|
|
174215
174564
|
var ftsMatchCountStatements = new WeakMap;
|
|
174216
|
-
|
|
174217
|
-
|
|
174218
|
-
|
|
174219
|
-
|
|
174220
|
-
|
|
174221
|
-
|
|
174222
|
-
|
|
174565
|
+
var ftsMatchCountStatementsWithCutoff = new WeakMap;
|
|
174566
|
+
function getSessionFtsRowCount(db, sessionId, cutoff) {
|
|
174567
|
+
const stmt = cutoff === null ? (() => {
|
|
174568
|
+
let cached2 = ftsRowCountStatements.get(db);
|
|
174569
|
+
if (!cached2) {
|
|
174570
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ?");
|
|
174571
|
+
ftsRowCountStatements.set(db, cached2);
|
|
174572
|
+
}
|
|
174573
|
+
return cached2;
|
|
174574
|
+
})() : (() => {
|
|
174575
|
+
let cached2 = ftsRowCountStatementsWithCutoff.get(db);
|
|
174576
|
+
if (!cached2) {
|
|
174577
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER) <= ?");
|
|
174578
|
+
ftsRowCountStatementsWithCutoff.set(db, cached2);
|
|
174579
|
+
}
|
|
174580
|
+
return cached2;
|
|
174581
|
+
})();
|
|
174582
|
+
const row = cutoff === null ? stmt.get(sessionId) : stmt.get(sessionId, cutoff);
|
|
174223
174583
|
return typeof row?.n === "number" ? row.n : 0;
|
|
174224
174584
|
}
|
|
174225
|
-
function countSessionFtsMatches(db, sessionId, ftsQuery) {
|
|
174226
|
-
|
|
174227
|
-
|
|
174228
|
-
|
|
174229
|
-
|
|
174230
|
-
|
|
174585
|
+
function countSessionFtsMatches(db, sessionId, ftsQuery, cutoff) {
|
|
174586
|
+
const stmt = cutoff === null ? (() => {
|
|
174587
|
+
let cached2 = ftsMatchCountStatements.get(db);
|
|
174588
|
+
if (!cached2) {
|
|
174589
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND message_history_fts MATCH ?");
|
|
174590
|
+
ftsMatchCountStatements.set(db, cached2);
|
|
174591
|
+
}
|
|
174592
|
+
return cached2;
|
|
174593
|
+
})() : (() => {
|
|
174594
|
+
let cached2 = ftsMatchCountStatementsWithCutoff.get(db);
|
|
174595
|
+
if (!cached2) {
|
|
174596
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND message_history_fts MATCH ? AND CAST(message_ordinal AS INTEGER) <= ?");
|
|
174597
|
+
ftsMatchCountStatementsWithCutoff.set(db, cached2);
|
|
174598
|
+
}
|
|
174599
|
+
return cached2;
|
|
174600
|
+
})();
|
|
174231
174601
|
try {
|
|
174232
|
-
const row = stmt.get(sessionId, ftsQuery);
|
|
174602
|
+
const row = cutoff === null ? stmt.get(sessionId, ftsQuery) : stmt.get(sessionId, ftsQuery, cutoff);
|
|
174233
174603
|
return typeof row?.n === "number" ? row.n : 0;
|
|
174234
174604
|
} catch {
|
|
174235
174605
|
return 0;
|
|
@@ -174479,7 +174849,7 @@ function searchMessages(args) {
|
|
|
174479
174849
|
role: row.role
|
|
174480
174850
|
}));
|
|
174481
174851
|
}
|
|
174482
|
-
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId);
|
|
174852
|
+
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId, cutoff);
|
|
174483
174853
|
const queryLists = [];
|
|
174484
174854
|
if (baseQuery.length > 0) {
|
|
174485
174855
|
queryLists.push({
|
|
@@ -174492,7 +174862,7 @@ function searchMessages(args) {
|
|
|
174492
174862
|
const probeQuery = sanitizeFtsQuery(probe);
|
|
174493
174863
|
if (probeQuery.length === 0)
|
|
174494
174864
|
continue;
|
|
174495
|
-
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery);
|
|
174865
|
+
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery, cutoff);
|
|
174496
174866
|
const weight = probeDiscriminationWeight(df, corpusSize);
|
|
174497
174867
|
probeWeights.set(probe, weight);
|
|
174498
174868
|
queryLists.push({
|
|
@@ -175667,17 +176037,119 @@ function createCtxSearchTool(deps) {
|
|
|
175667
176037
|
import {
|
|
175668
176038
|
truncateToWidth
|
|
175669
176039
|
} from "@earendil-works/pi-tui";
|
|
176040
|
+
|
|
176041
|
+
// ../plugin/src/hooks/magic-context/todo-view.ts
|
|
176042
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
176043
|
+
var TODO_STATUS_PENDING = "pending";
|
|
176044
|
+
var TODO_STATUS_IN_PROGRESS = "in_progress";
|
|
176045
|
+
var TODO_STATUS_COMPLETED = "completed";
|
|
176046
|
+
var TODO_STATUS_CANCELLED = "cancelled";
|
|
176047
|
+
var TODO_PRIORITY_HIGH = "high";
|
|
176048
|
+
var TODO_PRIORITY_MEDIUM = "medium";
|
|
176049
|
+
var TODO_PRIORITY_LOW = "low";
|
|
176050
|
+
var TODO_STATUSES = [
|
|
176051
|
+
TODO_STATUS_PENDING,
|
|
176052
|
+
TODO_STATUS_IN_PROGRESS,
|
|
176053
|
+
TODO_STATUS_COMPLETED,
|
|
176054
|
+
TODO_STATUS_CANCELLED
|
|
176055
|
+
];
|
|
176056
|
+
var TODO_PRIORITIES = [
|
|
176057
|
+
TODO_PRIORITY_HIGH,
|
|
176058
|
+
TODO_PRIORITY_MEDIUM,
|
|
176059
|
+
TODO_PRIORITY_LOW
|
|
176060
|
+
];
|
|
176061
|
+
var TODO_STATUS_SET = new Set(TODO_STATUSES);
|
|
176062
|
+
var TODO_PRIORITY_SET = new Set(TODO_PRIORITIES);
|
|
176063
|
+
var TERMINAL_STATUSES = new Set([
|
|
176064
|
+
TODO_STATUS_COMPLETED,
|
|
176065
|
+
TODO_STATUS_CANCELLED
|
|
176066
|
+
]);
|
|
176067
|
+
var TITLE_DONE_STATUSES = new Set([TODO_STATUS_COMPLETED]);
|
|
176068
|
+
var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
|
|
176069
|
+
function normalizeTodoStateJson(todos) {
|
|
176070
|
+
if (!Array.isArray(todos))
|
|
176071
|
+
return null;
|
|
176072
|
+
const normalized = [];
|
|
176073
|
+
for (const todo of todos) {
|
|
176074
|
+
if (!isTodoItem(todo))
|
|
176075
|
+
return null;
|
|
176076
|
+
normalized.push({
|
|
176077
|
+
content: todo.content,
|
|
176078
|
+
status: todo.status,
|
|
176079
|
+
priority: todo.priority ?? TODO_PRIORITY_MEDIUM
|
|
176080
|
+
});
|
|
176081
|
+
}
|
|
176082
|
+
return JSON.stringify(normalized);
|
|
176083
|
+
}
|
|
176084
|
+
function buildSyntheticTodoPart(stateJson) {
|
|
176085
|
+
const todos = parseTodoState(stateJson);
|
|
176086
|
+
if (todos === null || todos.length === 0)
|
|
176087
|
+
return null;
|
|
176088
|
+
if (todos.every((t) => TERMINAL_STATUSES.has(t.status)))
|
|
176089
|
+
return null;
|
|
176090
|
+
const callID = computeSyntheticCallId(stateJson);
|
|
176091
|
+
const activeCount = todos.filter((t) => !TITLE_DONE_STATUSES.has(t.status)).length;
|
|
176092
|
+
const output = JSON.stringify(todos, null, 2);
|
|
176093
|
+
const ts = 0;
|
|
176094
|
+
return {
|
|
176095
|
+
type: "tool",
|
|
176096
|
+
callID,
|
|
176097
|
+
tool: "todowrite",
|
|
176098
|
+
state: {
|
|
176099
|
+
status: "completed",
|
|
176100
|
+
input: { todos },
|
|
176101
|
+
output,
|
|
176102
|
+
title: `${activeCount} todos`,
|
|
176103
|
+
metadata: { todos, truncated: false },
|
|
176104
|
+
time: { start: ts, end: ts }
|
|
176105
|
+
},
|
|
176106
|
+
syntheticTodoMarker: true
|
|
176107
|
+
};
|
|
176108
|
+
}
|
|
176109
|
+
function computeSyntheticCallId(stateJson) {
|
|
176110
|
+
const hash2 = createHash8("sha256").update(stateJson).digest("hex").slice(0, 16);
|
|
176111
|
+
return `${SYNTHETIC_CALL_ID_PREFIX}${hash2}`;
|
|
176112
|
+
}
|
|
176113
|
+
function parseTodoState(stateJson) {
|
|
176114
|
+
if (stateJson.length === 0)
|
|
176115
|
+
return null;
|
|
176116
|
+
try {
|
|
176117
|
+
const parsed = JSON.parse(stateJson);
|
|
176118
|
+
if (!Array.isArray(parsed))
|
|
176119
|
+
return null;
|
|
176120
|
+
const result = [];
|
|
176121
|
+
for (const item of parsed) {
|
|
176122
|
+
if (!isTodoItem(item))
|
|
176123
|
+
return null;
|
|
176124
|
+
result.push({
|
|
176125
|
+
content: item.content,
|
|
176126
|
+
status: item.status,
|
|
176127
|
+
priority: item.priority ?? TODO_PRIORITY_MEDIUM
|
|
176128
|
+
});
|
|
176129
|
+
}
|
|
176130
|
+
return result;
|
|
176131
|
+
} catch {
|
|
176132
|
+
return null;
|
|
176133
|
+
}
|
|
176134
|
+
}
|
|
176135
|
+
function isTodoStatus(value) {
|
|
176136
|
+
return typeof value === "string" && TODO_STATUS_SET.has(value);
|
|
176137
|
+
}
|
|
176138
|
+
function isTodoPriority(value) {
|
|
176139
|
+
return typeof value === "string" && TODO_PRIORITY_SET.has(value);
|
|
176140
|
+
}
|
|
176141
|
+
function isTodoItem(value) {
|
|
176142
|
+
if (value === null || typeof value !== "object")
|
|
176143
|
+
return false;
|
|
176144
|
+
const todo = value;
|
|
176145
|
+
return typeof todo.content === "string" && isTodoStatus(todo.status) && (todo.priority === undefined || isTodoPriority(todo.priority));
|
|
176146
|
+
}
|
|
176147
|
+
|
|
176148
|
+
// src/tools/todo-view-pi.ts
|
|
175670
176149
|
var TODO_TOOL_NAME = "todowrite";
|
|
175671
176150
|
var TODOS_COMMAND_NAME = "todos";
|
|
175672
176151
|
var WIDGET_KEY = "magic-context-todos";
|
|
175673
|
-
var
|
|
175674
|
-
var TODO_STATUSES = [
|
|
175675
|
-
"pending",
|
|
175676
|
-
"in_progress",
|
|
175677
|
-
"completed",
|
|
175678
|
-
"cancelled"
|
|
175679
|
-
];
|
|
175680
|
-
var TODO_PRIORITIES = ["high", "medium", "low"];
|
|
176152
|
+
var MAX_TODO_CONTENT_ROWS = 12;
|
|
175681
176153
|
var STATUS_GLYPH = {
|
|
175682
176154
|
pending: "○",
|
|
175683
176155
|
in_progress: "◐",
|
|
@@ -175691,10 +176163,10 @@ var STATUS_COLOR = {
|
|
|
175691
176163
|
cancelled: "error"
|
|
175692
176164
|
};
|
|
175693
176165
|
var snapshotsBySession = new Map;
|
|
175694
|
-
function
|
|
176166
|
+
function isTodoStatus2(value) {
|
|
175695
176167
|
return TODO_STATUSES.includes(value);
|
|
175696
176168
|
}
|
|
175697
|
-
function
|
|
176169
|
+
function isTodoPriority2(value) {
|
|
175698
176170
|
return TODO_PRIORITIES.includes(value);
|
|
175699
176171
|
}
|
|
175700
176172
|
function cloneTodos(todos) {
|
|
@@ -175708,14 +176180,14 @@ function parseTodos(input) {
|
|
|
175708
176180
|
if (item === null || typeof item !== "object")
|
|
175709
176181
|
return null;
|
|
175710
176182
|
const raw = item;
|
|
175711
|
-
if (typeof raw.content !== "string" || !
|
|
176183
|
+
if (typeof raw.content !== "string" || !isTodoStatus2(raw.status)) {
|
|
175712
176184
|
return null;
|
|
175713
176185
|
}
|
|
175714
176186
|
const todo = {
|
|
175715
176187
|
content: raw.content,
|
|
175716
176188
|
status: raw.status
|
|
175717
176189
|
};
|
|
175718
|
-
if (
|
|
176190
|
+
if (isTodoPriority2(raw.priority))
|
|
175719
176191
|
todo.priority = raw.priority;
|
|
175720
176192
|
if (typeof raw.id === "string" && raw.id.length > 0)
|
|
175721
176193
|
todo.id = raw.id;
|
|
@@ -175792,7 +176264,7 @@ function countTodos(todos) {
|
|
|
175792
176264
|
return counts;
|
|
175793
176265
|
}
|
|
175794
176266
|
function activeTitleCount(todos) {
|
|
175795
|
-
return todos.filter((todo) => todo.status
|
|
176267
|
+
return todos.filter((todo) => !TITLE_DONE_STATUSES.has(todo.status)).length;
|
|
175796
176268
|
}
|
|
175797
176269
|
function formatCounts(counts) {
|
|
175798
176270
|
if (counts.total === 0)
|
|
@@ -175826,6 +176298,16 @@ function lineComponent(renderLines) {
|
|
|
175826
176298
|
invalidate() {}
|
|
175827
176299
|
};
|
|
175828
176300
|
}
|
|
176301
|
+
function capTodoRows(rows) {
|
|
176302
|
+
if (rows.length <= MAX_TODO_CONTENT_ROWS) {
|
|
176303
|
+
return { visible: [...rows], hiddenCount: 0 };
|
|
176304
|
+
}
|
|
176305
|
+
const visibleCount = Math.max(0, MAX_TODO_CONTENT_ROWS - 1);
|
|
176306
|
+
return {
|
|
176307
|
+
visible: rows.slice(0, visibleCount),
|
|
176308
|
+
hiddenCount: rows.length - visibleCount
|
|
176309
|
+
};
|
|
176310
|
+
}
|
|
175829
176311
|
function renderTodowriteCall(args, theme) {
|
|
175830
176312
|
const todos = parseTodos(args.todos) ?? [];
|
|
175831
176313
|
const active = activeTitleCount(todos);
|
|
@@ -175848,7 +176330,12 @@ function renderTodowriteResult(result, theme) {
|
|
|
175848
176330
|
return lineComponent((width) => {
|
|
175849
176331
|
if (renderedTodos.length === 0)
|
|
175850
176332
|
return [theme.fg("dim", "No todos")];
|
|
175851
|
-
|
|
176333
|
+
const { visible, hiddenCount } = capTodoRows(renderedTodos);
|
|
176334
|
+
const lines = visible.map((todo) => truncateToWidth(formatTodoLine(todo, theme, { showId: true }), width, "…"));
|
|
176335
|
+
if (hiddenCount > 0) {
|
|
176336
|
+
lines.push(truncateToWidth(theme.fg("dim", `+${hiddenCount} more`), width, "…"));
|
|
176337
|
+
}
|
|
176338
|
+
return lines;
|
|
175852
176339
|
});
|
|
175853
176340
|
}
|
|
175854
176341
|
function registerTodosCommand(pi) {
|
|
@@ -175878,8 +176365,11 @@ function registerTodosCommand(pi) {
|
|
|
175878
176365
|
if (group.length === 0)
|
|
175879
176366
|
continue;
|
|
175880
176367
|
lines.push(heading);
|
|
175881
|
-
|
|
176368
|
+
const { visible, hiddenCount } = capTodoRows(group);
|
|
176369
|
+
for (const todo of visible)
|
|
175882
176370
|
lines.push(formatCommandLine(todo));
|
|
176371
|
+
if (hiddenCount > 0)
|
|
176372
|
+
lines.push(` +${hiddenCount} more`);
|
|
175883
176373
|
}
|
|
175884
176374
|
ctx.ui.notify(lines.join(`
|
|
175885
176375
|
`), "info");
|
|
@@ -176007,19 +176497,16 @@ class TodoOverlay {
|
|
|
176007
176497
|
const heading = `${theme.fg(headingColor, headingIcon)} ${theme.fg(headingColor, "Todos")} ${theme.fg("muted", "—")} ${theme.fg("dim", formatCounts(counts))}`;
|
|
176008
176498
|
const truncate2 = (line) => truncateToWidth(line, width, "…");
|
|
176009
176499
|
const lines = [truncate2(heading)];
|
|
176010
|
-
const
|
|
176011
|
-
const visibleRows = hasTruncatedTail ? MAX_OVERLAY_CONTENT_ROWS - 1 : MAX_OVERLAY_CONTENT_ROWS;
|
|
176012
|
-
const truncatedTail = Math.max(0, overlayTodos.length - visibleRows);
|
|
176013
|
-
const visible = overlayTodos.slice(0, visibleRows);
|
|
176500
|
+
const { visible, hiddenCount } = capTodoRows(overlayTodos);
|
|
176014
176501
|
for (const [index, { todo, key }] of visible.entries()) {
|
|
176015
|
-
const isLast = index === visible.length - 1 &&
|
|
176502
|
+
const isLast = index === visible.length - 1 && hiddenCount === 0;
|
|
176016
176503
|
const branch = theme.fg("dim", isLast ? "└─" : "├─");
|
|
176017
176504
|
lines.push(truncate2(`${branch} ${formatTodoLine(todo, theme, { showId: true })}`));
|
|
176018
176505
|
if (todo.status === "completed")
|
|
176019
176506
|
this.completedTaskIdsPendingHide.add(key);
|
|
176020
176507
|
}
|
|
176021
|
-
if (
|
|
176022
|
-
lines.push(truncate2(`${theme.fg("dim", "└─")} ${theme.fg("dim", `+${
|
|
176508
|
+
if (hiddenCount > 0) {
|
|
176509
|
+
lines.push(truncate2(`${theme.fg("dim", "└─")} ${theme.fg("dim", `+${hiddenCount} more`)}`));
|
|
176023
176510
|
}
|
|
176024
176511
|
return lines;
|
|
176025
176512
|
}
|
|
@@ -176075,17 +176562,10 @@ function registerTodoStateLifecycle(pi, deps) {
|
|
|
176075
176562
|
}
|
|
176076
176563
|
|
|
176077
176564
|
// src/tools/todowrite.ts
|
|
176078
|
-
var STATUS_VALUES = [
|
|
176079
|
-
"pending",
|
|
176080
|
-
"in_progress",
|
|
176081
|
-
"completed",
|
|
176082
|
-
"cancelled"
|
|
176083
|
-
];
|
|
176084
|
-
var PRIORITY_VALUES = ["high", "medium", "low"];
|
|
176085
176565
|
var TodoItem = exports_typebox.Object({
|
|
176086
176566
|
content: exports_typebox.String({ description: "Brief description of the task" }),
|
|
176087
|
-
status: exports_typebox.Union(
|
|
176088
|
-
priority: exports_typebox.Optional(exports_typebox.Union(
|
|
176567
|
+
status: exports_typebox.Union(TODO_STATUSES.map((v) => exports_typebox.Literal(v))),
|
|
176568
|
+
priority: exports_typebox.Optional(exports_typebox.Union(TODO_PRIORITIES.map((v) => exports_typebox.Literal(v)))),
|
|
176089
176569
|
id: exports_typebox.Optional(exports_typebox.String({ description: "Optional stable id for the todo" }))
|
|
176090
176570
|
});
|
|
176091
176571
|
var TodowriteParams = exports_typebox.Object({
|
|
@@ -176110,8 +176590,7 @@ function createTodowriteTool() {
|
|
|
176110
176590
|
parameters: TodowriteParams,
|
|
176111
176591
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
176112
176592
|
const todos = params.todos ?? [];
|
|
176113
|
-
const
|
|
176114
|
-
const active = todos.length - completed;
|
|
176593
|
+
const active = todos.filter((todo) => !TITLE_DONE_STATUSES.has(todo.status)).length;
|
|
176115
176594
|
return {
|
|
176116
176595
|
content: [
|
|
176117
176596
|
{
|