@hasna/mementos 0.17.3 → 0.17.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/README.md +13 -2
- package/bun.lock +133 -0
- package/dist/cli/index.js +137 -32
- package/dist/db/agents.d.ts +7 -0
- package/dist/db/agents.d.ts.map +1 -1
- package/dist/index.js +15 -3
- package/dist/lib/agent-name.d.ts +94 -0
- package/dist/lib/agent-name.d.ts.map +1 -0
- package/dist/lib/claude-hook-install.d.ts +4 -2
- package/dist/lib/claude-hook-install.d.ts.map +1 -1
- package/dist/lib/claude-stop-hook.d.ts.map +1 -1
- package/dist/lib/open-sessions-connector.d.ts.map +1 -1
- package/dist/mcp/index.js +17 -3
- package/dist/sdk/index.d.ts +2 -1
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +21 -6
- package/dist/server/index.js +16 -2
- package/package.json +4 -3
package/dist/cli/index.js
CHANGED
|
@@ -7019,18 +7019,55 @@ var init_memories = __esm(() => {
|
|
|
7019
7019
|
]);
|
|
7020
7020
|
});
|
|
7021
7021
|
|
|
7022
|
+
// src/lib/agent-name.ts
|
|
7023
|
+
function trimControlBytes(value) {
|
|
7024
|
+
let name = value;
|
|
7025
|
+
for (;; ) {
|
|
7026
|
+
const next = name.trim().replace(CONTROL_BYTE_EDGES, "");
|
|
7027
|
+
if (next === name)
|
|
7028
|
+
return name;
|
|
7029
|
+
name = next;
|
|
7030
|
+
}
|
|
7031
|
+
}
|
|
7032
|
+
function isValidAgentName(name) {
|
|
7033
|
+
return name.length > 0 && name.length <= AGENT_NAME_MAX_LENGTH && /^[A-Za-z0-9._-]+$/.test(name) && /[A-Za-z0-9_-]/.test(name);
|
|
7034
|
+
}
|
|
7035
|
+
function isUrlDotSegment(value) {
|
|
7036
|
+
const segment = value.toLowerCase();
|
|
7037
|
+
return segment === "." || segment === ".." || segment === "%2e" || segment === ".%2e" || segment === "%2e." || segment === "%2e%2e";
|
|
7038
|
+
}
|
|
7039
|
+
function agentPathSegment(name) {
|
|
7040
|
+
if (isUrlDotSegment(name)) {
|
|
7041
|
+
throw new Error(`Refusing to build an agent URL from ${JSON.stringify(name)}: the URL parser collapses a dot segment, ` + `so the request path would not be the one built here.`);
|
|
7042
|
+
}
|
|
7043
|
+
return encodeURIComponent(name);
|
|
7044
|
+
}
|
|
7045
|
+
function sanitizeAgentName(raw) {
|
|
7046
|
+
if (typeof raw !== "string")
|
|
7047
|
+
return null;
|
|
7048
|
+
const name = trimControlBytes(raw);
|
|
7049
|
+
if (CONTROL_BYTE.test(name))
|
|
7050
|
+
return null;
|
|
7051
|
+
return isValidAgentName(name) ? name : null;
|
|
7052
|
+
}
|
|
7053
|
+
var AGENT_NAME_MAX_LENGTH = 128, CONTROL_BYTE, CONTROL_BYTE_EDGES;
|
|
7054
|
+
var init_agent_name = __esm(() => {
|
|
7055
|
+
CONTROL_BYTE = /[\u0000-\u001f\u007f]/;
|
|
7056
|
+
CONTROL_BYTE_EDGES = /^[\u0000-\u001f\u007f]+|[\u0000-\u001f\u007f]+$/g;
|
|
7057
|
+
});
|
|
7058
|
+
|
|
7022
7059
|
// src/db/agents.ts
|
|
7023
7060
|
import { homedir as homedir3 } from "os";
|
|
7024
7061
|
import { join as join6 } from "path";
|
|
7025
7062
|
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
|
|
7026
7063
|
function resolveWritingAgentName() {
|
|
7027
|
-
const envName = process.env["MEMENTOS_AGENT"]
|
|
7064
|
+
const envName = sanitizeAgentName(process.env["MEMENTOS_AGENT"]);
|
|
7028
7065
|
if (envName)
|
|
7029
7066
|
return envName;
|
|
7030
7067
|
try {
|
|
7031
7068
|
const path = join6(homedir3(), ".hasna", "conversations", "agent-id");
|
|
7032
7069
|
if (existsSync5(path)) {
|
|
7033
|
-
const fileAgent = readFileSync2(path, "utf8")
|
|
7070
|
+
const fileAgent = sanitizeAgentName(readFileSync2(path, "utf8"));
|
|
7034
7071
|
if (fileAgent)
|
|
7035
7072
|
return fileAgent;
|
|
7036
7073
|
}
|
|
@@ -7111,7 +7148,7 @@ function registerAgent(name, sessionId, description, role, projectId, db) {
|
|
|
7111
7148
|
}
|
|
7112
7149
|
function getAgent(idOrName, db) {
|
|
7113
7150
|
if (!db && isApiMode()) {
|
|
7114
|
-
const { status, data } = apiJson("GET", `/agents/${
|
|
7151
|
+
const { status, data } = apiJson("GET", `/agents/${agentPathSegment(idOrName)}`, undefined, { allow404: true });
|
|
7115
7152
|
if (status === 404 || !data)
|
|
7116
7153
|
return null;
|
|
7117
7154
|
return data;
|
|
@@ -7175,7 +7212,7 @@ function touchAgent(idOrName, db) {
|
|
|
7175
7212
|
const agent2 = getAgent(idOrName);
|
|
7176
7213
|
if (!agent2)
|
|
7177
7214
|
return;
|
|
7178
|
-
apiJson("PATCH", `/agents/${
|
|
7215
|
+
apiJson("PATCH", `/agents/${agentPathSegment(agent2.id)}`, {});
|
|
7179
7216
|
return;
|
|
7180
7217
|
}
|
|
7181
7218
|
const d = db || getDatabase();
|
|
@@ -7186,7 +7223,7 @@ function touchAgent(idOrName, db) {
|
|
|
7186
7223
|
}
|
|
7187
7224
|
function updateAgent(id, updates, db) {
|
|
7188
7225
|
if (!db && isApiMode()) {
|
|
7189
|
-
const { status, data } = apiJson("PATCH", `/agents/${
|
|
7226
|
+
const { status, data } = apiJson("PATCH", `/agents/${agentPathSegment(id)}`, updates, { allow404: true });
|
|
7190
7227
|
if (status === 404 || !data)
|
|
7191
7228
|
return null;
|
|
7192
7229
|
return data;
|
|
@@ -7232,6 +7269,7 @@ function updateAgent(id, updates, db) {
|
|
|
7232
7269
|
var CONFLICT_WINDOW_MS, UNBOUNDED_AGENT_LIST_LIMIT;
|
|
7233
7270
|
var init_agents = __esm(() => {
|
|
7234
7271
|
init_types();
|
|
7272
|
+
init_agent_name();
|
|
7235
7273
|
init_database();
|
|
7236
7274
|
init_api_mode();
|
|
7237
7275
|
CONFLICT_WINDOW_MS = 30 * 60 * 1000;
|
|
@@ -13680,7 +13718,7 @@ import {
|
|
|
13680
13718
|
ClientTransportConfigurationError as ClientTransportConfigurationError2
|
|
13681
13719
|
} from "@hasna/contracts/client";
|
|
13682
13720
|
function sdkProtocolError(operation, detail) {
|
|
13683
|
-
return new MementosError(`mementos ${operation} returned a malformed 2xx response: ${detail}`, 502);
|
|
13721
|
+
return new MementosError(`mementos ${operation} returned a malformed 2xx response: ${detail}`, 502, undefined, "MEMENTOS_RESPONSE_CONTRACT");
|
|
13684
13722
|
}
|
|
13685
13723
|
function sessionProtocolError(detail) {
|
|
13686
13724
|
return sdkProtocolError("session jobs", detail);
|
|
@@ -14135,11 +14173,12 @@ class MementosClient {
|
|
|
14135
14173
|
body: body !== undefined ? JSON.stringify(body) : undefined
|
|
14136
14174
|
});
|
|
14137
14175
|
if (!res.ok) {
|
|
14138
|
-
let errBody
|
|
14176
|
+
let errBody;
|
|
14139
14177
|
try {
|
|
14140
14178
|
errBody = await res.json();
|
|
14141
14179
|
} catch {}
|
|
14142
|
-
|
|
14180
|
+
const errorObject = errBody && typeof errBody === "object" && !Array.isArray(errBody) ? errBody : undefined;
|
|
14181
|
+
throw new MementosError(typeof errorObject?.["error"] === "string" ? errorObject["error"] : `HTTP ${res.status}`, res.status, errorObject?.["details"]);
|
|
14143
14182
|
}
|
|
14144
14183
|
if (res.status === 204)
|
|
14145
14184
|
return;
|
|
@@ -14341,10 +14380,10 @@ class MementosClient {
|
|
|
14341
14380
|
return this.post("/api/agents", input);
|
|
14342
14381
|
}
|
|
14343
14382
|
getAgent(idOrName) {
|
|
14344
|
-
return this.get(`/api/agents/${idOrName}`);
|
|
14383
|
+
return this.get(`/api/agents/${agentPathSegment(idOrName)}`);
|
|
14345
14384
|
}
|
|
14346
14385
|
updateAgent(idOrName, updates) {
|
|
14347
|
-
return this.patch(`/api/agents/${idOrName}`, updates);
|
|
14386
|
+
return this.patch(`/api/agents/${agentPathSegment(idOrName)}`, updates);
|
|
14348
14387
|
}
|
|
14349
14388
|
listAgentsByProject(projectId) {
|
|
14350
14389
|
return this.get(`/api/agents`, { project_id: projectId });
|
|
@@ -14887,14 +14926,17 @@ var MEMENTOS_MACHINE_REGISTRATION_CONTRACT = "mementos.machine-registration.v1",
|
|
|
14887
14926
|
var init_sdk = __esm(() => {
|
|
14888
14927
|
init_audit_contract();
|
|
14889
14928
|
init_local_opt_in();
|
|
14929
|
+
init_agent_name();
|
|
14890
14930
|
init_decisions();
|
|
14891
14931
|
MementosError = class MementosError extends Error {
|
|
14892
14932
|
status;
|
|
14893
14933
|
details;
|
|
14894
|
-
|
|
14934
|
+
code;
|
|
14935
|
+
constructor(message, status, details, code) {
|
|
14895
14936
|
super(message);
|
|
14896
14937
|
this.status = status;
|
|
14897
14938
|
this.details = details;
|
|
14939
|
+
this.code = code;
|
|
14898
14940
|
this.name = "MementosError";
|
|
14899
14941
|
}
|
|
14900
14942
|
};
|
|
@@ -14919,9 +14961,35 @@ __export(exports_claude_stop_hook, {
|
|
|
14919
14961
|
});
|
|
14920
14962
|
import { constants, openSync, closeSync, fstatSync, readSync } from "fs";
|
|
14921
14963
|
import { isAbsolute } from "path";
|
|
14964
|
+
function failureCategory(error, phase) {
|
|
14965
|
+
if (error instanceof HookFailure)
|
|
14966
|
+
return { phase, reason: error.reason };
|
|
14967
|
+
if (error instanceof MementosConfigError)
|
|
14968
|
+
return { phase: "configuration", reason: "configuration_unavailable" };
|
|
14969
|
+
if (error instanceof MementosError) {
|
|
14970
|
+
return { phase: "response", reason: error.code === "MEMENTOS_RESPONSE_CONTRACT" ? "response_contract" : "http_error" };
|
|
14971
|
+
}
|
|
14972
|
+
if (error instanceof SyntaxError) {
|
|
14973
|
+
return { phase: phase === "request" ? "response" : phase, reason: "invalid_json" };
|
|
14974
|
+
}
|
|
14975
|
+
if (phase === "input")
|
|
14976
|
+
return { phase, reason: "input_unreadable" };
|
|
14977
|
+
if (phase === "transcript") {
|
|
14978
|
+
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
14979
|
+
const reason = code === "ENOENT" ? "file_not_found" : code === "EACCES" || code === "EPERM" ? "file_access_denied" : code === "ELOOP" ? "file_symlink_refused" : "file_read_failed";
|
|
14980
|
+
return { phase, reason };
|
|
14981
|
+
}
|
|
14982
|
+
if (phase === "configuration")
|
|
14983
|
+
return { phase, reason: "configuration_unavailable" };
|
|
14984
|
+
if (error instanceof Error && error.name === "TimeoutError")
|
|
14985
|
+
return { phase, reason: "timeout" };
|
|
14986
|
+
if (error instanceof Error && error.name === "AbortError")
|
|
14987
|
+
return { phase, reason: "aborted" };
|
|
14988
|
+
return { phase, reason: phase === "response" ? "response_unreadable" : error instanceof TypeError ? "network_error" : "request_failed" };
|
|
14989
|
+
}
|
|
14922
14990
|
function object2(value) {
|
|
14923
14991
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
14924
|
-
throw new
|
|
14992
|
+
throw new HookFailure("invalid_shape", "invalid input");
|
|
14925
14993
|
return value;
|
|
14926
14994
|
}
|
|
14927
14995
|
function textContent(value) {
|
|
@@ -14938,13 +15006,15 @@ function textContent(value) {
|
|
|
14938
15006
|
function claudeTranscript(context) {
|
|
14939
15007
|
const path = context["transcript_path"];
|
|
14940
15008
|
if (typeof path !== "string" || !isAbsolute(path))
|
|
14941
|
-
throw new
|
|
15009
|
+
throw new HookFailure("transcript_path_required", "transcript path required");
|
|
14942
15010
|
const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
14943
15011
|
let raw;
|
|
14944
15012
|
try {
|
|
14945
15013
|
const stat = fstatSync(fd);
|
|
14946
|
-
if (!stat.isFile()
|
|
14947
|
-
throw new
|
|
15014
|
+
if (!stat.isFile())
|
|
15015
|
+
throw new HookFailure("transcript_not_regular", "invalid transcript file");
|
|
15016
|
+
if (stat.size > MAX_TRANSCRIPT_BYTES)
|
|
15017
|
+
throw new HookFailure("transcript_too_large", "invalid transcript file");
|
|
14948
15018
|
const buffer = Buffer.alloc(MAX_TRANSCRIPT_BYTES + 1);
|
|
14949
15019
|
let read = 0;
|
|
14950
15020
|
while (read < buffer.length) {
|
|
@@ -14954,7 +15024,7 @@ function claudeTranscript(context) {
|
|
|
14954
15024
|
read += count;
|
|
14955
15025
|
}
|
|
14956
15026
|
if (read > MAX_TRANSCRIPT_BYTES)
|
|
14957
|
-
throw new
|
|
15027
|
+
throw new HookFailure("transcript_too_large", "transcript grew beyond bound");
|
|
14958
15028
|
raw = buffer.subarray(0, read).toString("utf8");
|
|
14959
15029
|
} finally {
|
|
14960
15030
|
closeSync(fd);
|
|
@@ -14969,7 +15039,7 @@ function claudeTranscript(context) {
|
|
|
14969
15039
|
if (row["type"] !== "user" && row["type"] !== "assistant")
|
|
14970
15040
|
continue;
|
|
14971
15041
|
if (row["sessionId"] !== undefined && row["sessionId"] !== context["session_id"])
|
|
14972
|
-
throw new
|
|
15042
|
+
throw new HookFailure("transcript_session_mismatch", "transcript session mismatch");
|
|
14973
15043
|
const message = object2(row["message"]);
|
|
14974
15044
|
const text = textContent(message["content"]);
|
|
14975
15045
|
if (!text.trim())
|
|
@@ -14994,54 +15064,72 @@ async function runClaudeStopHook(options = {}) {
|
|
|
14994
15064
|
const report = options.stderr ?? ((text) => {
|
|
14995
15065
|
process.stderr.write(text);
|
|
14996
15066
|
});
|
|
15067
|
+
let phase = "input";
|
|
14997
15068
|
try {
|
|
14998
15069
|
const chunks = [];
|
|
14999
15070
|
let bytes = 0;
|
|
15000
15071
|
for await (const chunk of options.stdin ?? Bun.stdin.stream()) {
|
|
15001
15072
|
bytes += chunk.byteLength;
|
|
15002
15073
|
if (bytes > MAX_CONTEXT_BYTES)
|
|
15003
|
-
throw new
|
|
15074
|
+
throw new HookFailure("context_too_large", "context too large");
|
|
15004
15075
|
chunks.push(chunk);
|
|
15005
15076
|
}
|
|
15006
15077
|
const context = object2(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
15007
15078
|
if (context["hook_event_name"] !== "Stop")
|
|
15008
|
-
throw new
|
|
15079
|
+
throw new HookFailure("invalid_event", "not a Stop event");
|
|
15009
15080
|
if (context["stop_hook_active"] === true)
|
|
15010
15081
|
return 0;
|
|
15011
15082
|
const session = context["session_id"];
|
|
15012
15083
|
if (typeof session !== "string" || !session.trim() || session.length > 256)
|
|
15013
|
-
throw new
|
|
15084
|
+
throw new HookFailure("invalid_session", "session required");
|
|
15085
|
+
phase = "transcript";
|
|
15014
15086
|
const transcript = claudeTranscript(context);
|
|
15015
15087
|
if (!transcript.trim())
|
|
15016
15088
|
return 0;
|
|
15089
|
+
phase = "configuration";
|
|
15017
15090
|
const client = options.client ?? new MementosClient({
|
|
15018
|
-
fetch: (input, init) =>
|
|
15091
|
+
fetch: async (input, init) => {
|
|
15092
|
+
const response = await fetch(input, { ...init, redirect: "error", signal: AbortSignal.timeout(5000) });
|
|
15093
|
+
phase = "response";
|
|
15094
|
+
return response;
|
|
15095
|
+
}
|
|
15019
15096
|
});
|
|
15020
15097
|
const authority = new URL(client.apiUrl);
|
|
15021
15098
|
if (authority.protocol !== "https:" || !authority.pathname.endsWith("/v1"))
|
|
15022
|
-
throw new
|
|
15099
|
+
throw new HookFailure("hosted_authority_required", "hosted v1 authority required");
|
|
15100
|
+
const agentId = sanitizeAgentName(process.env["MEMENTOS_AGENT"]);
|
|
15101
|
+
phase = "request";
|
|
15023
15102
|
await client.ingestSession({
|
|
15024
15103
|
transcript,
|
|
15025
15104
|
session_id: session,
|
|
15026
15105
|
source: "claude-code",
|
|
15027
|
-
...
|
|
15106
|
+
...agentId ? { agent_id: agentId } : {}
|
|
15028
15107
|
});
|
|
15029
15108
|
report(`[mementos] Session queued for hosted memory extraction.
|
|
15030
15109
|
`);
|
|
15031
15110
|
return 0;
|
|
15032
15111
|
} catch (error) {
|
|
15033
|
-
const
|
|
15034
|
-
|
|
15112
|
+
const failure = failureCategory(error, phase);
|
|
15113
|
+
const status = error instanceof MementosError && error.code !== "MEMENTOS_RESPONSE_CONTRACT" && Number.isInteger(error.status) && error.status >= 100 && error.status <= 599 ? ` (HTTP ${error.status})` : "";
|
|
15114
|
+
report(`[mementos] Hosted session ingest failed${status} [phase=${failure.phase}; reason=${failure.reason}]; no local fallback or automatic retry.
|
|
15035
15115
|
`);
|
|
15036
15116
|
return 1;
|
|
15037
15117
|
}
|
|
15038
15118
|
}
|
|
15039
|
-
var MAX_CONTEXT_BYTES, MAX_TRANSCRIPT_BYTES;
|
|
15119
|
+
var MAX_CONTEXT_BYTES, MAX_TRANSCRIPT_BYTES, HookFailure;
|
|
15040
15120
|
var init_claude_stop_hook = __esm(() => {
|
|
15041
15121
|
init_sdk();
|
|
15122
|
+
init_agent_name();
|
|
15042
15123
|
init_redact();
|
|
15043
15124
|
MAX_CONTEXT_BYTES = 64 * 1024;
|
|
15044
15125
|
MAX_TRANSCRIPT_BYTES = 8 * 1024 * 1024;
|
|
15126
|
+
HookFailure = class HookFailure extends Error {
|
|
15127
|
+
reason;
|
|
15128
|
+
constructor(reason, message) {
|
|
15129
|
+
super(message);
|
|
15130
|
+
this.reason = reason;
|
|
15131
|
+
}
|
|
15132
|
+
};
|
|
15045
15133
|
});
|
|
15046
15134
|
|
|
15047
15135
|
// src/db/session-jobs.ts
|
|
@@ -16109,7 +16197,8 @@ function planClaudeHook(home, command = claudeHookCommand()) {
|
|
|
16109
16197
|
const changed = !before || JSON.stringify(settings) !== JSON.stringify(JSON.parse(next));
|
|
16110
16198
|
return { settingsPath, resolvedSettingsPath, legacyPath, directory, directory_sha256: directory.sha256, before, next, changed, settings_sha256: before ? digest2(before) : "absent", legacy_hook_sha256: legacyHash, command };
|
|
16111
16199
|
}
|
|
16112
|
-
function installClaudeHook(home, expected, command = claudeHookCommand()) {
|
|
16200
|
+
async function installClaudeHook(home, expected, command = claudeHookCommand(), options = {}) {
|
|
16201
|
+
const { planClaudeStopHookUpdate, applyAgentIntegration } = await import("@hasna/skills");
|
|
16113
16202
|
const plan = planClaudeHook(home, command);
|
|
16114
16203
|
if (plan.directory.alias && !expected.directorySha256)
|
|
16115
16204
|
throw new Error("MEMENTOS_HOOK_DIRECTORY_PREIMAGE_REQUIRED");
|
|
@@ -16117,8 +16206,18 @@ function installClaudeHook(home, expected, command = claudeHookCommand()) {
|
|
|
16117
16206
|
throw new Error("MEMENTOS_HOOK_DIRECTORY_PREIMAGE_CHANGED");
|
|
16118
16207
|
if (plan.settings_sha256 !== expected.settingsSha256 || (plan.legacy_hook_sha256 ?? undefined) !== expected.legacyHookSha256)
|
|
16119
16208
|
throw new Error("MEMENTOS_HOOK_PREIMAGE_CHANGED");
|
|
16120
|
-
|
|
16209
|
+
const integrationOptions = {
|
|
16210
|
+
home,
|
|
16211
|
+
dataDir: options.skillsDataDir,
|
|
16212
|
+
expectedSettingsSha256: plan.settings_sha256,
|
|
16213
|
+
replacement: plan.changed ? plan.next : plan.before.toString("utf8")
|
|
16214
|
+
};
|
|
16215
|
+
const integrationPlan = planClaudeStopHookUpdate(integrationOptions);
|
|
16216
|
+
if (!plan.changed) {
|
|
16217
|
+
if (integrationPlan)
|
|
16218
|
+
applyAgentIntegration(integrationPlan);
|
|
16121
16219
|
return { changed: false, settings_sha256: plan.settings_sha256 };
|
|
16220
|
+
}
|
|
16122
16221
|
const directory = plan.directory.path;
|
|
16123
16222
|
mkdirSync5(directory, { recursive: true, mode: 448 });
|
|
16124
16223
|
const claimedDirectory = claudeDirectory(home);
|
|
@@ -16136,13 +16235,19 @@ function installClaudeHook(home, expected, command = claudeHookCommand()) {
|
|
|
16136
16235
|
writeFileSync4(backup, plan.before, { flag: "wx", mode: 384 });
|
|
16137
16236
|
if (backup && digest2(readRegular(backup)) !== plan.settings_sha256)
|
|
16138
16237
|
throw new Error("MEMENTOS_HOOK_BACKUP_CONFLICT");
|
|
16139
|
-
writeFileSync4(temporary, plan.next, { flag: "wx", mode: 384 });
|
|
16140
16238
|
const current = planClaudeHook(home, command);
|
|
16141
16239
|
if (current.directory_sha256 !== claimedDirectory.sha256)
|
|
16142
16240
|
throw new Error("MEMENTOS_HOOK_DIRECTORY_PREIMAGE_CHANGED");
|
|
16143
16241
|
if (current.settings_sha256 !== plan.settings_sha256 || current.legacy_hook_sha256 !== plan.legacy_hook_sha256)
|
|
16144
16242
|
throw new Error("MEMENTOS_HOOK_PREIMAGE_CHANGED");
|
|
16145
|
-
|
|
16243
|
+
if (integrationPlan) {
|
|
16244
|
+
applyAgentIntegration(integrationPlan);
|
|
16245
|
+
} else {
|
|
16246
|
+
if (planClaudeStopHookUpdate(integrationOptions))
|
|
16247
|
+
throw new Error("MEMENTOS_HOOK_SKILLS_POLICY_CHANGED");
|
|
16248
|
+
writeFileSync4(temporary, plan.next, { flag: "wx", mode: 384 });
|
|
16249
|
+
renameSync(temporary, plan.resolvedSettingsPath);
|
|
16250
|
+
}
|
|
16146
16251
|
if (claudeDirectory(home).sha256 !== claimedDirectory.sha256 || digest2(readRegular(plan.resolvedSettingsPath)) !== digest2(plan.next))
|
|
16147
16252
|
throw new Error("MEMENTOS_HOOK_READBACK_FAILED");
|
|
16148
16253
|
} finally {
|
|
@@ -74685,7 +74790,7 @@ session_end = "bun ${script}"`);
|
|
|
74685
74790
|
if (opts.apply) {
|
|
74686
74791
|
if (!opts.expectSettingsSha256)
|
|
74687
74792
|
throw new Error("Preview first; --apply requires --expect-settings-sha256");
|
|
74688
|
-
outputJson(installClaudeHook2(homedir6(), { settingsSha256: opts.expectSettingsSha256, legacyHookSha256: opts.expectHookSha256, directorySha256: opts.expectDirectorySha256 }));
|
|
74793
|
+
outputJson(await installClaudeHook2(homedir6(), { settingsSha256: opts.expectSettingsSha256, legacyHookSha256: opts.expectHookSha256, directorySha256: opts.expectDirectorySha256 }));
|
|
74689
74794
|
} else {
|
|
74690
74795
|
const plan = planClaudeHook2(homedir6());
|
|
74691
74796
|
outputJson({ changed: plan.changed, settings_path: plan.settingsPath, resolved_settings_path: plan.resolvedSettingsPath, directory_sha256: plan.directory_sha256, directory_alias: plan.directory.alias, command: plan.command, settings_sha256: plan.settings_sha256, legacy_hook_sha256: plan.legacy_hook_sha256 });
|
|
@@ -76144,7 +76249,7 @@ function registerInitCommand(program2) {
|
|
|
76144
76249
|
try {
|
|
76145
76250
|
const { planClaudeHook: planClaudeHook2, installClaudeHook: installClaudeHook2 } = await Promise.resolve().then(() => (init_claude_hook_install(), exports_claude_hook_install));
|
|
76146
76251
|
const plan = planClaudeHook2(home);
|
|
76147
|
-
const result = installClaudeHook2(home, { settingsSha256: plan.settings_sha256, legacyHookSha256: plan.legacy_hook_sha256 ?? undefined, directorySha256: plan.directory_sha256 });
|
|
76252
|
+
const result = await installClaudeHook2(home, { settingsSha256: plan.settings_sha256, legacyHookSha256: plan.legacy_hook_sha256 ?? undefined, directorySha256: plan.directory_sha256 });
|
|
76148
76253
|
console.log(chalk41.green(result.changed ? " \u2713 Hosted Stop hook installed" : " \xB7 Hosted Stop hook already installed"));
|
|
76149
76254
|
} catch {
|
|
76150
76255
|
console.error(chalk41.red(" \u2717 Stop hook installation refused; run mementos session setup-hook --claude to inspect the preconditions"));
|
package/dist/db/agents.d.ts
CHANGED
|
@@ -16,6 +16,13 @@ export interface AgentListFilter {
|
|
|
16
16
|
* Returns null when neither source yields a non-empty name. An unreadable or
|
|
17
17
|
* missing identity file is treated as absent — a write must never fail merely
|
|
18
18
|
* because the identity file was mid-write.
|
|
19
|
+
*
|
|
20
|
+
* BOTH sources are sanitized: control-byte padding is stripped (station07's
|
|
21
|
+
* file held `autumn-bea\0\0`, and the padded value reached
|
|
22
|
+
* `/agents/autumn-bea%00%00` as a hosted 400) and the remainder must be a
|
|
23
|
+
* usable agent name. A value that is not usable — an error string written by a
|
|
24
|
+
* failed claim, say — is treated as absent rather than used, so the caller's
|
|
25
|
+
* own refusal path owns the outcome.
|
|
19
26
|
*/
|
|
20
27
|
export declare function resolveWritingAgentName(): string | null;
|
|
21
28
|
export declare function registerAgent(name: string, sessionId?: string, description?: string, role?: string, projectId?: string, db?: Database): Agent;
|
package/dist/db/agents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/db/agents.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAS/C,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAcvD;AAgBD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,CA0EP;AAED,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CA6Bd;AA+CD,wBAAgB,UAAU,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC;AACnD,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC;AAsB5E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAahE;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,KAAK,EAAE,CAAC;AAC/E,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,eAAe,EACvB,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,EAAE,CAAC;AA0BX,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACtI,EAAE,CAAC,EAAE,QAAQ,GACZ,KAAK,GAAG,IAAI,CAsDd"}
|
package/dist/index.js
CHANGED
|
@@ -55476,6 +55476,18 @@ function getMemoryProjectLinkReceipt(memoryId, receiptId, identity = linkAuthori
|
|
|
55476
55476
|
}
|
|
55477
55477
|
// src/memory-project-link/index.ts
|
|
55478
55478
|
init_schema();
|
|
55479
|
+
// src/lib/agent-name.ts
|
|
55480
|
+
function isUrlDotSegment(value) {
|
|
55481
|
+
const segment = value.toLowerCase();
|
|
55482
|
+
return segment === "." || segment === ".." || segment === "%2e" || segment === ".%2e" || segment === "%2e." || segment === "%2e%2e";
|
|
55483
|
+
}
|
|
55484
|
+
function agentPathSegment(name) {
|
|
55485
|
+
if (isUrlDotSegment(name)) {
|
|
55486
|
+
throw new Error(`Refusing to build an agent URL from ${JSON.stringify(name)}: the URL parser collapses a dot segment, ` + `so the request path would not be the one built here.`);
|
|
55487
|
+
}
|
|
55488
|
+
return encodeURIComponent(name);
|
|
55489
|
+
}
|
|
55490
|
+
|
|
55479
55491
|
// src/db/agents.ts
|
|
55480
55492
|
init_database();
|
|
55481
55493
|
init_api_mode();
|
|
@@ -55555,7 +55567,7 @@ function registerAgent(name, sessionId, description, role, projectId, db) {
|
|
|
55555
55567
|
}
|
|
55556
55568
|
function getAgent(idOrName, db) {
|
|
55557
55569
|
if (!db && isApiMode()) {
|
|
55558
|
-
const { status, data } = apiJson("GET", `/agents/${
|
|
55570
|
+
const { status, data } = apiJson("GET", `/agents/${agentPathSegment(idOrName)}`, undefined, { allow404: true });
|
|
55559
55571
|
if (status === 404 || !data)
|
|
55560
55572
|
return null;
|
|
55561
55573
|
return data;
|
|
@@ -55619,7 +55631,7 @@ function touchAgent(idOrName, db) {
|
|
|
55619
55631
|
const agent2 = getAgent(idOrName);
|
|
55620
55632
|
if (!agent2)
|
|
55621
55633
|
return;
|
|
55622
|
-
apiJson("PATCH", `/agents/${
|
|
55634
|
+
apiJson("PATCH", `/agents/${agentPathSegment(agent2.id)}`, {});
|
|
55623
55635
|
return;
|
|
55624
55636
|
}
|
|
55625
55637
|
const d = db || getDatabase();
|
|
@@ -55645,7 +55657,7 @@ function listAgentsByProject(projectId, filterOrDb = {}, db) {
|
|
|
55645
55657
|
}
|
|
55646
55658
|
function updateAgent(id, updates, db) {
|
|
55647
55659
|
if (!db && isApiMode()) {
|
|
55648
|
-
const { status, data } = apiJson("PATCH", `/agents/${
|
|
55660
|
+
const { status, data } = apiJson("PATCH", `/agents/${agentPathSegment(id)}`, updates, { allow404: true });
|
|
55649
55661
|
if (status === 404 || !data)
|
|
55650
55662
|
return null;
|
|
55651
55663
|
return data;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hygiene for agent identity VALUES, shared by every reader in this package.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS. On 2026-09-18 the fleet's machine identity surface
|
|
5
|
+
* (`~/.hasna/conversations/agent-id`, written by `conversations agents
|
|
6
|
+
* register`) was found holding two corrupt values: station07's was
|
|
7
|
+
* `autumn-bea\0\0` (NUL padded) and station01's was an error string with a
|
|
8
|
+
* literal newline, written by a failed claim. `resolveWritingAgentName()` read
|
|
9
|
+
* both at face value, and the padded name was interpolated into
|
|
10
|
+
* `/agents/autumn-bea%00%00` — a 400 from the hosted API, on a path that only
|
|
11
|
+
* ever receives a name.
|
|
12
|
+
*
|
|
13
|
+
* The rules are deliberately the same three the conversations CLI applies:
|
|
14
|
+
* control-byte PADDING is stripped, the remainder is trimmed, and what is left
|
|
15
|
+
* must be ONE token of `[A-Za-z0-9._-]` (at most 128 characters). A value that
|
|
16
|
+
* fails is refused, never "repaired" into something nobody wrote: an error
|
|
17
|
+
* message that landed in a name field is not an identity.
|
|
18
|
+
*
|
|
19
|
+
* This module has NO imports on purpose — it is reached from the db layer, the
|
|
20
|
+
* CLI, the MCP tools and the SDK, and must not drag any of them into a bundle.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Longest agent name this build will accept. Fleet names are short
|
|
24
|
+
* (`autumn-bear`, `chief-engineering-officer`); anything longer is a message
|
|
25
|
+
* that landed in a name field, not a name.
|
|
26
|
+
*/
|
|
27
|
+
export declare const AGENT_NAME_MAX_LENGTH = 128;
|
|
28
|
+
/** Remove every control byte from a raw value. Pure. */
|
|
29
|
+
export declare function stripControlBytes(value: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Strip control-byte PADDING and surrounding whitespace from one raw value.
|
|
32
|
+
*
|
|
33
|
+
* This is the shape the fleet actually saw: station07's file held
|
|
34
|
+
* `autumn-bea\0\0`, and the padded value reached the hosted API as
|
|
35
|
+
* `/agents/autumn-bea%00%00`. The padding is never part of a name, so the
|
|
36
|
+
* readable bytes under it are kept.
|
|
37
|
+
*
|
|
38
|
+
* Only the EDGES are stripped. A control byte that survives in the middle is
|
|
39
|
+
* corruption that cannot be repaired by deleting it — joining `autumn\0bear`
|
|
40
|
+
* into `autumnbear` would mint a name nobody wrote — so the sanitizer below
|
|
41
|
+
* refuses such a value instead. Pure.
|
|
42
|
+
*/
|
|
43
|
+
export declare function trimControlBytes(value: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Whether a value can be used as an agent name at all.
|
|
46
|
+
*
|
|
47
|
+
* A name is interpolated into URL paths and into attribution columns, so it
|
|
48
|
+
* must be one token — no spaces, no colons, no slashes — and contain at least
|
|
49
|
+
* one character that is not a dot. The last rule is what refuses `.` and `..`:
|
|
50
|
+
* a pure dot segment is collapsed by the URL parser, so `/api/agents/..`
|
|
51
|
+
* resolves to `/v1/` and the request silently changes shape (measured in review
|
|
52
|
+
* of hasna/apps#2619, todos A3-00715). `Error: already-claimed` fails the
|
|
53
|
+
* charset and is refused instead of being registered as an agent.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isValidAgentName(name: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* The segments a URL parser collapses while resolving a path: `.` and `..`,
|
|
58
|
+
* plus their percent-encoded spellings (the WHATWG URL spec's "single-dot" and
|
|
59
|
+
* "double-dot path segment").
|
|
60
|
+
*
|
|
61
|
+
* `encodeURIComponent` does NOT touch a dot, so encoding alone cannot
|
|
62
|
+
* neutralize these: `/api/agents/..` is resolved by the fetch URL parser to
|
|
63
|
+
* `/v1/` and the request silently changes shape — measured in review of
|
|
64
|
+
* hasna/apps#2619 (todos A3-00715), where the SDK's `getAgent("..")` issued
|
|
65
|
+
* `GET /v1/`. This predicate is the rule for the URL BOUNDARY below, where a
|
|
66
|
+
* value can be an explicit argument rather than a validated name;
|
|
67
|
+
* {@link isValidAgentName} applies the name-side equivalent (at least one
|
|
68
|
+
* non-dot character). Pure.
|
|
69
|
+
*/
|
|
70
|
+
export declare function isUrlDotSegment(value: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Encode one agent name for an `/agents/<name>` path segment, refusing a value
|
|
73
|
+
* the URL parser would collapse to a different path.
|
|
74
|
+
*
|
|
75
|
+
* This is the boundary rule for callers that take a name straight from argv, an
|
|
76
|
+
* MCP argument, a store row or an SDK call — places where
|
|
77
|
+
* {@link isValidAgentName} is not on the path. Everything else is
|
|
78
|
+
* percent-encoded; a dot segment is refused rather than encoded, because
|
|
79
|
+
* encoding cannot express it (see {@link isUrlDotSegment}).
|
|
80
|
+
*
|
|
81
|
+
* @throws {Error} when `name` is a URL dot segment.
|
|
82
|
+
*/
|
|
83
|
+
export declare function agentPathSegment(name: string): string;
|
|
84
|
+
/**
|
|
85
|
+
* Read one raw value into a usable agent name, or `null` when nothing usable
|
|
86
|
+
* remains. Control-byte padding is stripped and the result is trimmed before
|
|
87
|
+
* validation, so `autumn-bea\0\0\n` reads back as `autumn-bea`; a value with
|
|
88
|
+
* interior corruption, or one that is not a single token, is refused.
|
|
89
|
+
*
|
|
90
|
+
* `null` is the only failure value: callers decide whether an unusable name is
|
|
91
|
+
* absent (the file fallback in `resolveWritingAgentName`) or a refusal. Pure.
|
|
92
|
+
*/
|
|
93
|
+
export declare function sanitizeAgentName(raw: string | null | undefined): string | null;
|
|
94
|
+
//# sourceMappingURL=agent-name.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-name.d.ts","sourceRoot":"","sources":["../../src/lib/agent-name.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAWzC,wDAAwD;AACxD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAUtD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAO/E"}
|
|
@@ -21,7 +21,9 @@ export declare function installClaudeHook(home: string, expected: {
|
|
|
21
21
|
settingsSha256: string;
|
|
22
22
|
legacyHookSha256?: string;
|
|
23
23
|
directorySha256?: string;
|
|
24
|
-
}, command?: string
|
|
24
|
+
}, command?: string, options?: {
|
|
25
|
+
skillsDataDir?: string;
|
|
26
|
+
}): Promise<{
|
|
25
27
|
changed: boolean;
|
|
26
28
|
settings_sha256: string;
|
|
27
29
|
backup?: undefined;
|
|
@@ -29,5 +31,5 @@ export declare function installClaudeHook(home: string, expected: {
|
|
|
29
31
|
changed: boolean;
|
|
30
32
|
settings_sha256: string;
|
|
31
33
|
backup: string | null;
|
|
32
|
-
}
|
|
34
|
+
}>;
|
|
33
35
|
//# sourceMappingURL=claude-hook-install.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-hook-install.d.ts","sourceRoot":"","sources":["../../src/lib/claude-hook-install.ts"],"names":[],"mappings":"AAYA,wBAAgB,iBAAiB,IAAI,MAAM,CAI1C;AAuBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAsB;;;;;;;;;;;;;;;;;EAmCzE;AAED,
|
|
1
|
+
{"version":3,"file":"claude-hook-install.d.ts","sourceRoot":"","sources":["../../src/lib/claude-hook-install.ts"],"names":[],"mappings":"AAYA,wBAAgB,iBAAiB,IAAI,MAAM,CAI1C;AAuBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,SAAsB;;;;;;;;;;;;;;;;;EAmCzE;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,SAAsB,EAAE,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO;;;;;;;;GAiDvN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-stop-hook.d.ts","sourceRoot":"","sources":["../../src/lib/claude-stop-hook.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"claude-stop-hook.d.ts","sourceRoot":"","sources":["../../src/lib/claude-stop-hook.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAsC,MAAM,iBAAiB,CAAC;AA0DrF,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAwCzE;AAED,wBAAsB,iBAAiB,CAAC,OAAO,GAAE;IAC/C,KAAK,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,cAAc,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmDvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"open-sessions-connector.d.ts","sourceRoot":"","sources":["../../src/lib/open-sessions-connector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"open-sessions-connector.d.ts","sourceRoot":"","sources":["../../src/lib/open-sessions-connector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,WAAW,2BAA2B;IAC1C,iCAAiC;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAA8B;gBAEhC,MAAM,EAAE,2BAA2B;IAI/C;;OAEG;IACG,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GACrD,OAAO,CAAC,YAAY,CAAC;IAwCxB;;OAEG;IACG,kBAAkB,CAAC,OAAO,GAAE;QAChC,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YAelB,sBAAsB;YAiBtB,oBAAoB;CAqBnC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,IAAI,qBAAqB,CAaxD"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -61981,6 +61981,20 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
61981
61981
|
|
|
61982
61982
|
// src/db/agents.ts
|
|
61983
61983
|
init_types();
|
|
61984
|
+
|
|
61985
|
+
// src/lib/agent-name.ts
|
|
61986
|
+
function isUrlDotSegment(value) {
|
|
61987
|
+
const segment = value.toLowerCase();
|
|
61988
|
+
return segment === "." || segment === ".." || segment === "%2e" || segment === ".%2e" || segment === "%2e." || segment === "%2e%2e";
|
|
61989
|
+
}
|
|
61990
|
+
function agentPathSegment(name) {
|
|
61991
|
+
if (isUrlDotSegment(name)) {
|
|
61992
|
+
throw new Error(`Refusing to build an agent URL from ${JSON.stringify(name)}: the URL parser collapses a dot segment, ` + `so the request path would not be the one built here.`);
|
|
61993
|
+
}
|
|
61994
|
+
return encodeURIComponent(name);
|
|
61995
|
+
}
|
|
61996
|
+
|
|
61997
|
+
// src/db/agents.ts
|
|
61984
61998
|
init_database();
|
|
61985
61999
|
init_api_mode();
|
|
61986
62000
|
var CONFLICT_WINDOW_MS = 30 * 60 * 1000;
|
|
@@ -62059,7 +62073,7 @@ function registerAgent(name, sessionId, description, role, projectId, db) {
|
|
|
62059
62073
|
}
|
|
62060
62074
|
function getAgent(idOrName, db) {
|
|
62061
62075
|
if (!db && isApiMode()) {
|
|
62062
|
-
const { status, data } = apiJson("GET", `/agents/${
|
|
62076
|
+
const { status, data } = apiJson("GET", `/agents/${agentPathSegment(idOrName)}`, undefined, { allow404: true });
|
|
62063
62077
|
if (status === 404 || !data)
|
|
62064
62078
|
return null;
|
|
62065
62079
|
return data;
|
|
@@ -62123,7 +62137,7 @@ function touchAgent(idOrName, db) {
|
|
|
62123
62137
|
const agent2 = getAgent(idOrName);
|
|
62124
62138
|
if (!agent2)
|
|
62125
62139
|
return;
|
|
62126
|
-
apiJson("PATCH", `/agents/${
|
|
62140
|
+
apiJson("PATCH", `/agents/${agentPathSegment(agent2.id)}`, {});
|
|
62127
62141
|
return;
|
|
62128
62142
|
}
|
|
62129
62143
|
const d = db || getDatabase();
|
|
@@ -62149,7 +62163,7 @@ function listAgentsByProject(projectId, filterOrDb = {}, db) {
|
|
|
62149
62163
|
}
|
|
62150
62164
|
function updateAgent(id, updates, db) {
|
|
62151
62165
|
if (!db && isApiMode()) {
|
|
62152
|
-
const { status, data } = apiJson("PATCH", `/agents/${
|
|
62166
|
+
const { status, data } = apiJson("PATCH", `/agents/${agentPathSegment(id)}`, updates, { allow404: true });
|
|
62153
62167
|
if (status === 404 || !data)
|
|
62154
62168
|
return null;
|
|
62155
62169
|
return data;
|
package/dist/sdk/index.d.ts
CHANGED
|
@@ -581,7 +581,8 @@ export interface MementosClientConfig {
|
|
|
581
581
|
export declare class MementosError extends Error {
|
|
582
582
|
readonly status: number;
|
|
583
583
|
readonly details?: unknown | undefined;
|
|
584
|
-
|
|
584
|
+
readonly code?: "MEMENTOS_RESPONSE_CONTRACT" | undefined;
|
|
585
|
+
constructor(message: string, status: number, details?: unknown | undefined, code?: "MEMENTOS_RESPONSE_CONTRACT" | undefined);
|
|
585
586
|
}
|
|
586
587
|
/**
|
|
587
588
|
* Thrown when the SDK is asked to reach the memory store and NOTHING resolves:
|