agent-working-memory 0.14.5 → 0.14.6
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 +118 -122
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +234 -131
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/common.d.ts +44 -5
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +111 -41
- package/dist/adapters/common.js.map +1 -1
- package/dist/adapters/hook-scripts.d.ts +54 -0
- package/dist/adapters/hook-scripts.d.ts.map +1 -0
- package/dist/adapters/hook-scripts.js +340 -0
- package/dist/adapters/hook-scripts.js.map +1 -0
- package/dist/adapters/types.d.ts +13 -3
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/cli.js +21 -5
- package/dist/cli.js.map +1 -1
- package/dist/core/agent-id.d.ts +19 -0
- package/dist/core/agent-id.d.ts.map +1 -0
- package/dist/core/agent-id.js +28 -0
- package/dist/core/agent-id.js.map +1 -0
- package/dist/hooks/prime.d.ts +10 -4
- package/dist/hooks/prime.d.ts.map +1 -1
- package/dist/hooks/prime.js +16 -6
- package/dist/hooks/prime.js.map +1 -1
- package/dist/mcp.js +5 -12
- package/dist/mcp.js.map +1 -1
- package/package.json +94 -90
- package/src/adapters/claude-code.ts +223 -125
- package/src/adapters/common.ts +117 -42
- package/src/adapters/hook-scripts.ts +370 -0
- package/src/adapters/types.ts +14 -3
- package/src/cli.ts +19 -5
- package/src/core/agent-id.ts +28 -0
- package/src/hooks/prime.ts +22 -7
- package/src/mcp.ts +5 -11
package/src/adapters/types.ts
CHANGED
|
@@ -28,8 +28,12 @@ export interface SetupContext {
|
|
|
28
28
|
hasDist: boolean;
|
|
29
29
|
/** Hook sidecar secret token */
|
|
30
30
|
hookSecret: string;
|
|
31
|
-
/** Hook sidecar port */
|
|
31
|
+
/** Hook sidecar port (preferred; the sidecar walks upward from here when busy) */
|
|
32
32
|
hookPort: string;
|
|
33
|
+
/** How many ports upward from hookPort the sidecar may try (0.14.2) */
|
|
34
|
+
hookPortRange: string;
|
|
35
|
+
/** Install the UserPromptSubmit prime hook (0.14.6; `--no-prime` turns it off) */
|
|
36
|
+
installPrime: boolean;
|
|
33
37
|
/** Whether to use global scope */
|
|
34
38
|
isGlobal: boolean;
|
|
35
39
|
/** Windows platform */
|
|
@@ -70,6 +74,13 @@ export interface CLIAdapter {
|
|
|
70
74
|
/** Configure hooks (if supported). Returns action summary. */
|
|
71
75
|
writeHooks(ctx: SetupContext, skip: boolean): string;
|
|
72
76
|
|
|
73
|
-
/** Validate that the setup is healthy. */
|
|
74
|
-
diagnose(ctx: SetupContext): DiagnosticResult[]
|
|
77
|
+
/** Validate that the setup is healthy. May probe the network, hence possibly async. */
|
|
78
|
+
diagnose(ctx: SetupContext): DiagnosticResult[] | Promise<DiagnosticResult[]>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The env block of an AWM server entry this adapter previously wrote, if any.
|
|
82
|
+
* `awm setup` re-runs preserve these values unless a flag overrides them, so an
|
|
83
|
+
* upgrade never repoints the database or renames the agent (0.14.6).
|
|
84
|
+
*/
|
|
85
|
+
readExistingEnv?(isGlobal: boolean, cwd: string): Record<string, string> | null;
|
|
75
86
|
}
|
package/src/cli.ts
CHANGED
|
@@ -74,7 +74,10 @@ Setup options:
|
|
|
74
74
|
--no-instructions Skip instruction file (CLAUDE.md, AGENTS.md, etc.)
|
|
75
75
|
--no-claude-md Alias for --no-instructions
|
|
76
76
|
--no-hooks Skip hook installation
|
|
77
|
-
--
|
|
77
|
+
--no-prime Skip the UserPromptSubmit prime hook (auto-inject memories per prompt)
|
|
78
|
+
--hook-port PORT Preferred sidecar port (default: 8401; each session walks upward when busy)
|
|
79
|
+
--hook-port-range N Ports to try upward from --hook-port (default: 10)
|
|
80
|
+
Re-running setup keeps your existing agent id, db path, port and any env you added.
|
|
78
81
|
|
|
79
82
|
Examples:
|
|
80
83
|
awm setup --global Claude Code, global (recommended)
|
|
@@ -95,7 +98,9 @@ async function setup() {
|
|
|
95
98
|
let skipInstructions = false;
|
|
96
99
|
let isGlobal = false;
|
|
97
100
|
let skipHooks = false;
|
|
98
|
-
let
|
|
101
|
+
let installPrime = true;
|
|
102
|
+
let hookPort: string | undefined;
|
|
103
|
+
let hookPortRange: string | undefined;
|
|
99
104
|
|
|
100
105
|
for (let i = 1; i < args.length; i++) {
|
|
101
106
|
if (args[i] === '--agent-id' && args[i + 1]) {
|
|
@@ -106,8 +111,12 @@ async function setup() {
|
|
|
106
111
|
skipInstructions = true;
|
|
107
112
|
} else if (args[i] === '--no-hooks') {
|
|
108
113
|
skipHooks = true;
|
|
114
|
+
} else if (args[i] === '--no-prime') {
|
|
115
|
+
installPrime = false;
|
|
109
116
|
} else if (args[i] === '--hook-port' && args[i + 1]) {
|
|
110
117
|
hookPort = args[++i];
|
|
118
|
+
} else if (args[i] === '--hook-port-range' && args[i + 1]) {
|
|
119
|
+
hookPortRange = args[++i];
|
|
111
120
|
} else if (args[i] === '--global') {
|
|
112
121
|
isGlobal = true;
|
|
113
122
|
} else if (!args[i].startsWith('--')) {
|
|
@@ -133,8 +142,10 @@ async function setup() {
|
|
|
133
142
|
isGlobal = true;
|
|
134
143
|
}
|
|
135
144
|
|
|
136
|
-
// Build context
|
|
137
|
-
|
|
145
|
+
// Build context. A previous install's values survive unless a flag overrides them
|
|
146
|
+
// (0.14.6) — an upgrade must never repoint the database or rename the agent.
|
|
147
|
+
const existingEnv = adapter.readExistingEnv?.(isGlobal, process.cwd()) ?? null;
|
|
148
|
+
const ctx = buildSetupContext({ agentId, dbPath, isGlobal, hookPort, hookPortRange, installPrime, existingEnv });
|
|
138
149
|
|
|
139
150
|
// Run adapter
|
|
140
151
|
const configAction = adapter.writeMcpConfig(ctx);
|
|
@@ -157,6 +168,7 @@ AWM configured for ${adapter.name}${isGlobal ? ' (global)' : ''}
|
|
|
157
168
|
Next steps:
|
|
158
169
|
1. Restart ${adapter.name} to pick up the MCP server
|
|
159
170
|
2. Memory tools will appear automatically${adapter.id === 'codex' ? ' (verify with /mcp)' : ''}
|
|
171
|
+
3. \`awm doctor\` checks the install and lists the live sidecars
|
|
160
172
|
`.trim());
|
|
161
173
|
}
|
|
162
174
|
|
|
@@ -197,7 +209,9 @@ async function doctor() {
|
|
|
197
209
|
}
|
|
198
210
|
|
|
199
211
|
console.log(` ${adapter.name}:`);
|
|
200
|
-
const
|
|
212
|
+
const existingEnv = adapter.readExistingEnv?.(true, process.cwd()) ?? null;
|
|
213
|
+
const ctx = buildSetupContext({ isGlobal: true, existingEnv });
|
|
214
|
+
const results = await adapter.diagnose(ctx);
|
|
201
215
|
for (const r of results) {
|
|
202
216
|
const icon = r.status === 'ok' ? '+' : r.status === 'warn' ? '~' : 'x';
|
|
203
217
|
console.log(` [${icon}] ${r.check}: ${r.message}`);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Fallback agent identity when AWM_AGENT_ID / WORKER_NAME are unset.
|
|
6
|
+
*
|
|
7
|
+
* Derived from the project directory so a plain `claude` launch still binds to
|
|
8
|
+
* the right store: `Personal-Projects` → 'personal'; everything else → 'work'.
|
|
9
|
+
* The AWM package itself lives under Personal-Projects, so an AgentSynapse
|
|
10
|
+
* checkout is guarded first — a stray server cwd must not mis-bind.
|
|
11
|
+
*
|
|
12
|
+
* ONE definition, three consumers that must agree:
|
|
13
|
+
* - src/mcp.ts — what the server actually binds to
|
|
14
|
+
* - src/adapters/common.ts — what `awm setup` reports and seeds
|
|
15
|
+
* - the shipped hook scripts (src/adapters/hook-scripts.ts) carry a
|
|
16
|
+
* line-for-line JavaScript mirror, because a hook cannot import from dist.
|
|
17
|
+
* tests/adapters/hook-scripts.test.ts asserts the mirror agrees with this.
|
|
18
|
+
*/
|
|
19
|
+
export function deriveAgentFromDir(dir: string): string {
|
|
20
|
+
const d = String(dir ?? '').replace(/\\/g, '/');
|
|
21
|
+
if (/\/AgentSynapse\//i.test(d)) return 'work';
|
|
22
|
+
return /\/Personal-Projects(\/|$)/i.test(d) ? 'personal' : 'work';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The agent id a server process would resolve to for `dir`, honouring the env first. */
|
|
26
|
+
export function resolveAgentId(dir: string, env: NodeJS.ProcessEnv = process.env): string {
|
|
27
|
+
return env.AWM_AGENT_ID ?? env.WORKER_NAME ?? deriveAgentFromDir(dir);
|
|
28
|
+
}
|
package/src/hooks/prime.ts
CHANGED
|
@@ -36,7 +36,12 @@ import { packRecallByBudget, estimateTokens, type PackedRecall } from '../core/t
|
|
|
36
36
|
|
|
37
37
|
/** Shape returned by the sidecar's `activate` dependency. */
|
|
38
38
|
export interface PrimeCandidate {
|
|
39
|
-
engram: {
|
|
39
|
+
engram: {
|
|
40
|
+
id: string; concept: string; content: string; memoryClass?: string;
|
|
41
|
+
validTo?: string | null;
|
|
42
|
+
/** Used for the age label; Date from the store, string over HTTP. */
|
|
43
|
+
createdAt?: Date | string | null;
|
|
44
|
+
};
|
|
40
45
|
score: number;
|
|
41
46
|
summary?: string;
|
|
42
47
|
confidence?: number;
|
|
@@ -64,21 +69,31 @@ export interface PrimeResult {
|
|
|
64
69
|
export const PRIME_DEFAULTS = {
|
|
65
70
|
maxTokens: 600,
|
|
66
71
|
/**
|
|
67
|
-
* 0.25
|
|
68
|
-
*
|
|
69
|
-
*
|
|
72
|
+
* 0.10 (was 0.25 until 0.14.6). The confidence this gates on describes the
|
|
73
|
+
* SHAPE of the score distribution — how far the top results stand out from
|
|
74
|
+
* the rest — not relevance. Measured 2026-09-11 on a 30k-memory store: at
|
|
75
|
+
* 0.25 two specific prompts with one clear winner (top score 0.26 and 0.38)
|
|
76
|
+
* were silenced while a vague prompt passed at 0.92. Those specific prompts
|
|
77
|
+
* are exactly what priming exists for. 0.10 passed all three and still
|
|
78
|
+
* abstains on the garbage case (confidence ≈ 0.05).
|
|
70
79
|
*/
|
|
71
|
-
minConfidence: 0.
|
|
80
|
+
minConfidence: 0.10,
|
|
72
81
|
minScore: 0.10,
|
|
73
82
|
} as const;
|
|
74
83
|
|
|
75
84
|
/** One injected line. Deliberately terser than the MCP recall format — this is
|
|
76
85
|
* context the agent didn't ask for, so it should read as a brief note, not a
|
|
77
86
|
* report. The id is retained so the agent can act on it (feedback, supersede). */
|
|
78
|
-
function formatPrimeLine(c: PrimeCandidate): string {
|
|
87
|
+
function formatPrimeLine(c: PrimeCandidate, now: number = Date.now()): string {
|
|
79
88
|
const body = c.summary ?? c.engram.content;
|
|
80
89
|
const validity = c.engram.validTo ? ` [valid until ${c.engram.validTo}]` : '';
|
|
81
|
-
|
|
90
|
+
// Class + age make the volatility rubric mechanical: a 90-day-old working
|
|
91
|
+
// memory reads differently from a 2-day-old canonical one, and the agent
|
|
92
|
+
// should not have to look either up before deciding how much to trust it.
|
|
93
|
+
const cls = c.engram.memoryClass ?? 'working';
|
|
94
|
+
const created = c.engram.createdAt ? new Date(c.engram.createdAt as any).getTime() : NaN;
|
|
95
|
+
const age = Number.isFinite(created) ? ` · ${Math.max(0, Math.round((now - created) / 86_400_000))}d` : '';
|
|
96
|
+
return `- [${cls}${age}] ${c.engram.concept}${validity} [${c.engram.id}]: ${body}`;
|
|
82
97
|
}
|
|
83
98
|
|
|
84
99
|
/**
|
package/src/mcp.ts
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import { readFileSync } from 'node:fs';
|
|
33
|
+
import { deriveAgentFromDir as deriveAgentFromDirShared } from './core/agent-id.js';
|
|
33
34
|
import { resolve, basename } from 'node:path';
|
|
34
35
|
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
35
36
|
|
|
@@ -111,17 +112,10 @@ const BACKEND: StoreBackend = getConfiguredBackend();
|
|
|
111
112
|
const DB_PATH = process.env.AWM_DB_PATH ?? (BACKEND === 'pglite' ? 'memory-pglite' : 'memory.db');
|
|
112
113
|
|
|
113
114
|
// Fallback agent selection when AWM_AGENT_ID/WORKER_NAME are unset: derive from
|
|
114
|
-
// the project directory
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
// server's reads/writes never diverge. Guard the AWM package's own path
|
|
119
|
-
// (it lives under Personal-Projects) so a stray server cwd can't mis-bind.
|
|
120
|
-
function deriveAgentFromDir(): string {
|
|
121
|
-
const dir = (process.env.CLAUDE_PROJECT_DIR ?? process.cwd()).replace(/\\/g, '/');
|
|
122
|
-
if (/\/AgentSynapse\//i.test(dir)) return 'work';
|
|
123
|
-
return /\/Personal-Projects(\/|$)/i.test(dir) ? 'personal' : 'work';
|
|
124
|
-
}
|
|
115
|
+
// the project directory (core/agent-id.ts — one definition shared with `awm setup`
|
|
116
|
+
// and mirrored in the shipped hook scripts, so the server, the installer and the
|
|
117
|
+
// hooks always name the same pool for a given directory).
|
|
118
|
+
const deriveAgentFromDir = () => deriveAgentFromDirShared(process.env.CLAUDE_PROJECT_DIR ?? process.cwd());
|
|
125
119
|
const AGENT_ID = process.env.AWM_AGENT_ID ?? process.env.WORKER_NAME ?? deriveAgentFromDir();
|
|
126
120
|
const HOOK_PORT = parseInt(process.env.AWM_HOOK_PORT ?? '8401', 10);
|
|
127
121
|
// 0.14.2: ports to try upward from HOOK_PORT when it is busy (see sidecar.ts).
|