@fingerskier/augment 0.4.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 +37 -20
- package/dist/daemon/client.d.ts +6 -0
- package/dist/daemon/client.js +19 -0
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/http.js +79 -9
- package/dist/daemon/http.js.map +1 -1
- package/dist/decoction.d.ts +51 -0
- package/dist/decoction.js +184 -0
- package/dist/decoction.js.map +1 -0
- package/dist/mcp/server.js +83 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/memory/files.js +21 -2
- package/dist/memory/files.js.map +1 -1
- package/dist/memory/project.d.ts +3 -0
- package/dist/memory/project.js +20 -0
- package/dist/memory/project.js.map +1 -1
- package/dist/pi/extension.d.ts +6 -0
- package/dist/pi/extension.js +54 -0
- package/dist/pi/extension.js.map +1 -1
- package/dist/proposals.js +16 -15
- package/dist/proposals.js.map +1 -1
- package/dist/service.d.ts +134 -2
- package/dist/service.js +578 -51
- package/dist/service.js.map +1 -1
- package/docs/FEATURES.md +159 -16
- package/docs/verify-memory-flow.md +132 -131
- package/integrations/claude/skills/augment-decoction/SKILL.md +36 -0
- package/integrations/claude/skills/augment-dream/SKILL.md +34 -0
- package/integrations/claude/skills/augment-dream/agents/openai.yaml +4 -0
- package/integrations/codex/SKILL.md +33 -0
- package/integrations/pi/skills/augment-decoction/SKILL.md +37 -0
- package/integrations/pi/skills/augment-dream/SKILL.md +36 -0
- package/package.json +3 -2
- package/dist/tally.d.ts +0 -46
- package/dist/tally.js +0 -94
- package/dist/tally.js.map +0 -1
|
@@ -1,131 +1,132 @@
|
|
|
1
|
-
# Verifying the auto-memory flow
|
|
2
|
-
|
|
3
|
-
Augment now *enforces* the memory loop through host lifecycle hooks rather than
|
|
4
|
-
relying on the agent to remember to call the MCP tools:
|
|
5
|
-
|
|
6
|
-
| Event | Hook command | Effect |
|
|
7
|
-
| ------------------ | ---------------------------------- | ------ |
|
|
8
|
-
| `SessionStart` | `augment hook session-start` | Warms the daemon and registers the project. |
|
|
9
|
-
| `UserPromptSubmit` | `augment hook user-prompt-submit` | Recalls relevant memory and injects it as `additionalContext` **before** the model sees the prompt. |
|
|
10
|
-
| `PreToolUse` | `augment hook pre-tool-use` | Recalls **additional** memory keyed to the matched tool action (the edited file / the Bash command) and injects it as `additionalContext`. Matcher-scoped to `Edit\|Write\|Bash` to bound the hot path; injection-only (never blocks, gates, or rewrites a tool call). Uses a raised relevance floor (`min_score` 0.45 — above the 0.4 search default, calibrated on the real model because tool queries are command/path-shaped, not prose) and injects each distinct query at most once per session. |
|
|
11
|
-
| `Stop` | `augment hook stop` | Blocks the turn **once** with a directive to upsert a memory, then allows the stop (loop-guarded per turn). |
|
|
12
|
-
|
|
13
|
-
`PreToolUse` fires before **every matched** tool call, so it is the only hook with a tool-name
|
|
14
|
-
`matcher` (the others fire once per prompt/turn). Two host nuances: on Claude the injected text
|
|
15
|
-
rides alongside the *tool result* on the next model request (it supplements, rather than precedes,
|
|
16
|
-
the action); on Codex builds older than ~0.124.0 `PreToolUse` `additionalContext` is rejected and
|
|
17
|
-
silently dropped (the tool still runs). It is purely additive — `UserPromptSubmit` remains the
|
|
18
|
-
guaranteed memory path on both hosts.
|
|
19
|
-
|
|
20
|
-
The same `hooks/hooks.json` ships in both the Claude Code and Codex plugins
|
|
21
|
-
(Codex copied Claude's event names + stdin/stdout contract), so one set of
|
|
22
|
-
handlers serves both hosts.
|
|
23
|
-
|
|
24
|
-
This doc is the test process. Run the layers top-to-bottom; each is cheaper and
|
|
25
|
-
more isolated than the one below it.
|
|
26
|
-
|
|
27
|
-
## 1. Automated (no network, no host) — `npm test`
|
|
28
|
-
|
|
29
|
-
```sh
|
|
30
|
-
npm test
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Covers:
|
|
34
|
-
- `test/hooks.test.ts` — the stdin→stdout contract of every hook event
|
|
35
|
-
(UserPromptSubmit envelope, PreToolUse tool-query derivation + injection-only
|
|
36
|
-
envelope + 0.45 floor + per-session dedupe, Stop block-once loop guard,
|
|
37
|
-
fail-open paths).
|
|
38
|
-
- `test/recall.test.ts` — recall degrades to `""` on any failure (a hook must
|
|
39
|
-
never break the agent).
|
|
40
|
-
- `test/cli.test.ts` — the `recall` and `hook` CLI commands.
|
|
41
|
-
- `test/install.test.ts` — the installer writes `hooks/hooks.json` for both hosts.
|
|
42
|
-
- `test/integration/memory-flow.test.ts` — a planted memory flows
|
|
43
|
-
upsert → index → `recall()` → UserPromptSubmit **and** PreToolUse envelopes, against a **real**
|
|
44
|
-
`AugmentService` (real store + sqlite index), using the deterministic hash
|
|
45
|
-
embedder. Proves the wiring end-to-end without a model download.
|
|
46
|
-
|
|
47
|
-
## 2. CLI smoke (real daemon + real embeddings, one machine)
|
|
48
|
-
|
|
49
|
-
Build first, then drive the real daemon by hand. Use a throwaway memory root so
|
|
50
|
-
you don't pollute your real memories:
|
|
51
|
-
|
|
52
|
-
```sh
|
|
53
|
-
npm run build
|
|
54
|
-
export AUGMENT_MEMORY_ROOT="$(mktemp -d)" # PowerShell: $env:AUGMENT_MEMORY_ROOT = (New-Item -ItemType Directory -Path $env:TEMP\augver -Force).FullName
|
|
55
|
-
|
|
56
|
-
# Plant a memory (project_id 1 after the first init), then recall it.
|
|
57
|
-
node ./dist/bin/augment.js recall "the deploy pipeline" # prints recalled text (empty until something is stored)
|
|
58
|
-
|
|
59
|
-
# Exercise the hook handlers exactly as a host would (JSON on stdin):
|
|
60
|
-
echo '{"user_input":"fix the deploy pipeline","cwd":"."}' | node ./dist/bin/augment.js hook user-prompt-submit
|
|
61
|
-
# -> {"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"## Augment memory ..."}} (or nothing if no match)
|
|
62
|
-
|
|
63
|
-
# PreToolUse keys the query off the tool action (Bash -> command, Edit/Write -> file_path):
|
|
64
|
-
echo '{"tool_name":"Bash","tool_input":{"command":"deploy the pipeline"},"cwd":"."}' | node ./dist/bin/augment.js hook pre-tool-use
|
|
65
|
-
# -> {"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"## Augment memory ..."}} (or nothing if no match)
|
|
66
|
-
echo '{"tool_name":"Read","tool_input":{"file_path":"x"}}' | node ./dist/bin/augment.js hook pre-tool-use
|
|
67
|
-
# -> (empty) Read isn't in the installed matcher; even if invoked it derives a query but stays injection-only
|
|
68
|
-
|
|
69
|
-
echo '{"session_id":"s","prompt_id":"p"}' | node ./dist/bin/augment.js hook stop
|
|
70
|
-
# -> {"decision":"block","reason":"Before ending this turn, record durable memory ..."}
|
|
71
|
-
echo '{"session_id":"s","prompt_id":"p"}' | node ./dist/bin/augment.js hook stop
|
|
72
|
-
# -> (empty) second stop for the same turn is allowed — proves the loop guard
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
The first `recall`/`hook user-prompt-submit` call auto-starts the daemon and, on
|
|
76
|
-
a cold machine, downloads the bge-small model — so the very first run is slow.
|
|
77
|
-
This is real-model retrieval; ranking quality (not just wiring) is what you're
|
|
78
|
-
eyeballing here.
|
|
79
|
-
|
|
80
|
-
Two footnotes on the examples: they omit `session_id`, which deliberately skips
|
|
81
|
-
the PreToolUse once-per-session dedupe — include one to demo it. And the Stop
|
|
82
|
-
loop-guard marker persists in the OS tmpdir, so re-running the stop example
|
|
83
|
-
later with the same `session_id`/`prompt_id` yields empty on the *first* call
|
|
84
|
-
too — use fresh ids per run.
|
|
85
|
-
|
|
86
|
-
For a binding version of the ranking-quality check, `npm run test:model` runs
|
|
87
|
-
the pinned real-bge abstention/recall tripwires
|
|
88
|
-
(`test/real-model-abstention.model.ts`) — what this layer can otherwise only
|
|
89
|
-
eyeball.
|
|
90
|
-
|
|
91
|
-
## 3. Real project (host-level hook firing)
|
|
92
|
-
|
|
93
|
-
1. Install into a repo (or user scope):
|
|
94
|
-
```sh
|
|
95
|
-
npm exec --yes --package @fingerskier/augment -- augment install all --scope repo --memory-root "<your shared memory root>"
|
|
96
|
-
```
|
|
97
|
-
2. Confirm the plugin is actually installed/enabled — hooks only fire when the
|
|
98
|
-
plugin is active:
|
|
99
|
-
- **Claude Code:** `/plugin` → `augment` listed and enabled. The installer
|
|
100
|
-
best-effort-registers it via the `claude` CLI; if that was skipped, run the
|
|
101
|
-
printed `/plugin marketplace add … && /plugin install …` commands.
|
|
102
|
-
- **Codex:** plugin-shipped hooks need a **one-time trust** — run `/hooks` and
|
|
103
|
-
approve the augment hooks. (Managed/enterprise distribution can pre-trust;
|
|
104
|
-
a plain plugin install cannot.)
|
|
105
|
-
3. Observe the loop on a real task:
|
|
106
|
-
- **Inject:** start a session and give a task that matches something in your
|
|
107
|
-
memory. The recalled block ("## Augment memory (recalled for this task)")
|
|
108
|
-
should appear in the model's context — confirm it influenced the answer.
|
|
109
|
-
- **Capture:** finish the task. The Stop hook should prompt the agent to
|
|
110
|
-
`upsert` a WORK/ARCH/ISSUE/TODO/SPEC memory; confirm a new file appears under
|
|
111
|
-
your memory root and that `augment recall` surfaces it next time.
|
|
112
|
-
|
|
113
|
-
## Known caveats (logged, not blockers)
|
|
114
|
-
|
|
115
|
-
- **Hook startup cost (resolved).** Hooks now invoke the install-time-provisioned
|
|
116
|
-
runtime directly (`node <prefix>/node_modules/@fingerskier/augment/dist/bin/augment.js
|
|
117
|
-
hook <event>`, tens of milliseconds) — the old `npm exec --yes` form paid a
|
|
118
|
-
multi-second npm bootstrap per event. Residual caveat: provisioning is
|
|
119
|
-
best-effort. If npm was missing at install time the installer prints a manual
|
|
120
|
-
`npm install --prefix <prefix> @fingerskier/augment` command and hooks fail
|
|
121
|
-
open (silent no-ops) until it is run. The MCP server itself still launches via
|
|
122
|
-
`npm exec`; only the hook hot path changed.
|
|
123
|
-
- **Claude hook activation depends on plugin install.** The MCP server is also
|
|
124
|
-
direct-registered (so the tools always exist), but the hooks live in the plugin;
|
|
125
|
-
if the plugin isn't installed, the loop degrades to the CLAUDE.md prose guidance.
|
|
126
|
-
- **Retrieval floor calibration (resolved).** The `(cos+1)/2` mis-normalization
|
|
127
|
-
is fixed by an empirically-anchored cosine rescale (`SEMANTIC_COSINE_BASELINE`
|
|
128
|
-
0.33), and abstention is now proven on the real model by `npm run test:model`.
|
|
129
|
-
Remaining retrieval-quality follow-ups (
|
|
130
|
-
HF revision
|
|
131
|
-
|
|
1
|
+
# Verifying the auto-memory flow
|
|
2
|
+
|
|
3
|
+
Augment now *enforces* the memory loop through host lifecycle hooks rather than
|
|
4
|
+
relying on the agent to remember to call the MCP tools:
|
|
5
|
+
|
|
6
|
+
| Event | Hook command | Effect |
|
|
7
|
+
| ------------------ | ---------------------------------- | ------ |
|
|
8
|
+
| `SessionStart` | `augment hook session-start` | Warms the daemon and registers the project. |
|
|
9
|
+
| `UserPromptSubmit` | `augment hook user-prompt-submit` | Recalls relevant memory and injects it as `additionalContext` **before** the model sees the prompt. |
|
|
10
|
+
| `PreToolUse` | `augment hook pre-tool-use` | Recalls **additional** memory keyed to the matched tool action (the edited file / the Bash command) and injects it as `additionalContext`. Matcher-scoped to `Edit\|Write\|Bash` to bound the hot path; injection-only (never blocks, gates, or rewrites a tool call). Uses a raised relevance floor (`min_score` 0.45 — above the 0.4 search default, calibrated on the real model because tool queries are command/path-shaped, not prose) and injects each distinct query at most once per session. |
|
|
11
|
+
| `Stop` | `augment hook stop` | Blocks the turn **once** with a directive to upsert a memory, then allows the stop (loop-guarded per turn). |
|
|
12
|
+
|
|
13
|
+
`PreToolUse` fires before **every matched** tool call, so it is the only hook with a tool-name
|
|
14
|
+
`matcher` (the others fire once per prompt/turn). Two host nuances: on Claude the injected text
|
|
15
|
+
rides alongside the *tool result* on the next model request (it supplements, rather than precedes,
|
|
16
|
+
the action); on Codex builds older than ~0.124.0 `PreToolUse` `additionalContext` is rejected and
|
|
17
|
+
silently dropped (the tool still runs). It is purely additive — `UserPromptSubmit` remains the
|
|
18
|
+
guaranteed memory path on both hosts.
|
|
19
|
+
|
|
20
|
+
The same `hooks/hooks.json` ships in both the Claude Code and Codex plugins
|
|
21
|
+
(Codex copied Claude's event names + stdin/stdout contract), so one set of
|
|
22
|
+
handlers serves both hosts.
|
|
23
|
+
|
|
24
|
+
This doc is the test process. Run the layers top-to-bottom; each is cheaper and
|
|
25
|
+
more isolated than the one below it.
|
|
26
|
+
|
|
27
|
+
## 1. Automated (no network, no host) — `npm test`
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npm test
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Covers:
|
|
34
|
+
- `test/hooks.test.ts` — the stdin→stdout contract of every hook event
|
|
35
|
+
(UserPromptSubmit envelope, PreToolUse tool-query derivation + injection-only
|
|
36
|
+
envelope + 0.45 floor + per-session dedupe, Stop block-once loop guard,
|
|
37
|
+
fail-open paths).
|
|
38
|
+
- `test/recall.test.ts` — recall degrades to `""` on any failure (a hook must
|
|
39
|
+
never break the agent).
|
|
40
|
+
- `test/cli.test.ts` — the `recall` and `hook` CLI commands.
|
|
41
|
+
- `test/install.test.ts` — the installer writes `hooks/hooks.json` for both hosts.
|
|
42
|
+
- `test/integration/memory-flow.test.ts` — a planted memory flows
|
|
43
|
+
upsert → index → `recall()` → UserPromptSubmit **and** PreToolUse envelopes, against a **real**
|
|
44
|
+
`AugmentService` (real store + sqlite index), using the deterministic hash
|
|
45
|
+
embedder. Proves the wiring end-to-end without a model download.
|
|
46
|
+
|
|
47
|
+
## 2. CLI smoke (real daemon + real embeddings, one machine)
|
|
48
|
+
|
|
49
|
+
Build first, then drive the real daemon by hand. Use a throwaway memory root so
|
|
50
|
+
you don't pollute your real memories:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npm run build
|
|
54
|
+
export AUGMENT_MEMORY_ROOT="$(mktemp -d)" # PowerShell: $env:AUGMENT_MEMORY_ROOT = (New-Item -ItemType Directory -Path $env:TEMP\augver -Force).FullName
|
|
55
|
+
|
|
56
|
+
# Plant a memory (project_id 1 after the first init), then recall it.
|
|
57
|
+
node ./dist/bin/augment.js recall "the deploy pipeline" # prints recalled text (empty until something is stored)
|
|
58
|
+
|
|
59
|
+
# Exercise the hook handlers exactly as a host would (JSON on stdin):
|
|
60
|
+
echo '{"user_input":"fix the deploy pipeline","cwd":"."}' | node ./dist/bin/augment.js hook user-prompt-submit
|
|
61
|
+
# -> {"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"## Augment memory ..."}} (or nothing if no match)
|
|
62
|
+
|
|
63
|
+
# PreToolUse keys the query off the tool action (Bash -> command, Edit/Write -> file_path):
|
|
64
|
+
echo '{"tool_name":"Bash","tool_input":{"command":"deploy the pipeline"},"cwd":"."}' | node ./dist/bin/augment.js hook pre-tool-use
|
|
65
|
+
# -> {"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"## Augment memory ..."}} (or nothing if no match)
|
|
66
|
+
echo '{"tool_name":"Read","tool_input":{"file_path":"x"}}' | node ./dist/bin/augment.js hook pre-tool-use
|
|
67
|
+
# -> (empty) Read isn't in the installed matcher; even if invoked it derives a query but stays injection-only
|
|
68
|
+
|
|
69
|
+
echo '{"session_id":"s","prompt_id":"p"}' | node ./dist/bin/augment.js hook stop
|
|
70
|
+
# -> {"decision":"block","reason":"Before ending this turn, record durable memory ..."}
|
|
71
|
+
echo '{"session_id":"s","prompt_id":"p"}' | node ./dist/bin/augment.js hook stop
|
|
72
|
+
# -> (empty) second stop for the same turn is allowed — proves the loop guard
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The first `recall`/`hook user-prompt-submit` call auto-starts the daemon and, on
|
|
76
|
+
a cold machine, downloads the bge-small model — so the very first run is slow.
|
|
77
|
+
This is real-model retrieval; ranking quality (not just wiring) is what you're
|
|
78
|
+
eyeballing here.
|
|
79
|
+
|
|
80
|
+
Two footnotes on the examples: they omit `session_id`, which deliberately skips
|
|
81
|
+
the PreToolUse once-per-session dedupe — include one to demo it. And the Stop
|
|
82
|
+
loop-guard marker persists in the OS tmpdir, so re-running the stop example
|
|
83
|
+
later with the same `session_id`/`prompt_id` yields empty on the *first* call
|
|
84
|
+
too — use fresh ids per run.
|
|
85
|
+
|
|
86
|
+
For a binding version of the ranking-quality check, `npm run test:model` runs
|
|
87
|
+
the pinned real-bge abstention/recall tripwires
|
|
88
|
+
(`test/real-model-abstention.model.ts`) — what this layer can otherwise only
|
|
89
|
+
eyeball.
|
|
90
|
+
|
|
91
|
+
## 3. Real project (host-level hook firing)
|
|
92
|
+
|
|
93
|
+
1. Install into a repo (or user scope):
|
|
94
|
+
```sh
|
|
95
|
+
npm exec --yes --package @fingerskier/augment -- augment install all --scope repo --memory-root "<your shared memory root>"
|
|
96
|
+
```
|
|
97
|
+
2. Confirm the plugin is actually installed/enabled — hooks only fire when the
|
|
98
|
+
plugin is active:
|
|
99
|
+
- **Claude Code:** `/plugin` → `augment` listed and enabled. The installer
|
|
100
|
+
best-effort-registers it via the `claude` CLI; if that was skipped, run the
|
|
101
|
+
printed `/plugin marketplace add … && /plugin install …` commands.
|
|
102
|
+
- **Codex:** plugin-shipped hooks need a **one-time trust** — run `/hooks` and
|
|
103
|
+
approve the augment hooks. (Managed/enterprise distribution can pre-trust;
|
|
104
|
+
a plain plugin install cannot.)
|
|
105
|
+
3. Observe the loop on a real task:
|
|
106
|
+
- **Inject:** start a session and give a task that matches something in your
|
|
107
|
+
memory. The recalled block ("## Augment memory (recalled for this task)")
|
|
108
|
+
should appear in the model's context — confirm it influenced the answer.
|
|
109
|
+
- **Capture:** finish the task. The Stop hook should prompt the agent to
|
|
110
|
+
`upsert` a WORK/ARCH/ISSUE/TODO/SPEC memory; confirm a new file appears under
|
|
111
|
+
your memory root and that `augment recall` surfaces it next time.
|
|
112
|
+
|
|
113
|
+
## Known caveats (logged, not blockers)
|
|
114
|
+
|
|
115
|
+
- **Hook startup cost (resolved).** Hooks now invoke the install-time-provisioned
|
|
116
|
+
runtime directly (`node <prefix>/node_modules/@fingerskier/augment/dist/bin/augment.js
|
|
117
|
+
hook <event>`, tens of milliseconds) — the old `npm exec --yes` form paid a
|
|
118
|
+
multi-second npm bootstrap per event. Residual caveat: provisioning is
|
|
119
|
+
best-effort. If npm was missing at install time the installer prints a manual
|
|
120
|
+
`npm install --prefix <prefix> @fingerskier/augment` command and hooks fail
|
|
121
|
+
open (silent no-ops) until it is run. The MCP server itself still launches via
|
|
122
|
+
`npm exec`; only the hook hot path changed.
|
|
123
|
+
- **Claude hook activation depends on plugin install.** The MCP server is also
|
|
124
|
+
direct-registered (so the tools always exist), but the hooks live in the plugin;
|
|
125
|
+
if the plugin isn't installed, the loop degrades to the CLAUDE.md prose guidance.
|
|
126
|
+
- **Retrieval floor calibration (resolved).** The `(cos+1)/2` mis-normalization
|
|
127
|
+
is fixed by an empirically-anchored cosine rescale (`SEMANTIC_COSINE_BASELINE`
|
|
128
|
+
0.33), and abstention is now proven on the real model by `npm run test:model`.
|
|
129
|
+
Remaining retrieval-quality follow-ups (ordinary-upsert credential warnings
|
|
130
|
+
and HF revision pinning) are tracked in
|
|
131
|
+
`.council/memory/retrieval-quality-roadmap.md` and
|
|
132
|
+
[ROADMAP.md](../ROADMAP.md).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: augment-decoction
|
|
3
|
+
description: Distill recurring cross-project memory into generic SPEC/ARCH records and optionally remove fully subsumed originals. Use when the user asks to "decoct", "glean", generalize, or clean up repeated memories across projects.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Augment Decoction
|
|
7
|
+
|
|
8
|
+
Decoction extracts durable, project-independent knowledge from recurring
|
|
9
|
+
memories. It is separate from sleep: sleep consolidates settled work within one
|
|
10
|
+
project, while decoction finds patterns across projects and writes them under
|
|
11
|
+
`.generic/`.
|
|
12
|
+
|
|
13
|
+
1. Call `decoction_glean` with `action: "discover"`. If no cluster spans at
|
|
14
|
+
least four distinct projects, report that and stop.
|
|
15
|
+
2. Pick a cluster and `read` every source. Synthesize one generic record:
|
|
16
|
+
- use SPEC for a reusable contract or rule;
|
|
17
|
+
- use ARCH for a reusable decision or tradeoff;
|
|
18
|
+
- retain only claims supported across the projects;
|
|
19
|
+
- put every source used in `derived_from`.
|
|
20
|
+
3. Suggest source removal only when the generic preserves the original's entire
|
|
21
|
+
durable content. Do not suggest a source that also contains project-specific
|
|
22
|
+
facts, history, caveats, or unresolved work.
|
|
23
|
+
4. Call `decoction_glean` with `action: "write"`, the complete draft, and the
|
|
24
|
+
reasoned `suggested_removals`. For an existing generic, read it fresh and
|
|
25
|
+
pass both of its expected hashes.
|
|
26
|
+
5. Report the generic path and any warnings. Warnings are advisory and do not
|
|
27
|
+
block the write.
|
|
28
|
+
6. Show each suggested cleanup path and reason verbatim. Ask for explicit user
|
|
29
|
+
approval of the exact paths; never infer approval from approval of the
|
|
30
|
+
generic itself.
|
|
31
|
+
7. Only after approval, call `decoction_cleanup` with the generic path and the
|
|
32
|
+
approved source paths. Report every deletion and any failure.
|
|
33
|
+
|
|
34
|
+
Cleanup hard-deletes the selected originals after retargeting inbound links to
|
|
35
|
+
the generic. It has no journal or rollback. The user may approve a subset or
|
|
36
|
+
decline cleanup entirely.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: augment-dream
|
|
3
|
+
description: Explore globally recalled Augment memories and propose speculative memories or links for explicit user approval. Use when the user asks to "dream" over memory, imagine hypotheses, or surface hidden connections across the current, generic, or foreign projects.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Augment Dream
|
|
7
|
+
|
|
8
|
+
Dreams are hypotheses, not established facts. Discover and draft freely, but
|
|
9
|
+
never mutate the corpus before the user explicitly approves a named proposal.
|
|
10
|
+
Use a real project as the active project; `.generic` is inspiration or a link
|
|
11
|
+
target only.
|
|
12
|
+
|
|
13
|
+
1. Call `search` separately for each exploratory query, always passing the
|
|
14
|
+
active `project_name` and the singular `query` argument. Use normal global
|
|
15
|
+
results as ranked: the current project receives its normal priority, while
|
|
16
|
+
generic and foreign memories remain eligible.
|
|
17
|
+
Do not reserve a generic quota or add a generic fallback.
|
|
18
|
+
2. Call `read` with each useful result's numeric memory id, and retain its
|
|
19
|
+
returned relative path for provenance. Imagine 1-5 coherent candidates:
|
|
20
|
+
- dream memory: a new record in the active project, with `derived_from`
|
|
21
|
+
equal to the exact inspiration paths;
|
|
22
|
+
- dream link: a link from an active-project memory to any current, generic,
|
|
23
|
+
or foreign memory.
|
|
24
|
+
Generic-only and foreign-only inspiration are valid.
|
|
25
|
+
3. Call `dream_propose` for each worthwhile candidate. State the speculation
|
|
26
|
+
plainly in the content and rationale; do not present it as settled truth.
|
|
27
|
+
4. Show each proposal to the user. Only after explicit approval call
|
|
28
|
+
`dream_apply` with its filename. Call `dream_reject` when the user rejects
|
|
29
|
+
it. If approval is unclear, leave the proposal pending.
|
|
30
|
+
5. Report the applied speculative memory or link and its `dream:` commit.
|
|
31
|
+
|
|
32
|
+
Keep evidence and conjecture distinguishable. Do not invent supporting source
|
|
33
|
+
paths, and do not rewrite a foreign memory: foreign projects are inspiration
|
|
34
|
+
and link targets only.
|
|
@@ -52,6 +52,39 @@ After creating or updating a memory:
|
|
|
52
52
|
2. Prefer linking new `WORK` notes to the `SPEC`, `ARCH`, `ISSUE`, or `TODO`
|
|
53
53
|
they address.
|
|
54
54
|
|
|
55
|
+
## Decoction
|
|
56
|
+
|
|
57
|
+
When the user asks to decoct, glean, or generalize cross-project memory:
|
|
58
|
+
|
|
59
|
+
1. Call `decoction_glean` with `action: "discover"`, then read every source in
|
|
60
|
+
the selected cluster.
|
|
61
|
+
2. Draft one reusable SPEC or ARCH under `.generic/`, keeping only claims
|
|
62
|
+
supported across the projects and listing every source in `derived_from`.
|
|
63
|
+
3. Suggest removal only for originals whose entire durable content the generic
|
|
64
|
+
preserves. Call `decoction_glean` with `action: "write"`; report advisory
|
|
65
|
+
warnings without treating them as write failures. When updating an existing
|
|
66
|
+
generic, read it fresh and pass both expected hashes.
|
|
67
|
+
4. Show the exact suggested paths and reasons. Call `decoction_cleanup` only
|
|
68
|
+
after explicit approval of those paths; the hard deletes have no rollback.
|
|
69
|
+
|
|
70
|
+
Decoction is separate from sleep: sleep consolidates settled work inside one
|
|
71
|
+
project, while decoction extracts reusable knowledge across projects.
|
|
72
|
+
|
|
73
|
+
## Dreaming
|
|
74
|
+
|
|
75
|
+
When the user asks to dream over memory or surface speculative connections:
|
|
76
|
+
|
|
77
|
+
1. Search globally with the current project context, then read the relevant
|
|
78
|
+
current, generic, and foreign memories.
|
|
79
|
+
2. Call `dream_propose` for each candidate memory or link. Dream memories land
|
|
80
|
+
in the current real project (`.generic` is never active); dream links start
|
|
81
|
+
there but may target another project. State what evidence inspired the idea
|
|
82
|
+
and what remains unverified.
|
|
83
|
+
3. Present each editable proposal. Call `dream_apply` only after explicit user
|
|
84
|
+
approval; otherwise call `dream_reject`.
|
|
85
|
+
|
|
86
|
+
Never present a dream as established fact.
|
|
87
|
+
|
|
55
88
|
## Session Finish
|
|
56
89
|
|
|
57
90
|
For almost every session that did real work (skip only pure Q&A, read-only
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: augment-decoction
|
|
3
|
+
description: Distill recurring cross-project Augment memory into generic SPEC/ARCH records and optionally remove fully subsumed originals from Pi. Use when the user asks to "decoct", "glean", generalize, or clean up repeated memories across projects.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Augment Decoction for Pi
|
|
7
|
+
|
|
8
|
+
Decoction extracts durable, project-independent knowledge from recurring
|
|
9
|
+
memories. It is separate from sleep: sleep consolidates settled work within one
|
|
10
|
+
project, while decoction finds patterns across projects and writes them under
|
|
11
|
+
`.generic/`.
|
|
12
|
+
|
|
13
|
+
1. Call `augment_decoction_glean` with `action: "discover"`. If no cluster
|
|
14
|
+
spans at least four distinct projects, report that and stop.
|
|
15
|
+
2. Pick a cluster and call `augment_read_memory` for every source. Synthesize
|
|
16
|
+
one generic record:
|
|
17
|
+
- use SPEC for a reusable contract or rule;
|
|
18
|
+
- use ARCH for a reusable decision or tradeoff;
|
|
19
|
+
- retain only claims supported across the projects;
|
|
20
|
+
- put every source used in `derived_from`.
|
|
21
|
+
3. Suggest source removal only when the generic preserves the original's entire
|
|
22
|
+
durable content. Do not suggest a source that also contains project-specific
|
|
23
|
+
facts, history, caveats, or unresolved work.
|
|
24
|
+
4. Call `augment_decoction_glean` with `action: "write"`, the complete draft,
|
|
25
|
+
and the reasoned `suggested_removals`. For an existing generic, read it fresh
|
|
26
|
+
and pass both of its expected hashes.
|
|
27
|
+
5. Report the generic path and any warnings. Warnings are advisory and do not
|
|
28
|
+
block the write.
|
|
29
|
+
6. Show each suggested cleanup path and reason verbatim. Ask for explicit user
|
|
30
|
+
approval of the exact paths; never infer approval from approval of the
|
|
31
|
+
generic itself.
|
|
32
|
+
7. Only after approval, call `augment_decoction_cleanup` with the generic path
|
|
33
|
+
and the approved source paths. Report every deletion and any failure.
|
|
34
|
+
|
|
35
|
+
Cleanup hard-deletes the selected originals after retargeting inbound links to
|
|
36
|
+
the generic. It has no journal or rollback. The user may approve a subset or
|
|
37
|
+
decline cleanup entirely.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: augment-dream
|
|
3
|
+
description: Explore globally recalled Augment memories and propose speculative memories or links for explicit user approval. Use when the user asks to "dream" over memory, imagine hypotheses, or surface hidden connections across the current, generic, or foreign projects.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Augment Dream
|
|
7
|
+
|
|
8
|
+
Dreams are hypotheses, not established facts. Discover and draft freely, but
|
|
9
|
+
never mutate the corpus before the user explicitly approves a named proposal.
|
|
10
|
+
Use a real project as the active project; `.generic` is inspiration or a link
|
|
11
|
+
target only.
|
|
12
|
+
|
|
13
|
+
1. Call `augment_search` separately for each exploratory query, always passing
|
|
14
|
+
the active `project_name` and singular `query` argument. Use normal global
|
|
15
|
+
results as ranked: the current project receives its normal priority, while
|
|
16
|
+
generic and foreign memories remain eligible. Do not reserve a generic
|
|
17
|
+
quota or add a generic fallback.
|
|
18
|
+
2. Call `augment_read_memory` with each useful result's numeric memory id, and
|
|
19
|
+
retain its returned relative path for provenance. Imagine 1-5 coherent
|
|
20
|
+
candidates:
|
|
21
|
+
- dream memory: a new record in the active project, with `derived_from`
|
|
22
|
+
equal to the exact inspiration paths;
|
|
23
|
+
- dream link: a link from an active-project memory to any current, generic,
|
|
24
|
+
or foreign memory.
|
|
25
|
+
Generic-only and foreign-only inspiration are valid.
|
|
26
|
+
3. Call `augment_dream_propose` for each worthwhile candidate. State the
|
|
27
|
+
speculation plainly in the content and rationale; do not present it as
|
|
28
|
+
settled truth.
|
|
29
|
+
4. Show each proposal to the user. Only after explicit approval call
|
|
30
|
+
`augment_dream_apply` with its filename. Call `augment_dream_reject` when
|
|
31
|
+
the user rejects it. If approval is unclear, leave the proposal pending.
|
|
32
|
+
5. Report the applied speculative memory or link and its `dream:` commit.
|
|
33
|
+
|
|
34
|
+
Keep evidence and conjecture distinguishable. Do not invent supporting source
|
|
35
|
+
paths, and do not rewrite a foreign memory: foreign projects are inspiration
|
|
36
|
+
and link targets only.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fingerskier/augment",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Local RAG memory system for coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"LICENSE"
|
|
39
39
|
],
|
|
40
40
|
"scripts": {
|
|
41
|
-
"
|
|
41
|
+
"clean": "node --eval \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
42
|
+
"build": "npm run clean && tsc -p tsconfig.json",
|
|
42
43
|
"test": "node --import tsx --test \"test/**/*.test.ts\"",
|
|
43
44
|
"test:model": "node --import tsx --import ./test/setup-real-model.mjs --test \"test/**/*.model.ts\"",
|
|
44
45
|
"check": "npm run build && npm test",
|
package/dist/tally.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase-2 dogfood tally (council 2026-06-30): ~30 human-scored datapoints over
|
|
3
|
-
* ~2 weeks of real use, per injection surface, never a CI gate. Data lives as
|
|
4
|
-
* JSONL under `<stateDir>/tally/` — deliberately OUTSIDE the memory corpus so
|
|
5
|
-
* the corpus Phase 2 measures stays clean.
|
|
6
|
-
*/
|
|
7
|
-
export declare const TALLY_TARGET = 30;
|
|
8
|
-
export declare const TALLY_VERDICTS: readonly ["helpful", "harmless", "harmful", "correct-abstention"];
|
|
9
|
-
export type TallyVerdict = (typeof TALLY_VERDICTS)[number];
|
|
10
|
-
/** One human rating of a recall outcome. */
|
|
11
|
-
export interface TallyRating {
|
|
12
|
-
ts: string;
|
|
13
|
-
verdict: TallyVerdict;
|
|
14
|
-
/** Injection surface being rated: user-prompt-submit | pre-tool-use | mcp-search | cli-recall | unknown. */
|
|
15
|
-
surface: string;
|
|
16
|
-
note?: string;
|
|
17
|
-
}
|
|
18
|
-
/** One automatically-logged injection event (what search actually returned). */
|
|
19
|
-
export interface TallyEvent {
|
|
20
|
-
ts: string;
|
|
21
|
-
surface: string;
|
|
22
|
-
project?: string;
|
|
23
|
-
/** Query text, truncated by the writer — context for later audit, not for ranking. */
|
|
24
|
-
query: string;
|
|
25
|
-
abstained: boolean;
|
|
26
|
-
result_count: number;
|
|
27
|
-
top_score?: number;
|
|
28
|
-
/** Durable relative paths of the memories injected. */
|
|
29
|
-
paths: string[];
|
|
30
|
-
}
|
|
31
|
-
export interface TallySummary {
|
|
32
|
-
total: number;
|
|
33
|
-
target: number;
|
|
34
|
-
by_verdict: Record<TallyVerdict, number>;
|
|
35
|
-
by_surface: Record<string, number>;
|
|
36
|
-
first_at?: string;
|
|
37
|
-
last_at?: string;
|
|
38
|
-
}
|
|
39
|
-
export declare function isTallyVerdict(value: unknown): value is TallyVerdict;
|
|
40
|
-
export declare function ratingsPath(stateDir: string): string;
|
|
41
|
-
export declare function eventsPath(stateDir: string): string;
|
|
42
|
-
export declare function appendTallyRating(stateDir: string, rating: TallyRating): Promise<void>;
|
|
43
|
-
export declare function appendTallyEvent(stateDir: string, event: TallyEvent): Promise<void>;
|
|
44
|
-
export declare function readTallyRatings(stateDir: string): Promise<TallyRating[]>;
|
|
45
|
-
export declare function summarizeTally(ratings: TallyRating[]): TallySummary;
|
|
46
|
-
export declare function renderTallySummary(summary: TallySummary): string;
|
package/dist/tally.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { appendFile, mkdir, readFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
/**
|
|
4
|
-
* Phase-2 dogfood tally (council 2026-06-30): ~30 human-scored datapoints over
|
|
5
|
-
* ~2 weeks of real use, per injection surface, never a CI gate. Data lives as
|
|
6
|
-
* JSONL under `<stateDir>/tally/` — deliberately OUTSIDE the memory corpus so
|
|
7
|
-
* the corpus Phase 2 measures stays clean.
|
|
8
|
-
*/
|
|
9
|
-
export const TALLY_TARGET = 30;
|
|
10
|
-
export const TALLY_VERDICTS = ["helpful", "harmless", "harmful", "correct-abstention"];
|
|
11
|
-
export function isTallyVerdict(value) {
|
|
12
|
-
return typeof value === "string" && TALLY_VERDICTS.includes(value);
|
|
13
|
-
}
|
|
14
|
-
export function ratingsPath(stateDir) {
|
|
15
|
-
return path.join(stateDir, "tally", "ratings.jsonl");
|
|
16
|
-
}
|
|
17
|
-
export function eventsPath(stateDir) {
|
|
18
|
-
return path.join(stateDir, "tally", "events.jsonl");
|
|
19
|
-
}
|
|
20
|
-
async function appendLine(file, record) {
|
|
21
|
-
await mkdir(path.dirname(file), { recursive: true });
|
|
22
|
-
await appendFile(file, `${JSON.stringify(record)}\n`, "utf8");
|
|
23
|
-
}
|
|
24
|
-
export async function appendTallyRating(stateDir, rating) {
|
|
25
|
-
await appendLine(ratingsPath(stateDir), rating);
|
|
26
|
-
}
|
|
27
|
-
export async function appendTallyEvent(stateDir, event) {
|
|
28
|
-
await appendLine(eventsPath(stateDir), event);
|
|
29
|
-
}
|
|
30
|
-
export async function readTallyRatings(stateDir) {
|
|
31
|
-
let raw;
|
|
32
|
-
try {
|
|
33
|
-
raw = await readFile(ratingsPath(stateDir), "utf8");
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
38
|
-
const ratings = [];
|
|
39
|
-
for (const line of raw.split("\n")) {
|
|
40
|
-
if (!line.trim()) {
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
try {
|
|
44
|
-
const parsed = JSON.parse(line);
|
|
45
|
-
if (isTallyVerdict(parsed.verdict)) {
|
|
46
|
-
ratings.push(parsed);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
// A corrupt line loses one datapoint, never the tally.
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return ratings;
|
|
54
|
-
}
|
|
55
|
-
export function summarizeTally(ratings) {
|
|
56
|
-
const by_verdict = {
|
|
57
|
-
helpful: 0,
|
|
58
|
-
harmless: 0,
|
|
59
|
-
harmful: 0,
|
|
60
|
-
"correct-abstention": 0,
|
|
61
|
-
};
|
|
62
|
-
const by_surface = {};
|
|
63
|
-
for (const rating of ratings) {
|
|
64
|
-
by_verdict[rating.verdict] += 1;
|
|
65
|
-
by_surface[rating.surface || "unknown"] = (by_surface[rating.surface || "unknown"] ?? 0) + 1;
|
|
66
|
-
}
|
|
67
|
-
return {
|
|
68
|
-
total: ratings.length,
|
|
69
|
-
target: TALLY_TARGET,
|
|
70
|
-
by_verdict,
|
|
71
|
-
by_surface,
|
|
72
|
-
...(ratings.length > 0
|
|
73
|
-
? { first_at: ratings[0].ts, last_at: ratings[ratings.length - 1].ts }
|
|
74
|
-
: {}),
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
export function renderTallySummary(summary) {
|
|
78
|
-
const lines = [
|
|
79
|
-
`Dogfood tally: ${summary.total}/${summary.target} datapoints`,
|
|
80
|
-
` helpful: ${summary.by_verdict.helpful} harmless: ${summary.by_verdict.harmless} ` +
|
|
81
|
-
`harmful: ${summary.by_verdict.harmful} correct-abstention: ${summary.by_verdict["correct-abstention"]}`,
|
|
82
|
-
];
|
|
83
|
-
const surfaces = Object.entries(summary.by_surface)
|
|
84
|
-
.map(([surface, count]) => `${surface}:${count}`)
|
|
85
|
-
.join(" ");
|
|
86
|
-
if (surfaces) {
|
|
87
|
-
lines.push(` by surface: ${surfaces}`);
|
|
88
|
-
}
|
|
89
|
-
if (summary.first_at && summary.last_at) {
|
|
90
|
-
lines.push(` window: ${summary.first_at} .. ${summary.last_at}`);
|
|
91
|
-
}
|
|
92
|
-
return lines.join("\n");
|
|
93
|
-
}
|
|
94
|
-
//# sourceMappingURL=tally.js.map
|
package/dist/tally.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tally.js","sourceRoot":"","sources":["../src/tally.ts"],"names":[],"mappings":"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"}
|