pi-para 0.5.2 → 0.6.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/ARCHITECTURE.md +160 -0
- package/CHANGELOG.md +51 -0
- package/CONFIGURATION.md +243 -0
- package/README.md +140 -237
- package/SETUP.md +196 -112
- package/STRUCTURE.md +140 -0
- package/package.json +86 -18
- package/scripts/build-assets.mjs +29 -0
- package/scripts/install.ps1 +55 -0
- package/scripts/install.sh +63 -0
- package/scripts/smoke-install.mjs +63 -0
- package/skills/para/SKILL.md +1 -1
- package/skills/setup/SKILL.md +1 -1
- package/src/atomic-write.ts +17 -0
- package/src/capture.ts +4 -4
- package/src/cli-legacy.ts +43 -0
- package/src/cli.ts +133 -28
- package/src/commands.ts +203 -243
- package/src/config.ts +355 -0
- package/src/context.ts +29 -14
- package/src/credentials.ts +100 -0
- package/src/daemon.ts +1 -1
- package/src/doctor.ts +209 -0
- package/src/gepa/index.ts +1 -1
- package/src/index.ts +119 -40
- package/src/maintainer.ts +4 -4
- package/src/model-resolver.ts +96 -0
- package/src/paths.ts +48 -0
- package/src/processor.ts +4 -4
- package/src/qmd-providers.ts +63 -0
- package/src/query.ts +15 -1
- package/src/repair.ts +49 -0
- package/src/scheduler/controls.ts +125 -0
- package/src/scheduler/index.ts +210 -0
- package/src/scheduler/leases.ts +98 -0
- package/src/scheduler/session-capture.ts +146 -0
- package/src/scheduler/state.ts +304 -0
- package/src/session-tools.ts +1 -1
- package/src/settings.ts +126 -0
- package/src/setup.ts +116 -0
- package/src/status.ts +115 -0
- package/src/store.ts +116 -31
- package/src/summarize.ts +72 -16
- package/src/templates/prompts.ts +1 -1
- package/src/tools.ts +280 -90
- package/src/wiki-tool-guidance.ts +84 -0
- package/src/wiki.ts +98 -26
- package/dist/capture.d.ts +0 -34
- package/dist/capture.d.ts.map +0 -1
- package/dist/capture.js +0 -461
- package/dist/capture.js.map +0 -1
- package/dist/cli.d.ts +0 -15
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -405
- package/dist/cli.js.map +0 -1
- package/dist/commands.d.ts +0 -15
- package/dist/commands.d.ts.map +0 -1
- package/dist/commands.js +0 -786
- package/dist/commands.js.map +0 -1
- package/dist/context.d.ts +0 -61
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -289
- package/dist/context.js.map +0 -1
- package/dist/daemon.d.ts +0 -49
- package/dist/daemon.d.ts.map +0 -1
- package/dist/daemon.js +0 -226
- package/dist/daemon.js.map +0 -1
- package/dist/frontmatter.d.ts +0 -59
- package/dist/frontmatter.d.ts.map +0 -1
- package/dist/frontmatter.js +0 -170
- package/dist/frontmatter.js.map +0 -1
- package/dist/gepa/dataset.d.ts +0 -36
- package/dist/gepa/dataset.d.ts.map +0 -1
- package/dist/gepa/dataset.js +0 -148
- package/dist/gepa/dataset.js.map +0 -1
- package/dist/gepa/evaluator.d.ts +0 -51
- package/dist/gepa/evaluator.d.ts.map +0 -1
- package/dist/gepa/evaluator.js +0 -317
- package/dist/gepa/evaluator.js.map +0 -1
- package/dist/gepa/index.d.ts +0 -66
- package/dist/gepa/index.d.ts.map +0 -1
- package/dist/gepa/index.js +0 -240
- package/dist/gepa/index.js.map +0 -1
- package/dist/gepa/llm.d.ts +0 -35
- package/dist/gepa/llm.d.ts.map +0 -1
- package/dist/gepa/llm.js +0 -268
- package/dist/gepa/llm.js.map +0 -1
- package/dist/gepa/optimized/capture-prompt.txt +0 -65
- package/dist/gepa/optimizer.d.ts +0 -29
- package/dist/gepa/optimizer.d.ts.map +0 -1
- package/dist/gepa/optimizer.js +0 -249
- package/dist/gepa/optimizer.js.map +0 -1
- package/dist/gepa/targets.d.ts +0 -39
- package/dist/gepa/targets.d.ts.map +0 -1
- package/dist/gepa/targets.js +0 -326
- package/dist/gepa/targets.js.map +0 -1
- package/dist/gepa/types.d.ts +0 -168
- package/dist/gepa/types.d.ts.map +0 -1
- package/dist/gepa/types.js +0 -124
- package/dist/gepa/types.js.map +0 -1
- package/dist/index.d.ts +0 -21
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -294
- package/dist/index.js.map +0 -1
- package/dist/ingest.d.ts +0 -88
- package/dist/ingest.d.ts.map +0 -1
- package/dist/ingest.js +0 -105
- package/dist/ingest.js.map +0 -1
- package/dist/link-discovery.d.ts +0 -40
- package/dist/link-discovery.d.ts.map +0 -1
- package/dist/link-discovery.js +0 -122
- package/dist/link-discovery.js.map +0 -1
- package/dist/link-utils.d.ts +0 -30
- package/dist/link-utils.d.ts.map +0 -1
- package/dist/link-utils.js +0 -145
- package/dist/link-utils.js.map +0 -1
- package/dist/lint.d.ts +0 -39
- package/dist/lint.d.ts.map +0 -1
- package/dist/lint.js +0 -827
- package/dist/lint.js.map +0 -1
- package/dist/maintainer.d.ts +0 -24
- package/dist/maintainer.d.ts.map +0 -1
- package/dist/maintainer.js +0 -358
- package/dist/maintainer.js.map +0 -1
- package/dist/processor.d.ts +0 -14
- package/dist/processor.d.ts.map +0 -1
- package/dist/processor.js +0 -361
- package/dist/processor.js.map +0 -1
- package/dist/query.d.ts +0 -69
- package/dist/query.d.ts.map +0 -1
- package/dist/query.js +0 -107
- package/dist/query.js.map +0 -1
- package/dist/raw.d.ts +0 -57
- package/dist/raw.d.ts.map +0 -1
- package/dist/raw.js +0 -219
- package/dist/raw.js.map +0 -1
- package/dist/redact.d.ts +0 -21
- package/dist/redact.d.ts.map +0 -1
- package/dist/redact.js +0 -143
- package/dist/redact.js.map +0 -1
- package/dist/scope.d.ts +0 -54
- package/dist/scope.d.ts.map +0 -1
- package/dist/scope.js +0 -161
- package/dist/scope.js.map +0 -1
- package/dist/scripts/bulk-link-fix.d.ts +0 -9
- package/dist/scripts/bulk-link-fix.d.ts.map +0 -1
- package/dist/scripts/bulk-link-fix.js +0 -106
- package/dist/scripts/bulk-link-fix.js.map +0 -1
- package/dist/session-tools.d.ts +0 -33
- package/dist/session-tools.d.ts.map +0 -1
- package/dist/session-tools.js +0 -211
- package/dist/session-tools.js.map +0 -1
- package/dist/state.d.ts +0 -54
- package/dist/state.d.ts.map +0 -1
- package/dist/state.js +0 -176
- package/dist/state.js.map +0 -1
- package/dist/store.d.ts +0 -56
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -227
- package/dist/store.js.map +0 -1
- package/dist/summarize.d.ts +0 -56
- package/dist/summarize.d.ts.map +0 -1
- package/dist/summarize.js +0 -231
- package/dist/summarize.js.map +0 -1
- package/dist/tag-registry.d.ts +0 -56
- package/dist/tag-registry.d.ts.map +0 -1
- package/dist/tag-registry.js +0 -146
- package/dist/tag-registry.js.map +0 -1
- package/dist/templates/prompts.d.ts +0 -63
- package/dist/templates/prompts.d.ts.map +0 -1
- package/dist/templates/prompts.js +0 -260
- package/dist/templates/prompts.js.map +0 -1
- package/dist/tools.d.ts +0 -26
- package/dist/tools.d.ts.map +0 -1
- package/dist/tools.js +0 -1033
- package/dist/tools.js.map +0 -1
- package/dist/watcher.d.ts +0 -25
- package/dist/watcher.d.ts.map +0 -1
- package/dist/watcher.js +0 -84
- package/dist/watcher.js.map +0 -1
- package/dist/webui/client/dist/index.html +0 -1885
- package/dist/webui/server.d.ts +0 -31
- package/dist/webui/server.d.ts.map +0 -1
- package/dist/webui/server.js +0 -566
- package/dist/webui/server.js.map +0 -1
- package/dist/wiki.d.ts +0 -103
- package/dist/wiki.d.ts.map +0 -1
- package/dist/wiki.js +0 -428
- package/dist/wiki.js.map +0 -1
- package/scripts/gepa/__pycache__/dataset.cpython-313.pyc +0 -0
- package/scripts/gepa/__pycache__/lm_providers.cpython-313.pyc +0 -0
- package/scripts/gepa/__pycache__/metric.cpython-313.pyc +0 -0
- package/scripts/gepa/__pycache__/program.cpython-313.pyc +0 -0
- package/scripts/gepa/__pycache__/wiki_reader.cpython-313.pyc +0 -0
- package/scripts/gepa/uv.lock +0 -2241
package/src/doctor.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { existsSync, statSync } from "node:fs";
|
|
2
|
+
import { initWiki } from "./wiki.js";
|
|
3
|
+
import { loadParaConfig, type ParaUserConfig } from "./config.js";
|
|
4
|
+
import { readSecretStore, resolveCredentialRef } from "./credentials.js";
|
|
5
|
+
import { getParaPaths } from "./paths.js";
|
|
6
|
+
import { openStore, closeStore } from "./store.js";
|
|
7
|
+
import type { ProviderCredentialRef } from "./config.js";
|
|
8
|
+
import { createPiModelRegistry, getCaptureSelection, resolveSelectedModel } from "./model-resolver.js";
|
|
9
|
+
import { SchedulerStateDB } from "./scheduler/state.js";
|
|
10
|
+
import { ensureGeneratedStateGitignore, fixSecretPermissions, missingGeneratedStateGitignorePatterns } from "./repair.js";
|
|
11
|
+
|
|
12
|
+
export type DoctorStatus = "ok" | "warn" | "error";
|
|
13
|
+
|
|
14
|
+
export interface DoctorCheck {
|
|
15
|
+
name: string;
|
|
16
|
+
status: DoctorStatus;
|
|
17
|
+
message: string;
|
|
18
|
+
fixable?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DoctorOptions {
|
|
22
|
+
homeDir?: string;
|
|
23
|
+
fix?: boolean;
|
|
24
|
+
validateQmd?: boolean;
|
|
25
|
+
testCaptureModel?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DoctorResult {
|
|
29
|
+
ok: boolean;
|
|
30
|
+
checks: DoctorCheck[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function runDoctor(options: DoctorOptions = {}): Promise<DoctorResult> {
|
|
34
|
+
const checks: DoctorCheck[] = [];
|
|
35
|
+
const loaded = await loadParaConfig({ homeDir: options.homeDir, migrate: options.fix ?? false });
|
|
36
|
+
const paths = getParaPaths({ homeDir: options.homeDir, wikiDir: loaded.config.wiki.dir });
|
|
37
|
+
|
|
38
|
+
checks.push(checkExists("config", paths.userConfigPath, "warn"));
|
|
39
|
+
checks.push(checkExists("pi-settings", paths.piSettingsPath, "warn"));
|
|
40
|
+
|
|
41
|
+
if (options.fix) await initWiki(paths.wikiDir);
|
|
42
|
+
checks.push(checkExists("wiki-dir", paths.wikiDir, "warn", true));
|
|
43
|
+
|
|
44
|
+
const schedulerCheck = checkScheduler(paths.schedulerDbPath);
|
|
45
|
+
checks.push(schedulerCheck);
|
|
46
|
+
|
|
47
|
+
if (options.fix) {
|
|
48
|
+
fixSecretPermissions(paths.secretsPath);
|
|
49
|
+
await ensureGeneratedStateGitignore(paths.wikiDir);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
checks.push(await checkSecrets(paths.secretsPath));
|
|
53
|
+
checks.push(await checkGeneratedStateGitignore(paths.wikiDir));
|
|
54
|
+
checks.push(...await checkProviderDiagnostics(loaded.config, paths.secretsPath, options));
|
|
55
|
+
|
|
56
|
+
if (options.validateQmd !== false) {
|
|
57
|
+
checks.push(await checkQmd(paths.wikiDir, loaded.config, paths.secretsPath));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const backlogPath = `${paths.wikiDir}/.completed-sessions`;
|
|
61
|
+
if (existsSync(backlogPath)) {
|
|
62
|
+
checks.push({
|
|
63
|
+
name: "capture-backlog",
|
|
64
|
+
status: "warn",
|
|
65
|
+
message: ".completed-sessions exists; startup catch-up will process queued entries once capture task is enabled",
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
checks.push({ name: "capture-backlog", status: "ok", message: "no completed-session registry found" });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { ok: checks.every((check) => check.status !== "error"), checks };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function formatDoctorResult(result: DoctorResult): string {
|
|
75
|
+
const lines = ["pi-para doctor"];
|
|
76
|
+
for (const check of result.checks) {
|
|
77
|
+
const icon = check.status === "ok" ? "✓" : check.status === "warn" ? "⚠" : "✗";
|
|
78
|
+
lines.push(` ${icon} ${check.name}: ${check.message}`);
|
|
79
|
+
}
|
|
80
|
+
return lines.join("\n");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function checkExists(name: string, path: string, missingStatus: DoctorStatus, fixable = false): DoctorCheck {
|
|
84
|
+
return existsSync(path)
|
|
85
|
+
? { name, status: "ok", message: path }
|
|
86
|
+
: { name, status: missingStatus, message: `${path} not found`, fixable };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function checkScheduler(dbPath: string): DoctorCheck {
|
|
90
|
+
try {
|
|
91
|
+
const db = new SchedulerStateDB(dbPath);
|
|
92
|
+
const queued = db.list("queued").length;
|
|
93
|
+
db.close();
|
|
94
|
+
return { name: "scheduler", status: "ok", message: `${dbPath} (${queued} queued)` };
|
|
95
|
+
} catch (err) {
|
|
96
|
+
return { name: "scheduler", status: "error", message: err instanceof Error ? err.message : String(err) };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function checkSecrets(secretsPath: string): Promise<DoctorCheck> {
|
|
101
|
+
try {
|
|
102
|
+
const store = await readSecretStore(secretsPath);
|
|
103
|
+
if (!existsSync(secretsPath)) return { name: "secrets", status: "ok", message: "no local pi-para secrets configured" };
|
|
104
|
+
// POSIX mode bits are meaningless on Windows/NTFS — stat reports 666 and
|
|
105
|
+
// chmod is a no-op, so a permissions warning there is a false positive.
|
|
106
|
+
if (process.platform === "win32") {
|
|
107
|
+
return { name: "secrets", status: "ok", message: `${Object.keys(store.secrets).length} local secret(s) (POSIX permission check skipped on Windows)` };
|
|
108
|
+
}
|
|
109
|
+
const mode = statSync(secretsPath).mode & 0o777;
|
|
110
|
+
if (mode & 0o077) {
|
|
111
|
+
return { name: "secrets", status: "warn", message: `${secretsPath} permissions are too open (${mode.toString(8)})`, fixable: true };
|
|
112
|
+
}
|
|
113
|
+
return { name: "secrets", status: "ok", message: `${Object.keys(store.secrets).length} local secret(s), permissions ${mode.toString(8)}` };
|
|
114
|
+
} catch (err) {
|
|
115
|
+
return { name: "secrets", status: "error", message: err instanceof Error ? err.message : String(err) };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function checkGeneratedStateGitignore(wikiDir: string): Promise<DoctorCheck> {
|
|
120
|
+
try {
|
|
121
|
+
const missing = missingGeneratedStateGitignorePatterns(wikiDir);
|
|
122
|
+
if (missing.length > 0) {
|
|
123
|
+
return { name: "gitignore", status: "warn", message: `missing generated-state ignores: ${missing.join(", ")}`, fixable: true };
|
|
124
|
+
}
|
|
125
|
+
return { name: "gitignore", status: "ok", message: "generated SQLite/state files are ignored" };
|
|
126
|
+
} catch (err) {
|
|
127
|
+
return { name: "gitignore", status: "error", message: err instanceof Error ? err.message : String(err) };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function checkProviderDiagnostics(
|
|
132
|
+
config: ParaUserConfig,
|
|
133
|
+
secretsPath: string,
|
|
134
|
+
options: DoctorOptions,
|
|
135
|
+
): Promise<DoctorCheck[]> {
|
|
136
|
+
const checks: DoctorCheck[] = [];
|
|
137
|
+
|
|
138
|
+
if (config.qmd.providerConfig === "legacy-qmd-compatible") {
|
|
139
|
+
checks.push({ name: "qmd-providers", status: "warn", message: "using legacy QMD provider config compatibility mode" });
|
|
140
|
+
} else {
|
|
141
|
+
checks.push({ name: "qmd-providers", status: "ok", message: "using pi-para provider profiles" });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!config.qmd.embedEnabled) {
|
|
145
|
+
checks.push({ name: "qmd-embedding", status: "ok", message: "embedding disabled" });
|
|
146
|
+
} else if (config.qmd.embedding) {
|
|
147
|
+
checks.push(await checkCredentialProfile("qmd-embedding", config.qmd.embedding, secretsPath));
|
|
148
|
+
} else {
|
|
149
|
+
checks.push({ name: "qmd-embedding", status: "warn", message: "embedding enabled but no pi-para embedding profile is configured" });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (config.qmd.rerank) {
|
|
153
|
+
checks.push(await checkCredentialProfile("qmd-rerank", config.qmd.rerank, secretsPath));
|
|
154
|
+
} else {
|
|
155
|
+
checks.push({ name: "qmd-rerank", status: "ok", message: "rerank disabled" });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
checks.push(await checkCaptureModel(config, secretsPath, options.testCaptureModel === true));
|
|
159
|
+
return checks;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function checkCredentialProfile(name: string, profile: ProviderCredentialRef, secretsPath: string): Promise<DoctorCheck> {
|
|
163
|
+
const label = `${profile.provider}/${profile.model ?? "model not set"}`;
|
|
164
|
+
if (profile.credentialRef === "none") {
|
|
165
|
+
return { name, status: "ok", message: `${label} uses no credential` };
|
|
166
|
+
}
|
|
167
|
+
const resolved = await resolveCredentialRef(profile.credentialRef, { secretsPath });
|
|
168
|
+
if (!resolved.ok) {
|
|
169
|
+
return { name, status: "warn", message: `${label} credential missing: ${resolved.error}` };
|
|
170
|
+
}
|
|
171
|
+
return { name, status: "ok", message: `${label} credential available via ${resolved.source}` };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function checkCaptureModel(config: ParaUserConfig, secretsPath: string, testModel: boolean): Promise<DoctorCheck> {
|
|
175
|
+
const selection = getCaptureSelection(config);
|
|
176
|
+
if (!testModel) {
|
|
177
|
+
if (selection === "auto") return { name: "capture-model", status: "ok", message: "auto selection; run doctor --test-capture-model to resolve" };
|
|
178
|
+
return checkCredentialProfile("capture-model", selection, secretsPath);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const registry = await createPiModelRegistry();
|
|
183
|
+
if (!registry) return { name: "capture-model", status: "warn", message: "Pi model registry unavailable" };
|
|
184
|
+
const model = resolveSelectedModel(selection, registry.modelRegistry, { preferredModelSpec: "anthropic/claude-sonnet-4-20250514" });
|
|
185
|
+
if (!model) return { name: "capture-model", status: "warn", message: "no capture model resolved" };
|
|
186
|
+
const getKey = selection === "auto"
|
|
187
|
+
? () => registry.modelRegistry.getApiKeyForProvider(model.provider)
|
|
188
|
+
: () => resolveCredentialRef(selection.credentialRef, { secretsPath, authStorage: registry.authStorage }).then((r) => r.value);
|
|
189
|
+
const key = await getKey();
|
|
190
|
+
return key
|
|
191
|
+
? { name: "capture-model", status: "ok", message: `${model.provider}/${model.id} credential available` }
|
|
192
|
+
: { name: "capture-model", status: "warn", message: `${model.provider}/${model.id} resolved but credential is missing` };
|
|
193
|
+
} catch (err) {
|
|
194
|
+
return { name: "capture-model", status: "warn", message: err instanceof Error ? err.message : String(err) };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function checkQmd(wikiDir: string, config: ParaUserConfig, secretsPath: string): Promise<DoctorCheck> {
|
|
199
|
+
try {
|
|
200
|
+
const store = await openStore(wikiDir, {
|
|
201
|
+
paraConfig: { ...config, qmd: { ...config.qmd, embedEnabled: false } },
|
|
202
|
+
secretsPath,
|
|
203
|
+
});
|
|
204
|
+
await closeStore(store);
|
|
205
|
+
return { name: "qmd-sdk", status: "ok", message: "store opened and updated with pi-para provider profiles" };
|
|
206
|
+
} catch (err) {
|
|
207
|
+
return { name: "qmd-sdk", status: "error", message: err instanceof Error ? err.message : String(err), fixable: false };
|
|
208
|
+
}
|
|
209
|
+
}
|
package/src/gepa/index.ts
CHANGED
|
@@ -80,7 +80,7 @@ function findPackageRoot(): string {
|
|
|
80
80
|
if (existsSync(pkgPath)) {
|
|
81
81
|
try {
|
|
82
82
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
83
|
-
if (pkg.name === "@picassio/pi-para") return dir;
|
|
83
|
+
if (pkg.name === "pi-para" || pkg.name === "@picassio/pi-para") return dir;
|
|
84
84
|
} catch {}
|
|
85
85
|
}
|
|
86
86
|
dir = dirname(dir);
|
package/src/index.ts
CHANGED
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
* Main extension entry point. Wires together wiki filesystem, qmd store,
|
|
5
5
|
* scope detection, context injection, tools, and commands.
|
|
6
6
|
*
|
|
7
|
-
* Session capture
|
|
8
|
-
*
|
|
7
|
+
* Session capture and maintenance are coordinated by an in-process scheduler.
|
|
8
|
+
* Completed-session registry entries are kept as a durable catch-up queue.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
import { join, dirname } from "node:path";
|
|
11
|
+
import { join } from "node:path";
|
|
13
12
|
import { homedir, networkInterfaces } from "node:os";
|
|
14
13
|
|
|
15
|
-
import type { ExtensionAPI, ExtensionContext } from "@
|
|
14
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
16
15
|
import type { QMDStore } from "qmd-engine";
|
|
17
16
|
|
|
18
17
|
import type { ProjectScope } from "./scope.js";
|
|
@@ -24,6 +23,15 @@ import { setupContextInjection, markContextDirty } from "./context.js";
|
|
|
24
23
|
import type { ContextConfig } from "./context.js";
|
|
25
24
|
import { registerCommands } from "./commands.js";
|
|
26
25
|
import { createServer } from "node:http";
|
|
26
|
+
import { startWikiScheduler, stopWikiScheduler } from "./scheduler/index.js";
|
|
27
|
+
import {
|
|
28
|
+
appendCompletedSession,
|
|
29
|
+
createCaptureSessionHandler,
|
|
30
|
+
enqueueCompletedSessionsFromRegistry,
|
|
31
|
+
} from "./scheduler/session-capture.js";
|
|
32
|
+
import { loadParaConfig, toLegacyRuntimeConfig, type ParaUserConfig } from "./config.js";
|
|
33
|
+
import { createModelApiKeyResolver, createPiModelRegistry, getCaptureSelection, resolveSelectedModel } from "./model-resolver.js";
|
|
34
|
+
import { StateDB } from "./state.js";
|
|
27
35
|
|
|
28
36
|
// Re-export public types for consumers
|
|
29
37
|
export type { ParaCategory, WikiPage, PageFrontmatter, PageRef, LogEntry } from "./wiki.js";
|
|
@@ -48,8 +56,8 @@ interface ParaConfig {
|
|
|
48
56
|
searchLimit: number;
|
|
49
57
|
searchIncludeArchives: boolean;
|
|
50
58
|
searchGraphBoost: boolean;
|
|
51
|
-
/**
|
|
52
|
-
*
|
|
59
|
+
/** Legacy capture LLM: "provider/model-id" (e.g. "anthropic/claude-sonnet-4").
|
|
60
|
+
* New config stores this as models.capture; legacy field remains for migration. */
|
|
53
61
|
daemonModel: string | null;
|
|
54
62
|
/** Web wiki UI settings */
|
|
55
63
|
webWiki: {
|
|
@@ -108,30 +116,37 @@ function getDefaultConfig(): ParaConfig {
|
|
|
108
116
|
};
|
|
109
117
|
}
|
|
110
118
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
interface LoadedRuntimeConfig {
|
|
120
|
+
runtime: ParaConfig;
|
|
121
|
+
userConfig: ParaUserConfig | null;
|
|
122
|
+
secretsPath: string | null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function loadConfig(): Promise<LoadedRuntimeConfig> {
|
|
114
126
|
try {
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
const loaded = await loadParaConfig({ migrate: true });
|
|
128
|
+
const runtime = toLegacyRuntimeConfig(loaded.config);
|
|
129
|
+
return {
|
|
130
|
+
runtime: {
|
|
131
|
+
...runtime,
|
|
132
|
+
gepa: {
|
|
133
|
+
...runtime.gepa,
|
|
134
|
+
studentModel: runtime.gepa.studentModel ?? null,
|
|
135
|
+
teacherModel: runtime.gepa.teacherModel ?? null,
|
|
136
|
+
judgeModel: runtime.gepa.judgeModel ?? null,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
userConfig: loaded.config,
|
|
140
|
+
secretsPath: loaded.paths.secretsPath,
|
|
141
|
+
};
|
|
121
142
|
} catch {
|
|
122
|
-
|
|
123
|
-
await mkdir(dirname(configPath), { recursive: true });
|
|
124
|
-
await writeFile(configPath, JSON.stringify(defaults, null, 2), "utf-8");
|
|
125
|
-
} catch {
|
|
126
|
-
// Non-fatal
|
|
127
|
-
}
|
|
128
|
-
return { ...defaults };
|
|
143
|
+
return { runtime: getDefaultConfig(), userConfig: null, secretsPath: null };
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
146
|
|
|
132
147
|
// -- LAN IP detection --------------------------------------------------------
|
|
133
148
|
|
|
134
|
-
/** Check if the
|
|
149
|
+
/** Check if the optional web wiki server is alive on the given port. */
|
|
135
150
|
function checkWebServerAlive(port: number): Promise<boolean> {
|
|
136
151
|
return new Promise((resolve) => {
|
|
137
152
|
const req = createServer().listen(port, "127.0.0.1");
|
|
@@ -148,6 +163,10 @@ function checkWebServerAlive(port: number): Promise<boolean> {
|
|
|
148
163
|
});
|
|
149
164
|
}
|
|
150
165
|
|
|
166
|
+
function isPrintModeProcess(argv = process.argv): boolean {
|
|
167
|
+
return argv.includes("-p") || argv.includes("--print") || argv.includes("--mode") && argv[argv.indexOf("--mode") + 1] === "json";
|
|
168
|
+
}
|
|
169
|
+
|
|
151
170
|
function getLanIp(): string | null {
|
|
152
171
|
const interfaces = networkInterfaces();
|
|
153
172
|
for (const name of Object.keys(interfaces)) {
|
|
@@ -184,7 +203,8 @@ function reconstructState(ctx: ExtensionContext): ParaSessionState | null {
|
|
|
184
203
|
// -- Extension entry point ---------------------------------------------------
|
|
185
204
|
|
|
186
205
|
export default async function piPara(pi: ExtensionAPI): Promise<void> {
|
|
187
|
-
const
|
|
206
|
+
const loadedConfig = await loadConfig();
|
|
207
|
+
const config = loadedConfig.runtime;
|
|
188
208
|
const wikiDir = config.wikiDir;
|
|
189
209
|
|
|
190
210
|
let store: QMDStore | null = null;
|
|
@@ -221,7 +241,11 @@ export default async function piPara(pi: ExtensionAPI): Promise<void> {
|
|
|
221
241
|
if (storeRetrying) return false;
|
|
222
242
|
storeRetrying = true;
|
|
223
243
|
try {
|
|
224
|
-
store = await openStore(wikiDir
|
|
244
|
+
store = await openStore(wikiDir, {
|
|
245
|
+
paraConfig: loadedConfig.userConfig ?? undefined,
|
|
246
|
+
secretsPath: loadedConfig.secretsPath ?? undefined,
|
|
247
|
+
backgroundEmbed: false,
|
|
248
|
+
});
|
|
225
249
|
storeDisabled = false;
|
|
226
250
|
markContextDirty(); // rebuild context now that store works
|
|
227
251
|
return true;
|
|
@@ -280,7 +304,11 @@ export default async function piPara(pi: ExtensionAPI): Promise<void> {
|
|
|
280
304
|
ctx.ui.setStatus("pi-para", "wiki: indexing...");
|
|
281
305
|
storeDisabled = false;
|
|
282
306
|
try {
|
|
283
|
-
store = await openStore(wikiDir
|
|
307
|
+
store = await openStore(wikiDir, {
|
|
308
|
+
paraConfig: loadedConfig.userConfig ?? undefined,
|
|
309
|
+
secretsPath: loadedConfig.secretsPath ?? undefined,
|
|
310
|
+
backgroundEmbed: false,
|
|
311
|
+
});
|
|
284
312
|
} catch (err) {
|
|
285
313
|
store = null;
|
|
286
314
|
storeDisabled = true;
|
|
@@ -302,7 +330,60 @@ export default async function piPara(pi: ExtensionAPI): Promise<void> {
|
|
|
302
330
|
sessionFile: ctx.sessionManager.getSessionFile() ?? null,
|
|
303
331
|
} satisfies ParaSessionState);
|
|
304
332
|
|
|
305
|
-
|
|
333
|
+
const schedulerHandlers = {} as Record<string, any>;
|
|
334
|
+
const schedulerConfig = loadedConfig.userConfig?.scheduler;
|
|
335
|
+
const wikiConfig = loadedConfig.userConfig?.wiki;
|
|
336
|
+
const autoCaptureEnabled = wikiConfig?.autoCapture !== false;
|
|
337
|
+
const startupCatchupEnabled = autoCaptureEnabled && wikiConfig?.captureOnStartup !== false && schedulerConfig?.startupCatchup !== false;
|
|
338
|
+
try {
|
|
339
|
+
const loadedParaConfig = await loadParaConfig({ migrate: true });
|
|
340
|
+
const registry = autoCaptureEnabled ? await createPiModelRegistry() : null;
|
|
341
|
+
if (registry) {
|
|
342
|
+
const captureSelection = getCaptureSelection(loadedParaConfig.config);
|
|
343
|
+
const captureModel = resolveSelectedModel(captureSelection, registry.modelRegistry, {
|
|
344
|
+
legacyModelSpec: config.daemonModel,
|
|
345
|
+
preferredModelSpec: "anthropic/claude-sonnet-4-20250514",
|
|
346
|
+
});
|
|
347
|
+
if (captureModel) {
|
|
348
|
+
schedulerHandlers["capture-session"] = createCaptureSessionHandler({
|
|
349
|
+
wikiDir,
|
|
350
|
+
storeProvider: () => store,
|
|
351
|
+
model: captureModel,
|
|
352
|
+
getApiKey: createModelApiKeyResolver(captureSelection, registry.modelRegistry, {
|
|
353
|
+
authStorage: registry.authStorage,
|
|
354
|
+
secretsPath: loadedParaConfig.paths.secretsPath,
|
|
355
|
+
}),
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} catch {
|
|
360
|
+
// Capture handler registration is best-effort; manual capture remains available.
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const schedulerEnabled = !isPrintModeProcess() && schedulerConfig?.enabled !== false;
|
|
364
|
+
const scheduler = schedulerEnabled
|
|
365
|
+
? startWikiScheduler({
|
|
366
|
+
wikiDir,
|
|
367
|
+
enabled: true,
|
|
368
|
+
intervalMs: (schedulerConfig?.intervalMinutes ?? 15) * 60_000,
|
|
369
|
+
storeProvider: () => store,
|
|
370
|
+
markDirty: () => markContextDirty(),
|
|
371
|
+
handlers: schedulerHandlers,
|
|
372
|
+
})
|
|
373
|
+
: null;
|
|
374
|
+
|
|
375
|
+
if (scheduler && startupCatchupEnabled) {
|
|
376
|
+
try {
|
|
377
|
+
const stateDb = new StateDB(wikiDir);
|
|
378
|
+
await enqueueCompletedSessionsFromRegistry(scheduler, wikiDir, { stateDb });
|
|
379
|
+
stateDb.close();
|
|
380
|
+
void scheduler.tick();
|
|
381
|
+
} catch {
|
|
382
|
+
// Startup capture catch-up is best-effort.
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Check if web wiki server is running (don't start our own yet)
|
|
306
387
|
if (config.webWiki.enabled) {
|
|
307
388
|
const port = config.webWiki.port;
|
|
308
389
|
try {
|
|
@@ -312,7 +393,7 @@ export default async function piPara(pi: ExtensionAPI): Promise<void> {
|
|
|
312
393
|
const url = lanIp ? `http://${lanIp}:${port}` : `http://localhost:${port}`;
|
|
313
394
|
ctx.ui.setStatus("pi-para", `wiki: ${url}`);
|
|
314
395
|
} else {
|
|
315
|
-
ctx.ui.setStatus("pi-para", "wiki: ready (
|
|
396
|
+
ctx.ui.setStatus("pi-para", "wiki: ready (web UI disabled)");
|
|
316
397
|
setTimeout(() => ctx.ui.setStatus("pi-para", undefined), 5000);
|
|
317
398
|
}
|
|
318
399
|
} catch {
|
|
@@ -337,7 +418,7 @@ export default async function piPara(pi: ExtensionAPI): Promise<void> {
|
|
|
337
418
|
markContextDirty();
|
|
338
419
|
});
|
|
339
420
|
|
|
340
|
-
// -- session_compact: register session for
|
|
421
|
+
// -- session_compact: register session for scheduler capture on compaction --
|
|
341
422
|
|
|
342
423
|
pi.on("session_compact", async (_event, ctx) => {
|
|
343
424
|
// Compaction means the session has accumulated enough content that details
|
|
@@ -345,9 +426,7 @@ export default async function piPara(pi: ExtensionAPI): Promise<void> {
|
|
|
345
426
|
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
346
427
|
if (sessionFile) {
|
|
347
428
|
try {
|
|
348
|
-
|
|
349
|
-
const entry = `${new Date().toISOString()}|${sessionFile}\n`;
|
|
350
|
-
await appendFile(registry, entry);
|
|
429
|
+
await appendCompletedSession(wikiDir, sessionFile);
|
|
351
430
|
ctx.ui.setStatus("pi-para", "wiki: capture queued (compaction)");
|
|
352
431
|
setTimeout(() => ctx.ui.setStatus("pi-para", undefined), 5000);
|
|
353
432
|
} catch {
|
|
@@ -356,22 +435,22 @@ export default async function piPara(pi: ExtensionAPI): Promise<void> {
|
|
|
356
435
|
}
|
|
357
436
|
});
|
|
358
437
|
|
|
359
|
-
// -- session_shutdown: register session for
|
|
438
|
+
// -- session_shutdown: register session for scheduler, close store ---------
|
|
360
439
|
|
|
361
440
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
362
|
-
// 1. Register session as completed (
|
|
441
|
+
// 1. Register session as completed (scheduler catch-up processes later)
|
|
363
442
|
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
364
443
|
if (sessionFile) {
|
|
365
444
|
try {
|
|
366
|
-
|
|
367
|
-
const entry = `${new Date().toISOString()}|${sessionFile}\n`;
|
|
368
|
-
await appendFile(registry, entry);
|
|
445
|
+
await appendCompletedSession(wikiDir, sessionFile);
|
|
369
446
|
} catch {
|
|
370
|
-
// Non-fatal —
|
|
447
|
+
// Non-fatal — startup catch-up can also discover sessions via filesystem scan
|
|
371
448
|
}
|
|
372
449
|
}
|
|
373
450
|
|
|
374
|
-
// 2.
|
|
451
|
+
// 2. Stop scheduler and close the store (instant)
|
|
452
|
+
stopWikiScheduler(wikiDir);
|
|
453
|
+
|
|
375
454
|
if (store) {
|
|
376
455
|
try {
|
|
377
456
|
await closeStore(store);
|
package/src/maintainer.ts
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
* stay in code. This agent handles everything that needs intelligence.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { Agent } from "@
|
|
13
|
-
import type { AgentTool } from "@
|
|
14
|
-
import type { Model } from "@
|
|
12
|
+
import { Agent } from "@earendil-works/pi-agent-core";
|
|
13
|
+
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
14
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
15
15
|
import type { QMDStore } from "qmd-engine";
|
|
16
16
|
import { Type } from "typebox";
|
|
17
|
-
import { StringEnum } from "@
|
|
17
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
18
18
|
|
|
19
19
|
import {
|
|
20
20
|
readPage,
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ProviderCredentialRef, ParaUserConfig } from "./config.js";
|
|
3
|
+
import { resolveCredentialRef } from "./credentials.js";
|
|
4
|
+
|
|
5
|
+
export interface ModelRegistryLike {
|
|
6
|
+
getAvailable(): Model<any>[];
|
|
7
|
+
find(provider: string, modelId: string): Model<any> | undefined;
|
|
8
|
+
getApiKeyForProvider(provider: string): Promise<string | undefined>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AuthStorageLike {
|
|
12
|
+
getApiKey(provider: string): Promise<string | undefined>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type ModelSelection = ProviderCredentialRef | "auto";
|
|
16
|
+
|
|
17
|
+
export function parseProviderModelSpec(spec: string): { provider: string; modelId: string } | null {
|
|
18
|
+
const idx = spec.indexOf("/");
|
|
19
|
+
if (idx <= 0 || idx === spec.length - 1) return null;
|
|
20
|
+
return { provider: spec.slice(0, idx), modelId: spec.slice(idx + 1) };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resolveSelectedModel(
|
|
24
|
+
selection: ModelSelection,
|
|
25
|
+
registry: ModelRegistryLike,
|
|
26
|
+
opts: { legacyModelSpec?: string | null; preferredModelSpec?: string } = {},
|
|
27
|
+
): Model<any> | null {
|
|
28
|
+
if (selection !== "auto") {
|
|
29
|
+
if (selection.model) return registry.find(selection.provider, selection.model) ?? null;
|
|
30
|
+
return registry.getAvailable().find((model) => model.provider === selection.provider) ?? null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const legacy = opts.legacyModelSpec ? parseProviderModelSpec(opts.legacyModelSpec) : null;
|
|
34
|
+
if (legacy) {
|
|
35
|
+
const model = registry.find(legacy.provider, legacy.modelId);
|
|
36
|
+
if (model) return model;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const preferred = opts.preferredModelSpec ? parseProviderModelSpec(opts.preferredModelSpec) : null;
|
|
40
|
+
if (preferred) {
|
|
41
|
+
const model = registry.find(preferred.provider, preferred.modelId);
|
|
42
|
+
if (model) return model;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return pickBestAvailableModel(registry.getAvailable());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function pickBestAvailableModel(models: Model<any>[]): Model<any> | null {
|
|
49
|
+
if (models.length === 0) return null;
|
|
50
|
+
const sonnet = models.find((model) => model.provider === "anthropic" && /sonnet/i.test(model.id));
|
|
51
|
+
if (sonnet) return sonnet;
|
|
52
|
+
return [...models].sort((a, b) => (b.contextWindow ?? 0) - (a.contextWindow ?? 0))[0] ?? null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createModelApiKeyResolver(
|
|
56
|
+
selection: ModelSelection,
|
|
57
|
+
registry: ModelRegistryLike,
|
|
58
|
+
opts: { authStorage?: AuthStorageLike; secretsPath?: string } = {},
|
|
59
|
+
): (provider: string) => Promise<string | undefined> {
|
|
60
|
+
return async (provider: string) => {
|
|
61
|
+
if (selection === "auto") return registry.getApiKeyForProvider(provider);
|
|
62
|
+
|
|
63
|
+
if (selection.credentialRef.startsWith("pi-auth:")) {
|
|
64
|
+
const configuredProvider = selection.credentialRef.slice("pi-auth:".length);
|
|
65
|
+
if (configuredProvider !== provider && selection.provider !== provider) return undefined;
|
|
66
|
+
return (await opts.authStorage?.getApiKey(provider)) ?? await registry.getApiKeyForProvider(provider);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (selection.credentialRef.startsWith("secret:")) {
|
|
70
|
+
if (selection.provider !== provider) return undefined;
|
|
71
|
+
const resolved = await resolveCredentialRef(selection.credentialRef, { secretsPath: opts.secretsPath });
|
|
72
|
+
return resolved.ok ? resolved.value : undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return undefined;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getCaptureSelection(config: ParaUserConfig): ModelSelection {
|
|
80
|
+
return config.models.capture;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function createPiModelRegistry(): Promise<{ authStorage: AuthStorageLike; modelRegistry: ModelRegistryLike } | null> {
|
|
84
|
+
try {
|
|
85
|
+
const mod = await import("@earendil-works/pi-coding-agent");
|
|
86
|
+
const typed = mod as unknown as {
|
|
87
|
+
AuthStorage?: { create(): AuthStorageLike };
|
|
88
|
+
ModelRegistry?: { create(authStorage: AuthStorageLike): ModelRegistryLike };
|
|
89
|
+
};
|
|
90
|
+
if (!typed.AuthStorage || !typed.ModelRegistry) return null;
|
|
91
|
+
const authStorage = typed.AuthStorage.create();
|
|
92
|
+
return { authStorage, modelRegistry: typed.ModelRegistry.create(authStorage) };
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
package/src/paths.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
export interface ParaPaths {
|
|
5
|
+
homeDir: string;
|
|
6
|
+
agentDir: string;
|
|
7
|
+
paraDir: string;
|
|
8
|
+
wikiDir: string;
|
|
9
|
+
userConfigPath: string;
|
|
10
|
+
secretsPath: string;
|
|
11
|
+
schedulerDbPath: string;
|
|
12
|
+
qmdDbPath: string;
|
|
13
|
+
legacyConfigPath: string;
|
|
14
|
+
legacyQmdConfigPath: string;
|
|
15
|
+
piSettingsPath: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function expandHome(path: string, homeDir = homedir()): string {
|
|
19
|
+
if (path === "~") return homeDir;
|
|
20
|
+
if (path.startsWith("~/") || path.startsWith("~\\")) return join(homeDir, path.slice(2));
|
|
21
|
+
return path;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolvePath(path: string, baseDir = process.cwd(), homeDir = homedir()): string {
|
|
25
|
+
const expanded = expandHome(path, homeDir);
|
|
26
|
+
return isAbsolute(expanded) ? expanded : resolve(baseDir, expanded);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getParaPaths(opts: { homeDir?: string; wikiDir?: string } = {}): ParaPaths {
|
|
30
|
+
const homeDir = opts.homeDir ?? homedir();
|
|
31
|
+
const agentDir = join(homeDir, ".pi", "agent");
|
|
32
|
+
const paraDir = join(homeDir, ".pi", "para");
|
|
33
|
+
const wikiDir = opts.wikiDir ? resolvePath(opts.wikiDir, process.cwd(), homeDir) : join(homeDir, ".pi", "wiki");
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
homeDir,
|
|
37
|
+
agentDir,
|
|
38
|
+
paraDir,
|
|
39
|
+
wikiDir,
|
|
40
|
+
userConfigPath: join(paraDir, "config.jsonc"),
|
|
41
|
+
secretsPath: join(paraDir, "secrets.json"),
|
|
42
|
+
schedulerDbPath: join(wikiDir, ".pi-para.sqlite"),
|
|
43
|
+
qmdDbPath: join(wikiDir, ".qmd.sqlite"),
|
|
44
|
+
legacyConfigPath: join(wikiDir, "config.json"),
|
|
45
|
+
legacyQmdConfigPath: join(homeDir, ".config", "qmd", "index.yml"),
|
|
46
|
+
piSettingsPath: join(agentDir, "settings.json"),
|
|
47
|
+
};
|
|
48
|
+
}
|
package/src/processor.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* to extract knowledge and write wiki pages.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Agent } from "@
|
|
7
|
-
import type { AgentMessage, AgentTool } from "@
|
|
8
|
-
import type { Model } from "@
|
|
6
|
+
import { Agent } from "@earendil-works/pi-agent-core";
|
|
7
|
+
import type { AgentMessage, AgentTool } from "@earendil-works/pi-agent-core";
|
|
8
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
9
9
|
import type { QMDStore } from "qmd-engine";
|
|
10
10
|
|
|
11
11
|
import {
|
|
@@ -25,7 +25,7 @@ import { redactSecrets } from "./redact.js";
|
|
|
25
25
|
import { reindex, searchWiki } from "./store.js";
|
|
26
26
|
import { appendSessionDigest } from "./raw.js";
|
|
27
27
|
import { Type } from "typebox";
|
|
28
|
-
import { StringEnum } from "@
|
|
28
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
29
29
|
|
|
30
30
|
import { loadSession, createSessionTools } from "./session-tools.js";
|
|
31
31
|
import type { LoadedSession } from "./session-tools.js";
|