portable-agent-layer 0.78.2 → 0.78.3
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 +3 -2
- package/assets/skills/pal-reflect/SKILL.md +2 -2
- package/assets/templates/PAL/SYSTEM_ARCHITECTURE.md +4 -4
- package/package.json +2 -2
- package/src/cli/builtin-tools.ts +1 -0
- package/src/cli/index.ts +3 -48
- package/src/cli/setup-auto-update.ts +7 -7
- package/src/hooks/AutoUpdate.ts +1 -1
- package/src/hooks/handlers/persist-last-exchange.ts +39 -24
- package/src/hooks/handlers/session-intelligence.ts +6 -2
- package/src/hooks/lib/algorithm-review.ts +12 -7
- package/src/hooks/lib/auto-update.ts +16 -6
- package/src/hooks/lib/context.ts +7 -1
- package/src/hooks/lib/inference.ts +12 -45
- package/src/hooks/lib/log.ts +44 -3
- package/src/hooks/lib/paths.ts +7 -2
- package/src/hooks/lib/relationship.ts +2 -2
- package/src/hooks/lib/retrieval-index.ts +13 -6
- package/src/hooks/lib/security.ts +29 -3
- package/src/hooks/lib/semi-static.ts +10 -3
- package/src/hooks/lib/settings.ts +11 -12
- package/src/tools/{relationship-reflect.ts → agent/relationship-reflect.ts} +13 -12
- package/src/tools/control-room/ui/dist/assets/{index-DyhnNmdi.js → index-D_o9Usd_.js} +1 -1
- package/src/tools/control-room/ui/dist/index.html +1 -1
- package/src/tools/control-room/ui/screens/settings.tsx +2 -2
- package/src/tools/self-model.ts +1 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
11
|
-
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { resolve } from "node:path";
|
|
13
13
|
import { SIMILARITY_THRESHOLD } from "./graduation";
|
|
14
14
|
import { readFailures, readReflections } from "./learning-store";
|
|
@@ -197,14 +197,21 @@ export function readIndex(): RetrievalIndex | null {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
/** True if any source directory was modified after the index was built. */
|
|
200
|
+
function newestFileMtime(dir: string): number {
|
|
201
|
+
let newest = 0;
|
|
202
|
+
for (const entry of readdirSync(dir, { recursive: true, withFileTypes: true })) {
|
|
203
|
+
if (!entry.isFile()) continue;
|
|
204
|
+
newest = Math.max(newest, statSync(resolve(entry.parentPath, entry.name)).mtimeMs);
|
|
205
|
+
}
|
|
206
|
+
return newest;
|
|
207
|
+
}
|
|
208
|
+
|
|
200
209
|
export function isStale(index: RetrievalIndex): boolean {
|
|
201
210
|
try {
|
|
202
211
|
const builtMs = new Date(index.builtAt).getTime();
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
return false;
|
|
212
|
+
return [paths.failures(), paths.wisdom(), paths.reflections()].some(
|
|
213
|
+
(dir) => newestFileMtime(dir) > builtMs
|
|
214
|
+
);
|
|
208
215
|
} catch {
|
|
209
216
|
return false;
|
|
210
217
|
}
|
|
@@ -79,9 +79,14 @@ const WIN_ELEVATED_THREATS: [RegExp, string][] = [
|
|
|
79
79
|
[/\bdiskpart\b/i, "Disk partitioning"],
|
|
80
80
|
];
|
|
81
81
|
|
|
82
|
+
const POSIX_ROOT_TARGET = String.raw`["']?(?:(?:~|\$HOME|\$\{HOME\})/?|/)\*?["']?(?=\s|[;&|]|$)`;
|
|
83
|
+
|
|
82
84
|
/** Dangerous command patterns — always blocked */
|
|
83
85
|
const BLOCKED_COMMANDS: [RegExp, string][] = [
|
|
84
|
-
[
|
|
86
|
+
[
|
|
87
|
+
new RegExp(String.raw`rm\s+-rf\s+${POSIX_ROOT_TARGET}`),
|
|
88
|
+
"Recursive delete of root or home",
|
|
89
|
+
],
|
|
85
90
|
[/mkfs\./, "Filesystem format"],
|
|
86
91
|
[/dd\s+if=.*of=\/dev\//, "Raw disk write"],
|
|
87
92
|
[/>\s*\/dev\/sd/, "Direct device write"],
|
|
@@ -232,6 +237,28 @@ function managedReasonInSegment(segment: string): string | null {
|
|
|
232
237
|
const READ_ONLY_COMMANDS =
|
|
233
238
|
/^\s*(?:cat|head|tail|less|more|grep|rg|wc|diff|stat|file|ls|dir|find|git\s+(?:log|diff|blame|show|status)|bat)\b/;
|
|
234
239
|
|
|
240
|
+
const COMMAND_SEPARATORS = new Set(["|", ";", "&"]);
|
|
241
|
+
|
|
242
|
+
function unquotedSegments(cmd: string): string[] {
|
|
243
|
+
const segments: string[] = [];
|
|
244
|
+
let current = "";
|
|
245
|
+
let quote: string | null = null;
|
|
246
|
+
for (const char of cmd) {
|
|
247
|
+
if (quote) {
|
|
248
|
+
if (char === quote) quote = null;
|
|
249
|
+
} else if (char === "'" || char === '"') {
|
|
250
|
+
quote = char;
|
|
251
|
+
} else if (COMMAND_SEPARATORS.has(char)) {
|
|
252
|
+
segments.push(current.trim());
|
|
253
|
+
current = "";
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
current += char;
|
|
257
|
+
}
|
|
258
|
+
segments.push(current.trim());
|
|
259
|
+
return segments;
|
|
260
|
+
}
|
|
261
|
+
|
|
235
262
|
/** Check a bash command against blocked patterns. Returns reason string or null. */
|
|
236
263
|
export function checkBashCommand(cmd: string): string | null {
|
|
237
264
|
for (const [pattern, reason] of BLOCKED_COMMANDS) {
|
|
@@ -242,8 +269,7 @@ export function checkBashCommand(cmd: string): string | null {
|
|
|
242
269
|
if (pattern.test(cmd)) return reason;
|
|
243
270
|
}
|
|
244
271
|
}
|
|
245
|
-
const
|
|
246
|
-
for (const segment of segments) {
|
|
272
|
+
for (const segment of unquotedSegments(cmd)) {
|
|
247
273
|
const reason = managedReasonInSegment(segment);
|
|
248
274
|
if (reason && !READ_ONLY_COMMANDS.test(segment)) return reason;
|
|
249
275
|
}
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { existsSync, readFileSync } from "node:fs";
|
|
10
|
-
import { resolve } from "node:path";
|
|
10
|
+
import { basename, resolve } from "node:path";
|
|
11
11
|
import { type FailureEntry, readFailures } from "./learning-store";
|
|
12
12
|
import { loadOpinionContext } from "./opinions";
|
|
13
13
|
import { palHome, paths } from "./paths";
|
|
14
|
+
import { type ProjectProgress, readAllProjects, resolveProjectFromCwd } from "./projects";
|
|
14
15
|
import { readFramePrinciples } from "./wisdom";
|
|
15
16
|
|
|
16
17
|
/** A single semi-static context source — built at session stop, loaded natively at session start. */
|
|
@@ -67,6 +68,12 @@ export function rankFailures(
|
|
|
67
68
|
.slice(0, limit);
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
function projectTag(cwd: string, projects: ProjectProgress[]): string {
|
|
72
|
+
if (!cwd) return "";
|
|
73
|
+
const name = resolveProjectFromCwd(cwd, projects)?.name ?? basename(cwd);
|
|
74
|
+
return `[${name}]`;
|
|
75
|
+
}
|
|
76
|
+
|
|
70
77
|
/** Build the failure avoid-list, prioritized by relevance to the current project. */
|
|
71
78
|
export function loadFailurePatterns(): string {
|
|
72
79
|
try {
|
|
@@ -74,11 +81,11 @@ export function loadFailurePatterns(): string {
|
|
|
74
81
|
const all = readFailures(paths.failures());
|
|
75
82
|
if (all.length === 0) return "";
|
|
76
83
|
|
|
84
|
+
const projects = readAllProjects();
|
|
77
85
|
const lines = rankFailures(all, cwd).map((e) => {
|
|
78
86
|
const label = e.rating ? `[${e.rating}/10]` : "";
|
|
79
|
-
const tag = e.cwd === cwd ? "[project]" : "[other]";
|
|
80
87
|
const text = e.principle || e.context;
|
|
81
|
-
return
|
|
88
|
+
return ["-", label, projectTag(e.cwd, projects), text].filter(Boolean).join(" ");
|
|
82
89
|
});
|
|
83
90
|
|
|
84
91
|
return ["## Lessons from Recent Failures — Apply These Now", ...lines].join("\n");
|
|
@@ -54,28 +54,27 @@ const IDENTITY_DEFAULTS: Identity = {
|
|
|
54
54
|
|
|
55
55
|
// ── Singleton ──
|
|
56
56
|
|
|
57
|
-
let cached: PalSettingsData | null = null;
|
|
57
|
+
let cached: { path: string; data: PalSettingsData } | null = null;
|
|
58
58
|
|
|
59
59
|
function settingsPath(): string {
|
|
60
60
|
return resolve(paths.memory(), "pal-settings.json");
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function
|
|
64
|
-
if (
|
|
65
|
-
const p = settingsPath();
|
|
66
|
-
if (!existsSync(p)) {
|
|
67
|
-
cached = {};
|
|
68
|
-
return cached;
|
|
69
|
-
}
|
|
63
|
+
function readSettingsFile(p: string): PalSettingsData {
|
|
64
|
+
if (!existsSync(p)) return {};
|
|
70
65
|
try {
|
|
71
|
-
|
|
72
|
-
return cached;
|
|
66
|
+
return JSON.parse(readFileSync(p, "utf-8")) as PalSettingsData;
|
|
73
67
|
} catch {
|
|
74
|
-
|
|
75
|
-
return cached;
|
|
68
|
+
return {};
|
|
76
69
|
}
|
|
77
70
|
}
|
|
78
71
|
|
|
72
|
+
function load(): PalSettingsData {
|
|
73
|
+
const p = settingsPath();
|
|
74
|
+
if (cached?.path !== p) cached = { path: p, data: readSettingsFile(p) };
|
|
75
|
+
return cached.data;
|
|
76
|
+
}
|
|
77
|
+
|
|
79
78
|
/** Force re-read from disk (useful after writes) */
|
|
80
79
|
export function reload(): PalSettingsData {
|
|
81
80
|
cached = null;
|
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
* - Generate a summary report
|
|
9
9
|
*
|
|
10
10
|
* Usage:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* pal cli relationship-reflect # Reflect on last 7 days
|
|
12
|
+
* pal cli relationship-reflect --month # Reflect on last 30 days
|
|
13
|
+
* pal cli relationship-reflect --dry-run # Preview without writing
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
17
17
|
import { resolve } from "node:path";
|
|
18
18
|
import { parseArgs } from "node:util";
|
|
19
|
-
import { readOpinions, saveOpinion, setLastReflectDate } from "
|
|
20
|
-
import { palHome } from "
|
|
21
|
-
import { emit } from "
|
|
19
|
+
import { readOpinions, saveOpinion, setLastReflectDate } from "../../hooks/lib/opinions";
|
|
20
|
+
import { palHome } from "../../hooks/lib/paths";
|
|
21
|
+
import { emit } from "../lib/emit";
|
|
22
22
|
import {
|
|
23
23
|
consoleLines,
|
|
24
24
|
formatReport,
|
|
@@ -27,7 +27,8 @@ import {
|
|
|
27
27
|
loadRatings,
|
|
28
28
|
planPromotions,
|
|
29
29
|
reportPath,
|
|
30
|
-
} from "
|
|
30
|
+
} from "../lib/relationship-reflect";
|
|
31
|
+
import { scriptArgs } from "../lib/script-args";
|
|
31
32
|
|
|
32
33
|
const HELP = `
|
|
33
34
|
RelationshipReflect — Periodic reflection + opinion promotion
|
|
@@ -36,9 +37,9 @@ Reads recent relationship notes and ratings. Promotes recurring
|
|
|
36
37
|
observations (O type) into tracked opinions with confidence scoring.
|
|
37
38
|
|
|
38
39
|
Usage:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
pal cli relationship-reflect Reflect on last 7 days (default)
|
|
41
|
+
pal cli relationship-reflect --month Reflect on last 30 days
|
|
42
|
+
pal cli relationship-reflect --dry-run Preview without writing
|
|
42
43
|
|
|
43
44
|
Output:
|
|
44
45
|
- Updates memory/relationship/opinions.json (confidence tracking)
|
|
@@ -56,9 +57,9 @@ function saveReport(report: string, period: string): string {
|
|
|
56
57
|
return filepath;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
function run() {
|
|
60
|
+
export function run(argv: string[] = scriptArgs()) {
|
|
60
61
|
const { values } = parseArgs({
|
|
61
|
-
args:
|
|
62
|
+
args: argv,
|
|
62
63
|
options: {
|
|
63
64
|
month: { type: "boolean" },
|
|
64
65
|
"dry-run": { type: "boolean" },
|