akm-opencode 0.9.0 → 0.9.202808211043

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 CHANGED
@@ -32,26 +32,82 @@ The plugin subscribes to OpenCode lifecycle events. Hook failures are logged thr
32
32
 
33
33
  | Event | Behavior |
34
34
  | --- | --- |
35
- | `session.created` | Resolves AKM, warms local data in the background, and prepares scoped context for the session. |
36
- | `chat.message` | Records feedback or memory intent and can schedule non-blocking curation for a substantive prompt. |
37
- | `experimental.chat.system.transform` | Injects cached AKM guidance and curated context into the system prompt. |
35
+ | `session.created` | Resolves AKM, warms local data in the background, and prepares hints, an active-workflow summary, and curated context for the session. |
36
+ | `session.updated` | Backfills hints and the workflow summary for a session the plugin has not prepared yet. It does not re-run session-created work. |
37
+ | `chat.message` | Records feedback or memory intent and can schedule non-blocking curation for a substantive prompt. The curate is fire-and-forget: its result is injected on a later turn rather than delaying this one. |
38
+ | `experimental.chat.system.transform` | Injects the AKM guidance and cached curated context into the system prompt. The host rebuilds the system prompt on every request, so these blocks are re-injected each turn — including after a compaction — rather than once per session. |
38
39
  | `tool.execute.after` | Tracks concepts used by AKM tools, records deduplicated feedback, and checkpoints session observations. |
39
40
  | `shell.env` | Exposes `AKM_PROJECT`, `AKM_PLUGIN_VERSION`, and the resolved `AKM_BUNDLE_DIR` to shell tools. |
40
- | Session idle, compacted, or deleted | Flushes sufficiently meaningful session observations through the memory lifecycle. |
41
+ | `session.idle` | Runs interval-gated memory extraction (`AKM_AUTO_MEMORY=0` disables it). Fires after every turn, so it is rate-limited. |
42
+ | `session.compacted` | Records a post-compaction event. |
43
+ | `session.deleted` | Refreshes the local AKM index, then drops all per-session state and the temporary curation file. |
44
+
45
+ The session observation buffer that retrospective feedback reads from survives every non-terminal event: it is bounded by `AKM_SESSION_BUFFER_MAX_ENTRIES` and dropped only on `session.deleted`. Discarding it at `session.idle` would empty it between turns, so "thanks, that worked" would credit nothing in exactly the sessions that used the most assets.
46
+
47
+ Automatic feedback skips references that AKM reports as ineligible, and a *successful* `akm_show` / `akm_search` / `akm_curate` submits nothing — inspecting a concept is not evidence that it helped, so on OpenCode the positive signal comes from a retrospective confirmation instead. Failures of those same tools still count as negative signal. Same rule as the Claude plugin, which applies it to the `akm` subcommand of a Bash invocation.
48
+
49
+ ## Locking down destructive commands
50
+
51
+ The plugin does not gate destructive `akm` commands. The `permission.ask` / `command.execute.before` hook that tokenized each Bash invocation and blocked a hard-coded list of risky `akm` subcommands was removed in 0.8.0: tokenized matching produced false positives on commit messages, heredoc bodies, and any other prose containing an `akm <verb>` substring.
52
+
53
+ OpenCode has no first-class permission DSL today, so enforce it outside the plugin — wrap `akm` in a confirmation script earlier on `PATH`, or use OS-level access controls.
54
+
55
+ Independently of any such control, agents should treat these verbs as requiring explicit user approval: `proposal accept`, `proposal reject`, `proposal revert`, `sync --push`, `remove`, env/secret writes, `task add` / `task run`, `upgrade`, `update --all`, `config set`.
41
56
 
42
57
  ## Environment
43
58
 
59
+ Every kill switch below is opt-out and reads the same way: only the literal `0` disables it. Any other value — including `false` — leaves the feature on.
60
+
61
+ ### Core
62
+
44
63
  | Variable | Default | Purpose |
45
64
  | --- | --- | --- |
65
+ | `AKM_BUNDLE_DIR` | unset | Absolute path to the AKM bundle root. When unset the plugin discovers it once per process by running `akm info`. The resolved value is re-exported to shell tools through the `shell.env` hook. |
46
66
  | `AKM_LOCAL_BUILD_CLI` | unset | Absolute path to a locally built AKM CLI entry point. |
67
+ | `AKM_OPENCODE_IGNORE_BUNDLED_CLI` | unset (off) | Set to `1` to drop the bundled AKM CLI from resolution so only an `akm` on `PATH` is considered. Used by the eval harness; also an escape hatch when the bundled dependency is broken. |
47
68
  | `AKM_AUTO_CURATE` | `1` | Set to `0` to disable automatic prompt curation. |
48
69
  | `AKM_AUTO_FEEDBACK` | `1` | Set to `0` to disable automatic outcome feedback. |
49
- | `AKM_AUTO_MEMORY` | `1` | Set to `0` to disable automatic session memories. |
70
+ | `AKM_AUTO_HINTS` | `1` | Set to `0` to skip the per-session `akm hints` call. The missing-bundle warning is deliberately not gated on this: it explains why the stash is empty in the first place. |
71
+ | `AKM_AUTO_MEMORY` | `1` | Set to `0` to disable automatic memory harvesting — the interval-gated `akm proposal extract` on `session.idle`, which is the whole of that harvest here. The Claude plugin honours the same variable for its `SessionEnd` extract, so one setting covers both harnesses. |
72
+ | `AKM_INDEX_ON_SESSION_END` | `1` | Set to `0` to skip the `akm index` refresh. It runs only on `session.deleted` — never on `session.idle`, which fires after every turn. |
73
+
74
+ ### Scope
75
+
76
+ | Variable | Default | Purpose |
77
+ | --- | --- | --- |
78
+ | `AKM_SCOPE_KEYS` | `user,agent,run,channel` | Which scope dimensions `akm_remember` forwards to the AKM CLI as flags. It does **not** gate what the plugin records locally — lifecycle events in `events.jsonl` always carry every dimension that has a value. |
79
+ | `AKM_USER_ID` | unset | Value for the `user` dimension. |
80
+ | `AKM_CHANNEL` | unset | Value for the `channel` dimension. |
81
+ | `AKM_REPO` | unset | Repository label recorded on lifecycle events. |
82
+ | `AKM_BRANCH` | unset | Branch label recorded on lifecycle events. |
83
+
84
+ The `agent`, `run`, and `project` dimensions come from OpenCode itself (the active agent, the session ID, and the worktree), so they have no environment variable. `AKM_PROJECT` is *written* by the `shell.env` hook for shell tools to read; the plugin never reads it.
85
+
86
+ ### Tuning
87
+
88
+ | Variable | Default | Purpose |
89
+ | --- | --- | --- |
50
90
  | `AKM_CURATE_LIMIT` | `5` | Maximum curated results injected per prompt. |
51
91
  | `AKM_CURATE_MIN_CHARS` | `16` | Minimum prompt length for automatic curation. |
52
92
  | `AKM_CURATE_TIMEOUT` | `8` | Timeout in seconds for AKM calls made by hooks. |
53
- | `AKM_CONTEXT_BUDGET_CHARS` | `4000` | Maximum AKM context injected into one turn. |
54
- | `AKM_SCOPE_KEYS` | `user,agent,run,channel` | Scope dimensions attached to remember calls and local lifecycle records. |
93
+ | `AKM_CONTEXT_BUDGET_CHARS` | `4000` | Maximum length of the AKM text injected into the system prompt on one turn. Past it the block is truncated with a marker. |
94
+ | `AKM_PENDING_PROPOSAL_TIMEOUT` | `2` | Timeout in seconds for the pending-proposal count. Floored at 0.5s. |
95
+ | `AKM_SESSION_BUFFER_MAX_ENTRIES` | `200` | Per-session cap on buffered observations. Oldest entries are dropped first. |
96
+ | `AKM_EXTRACT_MIN_INTERVAL_MS` | `600000` | Minimum gap between `akm proposal extract` runs for one session. `session.idle` fires after every turn, so without this gate extraction would flood. |
97
+ | `AKM_PLUGIN_MAX_LOG_BYTES` | `1048576` | Size cap for each append-only state file under `$XDG_STATE_HOME/akm-opencode`. Past the cap the newest half is retained. |
98
+ | `AKM_AUTO_FEEDBACK_MIN_CONFIDENCE` | `0.6` | Minimum classifier confidence before automatic feedback is actually submitted. Raise it to submit less. |
99
+ | `AKM_RETROSPECTIVE_FEEDBACK_PATTERN` | `\b(thanks\|perfect\|worked)\b` | Case-insensitive regex for "that worked" messages. Retune it for other languages or project jargon. An invalid regex falls back to the default rather than failing the hook. |
100
+ | `AKM_RETROSPECTIVE_NEGATIVE_PATTERN` | `\b(wrong\|failed\|broken\|didn't work\|did not work\|bad)\b` | Case-insensitive regex that vetoes retrospective credit, so a mixed message ("thanks, but it did not work") is skipped rather than misread as praise. Same fallback behavior. |
101
+
102
+ ### Redaction
103
+
104
+ These three are read once, when the plugin module is imported, so they must be set in the environment **before** OpenCode starts. Both matchers are off by default because both over-redact on ordinary logs.
105
+
106
+ | Variable | Default | Purpose |
107
+ | --- | --- | --- |
108
+ | `AKM_REDACT_HIGH_ENTROPY` | unset (off) | Set to `1` to redact long base64/hex-shaped strings that look like secrets. |
109
+ | `AKM_REDACT_ENTROPY_MIN_LEN` | `32` | Minimum length for the high-entropy matcher. Values below `32` are clamped to `32`. No effect unless `AKM_REDACT_HIGH_ENTROPY=1`. |
110
+ | `AKM_REDACT_PII` | unset (off) | Set to `1` to redact credit-card-shaped digit runs, US SSNs, and phone numbers. |
55
111
 
56
112
  ## Usage
57
113