@msm-core/mini 0.8.0 → 0.14.0
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/CHANGELOG.md +279 -0
- package/dist/adapters/index.d.ts +12 -2
- package/dist/adapters/index.js +10 -0
- package/dist/adapters/memory-control.d.ts +43 -0
- package/dist/adapters/memory-control.js +56 -0
- package/dist/adapters/memory-dedup.d.ts +36 -0
- package/dist/adapters/memory-dedup.js +59 -0
- package/dist/adapters/memory-lock.d.ts +45 -0
- package/dist/adapters/memory-lock.js +89 -0
- package/dist/adapters/redis-control.d.ts +2 -1
- package/dist/adapters/redis-lock.d.ts +9 -5
- package/dist/brain/anthropic.js +8 -1
- package/dist/brain/gemini.js +15 -2
- package/dist/brain/ollama.js +8 -2
- package/dist/brain/openai.js +9 -2
- package/dist/brain/retry.d.ts +98 -2
- package/dist/brain/retry.js +132 -2
- package/dist/brain/streaming.d.ts +42 -0
- package/dist/brain/streaming.js +44 -1
- package/dist/bridge/pipeline.js +11 -8
- package/dist/core/hooks.d.ts +14 -2
- package/dist/core/hooks.js +20 -3
- package/dist/core/loop.js +148 -23
- package/dist/core/types.d.ts +387 -5
- package/dist/core/types.js +130 -1
- package/dist/definition/skills.d.ts +74 -0
- package/dist/definition/skills.js +119 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +30 -0
- package/dist/tools/dedup.d.ts +22 -1
- package/dist/tools/dedup.js +28 -0
- package/dist/tools/delegate.d.ts +134 -0
- package/dist/tools/delegate.js +223 -0
- package/dist/tools/executor.d.ts +49 -5
- package/dist/tools/executor.js +38 -5
- package/package.json +4 -3
package/dist/tools/executor.d.ts
CHANGED
|
@@ -10,26 +10,70 @@
|
|
|
10
10
|
* 4. Execute — call tool.execute()
|
|
11
11
|
* 5. Cache — store result in dedup hash
|
|
12
12
|
*/
|
|
13
|
-
import type { Tool, ToolResult, ToolMeta, AgentHooks } from "../core/types.js";
|
|
13
|
+
import type { Tool, ToolResult, ToolMeta, AgentHooks, DedupPort } from "../core/types.js";
|
|
14
14
|
type RedisLike = {
|
|
15
15
|
hget(key: string, field: string): Promise<string | null>;
|
|
16
16
|
hset(key: string, field: string, value: string): Promise<unknown>;
|
|
17
17
|
expire(key: string, seconds: number): Promise<unknown>;
|
|
18
18
|
get(key: string): Promise<string | null>;
|
|
19
19
|
};
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Where step 3's cache lives — a port, or a Redis client to build one from.
|
|
22
|
+
*
|
|
23
|
+
* A union rather than "a port and an optional client", so the compiler still
|
|
24
|
+
* insists on exactly one answer. The second arm is the call this function has
|
|
25
|
+
* always taken (`{ redis, redisPrefix, … }`) and it keeps working character for
|
|
26
|
+
* character; the loop passes the first, because since س٦ it resolves the dedup
|
|
27
|
+
* port itself — injected or Redis-backed — and hands one thing down.
|
|
28
|
+
*/
|
|
29
|
+
type DedupSource = {
|
|
30
|
+
dedup: DedupPort;
|
|
31
|
+
redis?: never;
|
|
32
|
+
redisPrefix?: never;
|
|
33
|
+
} | {
|
|
34
|
+
dedup?: never;
|
|
21
35
|
redis: RedisLike;
|
|
22
36
|
redisPrefix: string;
|
|
37
|
+
};
|
|
38
|
+
export type ExecutorOptions = DedupSource & {
|
|
23
39
|
dedupTtlSeconds: number;
|
|
24
40
|
/** Optional hooks — only onBeforeTool is used at this layer */
|
|
25
41
|
hooks?: Pick<AgentHooks, "onBeforeTool">;
|
|
26
|
-
}
|
|
42
|
+
};
|
|
27
43
|
export interface ExecutionResult {
|
|
28
44
|
result: ToolResult;
|
|
29
45
|
cached: boolean;
|
|
30
46
|
durationMs: number;
|
|
31
47
|
}
|
|
32
48
|
export declare function executeTool(tool: Tool, params: Record<string, unknown>, meta: ToolMeta, opts: ExecutorOptions): Promise<ExecutionResult>;
|
|
33
|
-
/**
|
|
34
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Build a ToolDefinition list from app-provided Tool objects (for brain prompt).
|
|
51
|
+
*
|
|
52
|
+
* **The stamp travels (ر١/١).** `ToolDefinition` has declared `destructive` and
|
|
53
|
+
* `category` since before MCP existed and nothing ever filled them: a tool an
|
|
54
|
+
* operator stamped `destructive` at the composition root reached the model
|
|
55
|
+
* looking exactly like a read-only one. This was never a safety hole — the
|
|
56
|
+
* executor enforces `requiresApproval` above regardless of what the model was
|
|
57
|
+
* told — but a model that is never shown which of its tools change the world
|
|
58
|
+
* cannot be asked to be careful with them, and the two fields existed on the
|
|
59
|
+
* type precisely so that it could be.
|
|
60
|
+
*
|
|
61
|
+
* **Why the parameter widens instead of the body casting.** Mini's `Tool` does
|
|
62
|
+
* not declare the two fields; `McpTool` (ر١) and `DelegateTool` (ر٢) each add
|
|
63
|
+
* them to a plain `Tool`, and both reach here through `AgentConfig.tools:
|
|
64
|
+
* Tool[]` — on the object, off the type. Saying so in the signature keeps it
|
|
65
|
+
* honest: a plain `Tool[]` still satisfies it (both fields are optional), a
|
|
66
|
+
* stamped array is read with no `as` anywhere, and a caller who writes
|
|
67
|
+
* `destructive: "yes"` is told by the compiler instead of dropped in silence.
|
|
68
|
+
*
|
|
69
|
+
* **Absent stays absent.** The conditional spreads are not style: under
|
|
70
|
+
* `exactOptionalPropertyTypes` an unstamped tool must produce the object it
|
|
71
|
+
* produced yesterday, key for key. That is what keeps an unstamped run's
|
|
72
|
+
* `@msm-core/replay` fingerprint unmoved — only a tool that actually carries a
|
|
73
|
+
* stamp changes what the model is asked, and only its fingerprint moves.
|
|
74
|
+
*/
|
|
75
|
+
export declare function toToolDefinitions(tools: ReadonlyArray<Tool & {
|
|
76
|
+
destructive?: boolean;
|
|
77
|
+
category?: string;
|
|
78
|
+
}>): import("../core/types.js").ToolDefinition[];
|
|
35
79
|
export {};
|
package/dist/tools/executor.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 4. Execute — call tool.execute()
|
|
11
11
|
* 5. Cache — store result in dedup hash
|
|
12
12
|
*/
|
|
13
|
-
import { hashToolCall,
|
|
13
|
+
import { hashToolCall, RedisToolDedup } from "./dedup.js";
|
|
14
14
|
export async function executeTool(tool, params, meta, opts) {
|
|
15
15
|
const start = Date.now();
|
|
16
16
|
// Step 1: Validate required parameters
|
|
@@ -95,9 +95,15 @@ export async function executeTool(tool, params, meta, opts) {
|
|
|
95
95
|
}
|
|
96
96
|
// Step 3: Dedup check (keyed on the EFFECTIVE params, so edited-approval runs
|
|
97
97
|
// are not served a cached result for the original params).
|
|
98
|
+
//
|
|
99
|
+
// The hash stays here and stays pure — it is not I/O and there is nothing to
|
|
100
|
+
// swap about it, so every implementation of the port dedups on exactly the
|
|
101
|
+
// same key the Redis one always did.
|
|
102
|
+
const dedup = opts.dedup
|
|
103
|
+
? opts.dedup
|
|
104
|
+
: new RedisToolDedup(opts.redis, opts.redisPrefix);
|
|
98
105
|
const hash = hashToolCall(tool.name, effectiveParams);
|
|
99
|
-
const
|
|
100
|
-
const cached = await checkDedup(opts.redis, dedupKey, hash);
|
|
106
|
+
const cached = await dedup.check(meta.sessionId, hash);
|
|
101
107
|
if (cached) {
|
|
102
108
|
return { result: cached, cached: true, durationMs: Date.now() - start };
|
|
103
109
|
}
|
|
@@ -115,7 +121,7 @@ export async function executeTool(tool, params, meta, opts) {
|
|
|
115
121
|
}
|
|
116
122
|
// Step 5: Cache successful results only
|
|
117
123
|
if (result.status === "ok") {
|
|
118
|
-
await
|
|
124
|
+
await dedup.store(meta.sessionId, hash, result, opts.dedupTtlSeconds);
|
|
119
125
|
}
|
|
120
126
|
return { result, cached: false, durationMs: Date.now() - start };
|
|
121
127
|
}
|
|
@@ -135,7 +141,32 @@ function validateParams(tool, params) {
|
|
|
135
141
|
}
|
|
136
142
|
return null;
|
|
137
143
|
}
|
|
138
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* Build a ToolDefinition list from app-provided Tool objects (for brain prompt).
|
|
146
|
+
*
|
|
147
|
+
* **The stamp travels (ر١/١).** `ToolDefinition` has declared `destructive` and
|
|
148
|
+
* `category` since before MCP existed and nothing ever filled them: a tool an
|
|
149
|
+
* operator stamped `destructive` at the composition root reached the model
|
|
150
|
+
* looking exactly like a read-only one. This was never a safety hole — the
|
|
151
|
+
* executor enforces `requiresApproval` above regardless of what the model was
|
|
152
|
+
* told — but a model that is never shown which of its tools change the world
|
|
153
|
+
* cannot be asked to be careful with them, and the two fields existed on the
|
|
154
|
+
* type precisely so that it could be.
|
|
155
|
+
*
|
|
156
|
+
* **Why the parameter widens instead of the body casting.** Mini's `Tool` does
|
|
157
|
+
* not declare the two fields; `McpTool` (ر١) and `DelegateTool` (ر٢) each add
|
|
158
|
+
* them to a plain `Tool`, and both reach here through `AgentConfig.tools:
|
|
159
|
+
* Tool[]` — on the object, off the type. Saying so in the signature keeps it
|
|
160
|
+
* honest: a plain `Tool[]` still satisfies it (both fields are optional), a
|
|
161
|
+
* stamped array is read with no `as` anywhere, and a caller who writes
|
|
162
|
+
* `destructive: "yes"` is told by the compiler instead of dropped in silence.
|
|
163
|
+
*
|
|
164
|
+
* **Absent stays absent.** The conditional spreads are not style: under
|
|
165
|
+
* `exactOptionalPropertyTypes` an unstamped tool must produce the object it
|
|
166
|
+
* produced yesterday, key for key. That is what keeps an unstamped run's
|
|
167
|
+
* `@msm-core/replay` fingerprint unmoved — only a tool that actually carries a
|
|
168
|
+
* stamp changes what the model is asked, and only its fingerprint moves.
|
|
169
|
+
*/
|
|
139
170
|
export function toToolDefinitions(tools) {
|
|
140
171
|
return tools.map((t) => ({
|
|
141
172
|
name: t.name,
|
|
@@ -144,5 +175,7 @@ export function toToolDefinitions(tools) {
|
|
|
144
175
|
...(t.requiresApproval !== undefined
|
|
145
176
|
? { requiresApproval: t.requiresApproval }
|
|
146
177
|
: {}),
|
|
178
|
+
...(t.destructive !== undefined ? { destructive: t.destructive } : {}),
|
|
179
|
+
...(t.category !== undefined ? { category: t.category } : {}),
|
|
147
180
|
}));
|
|
148
181
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@msm-core/mini",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Portable AI agent execution loop — brain-agnostic, zero embedded databases",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"ioredis": "^5.3.2",
|
|
52
|
-
"@msm-core/session": "^0.
|
|
52
|
+
"@msm-core/session": "^0.3.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/node": "^20.0.0",
|
|
@@ -68,7 +68,8 @@
|
|
|
68
68
|
"license": "UNLICENSED",
|
|
69
69
|
"scripts": {
|
|
70
70
|
"build": "tsc",
|
|
71
|
-
"
|
|
71
|
+
"typecheck": "tsc -p tsconfig.test.json",
|
|
72
|
+
"test": "tsc -p tsconfig.test.json && vitest run",
|
|
72
73
|
"test:watch": "vitest",
|
|
73
74
|
"clean": "rm -rf dist"
|
|
74
75
|
}
|