cogmemory 0.0.1-beta.2
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 +139 -0
- package/dist/arbiter.cjs +22 -0
- package/dist/arbiter.cjs.map +1 -0
- package/dist/arbiter.d.ts +72 -0
- package/dist/arbiter.js +85 -0
- package/dist/arbiter.js.map +1 -0
- package/dist/cognitive-memory.cjs +15 -0
- package/dist/cognitive-memory.cjs.map +1 -0
- package/dist/cognitive-memory.js +1068 -0
- package/dist/cognitive-memory.js.map +1 -0
- package/dist/index.d.ts +1086 -0
- package/package.json +83 -0
- package/src/arbiter.ts +14 -0
- package/src/client.ts +105 -0
- package/src/cogmem.ts +74 -0
- package/src/cognitive/arbiter.ts +105 -0
- package/src/cognitive/fast-gate.ts +63 -0
- package/src/cognitive/index.ts +64 -0
- package/src/cognitive/memory.ts +878 -0
- package/src/cognitive/relevance.ts +199 -0
- package/src/cognitive/rules.ts +127 -0
- package/src/cognitive/types.ts +232 -0
- package/src/errors.ts +85 -0
- package/src/helpers/turn.ts +172 -0
- package/src/index.ts +134 -0
- package/src/resources/context.ts +17 -0
- package/src/resources/memories.ts +60 -0
- package/src/resources/recall.ts +17 -0
- package/src/resources/self-model.ts +21 -0
- package/src/resources/stats.ts +27 -0
- package/src/resources/tensions.ts +36 -0
- package/src/resources/turns.ts +16 -0
- package/src/types.ts +246 -0
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cogmemory",
|
|
3
|
+
"version": "0.0.1-beta.2",
|
|
4
|
+
"description": "Cognitive Memory for agents: a typed client for the service, plus the deterministic cognitive layer to run in-process. No model in the retrieval path.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/cognitive-memory.cjs",
|
|
8
|
+
"module": "./dist/cognitive-memory.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/cognitive-memory.js",
|
|
14
|
+
"require": "./dist/cognitive-memory.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./arbiter": {
|
|
17
|
+
"types": "./dist/arbiter.d.ts",
|
|
18
|
+
"import": "./dist/arbiter.js",
|
|
19
|
+
"require": "./dist/arbiter.cjs"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public",
|
|
31
|
+
"provenance": false
|
|
32
|
+
},
|
|
33
|
+
"author": "AstraCollab",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/astracollab/astracollab.git",
|
|
37
|
+
"directory": "packages/cognitive-memory"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"memory",
|
|
41
|
+
"llm",
|
|
42
|
+
"agent",
|
|
43
|
+
"context",
|
|
44
|
+
"cogmem",
|
|
45
|
+
"cognitive-memory",
|
|
46
|
+
"sdk",
|
|
47
|
+
"ai"
|
|
48
|
+
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "vite build",
|
|
51
|
+
"dev": "vite build --watch",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:watch": "vitest",
|
|
54
|
+
"lint": "tsc --noEmit",
|
|
55
|
+
"check-types": "tsc --noEmit"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"ai": "^7.0.0",
|
|
59
|
+
"ofetch": "^1.4.1",
|
|
60
|
+
"zod": "^3.25.76 || ^4.1.8"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^22.19.0",
|
|
64
|
+
"ai": "^7.0.127",
|
|
65
|
+
"ofetch": "^1.4.1",
|
|
66
|
+
"typescript": "^5.8.3",
|
|
67
|
+
"vite": "^5.4.19",
|
|
68
|
+
"vite-plugin-dts": "^4.5.4",
|
|
69
|
+
"vitest": "^3.2.4",
|
|
70
|
+
"zod": "^4.3.6"
|
|
71
|
+
},
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=22.0.0"
|
|
74
|
+
},
|
|
75
|
+
"peerDependenciesMeta": {
|
|
76
|
+
"ai": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"zod": {
|
|
80
|
+
"optional": true
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/arbiter.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The model-backed arbiter, as a separate entry point.
|
|
3
|
+
*
|
|
4
|
+
* It needs `ai` and `zod`, and nothing else in this package does. Keeping it at
|
|
5
|
+
* `@astracollab/cogmem/arbiter` means a consumer who only wants the client or
|
|
6
|
+
* the deterministic core is not made to install them, and does not get a peer
|
|
7
|
+
* warning for a dependency they will never use.
|
|
8
|
+
*
|
|
9
|
+
* The arbiter is optional in the design too. Without it the engine promotes by
|
|
10
|
+
* content-word overlap, which is what `CognitiveMemory` does by default — so a
|
|
11
|
+
* deployment with no model configured still has working memory.
|
|
12
|
+
*/
|
|
13
|
+
export { createModelArbiter, type CreateModelArbiterOptions } from "./cognitive/arbiter"
|
|
14
|
+
export type { ArbiterEvaluationResult, ArbiterFn } from "./cognitive/types"
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { FetchError, ofetch, type $Fetch } from "ofetch"
|
|
2
|
+
|
|
3
|
+
import { CognitiveMemoryError } from "./errors"
|
|
4
|
+
import type { CognitiveMemoryConfig, CognitiveMemoryErrorBody } from "./types"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration, as a factory rather than a constructor.
|
|
8
|
+
*
|
|
9
|
+
* A constructor with fifteen options is a dumping ground: configuration and
|
|
10
|
+
* instantiation get tangled, tests have to construct a whole class to check one
|
|
11
|
+
* header, and "the client with a different timeout" becomes a subclass. A factory
|
|
12
|
+
* is a plain function — no `new`, no `this`, composable, trivially mockable.
|
|
13
|
+
*
|
|
14
|
+
* The real work happens in the interceptors. Authentication, error translation
|
|
15
|
+
* and debug logging are configured once here instead of being repeated in each
|
|
16
|
+
* of the twenty-odd methods below, which is the difference between changing how
|
|
17
|
+
* auth works in one place and in twenty.
|
|
18
|
+
*/
|
|
19
|
+
export function createHttpClient(config: CognitiveMemoryConfig): $Fetch {
|
|
20
|
+
const {
|
|
21
|
+
apiKey,
|
|
22
|
+
baseUrl = defaultBaseUrl(),
|
|
23
|
+
timeout = 30_000,
|
|
24
|
+
retry = 2,
|
|
25
|
+
debug = false,
|
|
26
|
+
headers: extraHeaders = {}
|
|
27
|
+
} = config
|
|
28
|
+
|
|
29
|
+
if (typeof apiKey !== "string" || apiKey.trim() === "") {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"cognitive-memory: apiKey is required. Issue one in the dashboard — the secret is shown only once."
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const inner = ofetch.create({
|
|
36
|
+
baseURL: `${baseUrl.replace(/\/$/, "")}/api/v1`,
|
|
37
|
+
timeout,
|
|
38
|
+
// Retries are for transport hiccups, not for a 400. ofetch already limits
|
|
39
|
+
// this to idempotent-looking methods, and the service is idempotent for the
|
|
40
|
+
// reads that matter.
|
|
41
|
+
retry,
|
|
42
|
+
headers: {
|
|
43
|
+
accept: "application/json",
|
|
44
|
+
...extraHeaders
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
onRequest({ options, request }) {
|
|
48
|
+
// `options.headers` is a Headers instance by this point, not a plain
|
|
49
|
+
// object — assigning to it silently drops every header.
|
|
50
|
+
options.headers.set("authorization", `Bearer ${apiKey}`)
|
|
51
|
+
options.headers.set("content-type", "application/json")
|
|
52
|
+
if (debug) {
|
|
53
|
+
// eslint-disable-next-line no-console
|
|
54
|
+
console.log("[cognitive-memory]", options.method ?? "GET", `${options.baseURL ?? ""}${request ?? ""}`)
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
onResponse({ response, options, request }) {
|
|
59
|
+
if (debug) {
|
|
60
|
+
// eslint-disable-next-line no-console
|
|
61
|
+
console.log("[cognitive-memory]", response.status, options.method ?? "GET", request ?? "")
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Error translation happens out here rather than in `onResponseError`.
|
|
69
|
+
*
|
|
70
|
+
* ofetch 1.4 replaces anything thrown from that hook with its own `FetchError`
|
|
71
|
+
* before it reaches the caller, so a hook that throws is a hook whose error
|
|
72
|
+
* silently disappears — the caller gets an exception with no status, no scope
|
|
73
|
+
* and no issues. Catching here is the only place the service's envelope can be
|
|
74
|
+
* read, and it leaves genuine transport failures (no response) untouched.
|
|
75
|
+
*/
|
|
76
|
+
// The parameter types are taken from `$Fetch` itself rather than restated, so
|
|
77
|
+
// this wrapper cannot drift out of step with the client it wraps.
|
|
78
|
+
const wrap = async (request: unknown, options?: unknown): Promise<unknown> => {
|
|
79
|
+
try {
|
|
80
|
+
return await (inner as (r: unknown, o?: unknown) => Promise<unknown>)(request, options)
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (!(error instanceof FetchError) || !error.response) throw error
|
|
83
|
+
const response = error.response
|
|
84
|
+
// The service always answers with this envelope; a proxy or a crash in
|
|
85
|
+
// front of it might not, so the body is treated as untrusted.
|
|
86
|
+
const body = (response._data ?? undefined) as CognitiveMemoryErrorBody | undefined
|
|
87
|
+
throw new CognitiveMemoryError(body?.message ?? `cognitive-memory: request failed with ${response.status}`, {
|
|
88
|
+
status: response.status,
|
|
89
|
+
code: body?.error ?? "UnknownError",
|
|
90
|
+
...(body === undefined ? {} : { body }),
|
|
91
|
+
...(body?.requiredScope === undefined ? {} : { requiredScope: body.requiredScope }),
|
|
92
|
+
...(body?.issues === undefined ? {} : { issues: body.issues })
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return wrap as unknown as $Fetch
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function defaultBaseUrl(): string {
|
|
101
|
+
if (typeof window !== "undefined") return window.location.origin
|
|
102
|
+
return "http://localhost:3000"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type HttpClient = ReturnType<typeof createHttpClient>
|
package/src/cogmem.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createHttpClient, type HttpClient } from "./client"
|
|
2
|
+
import { ContextResource } from "./resources/context"
|
|
3
|
+
import { MemoriesResource } from "./resources/memories"
|
|
4
|
+
import { RecallResource } from "./resources/recall"
|
|
5
|
+
import { SelfModelResource } from "./resources/self-model"
|
|
6
|
+
import { StatsResource } from "./resources/stats"
|
|
7
|
+
import { TensionsResource } from "./resources/tensions"
|
|
8
|
+
import { TurnsResource } from "./resources/turns"
|
|
9
|
+
import type { Health, CognitiveMemoryConfig } from "./types"
|
|
10
|
+
import { fetchHealth } from "./resources/stats"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The client.
|
|
14
|
+
*
|
|
15
|
+
* Named `Cogmem` rather than `CognitiveMemory` because that name belongs to the
|
|
16
|
+
* in-process engine, and has for as long as this layer existed. Two exports
|
|
17
|
+
* cannot share a name, and the engine's is the older public API: quietly
|
|
18
|
+
* redefining it to mean an HTTP client would break every consumer of it.
|
|
19
|
+
*
|
|
20
|
+
* Resource classes rather than a flat list of functions, because
|
|
21
|
+
* `memory.memories.` in an editor shows every operation on memories and nothing
|
|
22
|
+
* else. The methods stay one-to-one with endpoints, so the class never becomes a
|
|
23
|
+
* second, divergent copy of the service.
|
|
24
|
+
*
|
|
25
|
+
* `readonly` on the resources is not decoration: it stops a caller reassigning
|
|
26
|
+
* `memory.memories` and wondering why the other half of the client stopped
|
|
27
|
+
* seeing the change.
|
|
28
|
+
*/
|
|
29
|
+
export class Cogmem {
|
|
30
|
+
private readonly client: HttpClient
|
|
31
|
+
readonly baseUrl: string
|
|
32
|
+
|
|
33
|
+
readonly memories: MemoriesResource
|
|
34
|
+
readonly context: ContextResource
|
|
35
|
+
readonly recall: RecallResource
|
|
36
|
+
readonly turns: TurnsResource
|
|
37
|
+
readonly tensions: TensionsResource
|
|
38
|
+
readonly selfModel: SelfModelResource
|
|
39
|
+
readonly stats: StatsResource
|
|
40
|
+
|
|
41
|
+
constructor(config: CognitiveMemoryConfig) {
|
|
42
|
+
this.baseUrl = (config.baseUrl ?? "http://localhost:3000").replace(/\/$/, "")
|
|
43
|
+
this.client = createHttpClient(config)
|
|
44
|
+
this.memories = new MemoriesResource(this.client)
|
|
45
|
+
this.context = new ContextResource(this.client)
|
|
46
|
+
this.recall = new RecallResource(this.client)
|
|
47
|
+
this.turns = new TurnsResource(this.client)
|
|
48
|
+
this.tensions = new TensionsResource(this.client)
|
|
49
|
+
this.selfModel = new SelfModelResource(this.client)
|
|
50
|
+
this.stats = new StatsResource(this.client)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Liveness and limits, without spending a request on the configured key.
|
|
55
|
+
*
|
|
56
|
+
* Useful in a readiness probe: if `extractor` is `rules-only` the service is up
|
|
57
|
+
* but is not doing model-backed extraction, and `problems` is non-empty when a
|
|
58
|
+
* setting is present but unusable.
|
|
59
|
+
*/
|
|
60
|
+
health(): Promise<Health> {
|
|
61
|
+
return fetchHealth(this.baseUrl)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Both construction styles, deliberately.
|
|
67
|
+
*
|
|
68
|
+
* Some people prefer `new Cogmem(...)` and some prefer `createClient(...)`; there
|
|
69
|
+
* is no reason to make anyone rename. The factory is also the easier one to mock
|
|
70
|
+
* in tests, which is the whole reason `createHttpClient` is a function.
|
|
71
|
+
*/
|
|
72
|
+
export function createClient(config: CognitiveMemoryConfig): Cogmem {
|
|
73
|
+
return new Cogmem(config)
|
|
74
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { generateObject, type LanguageModel } from "ai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { ArbiterEvaluationResult, ArbiterFn } from "./types.js";
|
|
4
|
+
|
|
5
|
+
const ArbiterSchema = z.object({
|
|
6
|
+
promotions: z.array(
|
|
7
|
+
z.object({
|
|
8
|
+
memoryId: z.string(),
|
|
9
|
+
targetTier: z.literal("L1"),
|
|
10
|
+
signalType: z.enum(["anticipatory", "tension", "proprioceptive", "recency"]),
|
|
11
|
+
urgency: z.number().min(0).max(1),
|
|
12
|
+
})
|
|
13
|
+
),
|
|
14
|
+
demotions: z.array(
|
|
15
|
+
z.object({
|
|
16
|
+
memoryId: z.string(),
|
|
17
|
+
targetTier: z.literal("L2"),
|
|
18
|
+
reason: z.string(),
|
|
19
|
+
})
|
|
20
|
+
),
|
|
21
|
+
pins: z.array(
|
|
22
|
+
z.object({
|
|
23
|
+
memoryId: z.string(),
|
|
24
|
+
targetTier: z.literal("L0"),
|
|
25
|
+
reason: z.string(),
|
|
26
|
+
})
|
|
27
|
+
),
|
|
28
|
+
detectedTensions: z.array(
|
|
29
|
+
z.object({
|
|
30
|
+
claimA: z.string().describe("First conflicting claim or statement"),
|
|
31
|
+
claimB: z.string().describe("Contradicting statement from code or user"),
|
|
32
|
+
impact: z.enum(["low", "medium", "critical"]),
|
|
33
|
+
actionableQuestion: z.string().describe("Specific clarifying question the agent should resolve"),
|
|
34
|
+
})
|
|
35
|
+
),
|
|
36
|
+
trajectoryPrediction: z
|
|
37
|
+
.object({
|
|
38
|
+
predictedDomains: z.array(z.string()),
|
|
39
|
+
predictedFiles: z.array(z.string()),
|
|
40
|
+
prefetchMemoryIds: z.array(z.string()),
|
|
41
|
+
confidence: z.number().min(0).max(1),
|
|
42
|
+
})
|
|
43
|
+
.optional(),
|
|
44
|
+
selfModelUpdate: z
|
|
45
|
+
.object({
|
|
46
|
+
domain: z.string(),
|
|
47
|
+
success: z.boolean().optional(),
|
|
48
|
+
failurePatternObserved: z.string().optional(),
|
|
49
|
+
})
|
|
50
|
+
.optional(),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export interface CreateModelArbiterOptions {
|
|
54
|
+
/** Fast decision model (e.g., Gemini 1.5/2.5 Flash, Claude 3.5 Haiku, or GPT-4o-mini) */
|
|
55
|
+
model: LanguageModel;
|
|
56
|
+
/** Custom system prompt override if desired */
|
|
57
|
+
systemPrompt?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates an Arbiter function powered by an AI SDK model with structured output.
|
|
62
|
+
*/
|
|
63
|
+
export const createModelArbiter = (options: CreateModelArbiterOptions): ArbiterFn => {
|
|
64
|
+
return async ({ turnText, assistantReply, l0Prompt, l1Summaries, candidates }): Promise<ArbiterEvaluationResult> => {
|
|
65
|
+
try {
|
|
66
|
+
const prompt = `You are the Cache Arbiter for an AI coding agent.
|
|
67
|
+
Evaluate the latest turn and candidate memories to decide what should be HOT (L1), COLD (L2), or PINNED (L0).
|
|
68
|
+
|
|
69
|
+
CURRENT TURN:
|
|
70
|
+
User: "${turnText}"
|
|
71
|
+
Assistant: "${assistantReply}"
|
|
72
|
+
|
|
73
|
+
L0 CORE STATE:
|
|
74
|
+
${l0Prompt || "(none)"}
|
|
75
|
+
|
|
76
|
+
CURRENT HOT L1 SUMMARIES:
|
|
77
|
+
${JSON.stringify(l1Summaries, null, 2)}
|
|
78
|
+
|
|
79
|
+
WARM L2 CANDIDATE MEMORIES:
|
|
80
|
+
${JSON.stringify(candidates, null, 2)}
|
|
81
|
+
|
|
82
|
+
Analyze:
|
|
83
|
+
1. What will the agent likely need in the NEXT 1-2 turns? (Anticipatory -> promote candidate to L1)
|
|
84
|
+
2. Are any active L1 items no longer relevant? (Demote L1 to L2)
|
|
85
|
+
3. Did the conversation reveal a contradiction between user claims and known facts? (Flag as detectedTension)
|
|
86
|
+
4. Did the agent succeed or struggle in a specific domain? (Update self-model)`;
|
|
87
|
+
|
|
88
|
+
const result = await generateObject({
|
|
89
|
+
model: options.model,
|
|
90
|
+
schema: ArbiterSchema,
|
|
91
|
+
prompt,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return result.object;
|
|
95
|
+
} catch {
|
|
96
|
+
// Return safe empty fallback on model failure so the cache remains stable
|
|
97
|
+
return {
|
|
98
|
+
promotions: [],
|
|
99
|
+
demotions: [],
|
|
100
|
+
pins: [],
|
|
101
|
+
detectedTensions: [],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fast Heuristic Gate
|
|
3
|
+
*
|
|
4
|
+
* Synchronous, sub-5ms gate running on the critical path before model invocation.
|
|
5
|
+
* Detects explicit user corrections/contradictions and significant domain pivots
|
|
6
|
+
* without triggering an LLM call.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface FastGateResult {
|
|
10
|
+
action: "proceed" | "inject_caution";
|
|
11
|
+
cautionNote?: string;
|
|
12
|
+
detectedDomains?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const CONTRADICTION_PATTERNS = [
|
|
16
|
+
/actually,?\s+(?:we|i)\s+(?:switched|changed|moved|migrated|replaced|use|want|have)/i,
|
|
17
|
+
/that(?:'s|\s+is)\s+(?:wrong|incorrect|outdated|false|not\s+right|no\s+longer)/i,
|
|
18
|
+
/no,?\s+(?:it(?:'s|\s+is)|we\s+use|we\s+switched|it\s+should\s+be|don(?:'t|\s+not)\s+use)/i,
|
|
19
|
+
/stop\s+using\s+/i,
|
|
20
|
+
/instead\s+of\s+/i,
|
|
21
|
+
/disregard\s+(?:prior|previous|what\s+i\s+said)/i,
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const DOMAIN_KEYWORDS: Record<string, RegExp[]> = {
|
|
25
|
+
auth: [/auth/i, /login/i, /session/i, /jwt/i, /oauth/i, /token/i, /password/i, /permission/i],
|
|
26
|
+
database: [/postgres/i, /sqlite/i, /sql/i, /schema/i, /migration/i, /prisma/i, /drizzle/i, /mongo/i, /redis/i],
|
|
27
|
+
frontend: [/css/i, /tailwind/i, /component/i, /jsx/i, /tsx/i, /layout/i, /ui/i, /render/i, /html/i],
|
|
28
|
+
build: [/vite/i, /webpack/i, /turbo/i, /tsconfig/i, /pnpm/i, /package\.json/i, /bundle/i],
|
|
29
|
+
api: [/rest/i, /graphql/i, /endpoint/i, /route/i, /fetch/i, /request/i, /response/i],
|
|
30
|
+
git: [/branch/i, /commit/i, /merge/i, /rebase/i, /worktree/i, /conflict/i],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const runFastGate = (userMessage: string): FastGateResult => {
|
|
34
|
+
if (!userMessage || userMessage.trim().length === 0) {
|
|
35
|
+
return { action: "proceed" };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 1. Check for explicit contradiction signals
|
|
39
|
+
for (const pattern of CONTRADICTION_PATTERNS) {
|
|
40
|
+
if (pattern.test(userMessage)) {
|
|
41
|
+
return {
|
|
42
|
+
action: "inject_caution",
|
|
43
|
+
cautionNote: "User appears to be explicitly correcting a prior premise or configuration. Verify current facts before proceeding with assumptions.",
|
|
44
|
+
detectedDomains: extractDomains(userMessage),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
action: "proceed",
|
|
51
|
+
detectedDomains: extractDomains(userMessage),
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const extractDomains = (text: string): string[] => {
|
|
56
|
+
const matched: string[] = [];
|
|
57
|
+
for (const [domain, patterns] of Object.entries(DOMAIN_KEYWORDS)) {
|
|
58
|
+
if (patterns.some((p) => p.test(text))) {
|
|
59
|
+
matched.push(domain);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return matched;
|
|
63
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cognitive layer.
|
|
3
|
+
*
|
|
4
|
+
* Everything that decides *what* to remember, *what* to put in front of a model,
|
|
5
|
+
* and *whether* a new statement says something new — with no database, no
|
|
6
|
+
* network, and no model in the retrieval path.
|
|
7
|
+
*
|
|
8
|
+
* That last part is the design constraint everything else follows from. A memory
|
|
9
|
+
* layer whose recall quality moves with provider availability is not a storage
|
|
10
|
+
* layer, it is a demo, so ranking, merge safety and index budgeting are all
|
|
11
|
+
* deterministic functions of the text in front of them.
|
|
12
|
+
*
|
|
13
|
+
* `CognitiveMemory` is the in-process implementation: four Maps, snapshots to
|
|
14
|
+
* and from JSON, no I/O beyond what you give it. The service is the same
|
|
15
|
+
* decisions with the tiers in a database, which is why the primitives below are
|
|
16
|
+
* shared rather than reimplemented.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export { CognitiveMemory } from "./memory.js"
|
|
20
|
+
|
|
21
|
+
export { runFastGate, extractDomains, type FastGateResult } from "./fast-gate.js"
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
extractDeterministic,
|
|
25
|
+
type DeterministicMemory
|
|
26
|
+
} from "./rules.js"
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
CANDIDATE_FLOOR,
|
|
30
|
+
COLLAPSE_THRESHOLD,
|
|
31
|
+
MAX_CANDIDATES,
|
|
32
|
+
PROMOTE_THRESHOLD,
|
|
33
|
+
distinctiveTokens,
|
|
34
|
+
estimateTokens,
|
|
35
|
+
extractIdentifiers,
|
|
36
|
+
gistOf,
|
|
37
|
+
isInteractionScoped,
|
|
38
|
+
isLossyRewrite,
|
|
39
|
+
normalise,
|
|
40
|
+
overlapScore,
|
|
41
|
+
relevanceTokens,
|
|
42
|
+
similarity
|
|
43
|
+
} from "./relevance.js"
|
|
44
|
+
|
|
45
|
+
export type {
|
|
46
|
+
MemoryTier,
|
|
47
|
+
TensionStatus,
|
|
48
|
+
TensionImpact,
|
|
49
|
+
KnowledgeTension,
|
|
50
|
+
DomainCapability,
|
|
51
|
+
ProprioceptiveSelfModel,
|
|
52
|
+
MemoryMetadata,
|
|
53
|
+
MemoryItem,
|
|
54
|
+
TrajectoryPrediction,
|
|
55
|
+
MemoryInclusionReason,
|
|
56
|
+
MemoryInclusionReason as InclusionReason,
|
|
57
|
+
MemoryInjectionEntry,
|
|
58
|
+
MemoryInjectionReport,
|
|
59
|
+
MemoryReconciliation,
|
|
60
|
+
ArbiterEvaluationResult,
|
|
61
|
+
ArbiterFn,
|
|
62
|
+
CognitiveMemoryOptions,
|
|
63
|
+
CognitiveMemoryStateSnapshot
|
|
64
|
+
} from "./types.js"
|