@llblab/pi-actors 0.16.3 → 0.16.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.4: Recipe Usage Fingerprints
4
+
5
+ - `[Recipe Usage]` Added content fingerprints to user recipe usage metadata. Impact: when a recipe file is edited and its authored meaning changes, the next launch resets `usage.calls`, records `usage.reset_at`, and starts counting usage for the current recipe content.
6
+ - `[Docs]` Documented fingerprint-backed usage reset semantics in the template recipe and tool registry docs.
7
+ - `[Package]` Bumped package and packaged skill metadata to `0.16.4` for the hotfix release.
8
+
3
9
  ## 0.16.3: Recipe Import Path Placeholders
4
10
 
5
11
  - `[Template Recipes]` Added static `{repo}` and `{agent}` expansion for recipe paths, including `imports` and `from` bindings. Impact: recipes can import sibling packaged/user recipes without hard-coded absolute paths while keeping imports load-time deterministic.
@@ -95,7 +95,7 @@ User-owned recipes may accumulate extension-maintained usage metadata:
95
95
  }
96
96
  ```
97
97
 
98
- The extension increments `usage.calls` and updates `usage.last_called` when it starts that concrete recipe, either through a recipe-backed tool call or a direct async recipe-file run. Agents should treat these fields as cleanup evidence, not as authored recipe contract. Packaged standard-library recipes are not mutated for usage metadata.
98
+ The extension increments `usage.calls` and updates `usage.last_called` when it starts that concrete recipe, either through a recipe-backed tool call or a direct async recipe-file run. It also stores a content `usage.fingerprint`; if the authored recipe content changes, the next launch resets `usage.calls` before counting the new launch and records `usage.reset_at`. Agents should treat these fields as cleanup evidence, not as authored recipe contract. Packaged standard-library recipes are not mutated for usage metadata.
99
99
 
100
100
  There is intentionally no failure counter in the recipe contract. A failed launch can reflect caller misuse, missing runtime values, or an environmental problem rather than recipe uselessness. Cleanup decisions should be explicit operator work: keep as a tool, set `tool: false`, merge, delete, or archive.
101
101
 
@@ -17,7 +17,7 @@ The 0.16 registry source is file-discovered recipes, not a live tool-only JSON f
17
17
 
18
18
  `~/.pi/agent/actors-tools.json` is legacy compatibility input. On startup, pi-actors migrates it into recipe files when possible, writes a migration report, and archives the source only when migration has no conflicts or invalid generated recipes.
19
19
 
20
- Because the user recipe directory is sticky agent muscle memory, runtime launches update `usage.calls` and `usage.last_called` on user-owned recipe files. Use that evidence during focused cleanup passes: keep valuable tools, set `tool: false` for useful components that should leave the active tool surface, merge duplicates, or delete/archive low-value files. The extension does not maintain a failure counter and agents should not silently clean tools during unrelated work.
20
+ Because the user recipe directory is sticky agent muscle memory, runtime launches update `usage.calls`, `usage.last_called`, and a content `usage.fingerprint` on user-owned recipe files. If authored recipe content changes, the next launch resets `usage.calls` and records `usage.reset_at` before counting the launch, so usage evidence follows the current recipe meaning rather than an older file history. Use that evidence during focused cleanup passes: keep valuable tools, set `tool: false` for useful components that should leave the active tool surface, merge duplicates, or delete/archive low-value files. The extension does not maintain a failure counter and agents should not silently clean tools during unrelated work.
21
21
 
22
22
  `register_tool` is the preferred agent-facing mutation API. It creates, updates, and deletes recipe files in `~/.pi/agent/recipes`; agents do not need to edit the files directly for normal registration. Direct file edits are still valid for operators and advanced agents. Runtime behavior is reactive: file creation, deletion, or edits in the user recipe root trigger validation and tool-set refresh, with invalid recipes surfaced as diagnostics rather than silently ignored.
23
23
 
@@ -4,6 +4,7 @@
4
4
  * Owns lightweight launch counters for user-owned recipe files
5
5
  */
6
6
 
7
+ import { createHash } from "node:crypto";
7
8
  import { existsSync, readFileSync } from "node:fs";
8
9
 
9
10
  import { writeJsonAtomic } from "./file-state.ts";
@@ -11,6 +12,8 @@ import { writeJsonAtomic } from "./file-state.ts";
11
12
  interface RecipeUsageRecord {
12
13
  calls?: number;
13
14
  last_called?: string;
15
+ fingerprint?: string;
16
+ reset_at?: string;
14
17
  }
15
18
 
16
19
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -23,18 +26,37 @@ function normalizeCalls(value: unknown): number {
23
26
  : 0;
24
27
  }
25
28
 
29
+ function stableStringify(value: unknown): string {
30
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
31
+ if (!isRecord(value)) return JSON.stringify(value);
32
+ return `{${Object.keys(value)
33
+ .sort()
34
+ .map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
35
+ .join(",")}}`;
36
+ }
37
+
38
+ function getRecipeFingerprint(raw: Record<string, unknown>): string {
39
+ const { usage: _usage, ...content } = raw;
40
+ return createHash("sha256").update(stableStringify(content)).digest("hex");
41
+ }
42
+
26
43
  export function recordRecipeLaunch(path: string, now = new Date()): boolean {
27
44
  if (!existsSync(path)) return false;
28
45
  try {
29
46
  const raw = JSON.parse(readFileSync(path, "utf8"));
30
47
  if (!isRecord(raw)) return false;
31
48
  const usage: RecipeUsageRecord = isRecord(raw.usage) ? raw.usage : {};
49
+ const fingerprint = getRecipeFingerprint(raw);
50
+ const changed = typeof usage.fingerprint === "string" && usage.fingerprint !== fingerprint;
51
+ const nowIso = now.toISOString();
32
52
  writeJsonAtomic(path, {
33
53
  ...raw,
34
54
  usage: {
35
55
  ...usage,
36
- calls: normalizeCalls(usage.calls) + 1,
37
- last_called: now.toISOString(),
56
+ calls: (changed ? 0 : normalizeCalls(usage.calls)) + 1,
57
+ last_called: nowIso,
58
+ fingerprint,
59
+ ...(changed ? { reset_at: nowIso } : {}),
38
60
  },
39
61
  });
40
62
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.16.3",
3
+ "version": "0.16.4",
4
4
  "private": false,
5
5
  "description": "Actor runtime and orchestrator for agent-managed local processes",
6
6
  "keywords": [
@@ -2,7 +2,7 @@
2
2
  name: actors
3
3
  description: Highest-density practical guide for pi-actors. Read this skill whenever prompt and tools are not enough for spawn, message, inspect, actor runs, tools, recipes, command templates, async lifecycle, mailboxes, artifacts, and local orchestration mechanics.
4
4
  metadata:
5
- version: 0.16.3
5
+ version: 0.16.4
6
6
  ---
7
7
 
8
8
  # Actors (pi-actors)
@@ -2,7 +2,7 @@
2
2
  name: swarm
3
3
  description: Subagent orchestration with scoped locks and quorum consensus. Use for multi-model review, parallel scoped work, delegated audit, and coordinated subagent execution.
4
4
  metadata:
5
- version: 0.16.3
5
+ version: 0.16.4
6
6
  ---
7
7
 
8
8
  # Swarm