pi-chalin 0.1.0 → 0.2.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/README.md +47 -4
- package/package.json +1 -1
- package/src/autoroute.ts +2 -2
- package/src/child-tools.ts +5 -4
- package/src/commands.ts +74 -7
- package/src/config.ts +58 -0
- package/src/index.ts +2 -0
- package/src/kernel.ts +7 -11
- package/src/memory-provider.ts +701 -0
- package/src/memory.ts +18 -1
- package/src/runner.ts +3 -2
- package/src/schemas.ts +1 -0
- package/src/tools.ts +7 -6
- package/src/ui.ts +355 -2
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="package.json"><img alt="version" src="https://img.shields.io/badge/version-0.
|
|
2
|
+
<a href="package.json"><img alt="version" src="https://img.shields.io/badge/version-0.2.0-111111?style=for-the-badge"></a>
|
|
3
3
|
<a href="package.json"><img alt="license" src="https://img.shields.io/badge/license-MIT-0f766e?style=for-the-badge"></a>
|
|
4
4
|
<a href="package.json"><img alt="bun" src="https://img.shields.io/badge/bun-%3E%3D1.3.14-111111?style=for-the-badge&logo=bun&logoColor=white"></a>
|
|
5
5
|
<a href="package.json"><img alt="typescript" src="https://img.shields.io/badge/typescript-6.0-3178c6?style=for-the-badge&logo=typescript&logoColor=white"></a>
|
|
@@ -31,7 +31,7 @@ Use it when a task benefits from:
|
|
|
31
31
|
- deeper repository discovery before implementation;
|
|
32
32
|
- isolated planning, execution, and review roles;
|
|
33
33
|
- resumable long-running work;
|
|
34
|
-
-
|
|
34
|
+
- first-class memory through either pi-chalin local review or native Engram;
|
|
35
35
|
- visible routing, safety, and runtime state in the Pi TUI;
|
|
36
36
|
- audited web context for current external information.
|
|
37
37
|
|
|
@@ -42,7 +42,7 @@ Use it when a task benefits from:
|
|
|
42
42
|
| Routing | Direct execution for bounded work, chalin workflows for broad or risky work. |
|
|
43
43
|
| Subagents | Built-in `scout`, `planner`, `worker`, `reviewer`, `researcher`, `delegate`, `oracle`, `context-builder`, and `conflict-resolver` agents. |
|
|
44
44
|
| Topologies | `single`, `chain`, `parallel`, `dag`, and `memory-only` workflow shapes. |
|
|
45
|
-
| Memory |
|
|
45
|
+
| Memory | First-class `pi-chalin local`, `engram`, or `auto` backends with search, write, revise, review, and cloud-aware Engram sync. |
|
|
46
46
|
| Artifacts | Resumable checkpoints, validation contracts, interviews, and handoffs. |
|
|
47
47
|
| Safety | Approval thresholds, autonomy modes, recursion guards, single-writer isolation, stale-run recovery, and mutation checks. |
|
|
48
48
|
| TUI | Smart Panel, agent manager, activity monitor, memory review, artifact panel, and web fetch audit. |
|
|
@@ -99,11 +99,12 @@ Once Pi loads the package, use `/chalin` inside a Pi session.
|
|
|
99
99
|
| `/chalin off` | Disable autonomous routing for the current project. |
|
|
100
100
|
| `/chalin agents` | Open the agent manager. |
|
|
101
101
|
| `/chalin memory` | Review memory records and pending candidates. |
|
|
102
|
-
| `/chalin memory <query>` | Search
|
|
102
|
+
| `/chalin memory <query>` | Search the configured memory backend. |
|
|
103
103
|
| `/chalin artifacts` | Open artifact and resumable task context. |
|
|
104
104
|
| `/chalin artifacts <feature>` | Resume context for a specific feature artifact. |
|
|
105
105
|
| `/chalin activity` | Inspect the active or latest run. |
|
|
106
106
|
| `/chalin web` | Open the web fetch audit panel. |
|
|
107
|
+
| `/chalin settings` | Choose the memory provider: `auto`, `engram`, or `pi-chalin` local. |
|
|
107
108
|
| `/chalin status` | Print routing, autonomy, safety, agent, memory, and guard status. |
|
|
108
109
|
|
|
109
110
|
## Tools
|
|
@@ -150,6 +151,7 @@ src/runner.ts mock and SDK-backed worker execution
|
|
|
150
151
|
src/agents.ts built-in, project, and user agent catalog
|
|
151
152
|
src/config.ts config, autonomy, safety, and overrides
|
|
152
153
|
src/memory.ts memory records, candidates, and search
|
|
154
|
+
src/memory-provider.ts configurable pi-chalin local and Engram memory backends
|
|
153
155
|
src/artifacts.ts resumable task state and handoffs
|
|
154
156
|
src/webfetch.ts audited external context
|
|
155
157
|
src/worktrees.ts isolated writer worktrees
|
|
@@ -207,6 +209,42 @@ Project-local runtime files live under `.pi-chalin/`:
|
|
|
207
209
|
.pi-chalin/runs/
|
|
208
210
|
```
|
|
209
211
|
|
|
212
|
+
Memory can run against pi-chalin's local SQLite store or Engram. Configure it in `.pi-chalin/config.json` or through `/chalin settings`:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"memory": {
|
|
217
|
+
"provider": "auto",
|
|
218
|
+
"engram": {
|
|
219
|
+
"baseUrl": "http://127.0.0.1:7437",
|
|
220
|
+
"command": "engram",
|
|
221
|
+
"autoStart": false,
|
|
222
|
+
"autoSync": true,
|
|
223
|
+
"syncThrottleMs": 30000,
|
|
224
|
+
"timeoutMs": 800
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`auto` uses Engram when the HTTP service is reachable and falls back to local memory. `engram` uses Engram as the memory source for `/chalin memory`; the `baseUrl` may point at any Engram local-runtime-compatible endpoint, including a remote `engram serve` instance. Engram Cloud is supported through Engram's local-first sync model: run `engram serve` against a cloud-enrolled/synced Engram store, then point pi-chalin at that runtime. When `autoSync` is enabled and the configured Engram runtime is local, pi-chalin checks `/sync/status` and runs the official `engram sync --cloud --import --project <project>` before reads, then `engram sync --cloud --project <project>` after writes. This uses `ENGRAM_CLOUD_TOKEN` from the runtime environment and never persists the token.
|
|
231
|
+
|
|
232
|
+
If the user already has `gentle-pi`/`gentle-engram` working, pi-chalin reuses that Engram runtime. No MCP bootstrap command is required from pi-chalin: choose `engram` in `/chalin settings`, and pi-chalin will use `ENGRAM_URL`, `ENGRAM_PORT`, `ENGRAM_BIN`, the default `http://127.0.0.1:7437` runtime, and Engram's own project detection. If Engram Cloud is enrolled and the Pi process has `ENGRAM_CLOUD_TOKEN`, reads automatically import pending cloud chunks before listing/searching memory.
|
|
233
|
+
|
|
234
|
+
When Engram is the active/preferred memory provider, `/chalin memory` lists Engram observations only, including project and personal scopes returned by Engram. pi-chalin does not expose its local `approve`/`reject` review flow in that mode. If the user selects `pi-chalin local`, the local SQLite memory store keeps its existing pending-review, approve, reject, delete, search, and revise behavior.
|
|
235
|
+
|
|
236
|
+
### Engram Memory
|
|
237
|
+
|
|
238
|
+
Engram is supported as a native memory backend, not as an external afterthought. When `/chalin settings` is set to `engram`, every memory-facing surface uses Engram directly:
|
|
239
|
+
|
|
240
|
+
- `/chalin memory` lists and searches Engram observations.
|
|
241
|
+
- `chalin_memory_search`, `chalin_memory_write`, and `chalin_memory_revise` operate against Engram.
|
|
242
|
+
- Routed, chained, parallel, DAG, and `memory-only` workflows receive memory from the configured Engram backend.
|
|
243
|
+
- Engram-backed memory does not use pi-chalin's local pending `approve`/`reject` queue.
|
|
244
|
+
- Engram Cloud works through Engram's official local-first sync path when the runtime is enrolled and the Pi process has `ENGRAM_CLOUD_TOKEN`.
|
|
245
|
+
|
|
246
|
+
Use `auto` when you want Engram if it is reachable with a local fallback, `engram` when Engram must be the source of truth, and `pi-chalin` when you want the local SQLite review workflow.
|
|
247
|
+
|
|
210
248
|
User-level configuration and agents currently live under `~/.pi/chalin/`. That path is part of the Pi chalin workflow namespace, not the package name.
|
|
211
249
|
|
|
212
250
|
## Environment Variables
|
|
@@ -221,6 +259,11 @@ PI_CHALIN_MOCK_STEP_DELAY_MS
|
|
|
221
259
|
PI_CHALIN_WORKFLOW_MODEL
|
|
222
260
|
PI_CHALIN_WORKFLOW_THINKING
|
|
223
261
|
PI_CHALIN_WORKFLOW_GATES
|
|
262
|
+
PI_CHALIN_MEMORY_PROVIDER
|
|
263
|
+
ENGRAM_URL
|
|
264
|
+
ENGRAM_PORT
|
|
265
|
+
ENGRAM_BIN
|
|
266
|
+
ENGRAM_CLOUD_TOKEN
|
|
224
267
|
```
|
|
225
268
|
|
|
226
269
|
See `package.json` and the evaluator files under `evals/` for the full set used by development scripts.
|
package/package.json
CHANGED
package/src/autoroute.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { AgentCatalog } from "./agents.ts";
|
|
3
3
|
import { loadEffectiveConfig } from "./config.ts";
|
|
4
|
-
import {
|
|
4
|
+
import { createConfiguredMemoryStore } from "./memory-provider.ts";
|
|
5
5
|
import { buildCompactChalinCriticalSystemPrompt, buildCompactChalinOrchestratorSystemPrompt, buildCompactChalinResumeSystemPrompt, buildChalinOrchestratorSystemPrompt } from "./orchestration.ts";
|
|
6
6
|
import { isUsableStepHandoff, loadResumableRunState } from "./runner-state.ts";
|
|
7
7
|
import { beginChalinTurn, recordDirectToolCompletion } from "./runtime-state.ts";
|
|
@@ -159,7 +159,7 @@ async function globalMemoryContextForPrompt(cwd: string, prompt: string): Promis
|
|
|
159
159
|
const query = prompt.trim();
|
|
160
160
|
if (query.length < 8) return undefined;
|
|
161
161
|
try {
|
|
162
|
-
const bundle = await
|
|
162
|
+
const bundle = await createConfiguredMemoryStore({ cwd }).retrieve({
|
|
163
163
|
query,
|
|
164
164
|
sourceAgent: "primary-pi-global",
|
|
165
165
|
limit: 5,
|
package/src/child-tools.ts
CHANGED
|
@@ -15,7 +15,8 @@ import { Type } from "typebox";
|
|
|
15
15
|
import { ArtifactStore, type ArtifactFeatureStatus } from "./artifacts.ts";
|
|
16
16
|
import type { BudgetPolicy } from "./budget.ts";
|
|
17
17
|
import { buildProjectDiscoveryIndex, formatProjectDiscoveryIndex } from "./discovery.ts";
|
|
18
|
-
import { createMemoryCandidate
|
|
18
|
+
import { createMemoryCandidate } from "./memory.ts";
|
|
19
|
+
import { createConfiguredMemoryStore } from "./memory-provider.ts";
|
|
19
20
|
import { buildProjectSnapshot, formatProjectSnapshot } from "./snapshot.ts";
|
|
20
21
|
import { fetchWebUrls, formatWebBundle, searchWeb } from "./webfetch.ts";
|
|
21
22
|
|
|
@@ -475,7 +476,7 @@ export function createChalinMemorySearchTool(policy: ChildToolPolicy): ToolDefin
|
|
|
475
476
|
const input = isRecord(params) ? params : {};
|
|
476
477
|
const gate = policy.beforeTool("chalin_memory_search", input);
|
|
477
478
|
if (!gate.allowed) return blockedToolResult(gate.reason);
|
|
478
|
-
const store =
|
|
479
|
+
const store = createConfiguredMemoryStore({ cwd: policy.cwd });
|
|
479
480
|
const bundle = await store.retrieve({
|
|
480
481
|
query: String(params.query ?? ""),
|
|
481
482
|
sourceAgent: policy.agentName,
|
|
@@ -510,7 +511,7 @@ export function createChalinMemoryWriteTool(policy: ChildToolPolicy): ToolDefini
|
|
|
510
511
|
if (!gate.allowed) return blockedToolResult(gate.reason);
|
|
511
512
|
const validation = validateMemoryWriteParams(params);
|
|
512
513
|
if (!validation.allowed) return blockedToolResult(validation.reason);
|
|
513
|
-
const store =
|
|
514
|
+
const store = createConfiguredMemoryStore({ cwd: policy.cwd });
|
|
514
515
|
const [record] = await store.submitCandidates([createMemoryCandidate({
|
|
515
516
|
category: params.category,
|
|
516
517
|
content: params.content,
|
|
@@ -546,7 +547,7 @@ export function createChalinMemoryReviseTool(policy: ChildToolPolicy): ToolDefin
|
|
|
546
547
|
if (!gate.allowed) return blockedToolResult(gate.reason);
|
|
547
548
|
const validation = validateMemoryRevisionParams(params);
|
|
548
549
|
if (!validation.allowed) return blockedToolResult(validation.reason);
|
|
549
|
-
const store =
|
|
550
|
+
const store = createConfiguredMemoryStore({ cwd: policy.cwd });
|
|
550
551
|
const record = await store.revise(params.id, {
|
|
551
552
|
category: params.category,
|
|
552
553
|
content: params.content,
|
package/src/commands.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { sessionModelOverrides, sessionThinkingOverrides } from "./agent-overrides.ts";
|
|
3
3
|
import { AgentCatalog } from "./agents.ts";
|
|
4
4
|
import { ArtifactStore } from "./artifacts.ts";
|
|
5
|
-
import { loadEffectiveConfig, writeProjectConfig } from "./config.ts";
|
|
6
|
-
import {
|
|
5
|
+
import { loadEffectiveConfig, writeProjectConfig, type ChalinConfig, type MemoryProvider } from "./config.ts";
|
|
6
|
+
import { createConfiguredMemoryStore, resolveMemoryBackendStatus, type MemoryBackendStatus } from "./memory-provider.ts";
|
|
7
7
|
import { getActiveRun, getLatestRun } from "./runtime-state.ts";
|
|
8
8
|
import { openAgentManager } from "./ui-agents.ts";
|
|
9
9
|
import {
|
|
@@ -20,7 +20,7 @@ export function registerChalinCommands(pi: ExtensionAPI): void {
|
|
|
20
20
|
pi.registerCommand("chalin", {
|
|
21
21
|
description: "Open pi-chalin Smart Panel or toggle autonomous routing with: /chalin on|off",
|
|
22
22
|
getArgumentCompletions: (prefix) => {
|
|
23
|
-
const values = ["on", "off", "agents", "memory", "artifacts", "activity", "web", "status"];
|
|
23
|
+
const values = ["on", "off", "agents", "memory", "artifacts", "activity", "web", "settings", "status"];
|
|
24
24
|
const filtered = values.filter((value) => value.startsWith(prefix.trim()));
|
|
25
25
|
return filtered.length > 0 ? filtered.map((value) => ({ value, label: value })) : null;
|
|
26
26
|
},
|
|
@@ -39,11 +39,12 @@ export function registerChalinCommands(pi: ExtensionAPI): void {
|
|
|
39
39
|
|
|
40
40
|
const loaded = loadEffectiveConfig({ cwd: ctx.cwd });
|
|
41
41
|
const catalog = AgentCatalog.load({ cwd: ctx.cwd });
|
|
42
|
-
const memory =
|
|
42
|
+
const memory = createConfiguredMemoryStore({ cwd: ctx.cwd }, loaded.config);
|
|
43
43
|
const artifacts = new ArtifactStore({ cwd: ctx.cwd });
|
|
44
44
|
const agents = catalog.list();
|
|
45
45
|
const diagnostics = [...loaded.diagnostics, ...catalog.diagnostics.warnings, ...catalog.diagnostics.errors];
|
|
46
46
|
const pendingMemories = await memory.list("pending");
|
|
47
|
+
const memoryStatus = await resolveMemoryBackendStatus({ cwd: ctx.cwd }, loaded.config);
|
|
47
48
|
const activeRun = getActiveRun();
|
|
48
49
|
const lastRun = getLatestRun();
|
|
49
50
|
|
|
@@ -62,11 +63,15 @@ export function registerChalinCommands(pi: ExtensionAPI): void {
|
|
|
62
63
|
approve: (id) => void memory.approve(id),
|
|
63
64
|
reject: (id) => void memory.reject(id),
|
|
64
65
|
delete: (id) => void memory.delete(id),
|
|
65
|
-
});
|
|
66
|
+
}, memoryReviewOptions(memoryStatus));
|
|
66
67
|
}
|
|
67
68
|
return;
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
if (command === "settings") {
|
|
72
|
+
await openChalinSettings(ctx, loaded.config);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
70
75
|
|
|
71
76
|
if (command === "artifacts") {
|
|
72
77
|
const featureId = rest.join(" ").trim();
|
|
@@ -94,6 +99,8 @@ export function registerChalinCommands(pi: ExtensionAPI): void {
|
|
|
94
99
|
`routing: ${loaded.config.enabled ? "on" : "off"}`,
|
|
95
100
|
`autonomy: ${loaded.config.autonomy}`,
|
|
96
101
|
`approval threshold: ${loaded.config.safety.approvalRiskThreshold}`,
|
|
102
|
+
`memory: ${memoryStatus.summary}`,
|
|
103
|
+
...(memoryStatus.detail ? [`memory detail: ${memoryStatus.detail}`] : []),
|
|
97
104
|
`agents: ${agents.length}`,
|
|
98
105
|
`pending memory: ${pendingMemories.length}`,
|
|
99
106
|
`last activity: ${lastRun?.id ?? "none"}`,
|
|
@@ -120,6 +127,7 @@ export function registerChalinCommands(pi: ExtensionAPI): void {
|
|
|
120
127
|
pendingApprovals: 0,
|
|
121
128
|
activeRuns: activeRun ? 1 : 0,
|
|
122
129
|
pendingMemoryCandidates: pendingMemories.length,
|
|
130
|
+
memoryBackend: memoryStatus.summary,
|
|
123
131
|
lastRun,
|
|
124
132
|
},
|
|
125
133
|
agents,
|
|
@@ -131,10 +139,69 @@ export function registerChalinCommands(pi: ExtensionAPI): void {
|
|
|
131
139
|
approve: (id) => void memory.approve(id),
|
|
132
140
|
reject: (id) => void memory.reject(id),
|
|
133
141
|
delete: (id) => void memory.delete(id),
|
|
134
|
-
}),
|
|
142
|
+
}, memoryReviewOptions(memoryStatus)),
|
|
135
143
|
onSelectArtifacts: () => openArtifactPanel(ctx, artifacts),
|
|
136
144
|
onSelectWebFetch: async () => openWebFetchAuditPanel(ctx, await listWebFetchAudit({ cwd: ctx.cwd })),
|
|
145
|
+
onSelectSettings: () => openChalinSettings(ctx, loaded.config),
|
|
137
146
|
});
|
|
138
147
|
},
|
|
139
148
|
});
|
|
140
149
|
}
|
|
150
|
+
|
|
151
|
+
async function openChalinSettings(ctx: ExtensionContext, config: ChalinConfig): Promise<void> {
|
|
152
|
+
const current = config.memory.provider;
|
|
153
|
+
if (!ctx.hasUI) {
|
|
154
|
+
ctx.ui.notify(`memory provider: ${current}`, "info");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const selected = await ctx.ui.select("pi-chalin Settings", [`Memory provider · ${labelForMemoryProvider(current)}`, "Close"]);
|
|
159
|
+
if (!selected?.startsWith("Memory provider")) return;
|
|
160
|
+
|
|
161
|
+
const choice = await ctx.ui.select("Memory Provider", [
|
|
162
|
+
"Auto · Engram when available",
|
|
163
|
+
"Engram · native Engram memory",
|
|
164
|
+
"pi-chalin local",
|
|
165
|
+
"Close",
|
|
166
|
+
]);
|
|
167
|
+
const provider = providerFromSettingsChoice(choice);
|
|
168
|
+
if (!provider) return;
|
|
169
|
+
|
|
170
|
+
const loaded = writeProjectConfig({ cwd: ctx.cwd }, { memory: { provider } } as Partial<ChalinConfig>);
|
|
171
|
+
const status = await resolveMemoryBackendStatus({ cwd: ctx.cwd }, loaded.config);
|
|
172
|
+
ctx.ui.notify(`Memory provider set to ${labelForMemoryProvider(provider)}.\nActive: ${status.summary}`, "info");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function providerFromSettingsChoice(choice: string | undefined): MemoryProvider | undefined {
|
|
176
|
+
if (choice?.startsWith("Auto")) return "auto";
|
|
177
|
+
if (choice?.startsWith("Engram")) return "engram";
|
|
178
|
+
if (choice?.startsWith("pi-chalin")) return "pi-chalin";
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function labelForMemoryProvider(provider: MemoryProvider): string {
|
|
183
|
+
if (provider === "auto") return "auto";
|
|
184
|
+
if (provider === "engram") return "engram";
|
|
185
|
+
return "pi-chalin local";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function memoryReviewOptions(status: MemoryBackendStatus): { title: string; emptyMessage: string } {
|
|
189
|
+
if (status.configuredProvider === "engram") {
|
|
190
|
+
return {
|
|
191
|
+
title: "Engram Memory",
|
|
192
|
+
emptyMessage: status.engramAvailable
|
|
193
|
+
? status.detail ?? "No Engram memory records found."
|
|
194
|
+
: "Engram memory is selected, but Engram is unavailable. Start Engram or update /chalin settings.",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (status.activeProvider === "engram") {
|
|
198
|
+
return {
|
|
199
|
+
title: "Engram Memory",
|
|
200
|
+
emptyMessage: status.detail ?? "No Engram memory records found.",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
title: "pi-chalin Memory",
|
|
205
|
+
emptyMessage: "No pi-chalin memory records found.",
|
|
206
|
+
};
|
|
207
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { isAgentThinkingLevel, riskRank, type AgentScope, type AgentThinkingLeve
|
|
|
6
6
|
export type AutonomyLevel = "low" | "balanced" | "high";
|
|
7
7
|
export type ApprovalRiskThreshold = RouteRisk;
|
|
8
8
|
export type ModelPersistenceTarget = "session" | "project" | "user";
|
|
9
|
+
export type MemoryProvider = "auto" | "engram" | "pi-chalin";
|
|
9
10
|
|
|
10
11
|
export interface ChalinConfig {
|
|
11
12
|
enabled: boolean;
|
|
@@ -22,6 +23,18 @@ export interface ChalinConfig {
|
|
|
22
23
|
thinkingOverrides: Record<string, AgentThinkingLevel>;
|
|
23
24
|
modelPersistenceDefaults: Record<AgentScope, ModelPersistenceTarget>;
|
|
24
25
|
};
|
|
26
|
+
memory: {
|
|
27
|
+
provider: MemoryProvider;
|
|
28
|
+
engram: {
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
command: string;
|
|
31
|
+
autoStart: boolean;
|
|
32
|
+
autoSync: boolean;
|
|
33
|
+
syncThrottleMs: number;
|
|
34
|
+
timeoutMs: number;
|
|
35
|
+
project?: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
25
38
|
}
|
|
26
39
|
|
|
27
40
|
export interface LoadedChalinConfig {
|
|
@@ -49,6 +62,17 @@ export const DEFAULT_CONFIG: ChalinConfig = {
|
|
|
49
62
|
project: "project",
|
|
50
63
|
},
|
|
51
64
|
},
|
|
65
|
+
memory: {
|
|
66
|
+
provider: "auto",
|
|
67
|
+
engram: {
|
|
68
|
+
baseUrl: "http://127.0.0.1:7437",
|
|
69
|
+
command: "engram",
|
|
70
|
+
autoStart: false,
|
|
71
|
+
autoSync: true,
|
|
72
|
+
syncThrottleMs: 30_000,
|
|
73
|
+
timeoutMs: 800,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
52
76
|
};
|
|
53
77
|
|
|
54
78
|
function isObject(value: unknown): value is Record<string, unknown> {
|
|
@@ -118,6 +142,40 @@ function coerceConfig(input: ChalinConfig, diagnostics: string[]): ChalinConfig
|
|
|
118
142
|
delete config.agents.thinkingOverrides[agentRef];
|
|
119
143
|
}
|
|
120
144
|
}
|
|
145
|
+
if (!isObject(config.memory)) config.memory = structuredClone(DEFAULT_CONFIG.memory);
|
|
146
|
+
if (!["auto", "engram", "pi-chalin"].includes(config.memory.provider)) {
|
|
147
|
+
diagnostics.push(`Invalid memory.provider '${String(config.memory.provider)}'; using '${DEFAULT_CONFIG.memory.provider}'.`);
|
|
148
|
+
config.memory.provider = DEFAULT_CONFIG.memory.provider;
|
|
149
|
+
}
|
|
150
|
+
if (!isObject(config.memory.engram)) config.memory.engram = structuredClone(DEFAULT_CONFIG.memory.engram);
|
|
151
|
+
if (typeof config.memory.engram.baseUrl !== "string" || !config.memory.engram.baseUrl.trim()) {
|
|
152
|
+
diagnostics.push(`Invalid memory.engram.baseUrl '${String(config.memory.engram.baseUrl)}'; using '${DEFAULT_CONFIG.memory.engram.baseUrl}'.`);
|
|
153
|
+
config.memory.engram.baseUrl = DEFAULT_CONFIG.memory.engram.baseUrl;
|
|
154
|
+
}
|
|
155
|
+
if (typeof config.memory.engram.command !== "string" || !config.memory.engram.command.trim()) {
|
|
156
|
+
diagnostics.push(`Invalid memory.engram.command '${String(config.memory.engram.command)}'; using '${DEFAULT_CONFIG.memory.engram.command}'.`);
|
|
157
|
+
config.memory.engram.command = DEFAULT_CONFIG.memory.engram.command;
|
|
158
|
+
}
|
|
159
|
+
if (typeof config.memory.engram.autoStart !== "boolean") {
|
|
160
|
+
diagnostics.push(`Invalid memory.engram.autoStart '${String(config.memory.engram.autoStart)}'; using '${DEFAULT_CONFIG.memory.engram.autoStart}'.`);
|
|
161
|
+
config.memory.engram.autoStart = DEFAULT_CONFIG.memory.engram.autoStart;
|
|
162
|
+
}
|
|
163
|
+
if (typeof config.memory.engram.autoSync !== "boolean") {
|
|
164
|
+
diagnostics.push(`Invalid memory.engram.autoSync '${String(config.memory.engram.autoSync)}'; using '${DEFAULT_CONFIG.memory.engram.autoSync}'.`);
|
|
165
|
+
config.memory.engram.autoSync = DEFAULT_CONFIG.memory.engram.autoSync;
|
|
166
|
+
}
|
|
167
|
+
if (!Number.isFinite(config.memory.engram.syncThrottleMs) || config.memory.engram.syncThrottleMs < 0 || config.memory.engram.syncThrottleMs > 300_000) {
|
|
168
|
+
diagnostics.push(`Invalid memory.engram.syncThrottleMs '${String(config.memory.engram.syncThrottleMs)}'; using '${DEFAULT_CONFIG.memory.engram.syncThrottleMs}'.`);
|
|
169
|
+
config.memory.engram.syncThrottleMs = DEFAULT_CONFIG.memory.engram.syncThrottleMs;
|
|
170
|
+
}
|
|
171
|
+
if (!Number.isFinite(config.memory.engram.timeoutMs) || config.memory.engram.timeoutMs < 100 || config.memory.engram.timeoutMs > 10_000) {
|
|
172
|
+
diagnostics.push(`Invalid memory.engram.timeoutMs '${String(config.memory.engram.timeoutMs)}'; using '${DEFAULT_CONFIG.memory.engram.timeoutMs}'.`);
|
|
173
|
+
config.memory.engram.timeoutMs = DEFAULT_CONFIG.memory.engram.timeoutMs;
|
|
174
|
+
}
|
|
175
|
+
if (config.memory.engram.project !== undefined && typeof config.memory.engram.project !== "string") {
|
|
176
|
+
diagnostics.push(`Invalid memory.engram.project '${String(config.memory.engram.project)}'; removing override.`);
|
|
177
|
+
delete config.memory.engram.project;
|
|
178
|
+
}
|
|
121
179
|
return config;
|
|
122
180
|
}
|
|
123
181
|
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,8 @@ export { ArtifactStore } from "./artifacts.ts";
|
|
|
35
35
|
export { DEFAULT_CONFIG, approvalDecision, loadEffectiveConfig, setAgentModelOverride, setAgentThinkingOverride, writeProjectConfig, writeUserConfig } from "./config.ts";
|
|
36
36
|
export { ChalinKernel } from "./kernel.ts";
|
|
37
37
|
export { MemoryStore, createMemoryCandidate } from "./memory.ts";
|
|
38
|
+
export { EngramMemoryStore, createConfiguredMemoryStore, resolveMemoryBackendStatus } from "./memory-provider.ts";
|
|
38
39
|
export { MockWorkerRunner, SdkWorkerRunner, parseAgentOutput } from "./runner.ts";
|
|
39
40
|
export { resolveChalinPaths } from "./paths.ts";
|
|
40
41
|
export type { AgentDefinition, AgentMemoryPolicy, AgentThinkingLevel, RouteDecision, RoutePlan, RunState, MemoryCandidate, MemoryRecord } from "./schemas.ts";
|
|
42
|
+
export type { MemoryProvider } from "./config.ts";
|
package/src/kernel.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { AgentCatalog } from "./agents.ts";
|
|
2
2
|
import { ArtifactStore } from "./artifacts.ts";
|
|
3
|
-
import { approvalDecision, type ChalinConfig } from "./config.ts";
|
|
4
|
-
import {
|
|
3
|
+
import { DEFAULT_CONFIG, approvalDecision, type ChalinConfig } from "./config.ts";
|
|
4
|
+
import type { MemoryStoreLike } from "./memory.ts";
|
|
5
|
+
import { createConfiguredMemoryStore } from "./memory-provider.ts";
|
|
5
6
|
import { MockWorkerRunner, SdkWorkerRunner, type WorkerRunner, type WorkerRunnerContext } from "./runner.ts";
|
|
6
7
|
import type { AgentDefinition, AgentStage, AgentStep, AgentThinkingLevel, ApprovalDecision, MemoryRecord, RouteDecision, RoutePlan, RunState } from "./schemas.ts";
|
|
7
8
|
|
|
@@ -9,7 +10,7 @@ export interface ChalinKernelOptions {
|
|
|
9
10
|
cwd?: string;
|
|
10
11
|
config?: ChalinConfig;
|
|
11
12
|
catalog?: AgentCatalog;
|
|
12
|
-
memory?:
|
|
13
|
+
memory?: MemoryStoreLike;
|
|
13
14
|
artifacts?: ArtifactStore;
|
|
14
15
|
runner?: WorkerRunner;
|
|
15
16
|
sdkRunner?: WorkerRunner;
|
|
@@ -29,7 +30,7 @@ export class ChalinKernel {
|
|
|
29
30
|
private readonly cwd: string;
|
|
30
31
|
private readonly config: ChalinConfig;
|
|
31
32
|
private readonly catalog: AgentCatalog;
|
|
32
|
-
private readonly memory:
|
|
33
|
+
private readonly memory: MemoryStoreLike;
|
|
33
34
|
private readonly artifacts: ArtifactStore;
|
|
34
35
|
private readonly runner: WorkerRunner;
|
|
35
36
|
private readonly sdkRunner: WorkerRunner;
|
|
@@ -38,14 +39,9 @@ export class ChalinKernel {
|
|
|
38
39
|
|
|
39
40
|
constructor(options?: ChalinKernelOptions) {
|
|
40
41
|
this.cwd = options?.cwd ?? process.cwd();
|
|
41
|
-
this.config = options?.config ??
|
|
42
|
-
enabled: true,
|
|
43
|
-
autonomy: "balanced",
|
|
44
|
-
safety: { approvalRiskThreshold: "medium", recursionGuard: true, singleWriterGuard: true, mutationExpectationGuard: true, blockCritical: true },
|
|
45
|
-
agents: { modelOverrides: {}, thinkingOverrides: {}, modelPersistenceDefaults: { "built-in": "user", user: "user", project: "project" } },
|
|
46
|
-
};
|
|
42
|
+
this.config = options?.config ?? DEFAULT_CONFIG;
|
|
47
43
|
this.catalog = options?.catalog ?? AgentCatalog.load({ cwd: this.cwd });
|
|
48
|
-
this.memory = options?.memory ??
|
|
44
|
+
this.memory = options?.memory ?? createConfiguredMemoryStore({ cwd: this.cwd }, this.config);
|
|
49
45
|
this.artifacts = options?.artifacts ?? new ArtifactStore({ cwd: this.cwd });
|
|
50
46
|
this.runner = options?.runner ?? new MockWorkerRunner();
|
|
51
47
|
this.sdkRunner = options?.sdkRunner ?? new SdkWorkerRunner();
|