om-memory-system 5.9.0 → 5.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -11
- package/bin/omms-launch.mjs +8 -4
- package/dist/adapters/opencode/conversation.js +4 -2
- package/dist/adapters/opencode/memory-tool.d.ts +4 -0
- package/dist/adapters/opencode/memory-tool.js +53 -0
- package/dist/adapters/opencode/package-skills.d.ts +1 -1
- package/dist/adapters/opencode/package-skills.js +17 -15
- package/dist/adapters/pi/extension.js +7 -0
- package/dist/cli/memory-command.d.ts +2 -0
- package/dist/cli/memory-command.js +35 -9
- package/dist/cli/web-command.js +25 -9
- package/dist/cli/web-step-aside.d.ts +17 -0
- package/dist/cli/web-step-aside.js +34 -0
- package/dist/config-paths.d.ts +5 -0
- package/dist/config-paths.js +15 -0
- package/dist/config-template.d.ts +1 -1
- package/dist/config-template.js +8 -0
- package/dist/config.d.ts +51 -40
- package/dist/config.js +8 -15
- package/dist/core/capture.d.ts +2 -1
- package/dist/core/capture.js +8 -1
- package/dist/core/host.d.ts +12 -0
- package/dist/core/memorisation-operations.d.ts +33 -0
- package/dist/core/memorisation-operations.js +160 -0
- package/dist/core/memorisation-settings.d.ts +11 -0
- package/dist/core/memorisation-settings.js +48 -0
- package/dist/core/memorisation.d.ts +43 -0
- package/dist/core/memorisation.js +76 -0
- package/dist/core/memorise-turn.d.ts +11 -0
- package/dist/core/memorise-turn.js +64 -0
- package/dist/core/memory-operations.d.ts +6 -1
- package/dist/core/memory-operations.js +29 -0
- package/dist/core/memory-tool-text.js +1 -1
- package/dist/core/retrieval.d.ts +3 -2
- package/dist/core/retrieval.js +11 -4
- package/dist/core/tool-outcome.d.ts +6 -0
- package/dist/core/tool-outcome.js +31 -0
- package/dist/importer/claude-conversation.js +8 -1
- package/dist/importer/claude-turn-units.js +1 -0
- package/dist/importer/importer.js +3 -3
- package/dist/importer/opencode-reader.js +2 -1
- package/dist/importer/pi-conversation.js +11 -5
- package/dist/index.js +2 -42
- package/dist/services/api-handlers.js +13 -35
- package/dist/services/capture-diagnostics.d.ts +3 -2
- package/dist/services/capture-diagnostics.js +5 -1
- package/dist/services/cleanup-service.js +2 -1
- package/dist/services/client-session-read.d.ts +16 -0
- package/dist/services/client-session-read.js +32 -0
- package/dist/services/client.d.ts +6 -1
- package/dist/services/client.js +20 -36
- package/dist/services/deduplication-service.js +12 -1
- package/dist/services/global-config-writer.js +14 -1
- package/dist/services/linked-search.d.ts +15 -0
- package/dist/services/linked-search.js +23 -2
- package/dist/services/live-captured-entries.js +2 -1
- package/dist/services/memorisation-routes.d.ts +16 -0
- package/dist/services/memorisation-routes.js +94 -0
- package/dist/services/memorisation-store.d.ts +45 -0
- package/dist/services/memorisation-store.js +120 -0
- package/dist/services/memory-kind.d.ts +6 -0
- package/dist/services/memory-kind.js +6 -0
- package/dist/services/memory-portability-service.js +2 -1
- package/dist/services/memory-replace.d.ts +8 -0
- package/dist/services/memory-replace.js +26 -0
- package/dist/services/retrieval-rerank.d.ts +3 -1
- package/dist/services/retrieval-rerank.js +1 -1
- package/dist/services/settings-snapshot.js +6 -0
- package/dist/services/turso/exact-scan.d.ts +2 -1
- package/dist/services/turso/exact-scan.js +4 -3
- package/dist/services/turso/keyword-search.d.ts +2 -1
- package/dist/services/turso/keyword-search.js +3 -2
- package/dist/services/turso/vector-search.d.ts +10 -9
- package/dist/services/turso/vector-search.js +38 -95
- package/dist/services/turso/vector-writes.d.ts +8 -0
- package/dist/services/turso/vector-writes.js +80 -0
- package/dist/services/web-ensure.d.ts +1 -0
- package/dist/services/web-ensure.js +5 -1
- package/dist/services/web-memory-rows.d.ts +12 -0
- package/dist/services/web-memory-rows.js +58 -0
- package/dist/services/web-route-modules.js +5 -0
- package/dist/services/web-update.d.ts +6 -1
- package/dist/services/web-update.js +12 -1
- package/dist/v2/adapter.d.ts +13 -1
- package/dist/v2/adapter.js +7 -0
- package/dist/web/assets/index-CoYuPYCS.css +2 -0
- package/dist/web/assets/index-DQgI670D.js +171 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/skills/omms-memory/SKILL.md +18 -0
- package/skills/omms-remember/SKILL.md +118 -0
- package/dist/web/assets/index-2BNRLGqe.css +0 -2
- package/dist/web/assets/index-Bhk1P5Z7.js +0 -170
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isPlaceholderApiKey } from "./services/ai/api-key-placeholder.js";
|
|
2
2
|
import { type KeywordMode } from "./utils/keyword-boost.js";
|
|
3
3
|
import { type ActiveSpace } from "./services/spaces/active-space.js";
|
|
4
|
+
import * as memorisation from "./core/memorisation-settings.js";
|
|
4
5
|
interface OmmsConfig {
|
|
5
6
|
storagePath?: string;
|
|
6
7
|
userEmailOverride?: string;
|
|
@@ -28,6 +29,10 @@ interface OmmsConfig {
|
|
|
28
29
|
keywordMode?: KeywordMode;
|
|
29
30
|
/** Shared budget for automatic memory context in approximate tokens (ceil(UTF-8 bytes / 4)). */
|
|
30
31
|
retrievalMaxTokens?: number;
|
|
32
|
+
/** Memorisation size and repeat behaviour. Global config only. */
|
|
33
|
+
memorisationMaxSize?: number;
|
|
34
|
+
memorisationSizeUnit?: memorisation.MemorisationSettings["memorisationSizeUnit"];
|
|
35
|
+
memorisationRepeat?: memorisation.MemorisationSettings["memorisationRepeat"];
|
|
31
36
|
maxProfileItems?: number;
|
|
32
37
|
injectProfile?: boolean;
|
|
33
38
|
containerTagPrefix?: string;
|
|
@@ -196,26 +201,6 @@ declare function buildConfig(fileConfig: OmmsConfig): {
|
|
|
196
201
|
skillLearning: boolean;
|
|
197
202
|
skillLearningRedact: boolean;
|
|
198
203
|
skillFolders: string[] | undefined;
|
|
199
|
-
storagePath: string;
|
|
200
|
-
/** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
|
|
201
|
-
skillStoragePath: string | undefined;
|
|
202
|
-
userEmailOverride: string | undefined;
|
|
203
|
-
userNameOverride: string | undefined;
|
|
204
|
-
embeddingModel: string;
|
|
205
|
-
embeddingDimensions: number;
|
|
206
|
-
embeddingUseTaskPrefixes: boolean;
|
|
207
|
-
embeddingApiUrl: string | undefined;
|
|
208
|
-
embeddingApiKey: string | undefined;
|
|
209
|
-
similarityThreshold: number;
|
|
210
|
-
rerankerEnabled: boolean;
|
|
211
|
-
rerankerApiUrl: string | undefined;
|
|
212
|
-
rerankerModel: string | undefined;
|
|
213
|
-
rerankerApiKey: string | undefined;
|
|
214
|
-
rerankerTimeoutMs: number;
|
|
215
|
-
keywordBoost: number;
|
|
216
|
-
keywordMode: KeywordMode;
|
|
217
|
-
maxMemories: number;
|
|
218
|
-
retrievalMaxTokens: number;
|
|
219
204
|
maxProfileItems: number;
|
|
220
205
|
injectProfile: boolean;
|
|
221
206
|
containerTagPrefix: string;
|
|
@@ -241,6 +226,29 @@ declare function buildConfig(fileConfig: OmmsConfig): {
|
|
|
241
226
|
captureTraceRetentionDays: number;
|
|
242
227
|
captureAttemptRetentionDays: number;
|
|
243
228
|
captureRetryRetentionHours: number;
|
|
229
|
+
memorisationMaxSize: number;
|
|
230
|
+
memorisationSizeUnit: import("./core/memorisation.js").MemorisationSizeUnit;
|
|
231
|
+
memorisationRepeat: import("./core/memorisation.js").MemorisationRepeat;
|
|
232
|
+
storagePath: string;
|
|
233
|
+
/** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
|
|
234
|
+
skillStoragePath: string | undefined;
|
|
235
|
+
userEmailOverride: string | undefined;
|
|
236
|
+
userNameOverride: string | undefined;
|
|
237
|
+
embeddingModel: string;
|
|
238
|
+
embeddingDimensions: number;
|
|
239
|
+
embeddingUseTaskPrefixes: boolean;
|
|
240
|
+
embeddingApiUrl: string | undefined;
|
|
241
|
+
embeddingApiKey: string | undefined;
|
|
242
|
+
similarityThreshold: number;
|
|
243
|
+
rerankerEnabled: boolean;
|
|
244
|
+
rerankerApiUrl: string | undefined;
|
|
245
|
+
rerankerModel: string | undefined;
|
|
246
|
+
rerankerApiKey: string | undefined;
|
|
247
|
+
rerankerTimeoutMs: number;
|
|
248
|
+
keywordBoost: number;
|
|
249
|
+
keywordMode: KeywordMode;
|
|
250
|
+
maxMemories: number;
|
|
251
|
+
retrievalMaxTokens: number;
|
|
244
252
|
};
|
|
245
253
|
export declare function getGlobalConfigSourcePath(): string | undefined;
|
|
246
254
|
export declare function getGlobalConfigWritePath(): string;
|
|
@@ -310,26 +318,6 @@ export declare let CONFIG: {
|
|
|
310
318
|
skillLearning: boolean;
|
|
311
319
|
skillLearningRedact: boolean;
|
|
312
320
|
skillFolders: string[] | undefined;
|
|
313
|
-
storagePath: string;
|
|
314
|
-
/** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
|
|
315
|
-
skillStoragePath: string | undefined;
|
|
316
|
-
userEmailOverride: string | undefined;
|
|
317
|
-
userNameOverride: string | undefined;
|
|
318
|
-
embeddingModel: string;
|
|
319
|
-
embeddingDimensions: number;
|
|
320
|
-
embeddingUseTaskPrefixes: boolean;
|
|
321
|
-
embeddingApiUrl: string | undefined;
|
|
322
|
-
embeddingApiKey: string | undefined;
|
|
323
|
-
similarityThreshold: number;
|
|
324
|
-
rerankerEnabled: boolean;
|
|
325
|
-
rerankerApiUrl: string | undefined;
|
|
326
|
-
rerankerModel: string | undefined;
|
|
327
|
-
rerankerApiKey: string | undefined;
|
|
328
|
-
rerankerTimeoutMs: number;
|
|
329
|
-
keywordBoost: number;
|
|
330
|
-
keywordMode: KeywordMode;
|
|
331
|
-
maxMemories: number;
|
|
332
|
-
retrievalMaxTokens: number;
|
|
333
321
|
maxProfileItems: number;
|
|
334
322
|
injectProfile: boolean;
|
|
335
323
|
containerTagPrefix: string;
|
|
@@ -355,6 +343,29 @@ export declare let CONFIG: {
|
|
|
355
343
|
captureTraceRetentionDays: number;
|
|
356
344
|
captureAttemptRetentionDays: number;
|
|
357
345
|
captureRetryRetentionHours: number;
|
|
346
|
+
memorisationMaxSize: number;
|
|
347
|
+
memorisationSizeUnit: import("./core/memorisation.js").MemorisationSizeUnit;
|
|
348
|
+
memorisationRepeat: import("./core/memorisation.js").MemorisationRepeat;
|
|
349
|
+
storagePath: string;
|
|
350
|
+
/** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
|
|
351
|
+
skillStoragePath: string | undefined;
|
|
352
|
+
userEmailOverride: string | undefined;
|
|
353
|
+
userNameOverride: string | undefined;
|
|
354
|
+
embeddingModel: string;
|
|
355
|
+
embeddingDimensions: number;
|
|
356
|
+
embeddingUseTaskPrefixes: boolean;
|
|
357
|
+
embeddingApiUrl: string | undefined;
|
|
358
|
+
embeddingApiKey: string | undefined;
|
|
359
|
+
similarityThreshold: number;
|
|
360
|
+
rerankerEnabled: boolean;
|
|
361
|
+
rerankerApiUrl: string | undefined;
|
|
362
|
+
rerankerModel: string | undefined;
|
|
363
|
+
rerankerApiKey: string | undefined;
|
|
364
|
+
rerankerTimeoutMs: number;
|
|
365
|
+
keywordBoost: number;
|
|
366
|
+
keywordMode: KeywordMode;
|
|
367
|
+
maxMemories: number;
|
|
368
|
+
retrievalMaxTokens: number;
|
|
358
369
|
};
|
|
359
370
|
type RuntimeConfig = ReturnType<typeof buildConfig>;
|
|
360
371
|
export { getAutoCaptureProviderStatus, type AutoCaptureProviderStatus, } from "./services/ai/live-model-choice.js";
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
3
|
import { stripJsoncComments } from "./services/jsonc.js";
|
|
5
4
|
import { isValidClaudeConfigDir } from "./services/claude-folder.js";
|
|
6
5
|
import { log } from "./services/logger.js";
|
|
@@ -22,20 +21,8 @@ import { expandPath, storagePathFrom, withSkillStorage, } from "./services/space
|
|
|
22
21
|
import { resolveActiveSpace } from "./services/spaces/active-space.js";
|
|
23
22
|
import { backfillSpaceLabelsFromDisk } from "./services/spaces/space-label.js";
|
|
24
23
|
import { resetStoreHandles } from "./services/spaces/store-handles.js";
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const LEGACY_CONFIG_FILES = [
|
|
28
|
-
join(homedir(), ".config", "opencode", "opencode-mem.jsonc"),
|
|
29
|
-
join(homedir(), ".config", "opencode", "opencode-mem.json"),
|
|
30
|
-
];
|
|
31
|
-
// Dual-read: the omms config wins; the legacy opencode-mem config is read
|
|
32
|
-
// only when no omms config file exists (decision D13 rule 6). The legacy
|
|
33
|
-
// file is never written.
|
|
34
|
-
const CONFIG_FILES = [
|
|
35
|
-
join(OMMS_CONFIG_DIR, "omms.jsonc"),
|
|
36
|
-
join(OMMS_CONFIG_DIR, "omms.json"),
|
|
37
|
-
...LEGACY_CONFIG_FILES,
|
|
38
|
-
];
|
|
24
|
+
import * as memorisation from "./core/memorisation-settings.js";
|
|
25
|
+
import { CONFIG_FILES, DATA_DIR, LEGACY_CONFIG_FILES, OMMS_CONFIG_DIR } from "./config-paths.js";
|
|
39
26
|
if (!existsSync(OMMS_CONFIG_DIR))
|
|
40
27
|
mkdirSync(OMMS_CONFIG_DIR, { recursive: true });
|
|
41
28
|
if (!existsSync(DATA_DIR))
|
|
@@ -51,6 +38,9 @@ const DEFAULTS = {
|
|
|
51
38
|
rerankerEnabled: false,
|
|
52
39
|
rerankerTimeoutMs: RERANKER_TIMEOUT_DEFAULT_MS,
|
|
53
40
|
retrievalMaxTokens: 2000,
|
|
41
|
+
memorisationMaxSize: 2000,
|
|
42
|
+
memorisationSizeUnit: "characters",
|
|
43
|
+
memorisationRepeat: "add",
|
|
54
44
|
maxProfileItems: 5,
|
|
55
45
|
injectProfile: true,
|
|
56
46
|
// omms since the tag prefix migration: new memories carry the fork's
|
|
@@ -325,6 +315,7 @@ function buildConfig(fileConfig) {
|
|
|
325
315
|
keywordMode: checkedKeywordMode(fileConfig.keywordMode),
|
|
326
316
|
maxMemories,
|
|
327
317
|
retrievalMaxTokens,
|
|
318
|
+
...memorisation.settingsFrom(fileConfig),
|
|
328
319
|
maxProfileItems: fileConfig.maxProfileItems ?? DEFAULTS.maxProfileItems,
|
|
329
320
|
injectProfile: fileConfig.injectProfile ?? DEFAULTS.injectProfile,
|
|
330
321
|
containerTagPrefix: fileConfig.containerTagPrefix ?? DEFAULTS.containerTagPrefix,
|
|
@@ -526,6 +517,8 @@ export function initConfig(directory, options = {}) {
|
|
|
526
517
|
delete projectOverrides.captureTraceRetentionDays;
|
|
527
518
|
// The queue holds conversation text, so only the global file sets how long.
|
|
528
519
|
delete projectOverrides.captureRetryRetentionHours;
|
|
520
|
+
for (const key of memorisation.MEMORISATION_KEYS)
|
|
521
|
+
delete projectOverrides[key];
|
|
529
522
|
// Skill learning stores full turns: a project may only turn it off.
|
|
530
523
|
const skillRule = projectSkillOverride(projectConfig);
|
|
531
524
|
if (skillRule.ignoredTurnOn) {
|
package/dist/core/capture.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CaptureFailureInfo } from "./capture-retry-policy.js";
|
|
2
|
-
import type { CaptureConversation, CapturePromptContext, CaptureProvenance, CaptureSummaryProvider } from "./host.js";
|
|
2
|
+
import type { CaptureConversation, CapturePromptContext, CaptureProvenance, CaptureSkipReason, CaptureSummaryProvider } from "./host.js";
|
|
3
3
|
export interface CaptureWorkUnit extends CaptureProvenance, CaptureConversation {
|
|
4
4
|
projectDirectory: string;
|
|
5
5
|
userPrompt: string;
|
|
@@ -12,6 +12,7 @@ export type CaptureResult = {
|
|
|
12
12
|
} | {
|
|
13
13
|
status: "skipped";
|
|
14
14
|
type?: string;
|
|
15
|
+
reason?: CaptureSkipReason;
|
|
15
16
|
};
|
|
16
17
|
/**
|
|
17
18
|
* Thrown by `captureConversation` for a failed attempt. Keeps the original
|
package/dist/core/capture.js
CHANGED
|
@@ -3,6 +3,7 @@ import { buildCaptureAttemptRecord, emitCaptureAttempt } from "../services/captu
|
|
|
3
3
|
import { memoryClient } from "../services/client.js";
|
|
4
4
|
import { getTags } from "../services/tags.js";
|
|
5
5
|
import { buildMarkdownContext, getAutoCaptureMarkdownBudget } from "./capture-context.js";
|
|
6
|
+
import { turnSavedMemorisation } from "./memorise-turn.js";
|
|
6
7
|
import { errorHttpStatus, errorRetryAfterMs, } from "./capture-retry-policy.js";
|
|
7
8
|
/**
|
|
8
9
|
* Thrown by `captureConversation` for a failed attempt. Keeps the original
|
|
@@ -18,7 +19,7 @@ export class CaptureAttemptError extends Error {
|
|
|
18
19
|
}
|
|
19
20
|
async function getLatestProjectMemory(containerTag) {
|
|
20
21
|
try {
|
|
21
|
-
const result = await memoryClient.listMemories(containerTag, 1);
|
|
22
|
+
const result = await memoryClient.listMemories(containerTag, 1, "project", { kind: "normal" });
|
|
22
23
|
if (!result.success || result.memories.length === 0)
|
|
23
24
|
return null;
|
|
24
25
|
const latest = result.memories[0];
|
|
@@ -65,6 +66,12 @@ export async function captureConversation(workUnit, provider) {
|
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
async function runCapture(workUnit, provider, diagnostics, markdownBudget) {
|
|
69
|
+
// A memorisation reaches an agent only through `recall`. Capturing the turn
|
|
70
|
+
// that saved one would put the snapshot back into per-prompt recall.
|
|
71
|
+
if (turnSavedMemorisation(workUnit.toolCalls)) {
|
|
72
|
+
diagnostics.skipReason = "memorisation-turn";
|
|
73
|
+
return { status: "skipped", reason: "memorisation-turn" };
|
|
74
|
+
}
|
|
68
75
|
const tags = getTags(workUnit.projectDirectory);
|
|
69
76
|
const latestMemory = await getLatestProjectMemory(tags.project.tag);
|
|
70
77
|
const context = buildMarkdownContext(workUnit.userPrompt, workUnit.textResponses, workUnit.toolCalls, latestMemory, markdownBudget);
|
package/dist/core/host.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { MemoryHost, MemorySourceType } from "../types/index.js";
|
|
2
2
|
export type CaptureToolOutcome = "ok" | "error" | "unknown";
|
|
3
|
+
/**
|
|
4
|
+
* Set only on a `memory` tool call in `memorise` mode: `saved` once its result
|
|
5
|
+
* reported success, `failed` when it did not, `unknown` until a result answers.
|
|
6
|
+
* Automatic capture skips a turn that holds a `saved` call.
|
|
7
|
+
*/
|
|
8
|
+
export type CaptureMemoriseState = "saved" | "failed" | "unknown";
|
|
3
9
|
export interface CaptureToolCall {
|
|
4
10
|
name: string;
|
|
5
11
|
input: string;
|
|
@@ -9,6 +15,8 @@ export interface CaptureToolCall {
|
|
|
9
15
|
error?: string;
|
|
10
16
|
/** Full path of a read-type call, for skill detection. Memory capture ignores it. */
|
|
11
17
|
path?: string;
|
|
18
|
+
/** Set by the host readers on a memorise call. Capture skips the turn when it is `saved`. */
|
|
19
|
+
memorise?: CaptureMemoriseState;
|
|
12
20
|
}
|
|
13
21
|
export interface CaptureConversation {
|
|
14
22
|
textResponses: string[];
|
|
@@ -30,6 +38,8 @@ export type CaptureAttemptOutcome = "saved" | "skipped" | "failed";
|
|
|
30
38
|
/** Fixed failure codes, in the order classification checks them. */
|
|
31
39
|
export declare const CAPTURE_FAILURE_REASONS: readonly ["call-error", "empty-text", "truncated", "invalid-json", "schema-mismatch", "persist-error"];
|
|
32
40
|
export type CaptureFailureReason = (typeof CAPTURE_FAILURE_REASONS)[number];
|
|
41
|
+
/** Why capture ended a turn as skipped before the capture model ran. */
|
|
42
|
+
export type CaptureSkipReason = "memorisation-turn";
|
|
33
43
|
export type CaptureExtractionPath = "host-model" | "external-api";
|
|
34
44
|
/**
|
|
35
45
|
* Filled in by the extraction path during one capture attempt. The capture
|
|
@@ -47,6 +57,8 @@ export interface CaptureAttemptDiagnostics {
|
|
|
47
57
|
userPrompt?: string;
|
|
48
58
|
rawReply?: string;
|
|
49
59
|
failureReason?: CaptureFailureReason;
|
|
60
|
+
/** Set when capture skipped the turn without calling the capture model. */
|
|
61
|
+
skipReason?: CaptureSkipReason;
|
|
50
62
|
/** HTTP status of a failed model call, when the path knows it. */
|
|
51
63
|
httpStatus?: number;
|
|
52
64
|
/** The wait a failed model call asked for in its `Retry-After` header. */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type MemoryScope } from "../services/client.js";
|
|
2
|
+
import type { LinkedSearchSettings } from "../services/linked-search.js";
|
|
3
|
+
import type { getTags } from "../services/tags.js";
|
|
4
|
+
import type { MemoryHost } from "../types/index.js";
|
|
5
|
+
import { type MemorisationSettings, type MemorisationSnapshot } from "./memorisation.js";
|
|
6
|
+
export interface MemoriseArgs extends MemorisationSnapshot {
|
|
7
|
+
content?: string;
|
|
8
|
+
tags?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface RecallArgs {
|
|
11
|
+
/** A memorisation ID (`ms-4k7q2p`) or free text. */
|
|
12
|
+
query?: string;
|
|
13
|
+
/** Also read as an ID or text when `query` is empty. */
|
|
14
|
+
memoryId?: string;
|
|
15
|
+
content?: string;
|
|
16
|
+
/** Comma-separated labels. Only memorisations that carry all of them match. */
|
|
17
|
+
tags?: string;
|
|
18
|
+
limit?: number;
|
|
19
|
+
scope?: MemoryScope;
|
|
20
|
+
}
|
|
21
|
+
interface MemorisationContext {
|
|
22
|
+
host?: MemoryHost;
|
|
23
|
+
hostSessionId?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Saves an exact memorisation: explicit text or a session snapshot. */
|
|
26
|
+
export declare function runMemorise(args: MemoriseArgs, context: MemorisationContext, tags: ReturnType<typeof getTags>, settings: MemorisationSettings, linked: LinkedSearchSettings): Promise<Record<string, unknown>>;
|
|
27
|
+
/**
|
|
28
|
+
* Fetches memorisations by exact ID, or by text across memorisations only. `tags` keeps only
|
|
29
|
+
* memorisations that carry every label. With `tags` and no text it returns the newest ones.
|
|
30
|
+
* An exact ID ignores `tags`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function runRecall(args: RecallArgs, tags: ReturnType<typeof getTags>, scope: MemoryScope | undefined, linked: LinkedSearchSettings): Promise<Record<string, unknown>>;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { memoryClient } from "../services/client.js";
|
|
2
|
+
import { findSnapshotByHostSession, getMemorisationById, listMemorisationsByTags, memorisationIdTaken, } from "../services/memorisation-store.js";
|
|
3
|
+
import { isFullyPrivate, stripPrivateContent } from "../services/privacy.js";
|
|
4
|
+
import { hasAllTags, isMemorisationId, MEMORISATION_TAG, memorisationId, memorisationSizeError, parseTagList, pickRecallMatches, snapshotText, visibleTags, } from "./memorisation.js";
|
|
5
|
+
const SNAPSHOT_FIELDS = ["topic", "done", "doing", "next"];
|
|
6
|
+
const BLOCKED = { success: false, error: "Private content blocked" };
|
|
7
|
+
/** Strips `<private>` text. A field that is fully private is dropped. */
|
|
8
|
+
function cleanField(value) {
|
|
9
|
+
if (!value?.trim() || isFullyPrivate(value))
|
|
10
|
+
return undefined;
|
|
11
|
+
return stripPrivateContent(value);
|
|
12
|
+
}
|
|
13
|
+
function callerTags(tags) {
|
|
14
|
+
return [...new Set([MEMORISATION_TAG, ...parseTagList(tags)])];
|
|
15
|
+
}
|
|
16
|
+
/** Builds the text to store, or the refusal to return. */
|
|
17
|
+
function memorisationText(args) {
|
|
18
|
+
const hasContent = Boolean(args.content?.trim());
|
|
19
|
+
const hasSnapshot = SNAPSHOT_FIELDS.some((field) => args[field]?.trim());
|
|
20
|
+
if (hasContent && hasSnapshot) {
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
error: "Give content or snapshot fields (topic, done, doing, next), not both",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (!hasContent && !hasSnapshot) {
|
|
27
|
+
return {
|
|
28
|
+
success: false,
|
|
29
|
+
error: "content or at least one snapshot field (topic, done, doing, next) required",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (hasContent) {
|
|
33
|
+
if (isFullyPrivate(args.content))
|
|
34
|
+
return BLOCKED;
|
|
35
|
+
return { kind: "explicit", text: stripPrivateContent(args.content) };
|
|
36
|
+
}
|
|
37
|
+
const text = snapshotText({
|
|
38
|
+
topic: cleanField(args.topic),
|
|
39
|
+
done: cleanField(args.done),
|
|
40
|
+
doing: cleanField(args.doing),
|
|
41
|
+
next: cleanField(args.next),
|
|
42
|
+
});
|
|
43
|
+
return text ? { kind: "snapshot", text } : BLOCKED;
|
|
44
|
+
}
|
|
45
|
+
/** Saves an exact memorisation: explicit text or a session snapshot. */
|
|
46
|
+
export async function runMemorise(args, context, tags, settings, linked) {
|
|
47
|
+
const built = memorisationText(args);
|
|
48
|
+
if ("success" in built)
|
|
49
|
+
return built;
|
|
50
|
+
const sizeError = memorisationSizeError(built.text, settings);
|
|
51
|
+
if (sizeError)
|
|
52
|
+
return { success: false, error: sizeError };
|
|
53
|
+
const sessionId = context.hostSessionId;
|
|
54
|
+
const wantsReplace = built.kind === "snapshot" && settings.memorisationRepeat === "replace";
|
|
55
|
+
const earlier = wantsReplace && sessionId ? await findSnapshotByHostSession(sessionId, tags.project.tag) : null;
|
|
56
|
+
const id = earlier?.id ?? (await memorisationId((candidate) => memorisationIdTaken(candidate, linked)));
|
|
57
|
+
const recordTags = callerTags(args.tags);
|
|
58
|
+
const info = tags.project;
|
|
59
|
+
const result = await memoryClient.addMemory(built.text, info.tag, {
|
|
60
|
+
id,
|
|
61
|
+
// The client swaps the earlier row for this one in one transaction, so a failed write keeps it.
|
|
62
|
+
replaceExisting: Boolean(earlier),
|
|
63
|
+
type: "memorisation",
|
|
64
|
+
tags: recordTags,
|
|
65
|
+
memorisation: built.kind,
|
|
66
|
+
host: context.host,
|
|
67
|
+
hostSessionId: sessionId,
|
|
68
|
+
source: "manual",
|
|
69
|
+
displayName: info.displayName,
|
|
70
|
+
userName: info.userName,
|
|
71
|
+
userEmail: info.userEmail,
|
|
72
|
+
projectPath: info.projectPath,
|
|
73
|
+
projectName: info.projectName,
|
|
74
|
+
gitRepoUrl: info.gitRepoUrl,
|
|
75
|
+
});
|
|
76
|
+
if (!result.success)
|
|
77
|
+
return { success: false, error: result.error };
|
|
78
|
+
const note = wantsReplace && !sessionId
|
|
79
|
+
? " It could not replace an earlier snapshot because the host gave no session ID, so it was added."
|
|
80
|
+
: "";
|
|
81
|
+
return {
|
|
82
|
+
success: true,
|
|
83
|
+
message: `Memorisation saved as ${id}.${note}`,
|
|
84
|
+
id,
|
|
85
|
+
replaced: Boolean(earlier),
|
|
86
|
+
tags: recordTags,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function toIso(value) {
|
|
90
|
+
return typeof value === "number" ? new Date(value).toISOString() : undefined;
|
|
91
|
+
}
|
|
92
|
+
/** How many labelled memorisations a recall with no text returns when the caller gives no limit. */
|
|
93
|
+
const LABEL_RECALL_LIMIT = 10;
|
|
94
|
+
/** A labelled text search reads this many hits, so the label filter does not drop a lower-ranked match. */
|
|
95
|
+
const LABEL_SEARCH_POOL = 200;
|
|
96
|
+
/**
|
|
97
|
+
* Fetches memorisations by exact ID, or by text across memorisations only. `tags` keeps only
|
|
98
|
+
* memorisations that carry every label. With `tags` and no text it returns the newest ones.
|
|
99
|
+
* An exact ID ignores `tags`.
|
|
100
|
+
*/
|
|
101
|
+
export async function runRecall(args, tags, scope, linked) {
|
|
102
|
+
const input = (args.query ?? args.memoryId ?? args.content)?.trim();
|
|
103
|
+
const wanted = parseTagList(args.tags);
|
|
104
|
+
if (!input && wanted.length === 0) {
|
|
105
|
+
return { success: false, error: "query or tags required: a memorisation ID, text or labels" };
|
|
106
|
+
}
|
|
107
|
+
if (!input)
|
|
108
|
+
return recallByLabels(wanted, tags, scope, args.limit);
|
|
109
|
+
if (isMemorisationId(input)) {
|
|
110
|
+
const found = await getMemorisationById(input, tags.project.tag, linked);
|
|
111
|
+
if (!found)
|
|
112
|
+
return { success: false, error: `No memorisation has the ID ${input}` };
|
|
113
|
+
return { success: true, count: 1, matches: [found] };
|
|
114
|
+
}
|
|
115
|
+
const result = await memoryClient.searchMemories(input, tags.project.tag, scope, {
|
|
116
|
+
includeLinked: true,
|
|
117
|
+
kind: "memorisation",
|
|
118
|
+
...(wanted.length > 0 ? { limit: LABEL_SEARCH_POOL } : {}),
|
|
119
|
+
});
|
|
120
|
+
if (!result.success)
|
|
121
|
+
return { success: false, error: result.error };
|
|
122
|
+
const candidates = (result.results ?? [])
|
|
123
|
+
.filter((hit) => hasAllTags(hit.tags, wanted))
|
|
124
|
+
.map((hit) => ({
|
|
125
|
+
id: hit.id,
|
|
126
|
+
content: (hit.memory || hit.chunk),
|
|
127
|
+
confidence: Math.round(hit.similarity * 100),
|
|
128
|
+
createdAt: toIso(hit.createdAt),
|
|
129
|
+
tags: visibleTags(hit.tags),
|
|
130
|
+
...(hit.spaceName ? { space: hit.spaceName } : {}),
|
|
131
|
+
}))
|
|
132
|
+
.sort((a, b) => b.confidence - a.confidence);
|
|
133
|
+
const matches = pickRecallMatches(candidates, args.limit);
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
query: input,
|
|
137
|
+
count: matches.length,
|
|
138
|
+
matches,
|
|
139
|
+
...(matches.length === 0 ? { message: "No memorisation matched" } : {}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/** The newest memorisations that carry every label. They are not ranked, so they have no confidence. */
|
|
143
|
+
async function recallByLabels(wanted, tags, scope, limit) {
|
|
144
|
+
const listed = await listMemorisationsByTags(tags.project.tag, wanted, scope ?? "project", Math.max(1, limit ?? LABEL_RECALL_LIMIT));
|
|
145
|
+
if (!listed.success)
|
|
146
|
+
return listed;
|
|
147
|
+
const matches = listed.memories.map((memory) => ({
|
|
148
|
+
id: memory.id,
|
|
149
|
+
content: memory.summary,
|
|
150
|
+
createdAt: memory.createdAt,
|
|
151
|
+
tags: visibleTags(memory.tags),
|
|
152
|
+
...(memory.spaceName ? { space: memory.spaceName } : {}),
|
|
153
|
+
}));
|
|
154
|
+
return {
|
|
155
|
+
success: true,
|
|
156
|
+
count: matches.length,
|
|
157
|
+
matches,
|
|
158
|
+
...(matches.length === 0 ? { message: "No memorisation matched" } : {}),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type MemorisationSettings } from "./memorisation.js";
|
|
2
|
+
export type { MemorisationSettings } from "./memorisation.js";
|
|
3
|
+
export declare const MEMORISATION_KEYS: readonly ["memorisationMaxSize", "memorisationSizeUnit", "memorisationRepeat"];
|
|
4
|
+
export declare const MEMORISATION_DEFAULTS: MemorisationSettings;
|
|
5
|
+
/** Global-only memorisation settings, with defaults for an older config file. */
|
|
6
|
+
export declare function settingsFrom(config: Partial<MemorisationSettings>): MemorisationSettings;
|
|
7
|
+
/** The web writer validates the pair, so a unit edit cannot strand an invalid number. */
|
|
8
|
+
export declare function checkMemorisationSettingsEdits(edits: Record<string, unknown>, current: MemorisationSettings): void;
|
|
9
|
+
export { settingsFrom as memorisationSettingsFrom };
|
|
10
|
+
/** The three settings are global-only; a project config cannot replace them. */
|
|
11
|
+
export declare function removeMemorisationProjectOverrides(config: Record<string, unknown>): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { memorisationCapError, memorisationCapIsValid, } from "./memorisation.js";
|
|
2
|
+
export const MEMORISATION_KEYS = [
|
|
3
|
+
"memorisationMaxSize",
|
|
4
|
+
"memorisationSizeUnit",
|
|
5
|
+
"memorisationRepeat",
|
|
6
|
+
];
|
|
7
|
+
export const MEMORISATION_DEFAULTS = {
|
|
8
|
+
memorisationMaxSize: 2000,
|
|
9
|
+
memorisationSizeUnit: "characters",
|
|
10
|
+
memorisationRepeat: "add",
|
|
11
|
+
};
|
|
12
|
+
/** Global-only memorisation settings, with defaults for an older config file. */
|
|
13
|
+
export function settingsFrom(config) {
|
|
14
|
+
const memorisationMaxSize = config.memorisationMaxSize ?? MEMORISATION_DEFAULTS.memorisationMaxSize;
|
|
15
|
+
const memorisationSizeUnit = config.memorisationSizeUnit ?? MEMORISATION_DEFAULTS.memorisationSizeUnit;
|
|
16
|
+
if (!memorisationCapIsValid(memorisationMaxSize, memorisationSizeUnit)) {
|
|
17
|
+
throw new Error("Invalid memorisation size settings");
|
|
18
|
+
}
|
|
19
|
+
const memorisationRepeat = config.memorisationRepeat ?? MEMORISATION_DEFAULTS.memorisationRepeat;
|
|
20
|
+
if (memorisationRepeat !== "add" && memorisationRepeat !== "replace") {
|
|
21
|
+
throw new Error("Invalid memorisationRepeat config");
|
|
22
|
+
}
|
|
23
|
+
return { memorisationMaxSize, memorisationSizeUnit, memorisationRepeat };
|
|
24
|
+
}
|
|
25
|
+
/** The web writer validates the pair, so a unit edit cannot strand an invalid number. */
|
|
26
|
+
export function checkMemorisationSettingsEdits(edits, current) {
|
|
27
|
+
if (edits.memorisationRepeat !== undefined) {
|
|
28
|
+
const repeat = edits.memorisationRepeat;
|
|
29
|
+
if (repeat !== "add" && repeat !== "replace") {
|
|
30
|
+
throw new Error("memorisationRepeat must be add or replace");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const hasSize = Object.hasOwn(edits, "memorisationMaxSize");
|
|
34
|
+
const hasUnit = Object.hasOwn(edits, "memorisationSizeUnit");
|
|
35
|
+
if (!hasSize && !hasUnit)
|
|
36
|
+
return;
|
|
37
|
+
const size = hasSize ? edits.memorisationMaxSize : current.memorisationMaxSize;
|
|
38
|
+
const unit = hasUnit ? edits.memorisationSizeUnit : current.memorisationSizeUnit;
|
|
39
|
+
if (!memorisationCapIsValid(size, unit)) {
|
|
40
|
+
throw new Error(memorisationCapError(unit === "tokens" ? "tokens" : "characters"));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export { settingsFrom as memorisationSettingsFrom };
|
|
44
|
+
/** The three settings are global-only; a project config cannot replace them. */
|
|
45
|
+
export function removeMemorisationProjectOverrides(config) {
|
|
46
|
+
for (const key of MEMORISATION_KEYS)
|
|
47
|
+
delete config[key];
|
|
48
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type MemorisationSizeUnit = "characters" | "tokens";
|
|
2
|
+
export type MemorisationRepeat = "add" | "replace";
|
|
3
|
+
export interface MemorisationSettings {
|
|
4
|
+
memorisationMaxSize: number;
|
|
5
|
+
memorisationSizeUnit: MemorisationSizeUnit;
|
|
6
|
+
memorisationRepeat: MemorisationRepeat;
|
|
7
|
+
}
|
|
8
|
+
export interface MemorisationSnapshot {
|
|
9
|
+
topic?: string;
|
|
10
|
+
done?: string;
|
|
11
|
+
doing?: string;
|
|
12
|
+
next?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function snapshotText(snapshot: MemorisationSnapshot): string;
|
|
15
|
+
export declare function memorisationId(exists: (id: string) => boolean | Promise<boolean>, random?: () => number): Promise<string>;
|
|
16
|
+
export declare function isMemorisationId(value: string): boolean;
|
|
17
|
+
export declare function memorisationSize(text: string, unit: MemorisationSizeUnit): number;
|
|
18
|
+
export declare function memorisationSizeError(text: string, settings: Pick<MemorisationSettings, "memorisationMaxSize" | "memorisationSizeUnit">): string | null;
|
|
19
|
+
export declare function memorisationSizeRange(unit: MemorisationSizeUnit): {
|
|
20
|
+
min: number;
|
|
21
|
+
max: number;
|
|
22
|
+
};
|
|
23
|
+
export declare function memorisationCapIsValid(maxSize: unknown, unit: unknown): maxSize is number;
|
|
24
|
+
export declare function memorisationCapError(unit: MemorisationSizeUnit): string;
|
|
25
|
+
export interface RecallCandidate {
|
|
26
|
+
id: string;
|
|
27
|
+
content: string;
|
|
28
|
+
confidence: number;
|
|
29
|
+
createdAt?: string;
|
|
30
|
+
space?: string;
|
|
31
|
+
}
|
|
32
|
+
/** One clear winner is enough. Otherwise return three, or the caller's limit when one is given. */
|
|
33
|
+
export declare function pickRecallMatches<T extends {
|
|
34
|
+
confidence: number;
|
|
35
|
+
}>(candidates: T[], limit?: number): T[];
|
|
36
|
+
/** The fixed tag every memorisation carries. It is not a label the user sees. */
|
|
37
|
+
export declare const MEMORISATION_TAG = "memorisation";
|
|
38
|
+
/** A comma-separated tag list as unique, trimmed, lower-case tags in the order given. */
|
|
39
|
+
export declare function parseTagList(text: string | undefined): string[];
|
|
40
|
+
/** The labels of a record: its tags without the fixed memorisation tag, in stored order. */
|
|
41
|
+
export declare function visibleTags(tags: string[] | undefined): string[];
|
|
42
|
+
/** True when the record carries every wanted tag. Case and surrounding spaces do not matter. */
|
|
43
|
+
export declare function hasAllTags(recordTags: string[] | undefined, wanted: string[]): boolean;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { estimateTokens } from "./context-budget.js";
|
|
2
|
+
export function snapshotText(snapshot) {
|
|
3
|
+
return ["topic", "done", "doing", "next"]
|
|
4
|
+
.filter((key) => snapshot[key]?.trim())
|
|
5
|
+
.map((key) => `${key[0].toUpperCase()}${key.slice(1)}: ${snapshot[key].trim()}`)
|
|
6
|
+
.join("\n");
|
|
7
|
+
}
|
|
8
|
+
export function memorisationId(exists, random = Math.random) {
|
|
9
|
+
return (async () => {
|
|
10
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
11
|
+
const suffix = Math.floor(random() * 36 ** 6)
|
|
12
|
+
.toString(36)
|
|
13
|
+
.padStart(6, "0");
|
|
14
|
+
const id = `ms-${suffix}`;
|
|
15
|
+
if (!(await exists(id)))
|
|
16
|
+
return id;
|
|
17
|
+
}
|
|
18
|
+
throw new Error("Could not allocate a unique memorisation ID after five tries");
|
|
19
|
+
})();
|
|
20
|
+
}
|
|
21
|
+
export function isMemorisationId(value) {
|
|
22
|
+
return /^ms-[a-z0-9]{6}$/.test(value.trim());
|
|
23
|
+
}
|
|
24
|
+
export function memorisationSize(text, unit) {
|
|
25
|
+
return unit === "tokens" ? estimateTokens(text) : text.length;
|
|
26
|
+
}
|
|
27
|
+
export function memorisationSizeError(text, settings) {
|
|
28
|
+
const size = memorisationSize(text, settings.memorisationSizeUnit);
|
|
29
|
+
if (size <= settings.memorisationMaxSize)
|
|
30
|
+
return null;
|
|
31
|
+
const unit = settings.memorisationSizeUnit;
|
|
32
|
+
return `Memorisation size limit is ${settings.memorisationMaxSize} ${unit}; the text has ${unit === "tokens" ? "about " : ""}${size} ${unit}. Shorten it and try again.`;
|
|
33
|
+
}
|
|
34
|
+
export function memorisationSizeRange(unit) {
|
|
35
|
+
return unit === "tokens" ? { min: 25, max: 5000 } : { min: 100, max: 20000 };
|
|
36
|
+
}
|
|
37
|
+
export function memorisationCapIsValid(maxSize, unit) {
|
|
38
|
+
return ((unit === "characters" || unit === "tokens") &&
|
|
39
|
+
Number.isSafeInteger(maxSize) &&
|
|
40
|
+
maxSize >= memorisationSizeRange(unit).min &&
|
|
41
|
+
maxSize <= memorisationSizeRange(unit).max);
|
|
42
|
+
}
|
|
43
|
+
export function memorisationCapError(unit) {
|
|
44
|
+
const { min, max } = memorisationSizeRange(unit);
|
|
45
|
+
return `memorisationMaxSize must be a whole number from ${min} to ${max} ${unit}`;
|
|
46
|
+
}
|
|
47
|
+
const DEFAULT_RECALL_MATCHES = 3;
|
|
48
|
+
/** One clear winner is enough. Otherwise return three, or the caller's limit when one is given. */
|
|
49
|
+
export function pickRecallMatches(candidates, limit) {
|
|
50
|
+
const maximum = Math.max(0, limit ?? DEFAULT_RECALL_MATCHES);
|
|
51
|
+
if (candidates.length === 0 || maximum === 0)
|
|
52
|
+
return [];
|
|
53
|
+
const [first, second] = candidates;
|
|
54
|
+
if (first && first.confidence >= 80 && first.confidence - (second?.confidence ?? 0) >= 10) {
|
|
55
|
+
return [first];
|
|
56
|
+
}
|
|
57
|
+
return candidates.slice(0, maximum);
|
|
58
|
+
}
|
|
59
|
+
/** The fixed tag every memorisation carries. It is not a label the user sees. */
|
|
60
|
+
export const MEMORISATION_TAG = "memorisation";
|
|
61
|
+
const normaliseTag = (tag) => tag.trim().toLowerCase();
|
|
62
|
+
/** A comma-separated tag list as unique, trimmed, lower-case tags in the order given. */
|
|
63
|
+
export function parseTagList(text) {
|
|
64
|
+
return [...new Set((text ?? "").split(",").map(normaliseTag).filter(Boolean))];
|
|
65
|
+
}
|
|
66
|
+
/** The labels of a record: its tags without the fixed memorisation tag, in stored order. */
|
|
67
|
+
export function visibleTags(tags) {
|
|
68
|
+
return (tags ?? [])
|
|
69
|
+
.map((tag) => tag.trim())
|
|
70
|
+
.filter((tag) => tag && normaliseTag(tag) !== MEMORISATION_TAG);
|
|
71
|
+
}
|
|
72
|
+
/** True when the record carries every wanted tag. Case and surrounding spaces do not matter. */
|
|
73
|
+
export function hasAllTags(recordTags, wanted) {
|
|
74
|
+
const have = new Set((recordTags ?? []).map(normaliseTag));
|
|
75
|
+
return wanted.every((tag) => have.has(normaliseTag(tag)));
|
|
76
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CaptureMemoriseState, CaptureToolCall } from "./host.js";
|
|
2
|
+
/** `unknown` for a memorise call (a result still has to answer it), otherwise undefined. */
|
|
3
|
+
export declare function memoriseCallState(name: string, args: unknown): "unknown" | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* The state a result gives a memorise call. A host error, or a JSON reply with
|
|
6
|
+
* `success: false`, is `failed`. The memory tool reports its own failures as a
|
|
7
|
+
* normal reply with `success: false`, so the host does not always mark them.
|
|
8
|
+
*/
|
|
9
|
+
export declare function memoriseResultState(isError: boolean, text: string): CaptureMemoriseState;
|
|
10
|
+
/** True when any call in the turn saved a memorisation. */
|
|
11
|
+
export declare function turnSavedMemorisation(toolCalls: readonly CaptureToolCall[]): boolean;
|