@eleboucher/pi-memini 0.7.2 → 0.7.3
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 +94 -47
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,9 +28,72 @@ function validateNamespace(ns) {
|
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// ../../../packages/memini-client/src/capture.ts
|
|
32
|
+
var TRUNCATION_MARKER = "\n[...truncated]";
|
|
33
|
+
function truncateForCapture(s, max) {
|
|
34
|
+
if (typeof max !== "number" || !Number.isFinite(max) || max <= 0) return s;
|
|
35
|
+
const cap = Math.floor(max);
|
|
36
|
+
if (s.length <= cap) return s;
|
|
37
|
+
let i = 0;
|
|
38
|
+
for (let n = 0; i < s.length && n < cap; n++) {
|
|
39
|
+
i += s.codePointAt(i) > 65535 ? 2 : 1;
|
|
40
|
+
}
|
|
41
|
+
if (i >= s.length) return s;
|
|
42
|
+
return s.slice(0, i) + TRUNCATION_MARKER;
|
|
43
|
+
}
|
|
44
|
+
function buildTurnCapture(userText, assistantText, userMax, assistantMax) {
|
|
45
|
+
return `${truncateForCapture(userText, userMax)}
|
|
46
|
+
|
|
47
|
+
${truncateForCapture(assistantText, assistantMax)}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
31
50
|
// ../../../packages/memini-client/src/session.ts
|
|
32
51
|
var SESSION_CWD_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
33
52
|
|
|
53
|
+
// ../../../packages/memini-client/src/bootstrap.ts
|
|
54
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
55
|
+
var MIN_TIMEOUT_MS = 100;
|
|
56
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
57
|
+
function envTimeoutMs(raw, fallback = DEFAULT_TIMEOUT_MS) {
|
|
58
|
+
if (raw == null || raw.trim() === "") return fallback;
|
|
59
|
+
const n = Number(raw.trim());
|
|
60
|
+
if (!Number.isFinite(n)) return fallback;
|
|
61
|
+
return Math.max(MIN_TIMEOUT_MS, Math.trunc(n));
|
|
62
|
+
}
|
|
63
|
+
function envEnabled(raw, defaultOn) {
|
|
64
|
+
if (raw == null || raw === "") return defaultOn;
|
|
65
|
+
return !/^(0|false|no|off)$/i.test(raw.trim());
|
|
66
|
+
}
|
|
67
|
+
function readBootstrap(env = process.env) {
|
|
68
|
+
return {
|
|
69
|
+
baseUrl: env["MEMINI_BASE_URL"] || "http://localhost:8080",
|
|
70
|
+
apiKey: env["MEMINI_API_KEY"] || "",
|
|
71
|
+
requireHttps: envEnabled(env["MEMINI_REQUIRE_HTTPS"], false),
|
|
72
|
+
debug: envEnabled(env["MEMINI_DEBUG"], false),
|
|
73
|
+
agent: env["MEMINI_AGENT"] || "",
|
|
74
|
+
namespaceEnv: (env["MEMINI_NAMESPACE"] || "").trim(),
|
|
75
|
+
namespacePrefixEnv: (env["MEMINI_NAMESPACE_PREFIX"] || "").trim(),
|
|
76
|
+
homeEnv: (env["MEMINI_HOME"] || "").trim(),
|
|
77
|
+
timeoutMs: envTimeoutMs(env["MEMINI_TIMEOUT_MS"])
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function isPlaintextBearerUnsafe(baseUrl, secret) {
|
|
81
|
+
if (!secret) return false;
|
|
82
|
+
try {
|
|
83
|
+
const u = new URL(baseUrl);
|
|
84
|
+
return u.protocol === "http:" && !LOOPBACK_HOSTS.has(u.hostname.replace(/^\[|\]$/g, "").toLowerCase());
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function assertBearerTransportSafe(baseUrl, secret, env = process.env) {
|
|
90
|
+
if (!isPlaintextBearerUnsafe(baseUrl, secret)) return;
|
|
91
|
+
if (!envEnabled(env["MEMINI_REQUIRE_HTTPS"], false)) return;
|
|
92
|
+
throw new Error(
|
|
93
|
+
`memini: a bearer token is configured for plaintext HTTP to ${baseUrl}. The token and memory payloads can be observed on the network; use HTTPS or an SSH tunnel.`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
34
97
|
// ../../../packages/memini-client/src/settings.ts
|
|
35
98
|
var BEHAVIOR_KNOBS = [
|
|
36
99
|
{ envName: "MEMINI_CAPTURE_TURNS", wireKey: "capture_turns", kind: "bool", default: true },
|
|
@@ -60,11 +123,30 @@ var BEHAVIOR_KNOBS = [
|
|
|
60
123
|
{ envName: "MEMINI_RECALL_LIMIT", wireKey: "recall_limit", kind: "int", default: 3 },
|
|
61
124
|
{ envName: "MEMINI_INJECT_RECALL_MAX_TOK", wireKey: "inject_recall_max_tok", kind: "int", default: 0 },
|
|
62
125
|
{ envName: "MEMINI_INJECT_RECALL_MIN_SCORE", wireKey: "inject_recall_min_score", kind: "float", default: 0 },
|
|
63
|
-
{ envName: "MEMINI_MIN_CAPTURE_CHARS", wireKey: "min_capture_chars", kind: "int", default: 0 }
|
|
126
|
+
{ envName: "MEMINI_MIN_CAPTURE_CHARS", wireKey: "min_capture_chars", kind: "int", default: 0 },
|
|
127
|
+
{ envName: "MEMINI_CAPTURE_USER_MAX_CHARS", wireKey: "capture_user_max_chars", kind: "int", default: 1e3 },
|
|
128
|
+
{
|
|
129
|
+
envName: "MEMINI_CAPTURE_ASSISTANT_MAX_CHARS",
|
|
130
|
+
wireKey: "capture_assistant_max_chars",
|
|
131
|
+
kind: "int",
|
|
132
|
+
default: 3e3
|
|
133
|
+
},
|
|
134
|
+
// Also read at the transport layer (bootstrap.ts's timeoutMs), because a
|
|
135
|
+
// request timeout has to exist before the handshake that fetches settings.
|
|
136
|
+
// Both spellings share DEFAULT_TIMEOUT_MS/MIN_TIMEOUT_MS, so there is exactly
|
|
137
|
+
// one number: raising it here (server-side) or in the env both work.
|
|
138
|
+
{
|
|
139
|
+
envName: "MEMINI_TIMEOUT_MS",
|
|
140
|
+
wireKey: "request_timeout_ms",
|
|
141
|
+
kind: "int",
|
|
142
|
+
default: DEFAULT_TIMEOUT_MS,
|
|
143
|
+
min: MIN_TIMEOUT_MS
|
|
144
|
+
}
|
|
64
145
|
];
|
|
65
|
-
function parseIntKnob(raw, fallback) {
|
|
146
|
+
function parseIntKnob(raw, fallback, min = 0) {
|
|
66
147
|
const n = Number.parseInt(raw, 10);
|
|
67
|
-
|
|
148
|
+
if (!Number.isFinite(n) || n < 0) return fallback;
|
|
149
|
+
return Math.max(min, n);
|
|
68
150
|
}
|
|
69
151
|
function parseFloatKnob(raw, fallback) {
|
|
70
152
|
const n = Number.parseFloat(raw);
|
|
@@ -82,7 +164,7 @@ function effectiveSetting(knob2, server, env = process.env) {
|
|
|
82
164
|
value = !/^(0|false|no|off)$/i.test(raw.trim());
|
|
83
165
|
break;
|
|
84
166
|
case "int":
|
|
85
|
-
value = parseIntKnob(raw, knob2.default);
|
|
167
|
+
value = parseIntKnob(raw, knob2.default, knob2.min);
|
|
86
168
|
break;
|
|
87
169
|
case "float":
|
|
88
170
|
value = parseFloatKnob(raw, knob2.default);
|
|
@@ -99,41 +181,6 @@ function effectiveSetting(knob2, server, env = process.env) {
|
|
|
99
181
|
return { value: knob2.default, source: "default" };
|
|
100
182
|
}
|
|
101
183
|
|
|
102
|
-
// ../../../packages/memini-client/src/bootstrap.ts
|
|
103
|
-
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
104
|
-
function envEnabled(raw, defaultOn) {
|
|
105
|
-
if (raw == null || raw === "") return defaultOn;
|
|
106
|
-
return !/^(0|false|no|off)$/i.test(raw.trim());
|
|
107
|
-
}
|
|
108
|
-
function readBootstrap(env = process.env) {
|
|
109
|
-
return {
|
|
110
|
-
baseUrl: env["MEMINI_BASE_URL"] || "http://localhost:8080",
|
|
111
|
-
apiKey: env["MEMINI_API_KEY"] || "",
|
|
112
|
-
requireHttps: envEnabled(env["MEMINI_REQUIRE_HTTPS"], false),
|
|
113
|
-
debug: envEnabled(env["MEMINI_DEBUG"], false),
|
|
114
|
-
agent: env["MEMINI_AGENT"] || "",
|
|
115
|
-
namespaceEnv: (env["MEMINI_NAMESPACE"] || "").trim(),
|
|
116
|
-
namespacePrefixEnv: (env["MEMINI_NAMESPACE_PREFIX"] || "").trim(),
|
|
117
|
-
homeEnv: (env["MEMINI_HOME"] || "").trim()
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
function isPlaintextBearerUnsafe(baseUrl, secret) {
|
|
121
|
-
if (!secret) return false;
|
|
122
|
-
try {
|
|
123
|
-
const u = new URL(baseUrl);
|
|
124
|
-
return u.protocol === "http:" && !LOOPBACK_HOSTS.has(u.hostname.replace(/^\[|\]$/g, "").toLowerCase());
|
|
125
|
-
} catch {
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function assertBearerTransportSafe(baseUrl, secret, env = process.env) {
|
|
130
|
-
if (!isPlaintextBearerUnsafe(baseUrl, secret)) return;
|
|
131
|
-
if (!envEnabled(env["MEMINI_REQUIRE_HTTPS"], false)) return;
|
|
132
|
-
throw new Error(
|
|
133
|
-
`memini: a bearer token is configured for plaintext HTTP to ${baseUrl}. The token and memory payloads can be observed on the network; use HTTPS or an SSH tunnel.`
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
184
|
// ../../../packages/memini-client/src/facts.ts
|
|
138
185
|
import { execFileSync } from "node:child_process";
|
|
139
186
|
import path from "node:path";
|
|
@@ -270,7 +317,7 @@ async function performHandshake(boot, facts, opts = {}) {
|
|
|
270
317
|
}
|
|
271
318
|
|
|
272
319
|
// src/index.ts
|
|
273
|
-
var
|
|
320
|
+
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
274
321
|
var DEFAULT_RECALL_LIMIT = 3;
|
|
275
322
|
var STATUS_TIMEOUT_MS = 4e3;
|
|
276
323
|
var HANDSHAKE_TIMEOUT_MS = 2500;
|
|
@@ -350,7 +397,7 @@ function resolveStaticConfig(env = process.env) {
|
|
|
350
397
|
const homeEnv = (e.MEMINI_HOME || "").trim();
|
|
351
398
|
return {
|
|
352
399
|
home: homeEnv || void 0,
|
|
353
|
-
timeout_ms: Number(e.MEMINI_TIMEOUT_MS ||
|
|
400
|
+
timeout_ms: Number(e.MEMINI_TIMEOUT_MS || DEFAULT_TIMEOUT_MS2),
|
|
354
401
|
fallback_on_error: envBool(e.MEMINI_FALLBACK, true)
|
|
355
402
|
};
|
|
356
403
|
}
|
|
@@ -370,7 +417,9 @@ function resolveLiveConfig(boot, facts, hs, env = process.env) {
|
|
|
370
417
|
capture: effectiveSetting(knob("capture"), server, env).value,
|
|
371
418
|
recall_limit: effectiveSetting(knob("recall_limit"), server, env).value,
|
|
372
419
|
recall_max_tokens: effectiveSetting(knob("inject_recall_max_tok"), server, env).value,
|
|
373
|
-
recall_min_score: effectiveSetting(knob("inject_recall_min_score"), server, env).value
|
|
420
|
+
recall_min_score: effectiveSetting(knob("inject_recall_min_score"), server, env).value,
|
|
421
|
+
capture_user_max_chars: effectiveSetting(knob("capture_user_max_chars"), server, env).value,
|
|
422
|
+
capture_assistant_max_chars: effectiveSetting(knob("capture_assistant_max_chars"), server, env).value
|
|
374
423
|
};
|
|
375
424
|
}
|
|
376
425
|
async function sessionLive(ctx, env = process.env) {
|
|
@@ -538,10 +587,8 @@ function extractLastAssistantText(messages) {
|
|
|
538
587
|
}
|
|
539
588
|
return "";
|
|
540
589
|
}
|
|
541
|
-
function buildTurnContent(userText, assistantText) {
|
|
542
|
-
return
|
|
543
|
-
|
|
544
|
-
${String(assistantText).slice(0, 3e3)}`;
|
|
590
|
+
function buildTurnContent(userText, assistantText, userMax, assistantMax) {
|
|
591
|
+
return buildTurnCapture(String(userText), String(assistantText), userMax, assistantMax);
|
|
545
592
|
}
|
|
546
593
|
function pinKeyFacts(facts) {
|
|
547
594
|
const out = {};
|
|
@@ -1004,7 +1051,7 @@ function meminiExtension(pi) {
|
|
|
1004
1051
|
const stored = await client.postJson(
|
|
1005
1052
|
"/v1/memories",
|
|
1006
1053
|
{
|
|
1007
|
-
content: buildTurnContent(userText, assistantText),
|
|
1054
|
+
content: buildTurnContent(userText, assistantText, live.capture_user_max_chars, live.capture_assistant_max_chars),
|
|
1008
1055
|
tags: ["pi"],
|
|
1009
1056
|
metadata
|
|
1010
1057
|
},
|