@tinoy/pi-canon 0.3.0 → 0.5.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/README.md +2 -1
- package/index.ts +22 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,8 @@ pi install npm:@tinoy/pi-canon
|
|
|
16
16
|
- **Tools** — `canon_add`, `canon_remove`, `canon_edit`, `canon_category`.
|
|
17
17
|
- **Commands** — `/canon` (list, add, remove, edit, category management) and `/canon-dump`.
|
|
18
18
|
- **Injection** — the block is appended at `before_agent_start` and re-normalized on every provider request, so a run started by an injected message carries the same bytes as an interactive prompt.
|
|
19
|
-
- **
|
|
19
|
+
- **Category headings** — a category sub-heading inside a scope group prints the store id after the word `category` (`#### Behavioural Preferences [category 1cg5lr]`), which is the same id a `canon_add` refusal lists; the word keeps it from reading as an entry handle, which the block renders as `[1i15c2]`. Uncategorized entries carry no id, and a scope group holding a single category stays flat with no sub-heading.
|
|
20
|
+
- **Peer notices** — entry changes are published over the `ipc` transport's bus (namespace `canon`); receivers match the entry scope against their own model and audience.
|
|
20
21
|
- **Tail sections** — another extension contributes prompt text through the `canon:section` event; its ids are published on `canon:sections`.
|
|
21
22
|
|
|
22
23
|
## Exports
|
package/index.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* changes via canon notices (and the user's /canon-dump).
|
|
15
15
|
* Edited at runtime via canon_add / canon_remove / canon_edit (plus
|
|
16
16
|
* the /canon and /canon-dump commands). Changes are broadcast to peer sessions
|
|
17
|
-
* over the
|
|
17
|
+
* over the `ipc` transport's extension bus (namespace "canon"); each receiver matches
|
|
18
18
|
* the entry scope against its own model + audience before showing a notice.
|
|
19
19
|
*
|
|
20
20
|
* Replaces APPEND_SYSTEM.md (global/parent) and FLASH.md (global/subagent).
|
|
@@ -31,11 +31,13 @@
|
|
|
31
31
|
*
|
|
32
32
|
* Store: ~/.pi/agent/canon/canon.json — { entries: [{id, text, model, audience, reason?, category?}], categories: [{id, title, description?}] }
|
|
33
33
|
* Categories are un-ordered; entries reference them by id. They render as sub-headings
|
|
34
|
-
* inside scope groups (store insertion order), with uncategorized entries last
|
|
34
|
+
* inside scope groups (store insertion order), with uncategorized entries last, and
|
|
35
|
+
* each heading carries the store's own id after the word `category`
|
|
36
|
+
* (`#### Behavioural Preferences [category 1cg5lr]`) — the word keeps the category id
|
|
37
|
+
* from reading as an entry handle, which the block renders as `[1i15c2]`.
|
|
35
38
|
* canon_add REQUIRES a category and canon_edit can only CHANGE one: every refusal
|
|
36
|
-
* lists the valid ids with their titles, so a retry costs no lookup call. The
|
|
37
|
-
*
|
|
38
|
-
* reaches the model. The store validator counts entries that are uncategorized or
|
|
39
|
+
* lists the valid ids with their titles, so a retry costs no lookup call. The store
|
|
40
|
+
* validator counts entries that are uncategorized or
|
|
39
41
|
* carry a category id the store no longer holds (both render as Uncategorized) and
|
|
40
42
|
* reports the count to the hook log once per distinct state.
|
|
41
43
|
* Both the injected block and /canon-dump group entries under scope headers (model ×
|
|
@@ -339,7 +341,8 @@ function scopeGroups(entries: CanonEntry[]): ScopeGroup[] {
|
|
|
339
341
|
);
|
|
340
342
|
}
|
|
341
343
|
|
|
342
|
-
/** Render the injected block: header, scope groups, category sub-headings inside
|
|
344
|
+
/** Render the injected block: header, scope groups, category sub-headings inside, each
|
|
345
|
+
* naming its store category id. */
|
|
343
346
|
function render(
|
|
344
347
|
store: Store,
|
|
345
348
|
model: string,
|
|
@@ -367,7 +370,7 @@ function render(
|
|
|
367
370
|
// only show category sub-headings when a scope group actually splits
|
|
368
371
|
const showCatHeadings = orderedCats.length + (uncat.length ? 1 : 0) >= 2;
|
|
369
372
|
for (const c of orderedCats) {
|
|
370
|
-
if (showCatHeadings) block += `\n\n#### ${c.title}`;
|
|
373
|
+
if (showCatHeadings) block += `\n\n#### ${c.title} [category ${c.id}]`;
|
|
371
374
|
block += `\n${buckets
|
|
372
375
|
.get(c.id)!
|
|
373
376
|
.map((e) => `[${e.id}] ${e.text}`)
|
|
@@ -417,11 +420,11 @@ function renderDump(
|
|
|
417
420
|
/**
|
|
418
421
|
* Resolve a category reference to its id.
|
|
419
422
|
*
|
|
420
|
-
* The
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
*
|
|
423
|
+
* The block prints each heading as its title followed by the store id
|
|
424
|
+
* (`#### <title> [category <id>]`), and a model that copies the heading copies the
|
|
425
|
+
* title most of the time, so both forms are accepted: an exact id wins, a title
|
|
426
|
+
* matches case-insensitively, and anything else fails with a short reason that the
|
|
427
|
+
* caller turns into a refusal.
|
|
425
428
|
*/
|
|
426
429
|
function lookupCategory(
|
|
427
430
|
store: Store,
|
|
@@ -447,10 +450,8 @@ function lookupCategory(
|
|
|
447
450
|
|
|
448
451
|
/**
|
|
449
452
|
* The refusal for every rejected category value: what failed, then every valid id
|
|
450
|
-
* with its title
|
|
451
|
-
*
|
|
452
|
-
* context, and the listing is the only place a category id is ever shown (the
|
|
453
|
-
* injected block renders titles).
|
|
453
|
+
* with its title, then the one instruction that answers it. Terse by construction —
|
|
454
|
+
* it lands in a model's context.
|
|
454
455
|
*/
|
|
455
456
|
function categoryRefusal(
|
|
456
457
|
store: Store,
|
|
@@ -713,8 +714,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
713
714
|
|
|
714
715
|
// Best-effort peer notice: the store write has already landed when this runs,
|
|
715
716
|
// so EVERY failure mode of the channel call is swallowed here and logged:
|
|
716
|
-
// -
|
|
717
|
-
//
|
|
717
|
+
// - the channel's publish may throw SYNCHRONOUSLY when the transport is
|
|
718
|
+
// down, and returns void, so there is no promise to catch;
|
|
718
719
|
// - it returns void, so .catch must only ever be reached through the
|
|
719
720
|
// optional call (chaining it directly was the TypeError that escaped);
|
|
720
721
|
// - a promise-returning build rejects instead of throwing — the guarded
|
|
@@ -747,7 +748,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
747
748
|
}
|
|
748
749
|
}
|
|
749
750
|
|
|
750
|
-
// ----------
|
|
751
|
+
// ---------- the bus channel (mirrors the ipc package's registration) ----------
|
|
751
752
|
|
|
752
753
|
const registration: CanonRegistration = {
|
|
753
754
|
namespace: NAMESPACE,
|
|
@@ -792,7 +793,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
792
793
|
pi.events.emit("intercom:extension-register", registration);
|
|
793
794
|
}
|
|
794
795
|
|
|
795
|
-
//
|
|
796
|
+
// The registrar may load after this extension; re-emit once the registry is
|
|
796
797
|
// reported ready. First successful registration wins (duplicate namespace
|
|
797
798
|
// is rejected, not thrown).
|
|
798
799
|
pi.events.on("intercom:extension-registry-ready", () => {
|
|
@@ -1243,7 +1244,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1243
1244
|
name: "canon_category",
|
|
1244
1245
|
label: "Manage canon categories",
|
|
1245
1246
|
description:
|
|
1246
|
-
"Manage canon categories: op add (title, description?) creates one; op edit (id, title?, description?) updates it; op remove (id) deletes it and detaches its entries to Uncategorized; op list shows all. Categories are un-ordered and render as sub-headings inside scope groups in store insertion order.",
|
|
1247
|
+
"Manage canon categories: op add (title, description?) creates one; op edit (id, title?, description?) updates it; op remove (id) deletes it and detaches its entries to Uncategorized; op list shows all. Categories are un-ordered and render as sub-headings inside scope groups in store insertion order, each heading naming the category id (`#### Tools [category u3q9pr]`).",
|
|
1247
1248
|
promptSnippet: "Manage canon categories (add/edit/remove/list)",
|
|
1248
1249
|
parameters: Type.Object({
|
|
1249
1250
|
op: Type.String({ description: "add | edit | remove | list" }),
|