@rubytech/create-maxy-code 0.1.321 → 0.1.322
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 +82 -1
- package/package.json +1 -1
- package/payload/platform/lib/embed-client/dist/index.d.ts +2 -0
- package/payload/platform/lib/embed-client/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/embed-client/dist/index.js +50 -10
- package/payload/platform/lib/embed-client/dist/index.js.map +1 -1
- package/payload/platform/lib/embed-client/src/index.ts +57 -9
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +4 -1
- package/payload/platform/plugins/docs/references/deployment.md +1 -0
- package/payload/platform/plugins/docs/references/internals.md +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.js +41 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-body-server-sliced.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +34 -4
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +75 -2
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +13 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +16 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-DKwWudsP.js → AdminLoginScreens-D1X4M9wX.js} +1 -1
- package/payload/server/public/assets/{AdminShell-BsPsXvJv.js → AdminShell-BGZ3d3gY.js} +1 -1
- package/payload/server/public/assets/{Checkbox-V5ubVZG1.js → Checkbox-DjANdKoK.js} +1 -1
- package/payload/server/public/assets/{OperatorConversations-DOGCHZhv.css → OperatorConversations-C-eFXT7w.css} +1 -1
- package/payload/server/public/assets/{admin-Cfqj5N4P.js → admin--FlkQOMu.js} +1 -1
- package/payload/server/public/assets/{browser-CtMwlpyW.js → browser-vowQc0TJ.js} +1 -1
- package/payload/server/public/assets/chat-Cw6rUWiC.js +1 -0
- package/payload/server/public/assets/{data-7_dN5vN5.js → data-9ArhwX1q.js} +1 -1
- package/payload/server/public/assets/{graph-Bez5QF37.js → graph-B-64Cq-y.js} +1 -1
- package/payload/server/public/assets/{graph-labels-BkbaBPIP.js → graph-labels-Dtd2ckXH.js} +1 -1
- package/payload/server/public/assets/{operator-F5KHg5WG.js → operator-Cqn1Ne60.js} +1 -1
- package/payload/server/public/assets/page-DQ5D6Dg9.js +30 -0
- package/payload/server/public/assets/{public-wy93nfmL.js → public-CrFepd06.js} +1 -1
- package/payload/server/public/browser.html +4 -4
- package/payload/server/public/chat.html +5 -5
- package/payload/server/public/data.html +4 -4
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +6 -6
- package/payload/server/public/operator.html +7 -7
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +185 -12
- package/payload/server/public/assets/chat-Bm11OGsU.js +0 -1
- package/payload/server/public/assets/page-CExyEcvh.js +0 -30
- /package/payload/server/public/assets/{OperatorConversations-DPRRlS_z.js → OperatorConversations-BkitSIiT.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import { classifyPortHolder } from "./preflight-port-classifier.js";
|
|
|
20
20
|
import { parsePluginList, computeInstallActions, parseExternalPlugins, findUnregisteredResyncs, } from "./lib/plugin-install.js";
|
|
21
21
|
import { findPremiumMcpDirs } from "./lib/premium-mcp-discover.js";
|
|
22
22
|
import { pickBindDecision, mergeSmbConf, formatSambaMarker, SAMBA_ENABLE_UNITS, } from "./samba-provision.js";
|
|
23
|
-
import { networkInterfaces, userInfo } from "node:os";
|
|
23
|
+
import { networkInterfaces, userInfo, cpus } from "node:os";
|
|
24
24
|
const PAYLOAD_DIR = resolve(import.meta.dirname, "../payload");
|
|
25
25
|
// Brand manifest — read from payload to derive all brand-specific installation values.
|
|
26
26
|
// The bundler stamps brand.json into the payload at build time.
|
|
@@ -1557,6 +1557,84 @@ function ensureOllamaServing() {
|
|
|
1557
1557
|
logFile(` Ollama serve timed out after 30s. Log tail:\n${serveLog}`);
|
|
1558
1558
|
throw new Error(`Ollama server did not respond within 30s. Log: ${ollamaLog}\n${serveLog}`);
|
|
1559
1559
|
}
|
|
1560
|
+
// Cap ollama's CPU so an embedding burst cannot thermally hard-reset a
|
|
1561
|
+
// passively-cooled Pi. The platform embeds a document's summary plus every
|
|
1562
|
+
// section body via one `/api/embed` POST; ollama's llama-server then runs
|
|
1563
|
+
// flat-out across every core for the whole batch with no idle gaps, and on a
|
|
1564
|
+
// passively-cooled Pi 5 the SoC climbs past its soft-temp limit until the board
|
|
1565
|
+
// reaches its thermal ceiling and hard-resets. A systemd `CPUQuota` drop-in on
|
|
1566
|
+
// `ollama.service` bounds llama-server to a fraction of the cores so peak
|
|
1567
|
+
// temperature plateaus below the limit regardless of batch shape or which
|
|
1568
|
+
// platform path issues the embeds — the caller-agnostic fix, inherited by every
|
|
1569
|
+
// install. Quota is half the cores (rounded down, min 1) so at least half stay
|
|
1570
|
+
// idle for the thermal budget: 200% on a 4-core Pi. Applied only when the ollama
|
|
1571
|
+
// system service exists (the normal Linux path, where ollama's install.sh
|
|
1572
|
+
// registers `ollama.service`); a detached `ollama serve` has no unit to cap.
|
|
1573
|
+
// ollama is one shared host service across co-resident brands, so the drop-in is
|
|
1574
|
+
// host-wide with a neutral filename — idempotent across brands and reinstalls,
|
|
1575
|
+
// and ollama restarts only when the drop-in content actually changes. Best-
|
|
1576
|
+
// effort: a failure warns, never aborts the install.
|
|
1577
|
+
function capOllamaServiceCpu() {
|
|
1578
|
+
try {
|
|
1579
|
+
const svcCheck = spawnSync("systemctl", ["cat", "ollama"], { stdio: "pipe", timeout: 5_000 });
|
|
1580
|
+
if (svcCheck.status !== 0) {
|
|
1581
|
+
logFile(" ollama CPU cap: skipped (no ollama system service to cap)");
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
const coreCount = Math.max(1, cpus().length);
|
|
1585
|
+
const quotaCores = Math.max(1, Math.floor(coreCount / 2));
|
|
1586
|
+
const quotaPct = quotaCores * 100;
|
|
1587
|
+
const desired = `[Service]\nCPUQuota=${quotaPct}%\n`;
|
|
1588
|
+
const dropinDir = "/etc/systemd/system/ollama.service.d";
|
|
1589
|
+
const dropinPath = `${dropinDir}/10-maxy-cpuquota.conf`;
|
|
1590
|
+
let current = "";
|
|
1591
|
+
if (existsSync(dropinPath)) {
|
|
1592
|
+
try {
|
|
1593
|
+
current = readFileSync(dropinPath, "utf-8");
|
|
1594
|
+
}
|
|
1595
|
+
catch {
|
|
1596
|
+
// Unreadable existing drop-in — treat as changed so we rewrite + restart.
|
|
1597
|
+
current = "";
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
if (current === desired) {
|
|
1601
|
+
logFile(` ollama CPU cap: already active (${dropinPath} matches CPUQuota=${quotaPct}%)`);
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
const tmpPath = "/tmp/10-maxy-ollama-cpuquota.conf";
|
|
1605
|
+
writeFileSync(tmpPath, desired);
|
|
1606
|
+
console.log(` [privileged] install ollama CPUQuota=${quotaPct}% drop-in (${quotaCores}/${coreCount} cores)`);
|
|
1607
|
+
shell("mkdir", ["-p", dropinDir], { sudo: true });
|
|
1608
|
+
shell("cp", [tmpPath, dropinPath], { sudo: true });
|
|
1609
|
+
spawnSync("rm", ["-f", tmpPath]);
|
|
1610
|
+
spawnSync("sudo", ["systemctl", "daemon-reload"], { stdio: "inherit" });
|
|
1611
|
+
console.log(" [privileged] systemctl restart ollama");
|
|
1612
|
+
spawnSync("sudo", ["systemctl", "restart", "ollama"], { stdio: "inherit", timeout: 30_000 });
|
|
1613
|
+
// The restart briefly drops the API. Wait for ollama to serve again before
|
|
1614
|
+
// returning so the immediately-following ensureOllamaServing() probe finds
|
|
1615
|
+
// it up and does not race the restart by spawning a second detached
|
|
1616
|
+
// `ollama serve` that would contend for port 11434. Bounded poll; the drop-in
|
|
1617
|
+
// is already on disk either way, so a timeout still applies the cap on the
|
|
1618
|
+
// next ollama start.
|
|
1619
|
+
let served = false;
|
|
1620
|
+
for (let elapsed = 0; elapsed < 15; elapsed += 3) {
|
|
1621
|
+
if (spawnSync("curl", ["-sf", "http://localhost:11434/api/tags"], { stdio: "pipe", timeout: 5_000 }).status === 0) {
|
|
1622
|
+
served = true;
|
|
1623
|
+
break;
|
|
1624
|
+
}
|
|
1625
|
+
spawnSync("sleep", ["3"]);
|
|
1626
|
+
}
|
|
1627
|
+
if (served) {
|
|
1628
|
+
logFile(` ollama CPU cap: enabled (wrote ${dropinPath} CPUQuota=${quotaPct}%, ollama serving again under the cap)`);
|
|
1629
|
+
}
|
|
1630
|
+
else {
|
|
1631
|
+
logFile(` ollama CPU cap: drop-in written (${dropinPath} CPUQuota=${quotaPct}%); ollama not serving yet after restart — cap applies on its next start`);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
catch (err) {
|
|
1635
|
+
console.error(` WARNING: failed to cap ollama CPU: ${err instanceof Error ? err.message : String(err)}`);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1560
1638
|
function installOllama(embedModel) {
|
|
1561
1639
|
if (!commandExists("ollama")) {
|
|
1562
1640
|
log("5", TOTAL, "Installing Ollama...");
|
|
@@ -1565,6 +1643,9 @@ function installOllama(embedModel) {
|
|
|
1565
1643
|
else {
|
|
1566
1644
|
log("5", TOTAL, "Ollama already installed.");
|
|
1567
1645
|
}
|
|
1646
|
+
// Bound ollama's CPU before bringing the service up so the capped unit is what
|
|
1647
|
+
// serves embeds (and the restart below lands the new quota).
|
|
1648
|
+
capOllamaServiceCpu();
|
|
1568
1649
|
ensureOllamaServing();
|
|
1569
1650
|
console.log(` Pulling ${embedModel} embedding model...`);
|
|
1570
1651
|
logFile(` Pulling embedding model: ${embedModel}`);
|
package/package.json
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export declare const EMBED_INPUT_MAX_CHARS = 8000;
|
|
2
|
+
export declare const EMBED_SUBBATCH_SIZE: number;
|
|
3
|
+
export declare const EMBED_SUBBATCH_COOLDOWN_MS: number;
|
|
2
4
|
export declare function embed(text: string): Promise<number[]>;
|
|
3
5
|
export declare function embedBatch(texts: string[]): Promise<number[][]>;
|
|
4
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,qBAAqB,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAmB1C,eAAO,MAAM,mBAAmB,QAA6D,CAAC;AAC9F,eAAO,MAAM,0BAA0B,QAGtC,CAAC;AA4BF,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAa3D;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAoBrE"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// and one POST shape across the platform (Task 636). fetch-only — no node:fs —
|
|
7
7
|
// so it is safe to bundle into the ESM payload.
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.EMBED_INPUT_MAX_CHARS = void 0;
|
|
9
|
+
exports.EMBED_SUBBATCH_COOLDOWN_MS = exports.EMBED_SUBBATCH_SIZE = exports.EMBED_INPUT_MAX_CHARS = void 0;
|
|
10
10
|
exports.embed = embed;
|
|
11
11
|
exports.embedBatch = embedBatch;
|
|
12
12
|
const OLLAMA_URL = process.env.OLLAMA_URL ?? "http://localhost:11434";
|
|
@@ -16,28 +16,49 @@ const EMBED_MODEL = process.env.EMBED_MODEL ?? "nomic-embed-text";
|
|
|
16
16
|
// an oversized body embeds (truncated) instead of throwing and aborting the
|
|
17
17
|
// caller.
|
|
18
18
|
exports.EMBED_INPUT_MAX_CHARS = 8000;
|
|
19
|
+
// Batch pacing (Task 960). On a passively-cooled Pi, embedding a whole
|
|
20
|
+
// document's sections in one `/api/embed` POST pins ollama's llama-server
|
|
21
|
+
// across every core for the entire batch with no idle gaps — the SoC climbs
|
|
22
|
+
// past its soft-temp limit and the board thermally hard-resets. embedBatch
|
|
23
|
+
// splits an oversized input array into sub-batches and waits a cooldown between
|
|
24
|
+
// them so the SoC gets cooling gaps. Centralising here means every batch caller
|
|
25
|
+
// (memory-ingest, memory-archive-write, memory-reindex) inherits the pacing.
|
|
26
|
+
// The systemd CPUQuota cap on ollama.service is the load-bearing thermal fix;
|
|
27
|
+
// this pacing is defence-in-depth and faster recovery. Both knobs are
|
|
28
|
+
// env-overridable so an operator can tune them from on-device thermal
|
|
29
|
+
// measurement without a code change.
|
|
30
|
+
function envInt(name, def) {
|
|
31
|
+
const raw = process.env[name];
|
|
32
|
+
if (raw === undefined || raw === "")
|
|
33
|
+
return def;
|
|
34
|
+
const n = Number(raw);
|
|
35
|
+
return Number.isFinite(n) ? n : def;
|
|
36
|
+
}
|
|
37
|
+
exports.EMBED_SUBBATCH_SIZE = Math.max(1, Math.floor(envInt("EMBED_SUBBATCH_SIZE", 32)));
|
|
38
|
+
exports.EMBED_SUBBATCH_COOLDOWN_MS = Math.max(0, envInt("EMBED_SUBBATCH_COOLDOWN_MS", 1000));
|
|
19
39
|
function capInput(text) {
|
|
20
40
|
if (text.length <= exports.EMBED_INPUT_MAX_CHARS)
|
|
21
41
|
return text;
|
|
22
42
|
process.stderr.write(`[embed] op=truncate origBytes=${text.length} cappedBytes=${exports.EMBED_INPUT_MAX_CHARS}\n`);
|
|
23
43
|
return text.slice(0, exports.EMBED_INPUT_MAX_CHARS);
|
|
24
44
|
}
|
|
25
|
-
|
|
26
|
-
|
|
45
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
46
|
+
// POST one already-capped sub-batch of inputs to the embed endpoint.
|
|
47
|
+
async function postBatch(inputs) {
|
|
27
48
|
const res = await fetch(`${OLLAMA_URL}/api/embed`, {
|
|
28
49
|
method: "POST",
|
|
29
50
|
headers: { "Content-Type": "application/json" },
|
|
30
|
-
body: JSON.stringify({ model: EMBED_MODEL, input }),
|
|
51
|
+
body: JSON.stringify({ model: EMBED_MODEL, input: inputs }),
|
|
31
52
|
});
|
|
32
53
|
if (!res.ok) {
|
|
33
54
|
const body = await res.text();
|
|
34
|
-
throw new Error(`Ollama embedding failed (${res.status}): ${body}`);
|
|
55
|
+
throw new Error(`Ollama batch embedding failed (${res.status}): ${body}`);
|
|
35
56
|
}
|
|
36
57
|
const data = (await res.json());
|
|
37
|
-
return data.embeddings
|
|
58
|
+
return data.embeddings;
|
|
38
59
|
}
|
|
39
|
-
async function
|
|
40
|
-
const input =
|
|
60
|
+
async function embed(text) {
|
|
61
|
+
const input = capInput(text);
|
|
41
62
|
const res = await fetch(`${OLLAMA_URL}/api/embed`, {
|
|
42
63
|
method: "POST",
|
|
43
64
|
headers: { "Content-Type": "application/json" },
|
|
@@ -45,9 +66,28 @@ async function embedBatch(texts) {
|
|
|
45
66
|
});
|
|
46
67
|
if (!res.ok) {
|
|
47
68
|
const body = await res.text();
|
|
48
|
-
throw new Error(`Ollama
|
|
69
|
+
throw new Error(`Ollama embedding failed (${res.status}): ${body}`);
|
|
49
70
|
}
|
|
50
71
|
const data = (await res.json());
|
|
51
|
-
return data.embeddings;
|
|
72
|
+
return data.embeddings[0];
|
|
73
|
+
}
|
|
74
|
+
async function embedBatch(texts) {
|
|
75
|
+
const inputs = texts.map(capInput);
|
|
76
|
+
// Common case — an array within the sub-batch size is one POST, unchanged.
|
|
77
|
+
if (inputs.length <= exports.EMBED_SUBBATCH_SIZE) {
|
|
78
|
+
return postBatch(inputs);
|
|
79
|
+
}
|
|
80
|
+
// Oversized: split into sub-batches, cooling between each (not after the
|
|
81
|
+
// last). Vectors are concatenated in input order so callers can keep their
|
|
82
|
+
// index-aligned `embeddings[i]` ↔ `texts[i]` contract.
|
|
83
|
+
const out = [];
|
|
84
|
+
for (let i = 0; i < inputs.length; i += exports.EMBED_SUBBATCH_SIZE) {
|
|
85
|
+
if (i > 0 && exports.EMBED_SUBBATCH_COOLDOWN_MS > 0) {
|
|
86
|
+
await sleep(exports.EMBED_SUBBATCH_COOLDOWN_MS);
|
|
87
|
+
}
|
|
88
|
+
const chunk = inputs.slice(i, i + exports.EMBED_SUBBATCH_SIZE);
|
|
89
|
+
out.push(...(await postBatch(chunk)));
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
52
92
|
}
|
|
53
93
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,yEAAyE;AACzE,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,+EAA+E;AAC/E,gDAAgD;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,29 @@ const EMBED_MODEL = process.env.EMBED_MODEL ?? "nomic-embed-text";
|
|
|
14
14
|
// caller.
|
|
15
15
|
export const EMBED_INPUT_MAX_CHARS = 8000;
|
|
16
16
|
|
|
17
|
+
// Batch pacing (Task 960). On a passively-cooled Pi, embedding a whole
|
|
18
|
+
// document's sections in one `/api/embed` POST pins ollama's llama-server
|
|
19
|
+
// across every core for the entire batch with no idle gaps — the SoC climbs
|
|
20
|
+
// past its soft-temp limit and the board thermally hard-resets. embedBatch
|
|
21
|
+
// splits an oversized input array into sub-batches and waits a cooldown between
|
|
22
|
+
// them so the SoC gets cooling gaps. Centralising here means every batch caller
|
|
23
|
+
// (memory-ingest, memory-archive-write, memory-reindex) inherits the pacing.
|
|
24
|
+
// The systemd CPUQuota cap on ollama.service is the load-bearing thermal fix;
|
|
25
|
+
// this pacing is defence-in-depth and faster recovery. Both knobs are
|
|
26
|
+
// env-overridable so an operator can tune them from on-device thermal
|
|
27
|
+
// measurement without a code change.
|
|
28
|
+
function envInt(name: string, def: number): number {
|
|
29
|
+
const raw = process.env[name];
|
|
30
|
+
if (raw === undefined || raw === "") return def;
|
|
31
|
+
const n = Number(raw);
|
|
32
|
+
return Number.isFinite(n) ? n : def;
|
|
33
|
+
}
|
|
34
|
+
export const EMBED_SUBBATCH_SIZE = Math.max(1, Math.floor(envInt("EMBED_SUBBATCH_SIZE", 32)));
|
|
35
|
+
export const EMBED_SUBBATCH_COOLDOWN_MS = Math.max(
|
|
36
|
+
0,
|
|
37
|
+
envInt("EMBED_SUBBATCH_COOLDOWN_MS", 1000),
|
|
38
|
+
);
|
|
39
|
+
|
|
17
40
|
function capInput(text: string): string {
|
|
18
41
|
if (text.length <= EMBED_INPUT_MAX_CHARS) return text;
|
|
19
42
|
process.stderr.write(
|
|
@@ -22,23 +45,26 @@ function capInput(text: string): string {
|
|
|
22
45
|
return text.slice(0, EMBED_INPUT_MAX_CHARS);
|
|
23
46
|
}
|
|
24
47
|
|
|
25
|
-
|
|
26
|
-
|
|
48
|
+
const sleep = (ms: number): Promise<void> =>
|
|
49
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
50
|
+
|
|
51
|
+
// POST one already-capped sub-batch of inputs to the embed endpoint.
|
|
52
|
+
async function postBatch(inputs: string[]): Promise<number[][]> {
|
|
27
53
|
const res = await fetch(`${OLLAMA_URL}/api/embed`, {
|
|
28
54
|
method: "POST",
|
|
29
55
|
headers: { "Content-Type": "application/json" },
|
|
30
|
-
body: JSON.stringify({ model: EMBED_MODEL, input }),
|
|
56
|
+
body: JSON.stringify({ model: EMBED_MODEL, input: inputs }),
|
|
31
57
|
});
|
|
32
58
|
if (!res.ok) {
|
|
33
59
|
const body = await res.text();
|
|
34
|
-
throw new Error(`Ollama embedding failed (${res.status}): ${body}`);
|
|
60
|
+
throw new Error(`Ollama batch embedding failed (${res.status}): ${body}`);
|
|
35
61
|
}
|
|
36
62
|
const data = (await res.json()) as { embeddings: number[][] };
|
|
37
|
-
return data.embeddings
|
|
63
|
+
return data.embeddings;
|
|
38
64
|
}
|
|
39
65
|
|
|
40
|
-
export async function
|
|
41
|
-
const input =
|
|
66
|
+
export async function embed(text: string): Promise<number[]> {
|
|
67
|
+
const input = capInput(text);
|
|
42
68
|
const res = await fetch(`${OLLAMA_URL}/api/embed`, {
|
|
43
69
|
method: "POST",
|
|
44
70
|
headers: { "Content-Type": "application/json" },
|
|
@@ -46,8 +72,30 @@ export async function embedBatch(texts: string[]): Promise<number[][]> {
|
|
|
46
72
|
});
|
|
47
73
|
if (!res.ok) {
|
|
48
74
|
const body = await res.text();
|
|
49
|
-
throw new Error(`Ollama
|
|
75
|
+
throw new Error(`Ollama embedding failed (${res.status}): ${body}`);
|
|
50
76
|
}
|
|
51
77
|
const data = (await res.json()) as { embeddings: number[][] };
|
|
52
|
-
return data.embeddings;
|
|
78
|
+
return data.embeddings[0];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function embedBatch(texts: string[]): Promise<number[][]> {
|
|
82
|
+
const inputs = texts.map(capInput);
|
|
83
|
+
|
|
84
|
+
// Common case — an array within the sub-batch size is one POST, unchanged.
|
|
85
|
+
if (inputs.length <= EMBED_SUBBATCH_SIZE) {
|
|
86
|
+
return postBatch(inputs);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Oversized: split into sub-batches, cooling between each (not after the
|
|
90
|
+
// last). Vectors are concatenated in input order so callers can keep their
|
|
91
|
+
// index-aligned `embeddings[i]` ↔ `texts[i]` contract.
|
|
92
|
+
const out: number[][] = [];
|
|
93
|
+
for (let i = 0; i < inputs.length; i += EMBED_SUBBATCH_SIZE) {
|
|
94
|
+
if (i > 0 && EMBED_SUBBATCH_COOLDOWN_MS > 0) {
|
|
95
|
+
await sleep(EMBED_SUBBATCH_COOLDOWN_MS);
|
|
96
|
+
}
|
|
97
|
+
const chunk = inputs.slice(i, i + EMBED_SUBBATCH_SIZE);
|
|
98
|
+
out.push(...(await postBatch(chunk)));
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
53
101
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:009036cb2953a53e5a5d85f5cdf3399d4a7dd12c40fa17e03ebcb46ebeec87a7
|
|
5
5
|
brand: maxy-code
|
|
6
6
|
product-name: Maxy
|
|
7
7
|
---
|
|
@@ -2995,6 +2995,8 @@ If Ollama is unavailable at write time, the node is stored without an embedding
|
|
|
2995
2995
|
|
|
2996
2996
|
The `memory-reindex` tool backfills missing embeddings by iterating nodes where `embedding IS NULL`. It uses the bounded representation (the `summary` for document/archive labels, never the raw body), embeds in batches with a per-node fallback on batch error, and **skips and continues** past a failing node — keeping skipped nodes out of the re-fetch via an exclude set — instead of aborting the whole run. `embed()`/`embedBatch()` cap oversized input (`EMBED_INPUT_MAX_CHARS`) before the Ollama POST, so a large body embeds truncated rather than throwing. Per-node `[reindex] op=embedded|skip-error` lines and a terminal `[reindex] op=done processed=N skipped=M`.
|
|
2997
2997
|
|
|
2998
|
+
`embedBatch()` also paces large batches. A document's summary plus every section body in one `/api/embed` POST pins ollama's `llama-server` across all cores for the whole batch with no idle gaps; on a passively-cooled Pi the SoC climbs past its soft-temp limit and the board thermally hard-resets. `embedBatch` splits an input array longer than `EMBED_SUBBATCH_SIZE` (default 32) into sub-batches, waiting `EMBED_SUBBATCH_COOLDOWN_MS` (default 1000) between them so the SoC gets cooling gaps; vectors are concatenated in input order. Both knobs are env-overridable for on-device tuning. The load-bearing thermal protection is the installer's `CPUQuota` cap on `ollama.service` (every embed caller is bounded there regardless of batch shape); this pacing is defence-in-depth. The UI's file-index path uses its own single-input embed client in `platform/ui/app/lib/neo4j-store.ts` (one embed per file, not a batch), so it is covered by the CPU cap, not by `embedBatch` pacing.
|
|
2999
|
+
|
|
2998
3000
|
---
|
|
2999
3001
|
|
|
3000
3002
|
## Knowledge Document Hierarchy
|
|
@@ -3703,6 +3705,7 @@ A single Pi or laptop can host more than one brand (for example Maxy and Real Ag
|
|
|
3703
3705
|
- **Brand-isolated Neo4j:** when a brand provisions a dedicated Neo4j instance (any port other than 7687), the installer stops and disables the apt-package's system `neo4j.service` after enabling the brand-dedicated unit, so only one Neo4j process holds the shared `/var/lib/neo4j/run/` PID file. The seed step receives the brand-correct `NEO4J_URI` and `NEO4J_PASSWORD` as explicit environment variables — the seed script no longer carries a `bolt://localhost:7687` default. A failed dedicated start aborts the install loudly with a journalctl tail; there is no silent fallback to the system instance. Stop/disable targets the literal `neo4j.service` only, so peer brands running their own `neo4j-{brand}.service` are unaffected.
|
|
3704
3706
|
- **Peer-aware system-unit guard:** before stopping the system `neo4j.service`, the installer checks whether any other brand on the device still depends on it — that is, has `NEO4J_URI=bolt://localhost:7687` in its `~/.<peer>/.env`. If so, the system unit is left enabled and active, and the install log shows `[neo4j] system unit kept active — peer brand <name> depends on port 7687` instead of the usual `[neo4j] disabling system unit` line. This prevents a `create-realagent` install from disabling Maxy's database on a host where Maxy still uses the shared system instance (the earlier platform fixes reproducer on Neo's laptop, 2026-04-28). On single-brand hosts and on multi-brand hosts where every peer runs a dedicated port, behaviour is unchanged. The dedicated unit exports `NEO4J_HOME=<per-brand-data-dir>` alongside `NEO4J_CONF`, so `server.directories.run`, `server.directories.plugins`, and `server.directories.import` resolve per-brand — no collision with `/var/lib/neo4j/run/neo4j.pid`. The conf sed-overrides, mkdir-p, chown, and unit-write are idempotent and re-run on every install, so a host whose prior install left a broken unit recovers on retry.
|
|
3705
3707
|
- **Shared:** both brands share the system Chromium/VNC stack, the Ollama model server, and the `cloudflared` command itself. Browser automation is serialised — one admin session at a time across both brands.
|
|
3708
|
+
- **Thermal cap on shared Ollama:** the installer writes a host-wide systemd `CPUQuota` drop-in (`/etc/systemd/system/ollama.service.d/10-maxy-cpuquota.conf`) bounding `ollama.service` to half the cores (200% on a 4-core Pi). An embedding burst — a document's summary plus every section body in one POST — otherwise pins `llama-server` across all cores with no idle gaps until a passively-cooled Pi crosses its soft-temp limit and hard-resets. The cap keeps peak temperature below the limit regardless of which brand or platform path issues the embeds. The drop-in has a neutral filename and is rewritten only when its content changes, so it is idempotent across brands and reinstalls. Applied only where the `ollama.service` system unit exists; a detached `ollama serve` has no unit to cap.
|
|
3706
3709
|
|
|
3707
3710
|
To install a second brand on a device that already runs the first, just run the other installer. No flags needed for isolation:
|
|
3708
3711
|
|
|
@@ -253,6 +253,7 @@ A single Pi or laptop can host more than one brand (for example Maxy and Real Ag
|
|
|
253
253
|
- **Brand-isolated Neo4j:** when a brand provisions a dedicated Neo4j instance (any port other than 7687), the installer stops and disables the apt-package's system `neo4j.service` after enabling the brand-dedicated unit, so only one Neo4j process holds the shared `/var/lib/neo4j/run/` PID file. The seed step receives the brand-correct `NEO4J_URI` and `NEO4J_PASSWORD` as explicit environment variables — the seed script no longer carries a `bolt://localhost:7687` default. A failed dedicated start aborts the install loudly with a journalctl tail; there is no silent fallback to the system instance. Stop/disable targets the literal `neo4j.service` only, so peer brands running their own `neo4j-{brand}.service` are unaffected.
|
|
254
254
|
- **Peer-aware system-unit guard:** before stopping the system `neo4j.service`, the installer checks whether any other brand on the device still depends on it — that is, has `NEO4J_URI=bolt://localhost:7687` in its `~/.<peer>/.env`. If so, the system unit is left enabled and active, and the install log shows `[neo4j] system unit kept active — peer brand <name> depends on port 7687` instead of the usual `[neo4j] disabling system unit` line. This prevents a `create-realagent` install from disabling Maxy's database on a host where Maxy still uses the shared system instance (the earlier platform fixes reproducer on Neo's laptop, 2026-04-28). On single-brand hosts and on multi-brand hosts where every peer runs a dedicated port, behaviour is unchanged. The dedicated unit exports `NEO4J_HOME=<per-brand-data-dir>` alongside `NEO4J_CONF`, so `server.directories.run`, `server.directories.plugins`, and `server.directories.import` resolve per-brand — no collision with `/var/lib/neo4j/run/neo4j.pid`. The conf sed-overrides, mkdir-p, chown, and unit-write are idempotent and re-run on every install, so a host whose prior install left a broken unit recovers on retry.
|
|
255
255
|
- **Shared:** both brands share the system Chromium/VNC stack, the Ollama model server, and the `cloudflared` command itself. Browser automation is serialised — one admin session at a time across both brands.
|
|
256
|
+
- **Thermal cap on shared Ollama:** the installer writes a host-wide systemd `CPUQuota` drop-in (`/etc/systemd/system/ollama.service.d/10-maxy-cpuquota.conf`) bounding `ollama.service` to half the cores (200% on a 4-core Pi). An embedding burst — a document's summary plus every section body in one POST — otherwise pins `llama-server` across all cores with no idle gaps until a passively-cooled Pi crosses its soft-temp limit and hard-resets. The cap keeps peak temperature below the limit regardless of which brand or platform path issues the embeds. The drop-in has a neutral filename and is rewritten only when its content changes, so it is idempotent across brands and reinstalls. Applied only where the `ollama.service` system unit exists; a detached `ollama serve` has no unit to cap.
|
|
256
257
|
|
|
257
258
|
To install a second brand on a device that already runs the first, just run the other installer. No flags needed for isolation:
|
|
258
259
|
|
|
@@ -95,6 +95,8 @@ If Ollama is unavailable at write time, the node is stored without an embedding
|
|
|
95
95
|
|
|
96
96
|
The `memory-reindex` tool backfills missing embeddings by iterating nodes where `embedding IS NULL`. It uses the bounded representation (the `summary` for document/archive labels, never the raw body), embeds in batches with a per-node fallback on batch error, and **skips and continues** past a failing node — keeping skipped nodes out of the re-fetch via an exclude set — instead of aborting the whole run. `embed()`/`embedBatch()` cap oversized input (`EMBED_INPUT_MAX_CHARS`) before the Ollama POST, so a large body embeds truncated rather than throwing. Per-node `[reindex] op=embedded|skip-error` lines and a terminal `[reindex] op=done processed=N skipped=M`.
|
|
97
97
|
|
|
98
|
+
`embedBatch()` also paces large batches. A document's summary plus every section body in one `/api/embed` POST pins ollama's `llama-server` across all cores for the whole batch with no idle gaps; on a passively-cooled Pi the SoC climbs past its soft-temp limit and the board thermally hard-resets. `embedBatch` splits an input array longer than `EMBED_SUBBATCH_SIZE` (default 32) into sub-batches, waiting `EMBED_SUBBATCH_COOLDOWN_MS` (default 1000) between them so the SoC gets cooling gaps; vectors are concatenated in input order. Both knobs are env-overridable for on-device tuning. The load-bearing thermal protection is the installer's `CPUQuota` cap on `ollama.service` (every embed caller is bounded there regardless of batch shape); this pacing is defence-in-depth. The UI's file-index path uses its own single-input embed client in `platform/ui/app/lib/neo4j-store.ts` (one embed per file, not a batch), so it is covered by the CPU cap, not by `embedBatch` pacing.
|
|
99
|
+
|
|
98
100
|
---
|
|
99
101
|
|
|
100
102
|
## Knowledge Document Hierarchy
|
|
@@ -43,11 +43,50 @@ describe("deriveSectionBodies", () => {
|
|
|
43
43
|
const sections = [{ sourceStart: 7, sourceEnd: 3 }];
|
|
44
44
|
expect(() => deriveSectionBodies(sections, cachedText, "att-rev")).toThrow(/reversed offsets.*sectionIndex=0.*sourceStart=7.*sourceEnd=3.*att-rev/);
|
|
45
45
|
});
|
|
46
|
-
it("(d) throws on out-of-bounds sourceEnd", () => {
|
|
46
|
+
it("(d) throws on a gross out-of-bounds sourceEnd (overrun beyond tolerance)", () => {
|
|
47
47
|
const cachedText = "Short text."; // length 11
|
|
48
|
-
const sections = [{ sourceStart: 0, sourceEnd: 100 }];
|
|
48
|
+
const sections = [{ sourceStart: 0, sourceEnd: 100 }]; // overrun 89 >> 32
|
|
49
49
|
expect(() => deriveSectionBodies(sections, cachedText, "att-oob")).toThrow(/out-of-bounds sourceEnd.*sectionIndex=0.*sourceEnd=100.*textLength=11/);
|
|
50
50
|
});
|
|
51
|
+
it("(d-boundary) returns the full slice when sourceEnd === textLength (no clamp, no log)", () => {
|
|
52
|
+
const cachedText = "Exactly to the end."; // length 19
|
|
53
|
+
const sections = [{ sourceStart: 0, sourceEnd: 19 }];
|
|
54
|
+
const result = deriveSectionBodies(sections, cachedText, "att-edge");
|
|
55
|
+
expect(result).toEqual(["Exactly to the end."]);
|
|
56
|
+
expect(logSpy).not.toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
it("(d-clamp) clamps a small end-of-text overrun to textLength and logs attributably", () => {
|
|
59
|
+
const cachedText = "0123456789"; // length 10
|
|
60
|
+
const sections = [{ sourceStart: 2, sourceEnd: 13 }]; // overrun 3, start in-bounds
|
|
61
|
+
const result = deriveSectionBodies(sections, cachedText, "att-clamp");
|
|
62
|
+
expect(result).toEqual(["23456789"]); // slice(2, 10)
|
|
63
|
+
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
64
|
+
expect(logSpy.mock.calls[0][0]).toContain("clamped out-of-bounds sourceEnd");
|
|
65
|
+
expect(logSpy.mock.calls[0][0]).toContain("sectionIndex=0");
|
|
66
|
+
expect(logSpy.mock.calls[0][0]).toContain("sourceStart=2");
|
|
67
|
+
expect(logSpy.mock.calls[0][0]).toContain("sourceEnd=13");
|
|
68
|
+
expect(logSpy.mock.calls[0][0]).toContain("textLength=10");
|
|
69
|
+
expect(logSpy.mock.calls[0][0]).toContain("overrun=3");
|
|
70
|
+
expect(logSpy.mock.calls[0][0]).toContain("att-clamp");
|
|
71
|
+
});
|
|
72
|
+
it("(d-clamp-edge) clamps an overrun exactly at the tolerance (32)", () => {
|
|
73
|
+
const cachedText = "0123456789"; // length 10
|
|
74
|
+
const sections = [{ sourceStart: 0, sourceEnd: 42 }]; // overrun exactly 32
|
|
75
|
+
const result = deriveSectionBodies(sections, cachedText, "att-clamp-edge");
|
|
76
|
+
expect(result).toEqual(["0123456789"]);
|
|
77
|
+
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
78
|
+
expect(logSpy.mock.calls[0][0]).toContain("clamped out-of-bounds sourceEnd");
|
|
79
|
+
});
|
|
80
|
+
it("(d-overrun) throws when the overrun is one past the tolerance", () => {
|
|
81
|
+
const cachedText = "0123456789"; // length 10
|
|
82
|
+
const sections = [{ sourceStart: 0, sourceEnd: 43 }]; // overrun 33 > 32
|
|
83
|
+
expect(() => deriveSectionBodies(sections, cachedText, "att-over")).toThrow(/out-of-bounds sourceEnd.*sectionIndex=0.*sourceEnd=43.*textLength=10.*overrun=33/);
|
|
84
|
+
});
|
|
85
|
+
it("(d-start-past-eof) throws when sourceStart is past end-of-text even within tolerance", () => {
|
|
86
|
+
const cachedText = "0123456789"; // length 10
|
|
87
|
+
const sections = [{ sourceStart: 12, sourceEnd: 12 }]; // start past EOF, end overrun 2
|
|
88
|
+
expect(() => deriveSectionBodies(sections, cachedText, "att-start-oob")).toThrow(/sourceStart past end-of-text.*sectionIndex=0.*sourceStart=12.*textLength=10/);
|
|
89
|
+
});
|
|
51
90
|
it("(d2) throws on negative sourceStart", () => {
|
|
52
91
|
const cachedText = "Some text.";
|
|
53
92
|
const sections = [{ sourceStart: -1, sourceEnd: 5 }];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-ingest-body-server-sliced.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-ingest-body-server-sliced.test.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,0EAA0E;AAC1E,uEAAuE;AACvE,oDAAoD;AAEpD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,IAAI,MAAmC,CAAC;IAExC,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,UAAU,GAAG,2DAA2D,CAAC;QAC/E,MAAM,QAAQ,GAAG;YACf,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;YACjC,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YAClC,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;SACnC,CAAC;QACF,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACrB,mBAAmB;YACnB,mBAAmB;YACnB,qBAAqB;SACtB,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,UAAU,GAAG,YAAY,CAAC;QAChC,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,UAAU,GAAG,YAAY,CAAC;QAChC,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CACxE,uEAAuE,CACxE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"memory-ingest-body-server-sliced.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/memory-ingest-body-server-sliced.test.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,0EAA0E;AAC1E,uEAAuE;AACvE,oDAAoD;AAEpD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,IAAI,MAAmC,CAAC;IAExC,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,UAAU,GAAG,2DAA2D,CAAC;QAC/E,MAAM,QAAQ,GAAG;YACf,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;YACjC,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YAClC,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;SACnC,CAAC;QACF,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC;YACrB,mBAAmB;YACnB,mBAAmB;YACnB,qBAAqB;SACtB,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,UAAU,GAAG,YAAY,CAAC;QAChC,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,UAAU,GAAG,YAAY,CAAC;QAChC,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CACxE,uEAAuE,CACxE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,YAAY;QAC9C,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,mBAAmB;QAC1E,MAAM,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CACxE,uEAAuE,CACxE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC9F,MAAM,UAAU,GAAG,qBAAqB,CAAC,CAAC,YAAY;QACtD,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,YAAY;QAC7C,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,6BAA6B;QACnF,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,eAAe;QACrD,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,iCAAiC,CAAC,CAAC;QAC7E,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC5D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,YAAY;QAC7C,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,qBAAqB;QAC3E,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC;QAC3E,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,iCAAiC,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,YAAY;QAC7C,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,kBAAkB;QACxE,MAAM,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CACzE,kFAAkF,CACnF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC9F,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,YAAY;QAC7C,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,gCAAgC;QACvF,MAAM,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,CAC9E,6EAA6E,CAC9E,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,UAAU,GAAG,YAAY,CAAC;QAChC,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CACxE,8CAA8C,CAC/C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,UAAU,GAAG,6BAA6B,CAAC;QACjD,MAAM,SAAS,GAAG,8CAA8C,CAAC;QACjE,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QACrE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,mBAAmB,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;QACjF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,UAAU,GAAG,mBAAmB,CAAC;QACvC,MAAM,QAAQ,GAAG;YACf,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;YAChC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;YAChC,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;SACnC,CAAC;QACF,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QACtE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,UAAU,GAAG,cAAc,CAAC;QAClC,MAAM,QAAQ,GAAG;YACf,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE;YAChC,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,WAAW;SAC/C,CAAC;QACF,MAAM,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CACxE,gBAAgB,CACjB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,UAAU,GAAG,KAAK,CAAC;QACzB,MAAM,QAAQ,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACpD,mBAAmB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-ingest.d.ts","sourceRoot":"","sources":["../../src/tools/memory-ingest.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,iBAAiB,EAEjB,gBAAgB,EAChB,eAAe,EAChB,MAAM,yBAAyB,CAAC;AAQjC,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAGzG,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-ingest.d.ts","sourceRoot":"","sources":["../../src/tools/memory-ingest.ts"],"names":[],"mappings":"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"}
|
|
@@ -55,6 +55,14 @@ import { isAllowedTypedEdge, isTypedEntityEdgeType } from "../lib/typed-edge-sch
|
|
|
55
55
|
const PREVIEW_LENGTH = 150;
|
|
56
56
|
const PROVENANCE_AGENT = "document-ingest";
|
|
57
57
|
const PROVENANCE_SOURCE = "document";
|
|
58
|
+
// Max characters a section's `sourceEnd` may exceed the cached text length and
|
|
59
|
+
// still be treated as a benign end-of-text overshoot (clamped, not thrown).
|
|
60
|
+
// The classifier (librarian LLM) emits offsets in-turn; its final section's
|
|
61
|
+
// `sourceEnd` estimate routinely lands a few characters past the trimmed
|
|
62
|
+
// document end (trailing whitespace `extractCache` removed + LLM rounding).
|
|
63
|
+
// That overrun is content-independent and small. A larger overrun means the
|
|
64
|
+
// classifier lost positional track — that stays a loud, attributable throw.
|
|
65
|
+
const SECTION_BODY_OVERRUN_TOLERANCE = 32;
|
|
58
66
|
// ---------------------------------------------------------------------------
|
|
59
67
|
// Section-body derivation. Shared by both ingest paths (Task 465 doc path,
|
|
60
68
|
// Task 468 conversation-archive path).
|
|
@@ -89,9 +97,31 @@ export function deriveSectionBodies(sections, cachedText, sourceKey = "unknown")
|
|
|
89
97
|
throw new Error(`[memory-ingest:derive] reversed offsets: sectionIndex=${i} ` +
|
|
90
98
|
`sourceStart=${sourceStart} sourceEnd=${sourceEnd} sourceKey=${sourceKey}`);
|
|
91
99
|
}
|
|
100
|
+
// The classifier's offsets are LLM estimates. Its final section's
|
|
101
|
+
// `sourceEnd` can land a few chars past the trimmed document end — a
|
|
102
|
+
// benign end-of-text overshoot. Clamp that to `textLength` (recovering the
|
|
103
|
+
// section's true content, start→EOF) with a loud, attributable adjustment.
|
|
104
|
+
// A start past end-of-text, or an overrun beyond the tolerance, means the
|
|
105
|
+
// classifier lost positional track: throw, so the ingest fails loudly
|
|
106
|
+
// rather than writing a bloated or truncated body.
|
|
107
|
+
let effectiveEnd = sourceEnd;
|
|
92
108
|
if (sourceEnd > textLength) {
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
const overrun = sourceEnd - textLength;
|
|
110
|
+
if (sourceStart <= textLength && overrun <= SECTION_BODY_OVERRUN_TOLERANCE) {
|
|
111
|
+
console.error(`[memory-ingest:derive] clamped out-of-bounds sourceEnd: sectionIndex=${i} ` +
|
|
112
|
+
`sourceStart=${sourceStart} sourceEnd=${sourceEnd} textLength=${textLength} ` +
|
|
113
|
+
`overrun=${overrun} sourceKey=${sourceKey}`);
|
|
114
|
+
effectiveEnd = textLength;
|
|
115
|
+
}
|
|
116
|
+
else if (sourceStart > textLength) {
|
|
117
|
+
throw new Error(`[memory-ingest:derive] sourceStart past end-of-text: sectionIndex=${i} ` +
|
|
118
|
+
`sourceStart=${sourceStart} textLength=${textLength} sourceKey=${sourceKey}`);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
throw new Error(`[memory-ingest:derive] out-of-bounds sourceEnd: sectionIndex=${i} ` +
|
|
122
|
+
`sourceEnd=${sourceEnd} textLength=${textLength} overrun=${overrun} ` +
|
|
123
|
+
`tolerance=${SECTION_BODY_OVERRUN_TOLERANCE} sourceKey=${sourceKey}`);
|
|
124
|
+
}
|
|
95
125
|
}
|
|
96
126
|
if (agentBody !== undefined) {
|
|
97
127
|
const agentBodyPreview = JSON.stringify(agentBody.slice(0, 120));
|
|
@@ -99,12 +129,12 @@ export function deriveSectionBodies(sections, cachedText, sourceKey = "unknown")
|
|
|
99
129
|
`agentBodyLength=${agentBody.length} ` +
|
|
100
130
|
`agentBodyPreview=${agentBodyPreview} sourceKey=${sourceKey}`);
|
|
101
131
|
}
|
|
102
|
-
if (sourceStart ===
|
|
132
|
+
if (sourceStart === effectiveEnd) {
|
|
103
133
|
console.error(`[memory-ingest:derive] zero-width section: sectionIndex=${i} ` +
|
|
104
134
|
`offset=${sourceStart} sourceKey=${sourceKey}`);
|
|
105
135
|
return "";
|
|
106
136
|
}
|
|
107
|
-
return cachedText.slice(sourceStart,
|
|
137
|
+
return cachedText.slice(sourceStart, effectiveEnd);
|
|
108
138
|
});
|
|
109
139
|
}
|
|
110
140
|
/** Identity-kind anchor edge writer set (UserProfile → Section:Kind). */
|