portable-agent-layer 0.45.0 → 0.45.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "portable-agent-layer",
3
- "version": "0.45.0",
3
+ "version": "0.45.1",
4
4
  "description": "PAL — Portable Agent Layer: persistent personal context for AI coding assistants",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,27 +1,26 @@
1
1
  /**
2
2
  * Auto-trigger for self-model synthesis — runs daily.
3
- * writeSelfModel has a 24h TTL guard, so this is safe to call every session.
4
- * Respects dynamicContext.selfModel if disabled, skips generation entirely.
3
+ *
4
+ * Spawns synthesis as a DETACHED child instead of awaiting it inline. A cold
5
+ * `claude -p` Sonnet spawn routinely exceeds a minute; awaiting it inside the
6
+ * Stop hook forced a tight 30s timeout that killed every synthesis and fell
7
+ * back to a raw data dump. Detaching lets self-model.ts use a real 90s budget
8
+ * without blocking session end. self-model.ts carries its own 24h TTL guard,
9
+ * so this is safe to call every session. Respects dynamicContext.selfModel.
5
10
  */
6
11
 
7
- import { writeSelfModel } from "../../tools/self-model";
12
+ import { resolve } from "node:path";
13
+ import { spawnDetachedInference } from "../lib/detached-inference";
8
14
  import { logDebug } from "../lib/log";
15
+ import { assets } from "../lib/paths";
9
16
  import { isEnabled } from "../lib/settings";
10
17
 
11
- export async function checkSelfModelTrigger(): Promise<void> {
18
+ export function checkSelfModelTrigger(): void {
12
19
  if (!isEnabled("selfModel")) {
13
20
  logDebug("self-model-trigger", "Disabled in pal-settings.json");
14
21
  return;
15
22
  }
16
23
 
17
- try {
18
- const result = await writeSelfModel(30);
19
- if (result.skipped) {
20
- logDebug("self-model-trigger", "Skipped — last synthesis < 24h ago");
21
- } else {
22
- logDebug("self-model-trigger", `Self-model written: ${result.path}`);
23
- }
24
- } catch {
25
- // Non-critical — self-model is best-effort
26
- }
24
+ const scriptPath = resolve(assets.tools(), "self-model.ts");
25
+ spawnDetachedInference(scriptPath, [], "self-model");
27
26
  }
@@ -83,5 +83,6 @@ export const assets = {
83
83
  codexHooksTemplate: () => pkg("assets", "templates", "hooks.codex.json"),
84
84
  codexRulesTemplate: () => pkg("assets", "templates", "rules.codex.rules"),
85
85
  agentTools: () => pkg("src", "tools", "agent"),
86
+ tools: () => pkg("src", "tools"),
86
87
  palDocs: () => pkg("assets", "templates", "PAL"),
87
88
  } as const;
@@ -47,11 +47,12 @@ export async function runStopHandlers(
47
47
  // Detach inference-bearing handlers — claude --print cold-start can exceed
48
48
  // any in-hook budget. These spawn detached bun subprocesses that run the
49
49
  // inference and write results to disk; they don't block this hook.
50
- // autoGraduate is idempotent (24h TTL + state-dedup + content-dedup), so
51
- // concurrent or overlapping detached runs are safe.
52
50
  await detachSessionIntelligence(transcript, options.sessionId);
53
51
  await detachFailurePrinciple(transcript);
54
- detachAutoGraduate();
52
+ // Failure auto-graduation is intentionally NOT wired here: every pattern it
53
+ // ever promoted was a frustration log, not a principle. Wisdom frames are
54
+ // populated by Claude in-conversation (see wisdom.ts header). The handler
55
+ // (handlers/auto-graduate.ts) remains runnable manually via `--run`.
55
56
 
56
57
  // Run remaining (non-inference) handlers concurrently.
57
58
  // project-touch only fires when cwd resolves to an active registered project.
@@ -177,16 +178,6 @@ async function detachSessionIntelligence(
177
178
  }
178
179
  }
179
180
 
180
- /** Spawn a detached child to run the full autoGraduate cycle. */
181
- function detachAutoGraduate(): void {
182
- try {
183
- const scriptPath = resolve(assets.hooks(), "handlers", "auto-graduate.ts");
184
- spawnDetachedInference(scriptPath, ["--run"], "auto-graduate");
185
- } catch (err) {
186
- logError("detachAutoGraduate", err);
187
- }
188
- }
189
-
190
181
  /**
191
182
  * If a pending-failure exists, rename it to a unique path (race-free claim),
192
183
  * write the transcript to tmp, spawn the failure-principle handler detached.
@@ -524,7 +524,11 @@ async function composeSelfModel(days: number): Promise<string> {
524
524
  const currentPath = selfModelPath();
525
525
  if (existsSync(currentPath)) {
526
526
  try {
527
- previousModel = readFileSync(currentPath, "utf-8");
527
+ const prev = readFileSync(currentPath, "utf-8");
528
+ // Never feed a failed-synthesis fallback back in — it is a raw data dump,
529
+ // not a model. Doing so bloats the prompt and drives the next run into the
530
+ // same timeout, a self-reinforcing failure loop. Skip it and synthesize fresh.
531
+ if (!prev.includes("Synthesis failed — raw data below")) previousModel = prev;
528
532
  } catch {
529
533
  /* best effort */
530
534
  }
@@ -539,7 +543,7 @@ async function composeSelfModel(days: number): Promise<string> {
539
543
  user: userContent,
540
544
  model: SONNET_MODEL,
541
545
  maxTokens: 1500,
542
- timeout: 30000,
546
+ timeout: 90000,
543
547
  caller: "self-model",
544
548
  });
545
549
 
@@ -565,7 +569,7 @@ async function composeSelfModel(days: number): Promise<string> {
565
569
 
566
570
  // ── Write ──
567
571
 
568
- export async function writeSelfModel(
572
+ async function writeSelfModel(
569
573
  days: number,
570
574
  force = false
571
575
  ): Promise<{ path: string; content: string; skipped?: boolean }> {