portable-agent-layer 0.85.2 → 0.85.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/assets/schema/pal-settings.schema.json +4 -0
- package/assets/templates/pal-settings.json +1 -0
- package/package.json +1 -1
- package/src/hooks/handlers/inject-retrieval.ts +2 -0
- package/src/hooks/lib/context.ts +3 -14
- package/src/hooks/lib/daily-nudge.ts +49 -6
- package/src/hooks/lib/settings.ts +5 -0
- package/src/hooks/lib/stop.ts +4 -1
|
@@ -70,6 +70,10 @@
|
|
|
70
70
|
"type": "boolean",
|
|
71
71
|
"description": "One line of the current time with every prompt, in identity.principal.timezone, so a session resumed hours later knows the day changed."
|
|
72
72
|
},
|
|
73
|
+
"dueReminders": {
|
|
74
|
+
"type": "boolean",
|
|
75
|
+
"description": "Opt-in, off by default until 1.0. Due maintenance reminders (learning analysis, relationship reflect) with every prompt until a reply names their command; then quiet until the next day."
|
|
76
|
+
},
|
|
73
77
|
"interactionAwareness": {
|
|
74
78
|
"type": "boolean",
|
|
75
79
|
"description": "Measure each user turn (pace, message length, skimming, corrections; never the text) into memory/signals/interaction/, and tell the agent when the session's pattern changes."
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { promptContextResponse } from "../lib/agent";
|
|
11
|
+
import { dueNudgeReminder } from "../lib/daily-nudge";
|
|
11
12
|
import { observeTurn } from "../lib/interaction";
|
|
12
13
|
import { logDebug, logError } from "../lib/log";
|
|
13
14
|
import { parkPromptContext } from "../lib/parked-context";
|
|
@@ -81,6 +82,7 @@ export async function getPromptContext(
|
|
|
81
82
|
const parts = [
|
|
82
83
|
getWallClockReminder(),
|
|
83
84
|
withinBudget(() => observeTurn(prompt, sessionId), BUDGET_MS),
|
|
85
|
+
withinBudget(() => dueNudgeReminder(), BUDGET_MS),
|
|
84
86
|
getSteeringReminder(prompt),
|
|
85
87
|
getSkillReminder(prompt),
|
|
86
88
|
await getRetrievalReminder(prompt),
|
package/src/hooks/lib/context.ts
CHANGED
|
@@ -5,16 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
import { existsSync, readFileSync } from "node:fs";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
|
-
import { loadReflectNudge } from "../handlers/reflect-trigger";
|
|
9
|
-
import { loadAlgorithmReviewNudge } from "./algorithm-review";
|
|
10
|
-
import { loadAnalyzeNudge } from "./analyze-nudge";
|
|
11
8
|
import { resolveAnchor } from "./anchor";
|
|
12
|
-
import {
|
|
9
|
+
import { dueNudgeSections } from "./daily-nudge";
|
|
13
10
|
import { loadHandoffContext } from "./handoff-context";
|
|
14
11
|
import { loadOpinionContext } from "./opinions";
|
|
15
12
|
import { paths, toPath } from "./paths";
|
|
16
13
|
import { loadActiveProjectsContext } from "./projects";
|
|
17
|
-
import { loadReactionAuditNudge } from "./reaction-audit";
|
|
18
14
|
import { loadRecentNotes } from "./relationship";
|
|
19
15
|
import { loadFailurePatterns } from "./semi-static";
|
|
20
16
|
import { sessionDir } from "./session-dir";
|
|
@@ -219,18 +215,11 @@ export function buildSystemReminder(
|
|
|
219
215
|
!opts.withoutHandoff && settings.isEnabled("handoff")
|
|
220
216
|
? loadHandoffContext(sessionDir())
|
|
221
217
|
: "";
|
|
222
|
-
|
|
223
|
-
const algoReview = oncePerDay("algorithm-review", loadAlgorithmReviewNudge());
|
|
224
|
-
const reactionAudit = oncePerDay("reaction-audit", loadReactionAuditNudge());
|
|
225
|
-
const reflectNudge = oncePerDay("reflect", loadReflectNudge());
|
|
226
|
-
const analyzeNudge = oncePerDay("analyze", loadAnalyzeNudge());
|
|
218
|
+
const dueNudges = opts.agent === "cursor" ? dueNudgeSections() : [];
|
|
227
219
|
const parts: string[] = [];
|
|
228
220
|
if (startup) parts.push(startup);
|
|
229
221
|
if (handoff) parts.push(handoff);
|
|
230
|
-
|
|
231
|
-
if (reactionAudit) parts.push(reactionAudit);
|
|
232
|
-
if (reflectNudge) parts.push(reflectNudge);
|
|
233
|
-
if (analyzeNudge) parts.push(analyzeNudge);
|
|
222
|
+
parts.push(...dueNudges);
|
|
234
223
|
if (selfModel) parts.push(selfModel);
|
|
235
224
|
if (wisdom) parts.push(wisdom);
|
|
236
225
|
if (opinions) parts.push(opinions);
|
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
+
import { loadReflectNudge } from "../handlers/reflect-trigger";
|
|
4
|
+
import { loadAlgorithmReviewNudge } from "./algorithm-review";
|
|
5
|
+
import { loadAnalyzeNudge } from "./analyze-nudge";
|
|
3
6
|
import { ensureDir, paths } from "./paths";
|
|
7
|
+
import { loadReactionAuditNudge } from "./reaction-audit";
|
|
8
|
+
import { isOptedIn } from "./settings";
|
|
4
9
|
import { localDay } from "./wall-clock";
|
|
5
10
|
|
|
6
11
|
type ShownOn = Record<string, string>;
|
|
7
12
|
|
|
13
|
+
interface DueNudge {
|
|
14
|
+
key: string;
|
|
15
|
+
command: string;
|
|
16
|
+
load: () => string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DUE_NUDGES: DueNudge[] = [
|
|
20
|
+
{
|
|
21
|
+
key: "algorithm-review",
|
|
22
|
+
command: "/algorithm-update",
|
|
23
|
+
load: loadAlgorithmReviewNudge,
|
|
24
|
+
},
|
|
25
|
+
{ key: "reaction-audit", command: "/reaction-audit", load: loadReactionAuditNudge },
|
|
26
|
+
{ key: "reflect", command: "/pal-reflect", load: loadReflectNudge },
|
|
27
|
+
{ key: "analyze", command: "/pal-analyze", load: loadAnalyzeNudge },
|
|
28
|
+
];
|
|
29
|
+
|
|
8
30
|
function shownPath(): string {
|
|
9
31
|
return resolve(ensureDir(paths.state()), "nudges-shown.json");
|
|
10
32
|
}
|
|
@@ -18,18 +40,39 @@ function readShownOn(): ShownOn {
|
|
|
18
40
|
}
|
|
19
41
|
|
|
20
42
|
/**
|
|
21
|
-
* A reminder asks the user for something, so it
|
|
22
|
-
*
|
|
43
|
+
* A reminder asks the user for something, so it stays until a reply has passed
|
|
44
|
+
* it on, then rests for the rest of the day.
|
|
23
45
|
*/
|
|
24
|
-
export function
|
|
46
|
+
export function pendingToday(
|
|
25
47
|
key: string,
|
|
26
48
|
reminder: string,
|
|
27
49
|
now: Date = new Date()
|
|
28
50
|
): string {
|
|
29
51
|
if (!reminder) return "";
|
|
52
|
+
return readShownOn()[key] === localDay(now) ? "" : reminder;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function acknowledgeMentioned(reply: string, now: Date = new Date()): void {
|
|
56
|
+
const mentioned = DUE_NUDGES.filter((nudge) => reply.includes(nudge.command));
|
|
57
|
+
if (!mentioned.length) return;
|
|
30
58
|
const today = localDay(now);
|
|
31
59
|
const shownOn = readShownOn();
|
|
32
|
-
|
|
33
|
-
writeFileSync(shownPath(), JSON.stringify(
|
|
34
|
-
|
|
60
|
+
for (const nudge of mentioned) shownOn[nudge.key] = today;
|
|
61
|
+
writeFileSync(shownPath(), JSON.stringify(shownOn), "utf-8");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function dueNudgeSections(now: Date = new Date()): string[] {
|
|
65
|
+
if (!isOptedIn("dueReminders")) return [];
|
|
66
|
+
return DUE_NUDGES.map((nudge) => pendingToday(nudge.key, nudge.load(), now)).filter(
|
|
67
|
+
Boolean
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function dueNudgeReminder(now: Date = new Date()): string | null {
|
|
72
|
+
const sections = dueNudgeSections(now);
|
|
73
|
+
if (!sections.length) return null;
|
|
74
|
+
return [
|
|
75
|
+
"Tell the user in one line at the end of this reply that the following is due, naming its command. This comes back every turn until a reply does.",
|
|
76
|
+
...sections,
|
|
77
|
+
].join("\n\n");
|
|
35
78
|
}
|
|
@@ -115,6 +115,11 @@ export function isEnabled(key: string): boolean {
|
|
|
115
115
|
return load().dynamicContext?.[key] !== false;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/** Check if an opt-in dynamic context section is enabled (defaults to false) */
|
|
119
|
+
export function isOptedIn(key: string): boolean {
|
|
120
|
+
return load().dynamicContext?.[key] === true;
|
|
121
|
+
}
|
|
122
|
+
|
|
118
123
|
/** Get the loadAtStartup file list */
|
|
119
124
|
export function startupFiles(): string[] {
|
|
120
125
|
return load().loadAtStartup?.files ?? [];
|
package/src/hooks/lib/stop.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { runSynthesis } from "../handlers/synthesis";
|
|
|
19
19
|
import { resetTab } from "../handlers/tab";
|
|
20
20
|
import { updateCounts } from "../handlers/update-counts";
|
|
21
21
|
import { captureWorkSession } from "../handlers/work-session";
|
|
22
|
+
import { acknowledgeMentioned } from "./daily-nudge";
|
|
22
23
|
import { spawnDetachedInference } from "./detached-inference";
|
|
23
24
|
import {
|
|
24
25
|
fileFinalReply,
|
|
@@ -274,7 +275,9 @@ function recordFinalReply(
|
|
|
274
275
|
sessionId: string
|
|
275
276
|
): void {
|
|
276
277
|
try {
|
|
277
|
-
|
|
278
|
+
const reply = extractContent(extractLastAssistant(messages));
|
|
279
|
+
recordReply(sessionId, reply);
|
|
280
|
+
acknowledgeMentioned(reply);
|
|
278
281
|
} catch (err) {
|
|
279
282
|
logError("runStopHandlers:recordReply", err);
|
|
280
283
|
}
|