freddie 0.0.105 → 0.0.107
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/AGENTS.md +143 -202
- package/package.json +4 -4
- package/plugins/gm-skill/plugin.js +14 -7
package/AGENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Freddie — Agent Guide
|
|
2
2
|
|
|
3
|
-
Instructions for AI coding assistants working on Freddie.
|
|
3
|
+
Instructions for AI coding assistants working on Freddie. Present-tense rules only — history lives in `git log` and `CHANGELOG.md`.
|
|
4
4
|
|
|
5
5
|
## Substrate (do not reimplement)
|
|
6
6
|
|
|
@@ -8,121 +8,156 @@ Instructions for AI coding assistants working on Freddie.
|
|
|
8
8
|
- `@mariozechner/pi-agent-core` — `Agent`, `agentLoop`, `runAgentLoop`, `streamProxy`. Wrap in xstate, do not rewrite.
|
|
9
9
|
- `@mariozechner/pi-ai` — `complete`, `completeSimple`, `AssistantMessageEventStream`, `registerApiProvider`, `getModel`, `calculateCost`, `parseStreamingJson`, `isContextOverflow`. THE provider layer.
|
|
10
10
|
- `@mariozechner/pi-tui` — TUI primitives (Ink-equivalent).
|
|
11
|
-
- `floosie`
|
|
12
|
-
- `anentrypoint-design`
|
|
11
|
+
- `floosie` — `ProcessorMachine` (xstate). Use for gateway pipelines. Compose, don't fork.
|
|
12
|
+
- `anentrypoint-design` — webjsx + ripple-ui. **All GUI for freddie and thebird lives here.** Source in `C:/dev/anentrypoint-design`; freddie pins from npm registry. For local SDK iteration, swap to `file:../anentrypoint-design` and rebuild via `node scripts/build.mjs`. Do NOT add React.
|
|
13
|
+
- `acptoapi` — THE LLM SDK (see "acptoapi is THE SDK" below).
|
|
13
14
|
- `xstate` v5 — every long-lived state machine (agent turns, gateway lifecycle, approvals).
|
|
14
15
|
|
|
15
|
-
## Dynamic stack contract
|
|
16
|
+
## Dynamic stack contract
|
|
16
17
|
|
|
17
18
|
The stack is **thebird → freddie → acptoapi**. Each layer owns one concern:
|
|
18
19
|
|
|
19
20
|
- **acptoapi** owns all upstream LLM/provider connectivity: HTTP/SSE to OpenAI, Anthropic, Gemini, brand providers, ACP daemons, Claude CLI. Plus chain/queue/sampler/matrix.
|
|
20
|
-
- **freddie** owns agent-loop orchestration: tools, skills, sessions, memory.
|
|
21
|
+
- **freddie** owns agent-loop orchestration: tools, skills, sessions, memory. Calls *only* acptoapi for LLM access. No direct `fetch('https://api.openai.com/...')`. Migration debt still present in `plugins/vision`, `plugins/image_gen`, `plugins/tts`, `plugins/transcription`, `src/agent/codex_responses_adapter.js`, `src/agent/image_gen_provider.js`, `src/agent/model-discovery.js` — when you touch one, add the matching endpoint to acptoapi and call through acptoapi.
|
|
21
22
|
- **thebird** owns browser presentation: webjsx UI, pyodide hermes shell. Talks to freddie for everything LLM-related when freddie is reachable; falls back to direct acptoapi only when there is no freddie.
|
|
22
23
|
|
|
23
24
|
Versioning: freddie pins `acptoapi: "latest"` so `npm install` always picks up the newest published acptoapi. Thebird vendors freddie via `scripts/sync-upstream.mjs` against upstream main. No manual version-bump churn between sibling repos.
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
## acptoapi is THE SDK
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
**Do not reimplement LLM resolution, chain fallback, sampler backoff, or matrix-aware scoring in freddie.** acptoapi is the single source of truth. `src/agent/llm_resolver.js` is a thin shim over `acptoapi.chat({model, messages, tools, queuesMap, matrixSource, onFallback, output})` that builds a comma-list model string from `[explicit, input.model, agent.model_preference, keyed buildAutoChain]` and delegates everything else.
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
Consume top-level acptoapi exports directly (no re-export shim, no helper module): `chat`, `stream`, `chain`, `chatChain`, `streamChain`, `fallback`, `buildAutoChain`, `resolveModel`, `parseCommaList`, `splitPrefix`, `listAllModelsAndQueues`, `resolveQueue`, `listAllQueues`, `loadMatrix`, `matrixScore`, `clearMatrixCache`, `peekStatus`, `getStatus`, `isAvailable`, `markFailed`, `markOk`, `resetAvailability`, `startSampler`, `stopSampler`, `createSampler`, `probe`, `probeModels`, `getCachedModels`, `getRunHistory`, `PROVIDER_KEYS`, `PROVIDER_DEFAULTS`.
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
Public surface reference: `node_modules/acptoapi/AGENTS.md` "Public API — unified chain SDK".
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
Acceptable freddie-side adapters:
|
|
35
|
+
- `model-discovery.js` — claude-cli/ACP/ollama probing breadth acptoapi doesn't cover. `listKnownProviders` merges `agent.discovered_models` keys + acptoapi `PROVIDER_KEYS` + `[claude-cli,kilo,opencode,ollama]`.
|
|
36
|
+
- `model-matrix.js` — MATRIX_FILE path helper + `matrixUsable` predicate. Freddie-side because the matrix file path is repo-local.
|
|
37
|
+
- `acptoapi-bridge.js` — HTTP daemon passthrough at `FREDDIE_LLM_URL` when reachable, for `claude/*` etc that need the OAuth-managed daemon.
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
Sampler funcs (`isAvailable`, `markFailed`, `markOk`, `resetAvailability`, `getStatus`, `probe`, `startSampler`, `stopSampler`, `createSampler`) come straight from `acptoapi` via `createRequire`. Backoff logic (5-step 30s→480s, createSampler factory, singleton) lives in `acptoapi/lib/sampler.js`. CJS/ESM boundary bridged via `createRequire(import.meta.url)`.
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
Matrix wired: shim passes `matrixSource: process.env.FREDDIE_MATRIX_URL || <repo>/.gm/model-availability.json` only for comma-list or `queue/<name>` model strings; single-shot omits to avoid leaking chain opts into upstream HTTP body.
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
## LLM resolver priority
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- `requires` cycles throw `plugin cycle: a -> b -> a` synchronously
|
|
45
|
+
1. explicit provider+key
|
|
46
|
+
2. acptoapi if `/v1/models` returns 200
|
|
47
|
+
3. `agent.model_preference` config array (ordered failover, sampler-gated)
|
|
48
|
+
4. `sdk.buildAutoChain()` env-key scan
|
|
49
|
+
5. throw
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
`PROVIDER_KEYS` and `PROVIDER_DEFAULTS` come from acptoapi — never maintained in freddie. `sdk.chat()` returns OpenAI `{choices:[{message}]}`; `sdkChat()` adapter in llm_resolver converts to freddie's `{content, tool_calls, raw}`.
|
|
52
|
+
|
|
53
|
+
`agent.model_preference: []` in `~/.freddie/config.yaml` is an array of `{ provider, model? }` objects; `resolveCallLLM` tries each in order, skipping unavailable (sampler-gated) and marking failures with backoff.
|
|
54
|
+
|
|
55
|
+
`src/agent/acptoapi-bridge.js` `max_tokens` defaults to 4096 — never lower. 1024 silently truncates generation tasks.
|
|
56
|
+
|
|
57
|
+
`src/agent/llm_resolver.js::acpChat()` speaks the kilo ACP protocol: POST `/session` → GET `/event` (SSE) → POST `/session/<id>/message`. Streams `message.part.updated` events to assemble content; terminates on `session.idle`. **`/event` must be opened BEFORE `/message` POST or messages drop.** `ACP_BACKENDS`: kilo on `http://localhost:4780`, opencode on `http://localhost:4790`. kilo + opencode ACP backends return content only, no tool_calls — for multi-iteration tool-using loops, use OpenAI-compatible providers (mistral, openrouter, sambanova, groq).
|
|
58
|
+
|
|
59
|
+
## Plugin architecture
|
|
60
|
+
|
|
61
|
+
Every tool, platform, memory provider, GUI route, and core subsystem is a plugin under `plugins/<name>/`. There is no `src/tools/registry.js`, `src/tools/<tool>.js`, `src/gateway/platforms/*.js`, or `src/plugins/memory/*.js` — do not reach for those paths.
|
|
62
|
+
|
|
63
|
+
Contract: `{ name, version?, surfaces: 'pi'|'gui'|'both', requires?: [...names], register(ctx) }` — defined in `src/host/contract.js`.
|
|
64
|
+
|
|
65
|
+
- PI_VERBS: `tool, env, command, cron, platform, memory, skill, context, agentExt, cli`
|
|
66
|
+
- GUI_VERBS: `route, page, nav, debug, api, asset`
|
|
67
|
+
- HOOK_NAMES: `preToolCall, postToolCall, preLlmCall, postLlmCall, onSessionStart, onSessionEnd, onTurnStart, onTurnEnd, onMessageInbound, onMessageOutbound`
|
|
68
|
+
- Surface guard throws `plugin <name>: surface verb '<verb>' not allowed` at load.
|
|
69
|
+
- `requires` cycles throw `plugin cycle: a -> b -> a` synchronously.
|
|
70
|
+
|
|
71
|
+
Host: `src/host/host.js` — `createHost({surfaces, configStore, env})` + `discoverPlugins(roots)`. Singleton in `src/host/index.js`: `host()`, `bootHost(extraRoots)`, `resetHostForTests()`. Roots walked: `<repo>/plugins`, `~/.freddie/plugins/`, `<cwd>/.freddie/plugins/`.
|
|
49
72
|
|
|
50
73
|
`register(ctx)` receives `{ pi, gui, hooks, log, config, host, env }`:
|
|
51
74
|
- `log` — scoped JSONL with plugin name
|
|
52
75
|
- `config` — scoped under `plugins.<name>` (`get/set/all`)
|
|
53
76
|
- `host` — `{plugins(), get(name)}`
|
|
54
77
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Thin shims (still resolved through host, do not bypass):
|
|
58
|
-
- `src/plugins/manager.js` — over the host
|
|
59
|
-
- `src/web/server.js` (23L) — iterates `host.gui.routes.list()`
|
|
60
|
-
- `bin/freddie.js` (19L) — iterates `host.pi.cli.list()` and registers commander commands
|
|
61
|
-
- `src/gateway/platforms.js` — `makePlatform/getPlatformAdapter/listPlatformNames` (finds adapter by `*Adapter$` name match)
|
|
62
|
-
- `src/plugins/memory/provider.js` — host-router (`createMemoryProvider`, `listMemoryProviders`, `registerMemoryProvider`, `MemoryProvider`)
|
|
63
|
-
- All consumers (`acp/server.js`, `acp/tools.js`, `mcp/server.js`, `agent/machine.js`, `toolsets.js`, `cli/gateway_cli.js`) resolve via `bootHost()`
|
|
78
|
+
Thin shims (resolved through host, do not bypass): `src/plugins/manager.js`, `src/web/server.js` (iterates `host.gui.routes.list()`), `bin/freddie.js` (iterates `host.pi.cli.list()`), `src/gateway/platforms.js` (`*Adapter$` name match), `src/plugins/memory/provider.js` (host-router).
|
|
64
79
|
|
|
65
|
-
|
|
80
|
+
## gm-skill plugin
|
|
66
81
|
|
|
67
|
-
|
|
82
|
+
`plugins/gm-skill/plugin.js` registers ONE canonical skill named `gm-skill`. Resolution order: (1) `~/.claude/skills/gm-skill/SKILL.md`, (2) `node_modules/gm-cc/skills/gm-skill/SKILL.md`. All other `gm-*` platform variants (gm-cc, gm-codex, gm-cursor, gm-jetbrains, gm-kilo, gm-oc, gm-vscode, gm-zed, gm-gc, gm-copilot-cli) are DEPRECATED — do not register them. `src/host/host_helpers.js::loadCcFromNodeModules` carries `CC_EXCLUDE = new Set(['gm-cc'])` so the gm-cc npm package is not auto-discovered as a cc-plugin. test.js asserts exactly one gm-prefixed skill is registered, named `gm-skill`.
|
|
68
83
|
|
|
69
|
-
## Multi-project workspace
|
|
84
|
+
## Multi-project workspace
|
|
70
85
|
|
|
71
86
|
Freddie supports multiple isolated projects, each with its own home directory and plugin set. Registry at `~/.freddie/projects.json` stores `{ active, projects: [{name, path, created_at}] }`. Default project (`~/.freddie`) is protected from deletion.
|
|
72
87
|
|
|
73
|
-
|
|
74
|
-
- `src/
|
|
75
|
-
- `src/
|
|
76
|
-
- `
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
- `src/projects.js` — `loadRegistry()`, `listProjects()`, `getActiveProject()`, `createProject({name, projectPath})`, `deleteProject(name)`, `setActiveProject(name)`, `applyActiveProjectFromRegistry()`.
|
|
89
|
+
- `src/home.js::applyHomeOverride(absPath)` sets `FREDDIE_HOME` and clears cached home.
|
|
90
|
+
- `src/host/index.js::bootHost()` calls `applyActiveProjectFromRegistry()` before plugin discovery.
|
|
91
|
+
- `plugins/gui-projects/plugin.js` — `GET/POST/DELETE /api/projects`, `POST /api/projects/active`.
|
|
92
|
+
|
|
93
|
+
Isolation boundary: each project gets its own sessions DB, config.json, skills/, plugins/, cron.db, batches/, logs/, auth.json (all under `getFreddieHome()`). Plugins re-read paths per-request.
|
|
94
|
+
|
|
95
|
+
**Runtime switch caveat**: switching active project calls `resetHostForTests()` and clears caches but does NOT re-discover plugins in the running dashboard. UI alerts user to restart dashboard for plugin reload. New processes pick up active project automatically.
|
|
96
|
+
|
|
97
|
+
## GUI surface (anentrypoint-design)
|
|
98
|
+
|
|
99
|
+
All web UI for freddie + thebird lives in `anentrypoint-design`. Consumers must not duplicate components inline.
|
|
79
100
|
|
|
80
|
-
|
|
101
|
+
- **freddie dashboard** (`src/web/`) is minimal: `index.html` (importmap), `app.js` (~100L thin mount), `state.js` (HTTP client), `routes.js` (re-exports SDK's `FREDDIE_PAGES`), `server.js`. No inline components. No inline CSS beyond reset. Any new page goes into `anentrypoint-design`'s `FREDDIE_PAGES`, not into `app.js`.
|
|
102
|
+
- **thebird** consumes the same SDK. Bespoke windowing (`wm.js`, `launcher.js`, `shell.js`) and any context-menu / theme-toggle DOM should migrate into the SDK as reusable kits; do not extend them in thebird.
|
|
103
|
+
- Theme toggle: SDK owns the controller. Consumers import it; they do NOT reimplement localStorage + `prefers-color-scheme` listeners.
|
|
81
104
|
|
|
82
|
-
|
|
105
|
+
Build: `node scripts/build.mjs` in `C:/dev/anentrypoint-design` emits `dist/247420.js` + `dist/247420.css`. Rebuild after SDK edits or `component is not a function` kills mount silently. `server.js` serves SDK from `node_modules/anentrypoint-design/dist/`.
|
|
106
|
+
|
|
107
|
+
Theme attribute scoping: `class="ds-247420"` on `<html>`, `data-theme="dark|light"` on `<body>`. Putting both on the same node breaks the descendant selector and themes do not switch.
|
|
108
|
+
|
|
109
|
+
Live page rerender: `AppState.body` is cached per navigation. Live routes (e.g. `#/chat` with SSE updates) must recompute body in `rerender()`:
|
|
110
|
+
```js
|
|
111
|
+
if (AppState.hash === '#/chat') { Promise.resolve(PAGES['#/chat']()).then(b => { AppState.body = b; _mount() }); return }
|
|
112
|
+
```
|
|
113
|
+
Any future live-streaming pages (cron output, traces) need the same treatment.
|
|
114
|
+
|
|
115
|
+
Inline `<script type="module">` parse errors swallow file:line in browsers. Extract the script body to a `.js` file and `node --check` it to get the exact line.
|
|
83
116
|
|
|
84
117
|
## Layout
|
|
85
118
|
|
|
86
119
|
```
|
|
87
120
|
src/home.js # getFreddieHome, applyProfileOverride, applyHomeOverride
|
|
88
|
-
src/projects.js # Multi-project registry CRUD
|
|
121
|
+
src/projects.js # Multi-project registry CRUD
|
|
89
122
|
src/config.js # loadConfig, saveConfigValue, DEFAULT_CONFIG, _config_version migrations
|
|
90
|
-
src/sessions.js #
|
|
123
|
+
src/sessions.js # libsql + FTS5 (async API — every callsite must await)
|
|
91
124
|
src/auth.js # FileAuthStore for credentials
|
|
92
|
-
src/tools/registry.js # tool registration + dispatch
|
|
93
|
-
src/tools/{bash,read,write,edit,grep,todo,memory,delegate,web_search,image_gen,browser}.js
|
|
94
|
-
src/tools/environments/{local,docker,ssh,index}.js # execution environments
|
|
95
125
|
src/toolsets.js # _FREDDIE_CORE_TOOLS, getEnabledToolSchemas
|
|
96
|
-
src/agent/machine.js # xstate turn machine
|
|
126
|
+
src/agent/machine.js # xstate turn machine + writeTrajectory
|
|
127
|
+
src/agent/llm_resolver.js # thin shim over acptoapi.chat
|
|
128
|
+
src/agent/acptoapi-bridge.js # HTTP passthrough to FREDDIE_LLM_URL daemon
|
|
129
|
+
src/agent/model-discovery.js # claude-cli/ACP/ollama discovery beyond acptoapi
|
|
130
|
+
src/agent/model-matrix.js # MATRIX_FILE path + matrixUsable predicate
|
|
97
131
|
src/agent/pi-bridge.js # @mariozechner/pi-ai callLLM adapter
|
|
98
|
-
src/agent/compress/{tokens,policy,prompt,prune,fallback,compressor,index}.js
|
|
132
|
+
src/agent/compress/{tokens,policy,prompt,prune,fallback,compressor,index}.js
|
|
99
133
|
src/commands/registry.js # CommandDef + resolveCommand + gateway/telegram/slack views
|
|
100
134
|
src/commands/profile.js # profile CRUD
|
|
101
135
|
src/cli/interactive.js # readline REPL, skin-aware
|
|
102
136
|
src/context/engine.js # context block builders (file, skills, memory)
|
|
103
|
-
src/cron/{scheduler,cron-parse}.js # persistent cron jobs
|
|
137
|
+
src/cron/{scheduler,cron-parse}.js # persistent cron jobs (async API)
|
|
104
138
|
src/batch.js # parallel batch runner
|
|
105
|
-
src/web/{server,index.html}
|
|
139
|
+
src/web/{server,app,state,routes,index.html} # thin dashboard mount over SDK
|
|
106
140
|
src/gateway/run.js # Gateway + hooks
|
|
107
|
-
src/gateway/platforms/*.js # webhook + api_server + 16 functional adapters
|
|
108
141
|
src/acp/server.js # JSON-RPC stdio
|
|
109
|
-
src/plugins/manager.js #
|
|
110
|
-
src/plugins/memory/
|
|
142
|
+
src/plugins/manager.js # thin shim over host
|
|
143
|
+
src/plugins/memory/provider.js # host-router
|
|
111
144
|
src/skills/index.js # SKILL.md loader
|
|
112
145
|
src/skin/engine.js # _BUILTIN_SKINS + load/get/set
|
|
113
146
|
src/observability/log.js # structured logs
|
|
114
147
|
src/observability/debug.js # /debug registry
|
|
148
|
+
src/host/{contract,host,host_helpers,index}.js # plugin contract + discovery + singleton
|
|
149
|
+
plugins/<name>/{plugin,handler}.js # ~150 plugins: tools, platforms, memory, gui, core
|
|
115
150
|
skills/ # bundled skill bundles (creative/, software-development/, ops/, data/, planning/)
|
|
116
|
-
website/ # flatspace docs site: flatspace.config.mjs + theme.mjs + content/pages/*.yaml
|
|
117
|
-
bin/freddie.js
|
|
151
|
+
website/ # flatspace docs site: flatspace.config.mjs + theme.mjs + content/pages/*.yaml
|
|
152
|
+
bin/freddie.js # commander CLI: tools, skills, profile, skin, sessions, search, gateway, acp, run, cron, batch, dashboard, help-all
|
|
118
153
|
```
|
|
119
154
|
|
|
120
155
|
## Adding a tool
|
|
121
156
|
|
|
122
|
-
Tools are
|
|
157
|
+
Tools are plugins. Create `plugins/<name>/plugin.js` + `plugins/<name>/handler.js`:
|
|
123
158
|
|
|
124
159
|
```js
|
|
125
|
-
//
|
|
160
|
+
// handler.js
|
|
126
161
|
export const _tool = {
|
|
127
162
|
name: 'my_tool',
|
|
128
163
|
toolset: 'core',
|
|
@@ -132,7 +167,7 @@ export const _tool = {
|
|
|
132
167
|
requiresEnv: ['MY_KEY'],
|
|
133
168
|
}
|
|
134
169
|
|
|
135
|
-
//
|
|
170
|
+
// plugin.js
|
|
136
171
|
import { _tool } from './handler.js'
|
|
137
172
|
export default {
|
|
138
173
|
name: 'my-tool',
|
|
@@ -151,7 +186,7 @@ Add a `CommandDef` to `COMMAND_REGISTRY` in `src/commands/registry.js`:
|
|
|
151
186
|
{ name: 'mycmd', description: '…', category: 'Session', aliases: ['mc'], args_hint: '[arg]' }
|
|
152
187
|
```
|
|
153
188
|
|
|
154
|
-
Dispatch
|
|
189
|
+
Dispatch resolves against the canonical name via `resolveCommand()`. Gateway/telegram/slack views derive automatically.
|
|
155
190
|
|
|
156
191
|
## Adding a gateway platform
|
|
157
192
|
|
|
@@ -174,7 +209,7 @@ export default {
|
|
|
174
209
|
}
|
|
175
210
|
```
|
|
176
211
|
|
|
177
|
-
`makePlatform('myname', opts)`
|
|
212
|
+
`makePlatform('myname', opts)` in `src/gateway/platforms.js` instantiates the adapter via `*Adapter$` name match.
|
|
178
213
|
|
|
179
214
|
## Profile-safe code
|
|
180
215
|
|
|
@@ -188,24 +223,23 @@ Slash commands that mutate system-prompt state default to deferred invalidation;
|
|
|
188
223
|
|
|
189
224
|
## Testing
|
|
190
225
|
|
|
191
|
-
One `test.js` at project root. ≤200 lines. Plain assertions, real data, real services. No mocks. No fixtures. No `tests/` dir. New behavior
|
|
226
|
+
One `test.js` at project root. ≤200 lines. Plain assertions, real data, real services. No mocks. No fixtures. No `tests/` dir. New behavior extends `test.js`, not a new test file.
|
|
227
|
+
|
|
228
|
+
test.js can pass while the CLI is broken — exercise every cli verb that wraps async-API modules (sessions, cron) explicitly, or smoke `node bin/freddie.js <verb>` after changes.
|
|
229
|
+
|
|
230
|
+
On Windows, test.js must call `closeDb()` and log-stream `closeAll()` before exit, otherwise libuv handle teardown crashes the process.
|
|
192
231
|
|
|
193
232
|
## Substrate gotchas
|
|
194
233
|
|
|
195
|
-
- `pi-coding-agent` ships a photon-rs wasm; install needs network.
|
|
234
|
+
- `pi-coding-agent` ships a photon-rs wasm; install needs network.
|
|
196
235
|
- `pi-ai` reads provider keys via `findEnvKeys` / `getEnvApiKey`. Match its env var names (`ANTHROPIC_API_KEY`, etc.).
|
|
197
|
-
- `
|
|
198
|
-
- **
|
|
199
|
-
- **
|
|
200
|
-
- **
|
|
201
|
-
- **
|
|
202
|
-
- **
|
|
203
|
-
- **
|
|
204
|
-
- **createHost decomposition** (2026-05-12) — `src/host/host.js` was 197L with a 111L `createHost` body. Refactored: helper factories `makePi`, `makeGui`, `makeCcHooks`, `makeHooksRegistry`, `makeCcLoaders` (+ `reg`, `guard`, `scopedCfg`, `nullStore`) extracted to sibling `src/host/host_helpers.js` (152L). host.js shrinks to 64L; createHost body ~24L. Both well under 200L cap. Public API unchanged: `import { createHost, discoverPlugins } from './host/host.js'` still works. test.js 12/12 green post-refactor.
|
|
205
|
-
- **freddie exec command Windows invocation** — `plugins/core-cli/plugin.js` registers the `exec` command (commit e5fb1b7) for non-interactive scripted use. Correct invocation on Windows: `bun run bin/freddie.js exec --prompt "..."`. Do NOT use `bun x freddie` — it hangs on Windows due to npm registry fetch timeouts. The command takes `--prompt` (required), `--model` (default ''), `--timeout` (default 60000ms) and is the validated entry point for CI pipelines.
|
|
206
|
-
- **acptoapi-bridge max_tokens silent truncation** — `src/agent/acptoapi-bridge.js` line 20 controls max_tokens passed to the LLM. Prior to commit e5fb1b7, this was set to 1024, which silently truncated responses on generation tasks. Raised to 4096 to prevent hidden content loss. If generation output appears incomplete, verify max_tokens is 4096 or higher.
|
|
207
|
-
- **GitHub Actions deploy-pages@v5 duplicate artifact rejection** — When using `actions/deploy-pages@v5` in a workflow, the action rejects if 2+ artifacts are named "github-pages" (e.g. from a previous failed run re-uploaded by `gh run rerun --failed`). Symptom: deployment step fails with "artifact with name github-pages already exists" or similar. Fix: trigger a fresh run via empty commit instead of rerunning the failed deploy step. `gh run rerun` can silently re-upload transient failures; avoid for deploy failures in particular.
|
|
208
|
-
- **Rebase regression trap — ROOT FIX applied 2026-05-04** — After `git pull --rebase` following a rejected push, a CI auto-bump commit on remote (based on pre-fix tree) can silently revert local fixes. The `anentrypoint-design: file:../anentrypoint-design → ^0.0.40` fix in commit `d469d25` was reverted this way. Underlying cause: `.github/workflows/restore-package.cjs` rewrote the dep back to `file:../anentrypoint-design` after every release, and `publish.yml` ran `git checkout -- package-lock.json`, discarding lockfile re-sync. **ROOT FIX (2026-05-04)**: restore-package.cjs now does `npm view anentrypoint-design version` and pins `^<latest>` instead; publish.yml runs `npm install --package-lock-only` to keep lockfile valid. Verification: after any rebase, check `package.json` + `package-lock.json` match expected values. Sanity check: `git show HEAD:package.json | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.dependencies['anentrypoint-design'])"`. **Recurrence tell** — if pages CI fails with "lock file's anentrypoint-design@X.Y.Z does not satisfy anentrypoint-design@" (empty version = file: dep), do not manually re-pin in package.json forever; audit `.github/workflows/restore-package.cjs` and `publish.yml` for reversion.
|
|
236
|
+
- **libsql async debt**: every call into `src/sessions.js` (listSessions/search/getMessages/createSession/appendMessage) and `src/cron/scheduler.js` (listJobs/createJob/cancelJob/deleteJob) must be `await`ed. Sync callsites silently wrap each call in a Promise that rejects on iteration, surfacing as `TypeError: ... is not iterable`. Tool ACTIONS inner functions are async; handlers await dispatched fn.
|
|
237
|
+
- **Bulk-rename: git grep is case-sensitive on literal patterns**: `git grep -lI <name>` only matches lowercase. For case-variant sweep, use `git grep -liI -e <lower> -e <Title> -e <UPPER>`. Single-form check is a false-clean trap.
|
|
238
|
+
- **codeinsight `🔐 hardcoded secrets` / `🔐 SQL injection` are regex-only**, not value/AST. False positives: env-var names like `DAYTONA_API_KEY` in `process.env.X` references; function param names like `secret` in HMAC helpers; URL query keys like `?appkey=&appsecret=`; HTTP `DELETE` URL paths flagged as SQL `DELETE FROM`. Always read the actual line before treating as a finding.
|
|
239
|
+
- **codeinsight orphan detector misses three reachability paths**: (1) `await import('./path/' + variable + '.js')` dynamic strings (test.js enumerates 10 agent adapters this way); (2) plugin auto-discovery walking `plugins/<dir>/plugin.js` from `discoverPlugins()` in `src/host/host.js`; (3) HTTP-served static files like `src/web/app.js` referenced only from `src/web/index.html`. Exempt these before deleting "dead" files.
|
|
240
|
+
- **freddie exec Windows invocation**: `bun run bin/freddie.js exec --prompt "..."`. Do NOT use `bun x freddie` — hangs on Windows from npm registry fetch timeouts. Args: `--prompt` (required), `--model` (default ''), `--timeout` (default 60000ms). Validated CI entry point.
|
|
241
|
+
- **GitHub Actions `deploy-pages@v5`**: rejects if 2+ artifacts named "github-pages" exist. `gh run rerun --failed` can silently re-upload transients; trigger a fresh run via empty commit instead.
|
|
242
|
+
- **Rebase regression trap**: after `git pull --rebase` following a rejected push, a CI auto-bump commit on remote can revert local fixes. `.github/workflows/restore-package.cjs` pins anentrypoint-design via `npm view` + `^<latest>`; `publish.yml` runs `npm install --package-lock-only` to keep lockfile valid. Recurrence tell: if pages CI fails with `lock file's anentrypoint-design@X.Y.Z does not satisfy anentrypoint-design@` (empty version = file: dep), audit `restore-package.cjs` and `publish.yml` for reversion — do not manually re-pin in `package.json` forever.
|
|
209
243
|
|
|
210
244
|
## Subsystem guide
|
|
211
245
|
|
|
@@ -213,126 +247,57 @@ One `test.js` at project root. ≤200 lines. Plain assertions, real data, real s
|
|
|
213
247
|
|---|---|
|
|
214
248
|
| Agent loop | `src/agent/machine.js` (xstate) + `@mariozechner/pi-agent-core` |
|
|
215
249
|
| CLI entry | `bin/freddie.js` (commander) + pi-coding-agent InteractiveMode |
|
|
216
|
-
|
|
|
250
|
+
| Tools | `plugins/<name>/{plugin,handler}.js` (no `src/tools/`) |
|
|
217
251
|
| Toolsets | `src/toolsets.js` |
|
|
218
|
-
| Session store | `src/sessions.js` (
|
|
252
|
+
| Session store | `src/sessions.js` (libsql + FTS5, async API) |
|
|
219
253
|
| Home + profiles | `src/home.js` |
|
|
220
254
|
| Multi-project registry | `src/projects.js` (isolated FREDDIE_HOME per project) |
|
|
221
255
|
| Structured logging | `src/observability/log.js` |
|
|
222
256
|
| Config | `src/config.js` |
|
|
223
257
|
| Commands | `src/commands/registry.js` |
|
|
224
258
|
| Skin engine | `src/skin/engine.js` |
|
|
225
|
-
| Gateway + platforms | `src/gateway/run.js` + `
|
|
259
|
+
| Gateway + platforms | `src/gateway/run.js` + `plugins/platform-*/` |
|
|
226
260
|
| ACP (JSON-RPC stdio) | `src/acp/server.js` |
|
|
227
261
|
| TUI | substrate (`pi-tui` + pi-coding-agent) |
|
|
228
|
-
| Plugins + memory | `src/plugins/manager.js` + `src/plugins/memory/provider.js` |
|
|
262
|
+
| Plugins + memory | `src/plugins/manager.js` + `src/plugins/memory/provider.js` + `plugins/memory-*/` |
|
|
229
263
|
| Skills loader | `src/skills/index.js` — content drops into `~/.freddie/skills/` |
|
|
230
264
|
| Context compressor | `src/agent/compress/{tokens,policy,prompt,prune,fallback,compressor,index}.js` |
|
|
231
265
|
| Documentation site | `website/` (flatspace + content/pages/*.yaml + theme.mjs) |
|
|
232
|
-
| Cron scheduler | `src/cron/{scheduler,cron-parse}.js` |
|
|
266
|
+
| Cron scheduler | `src/cron/{scheduler,cron-parse}.js` (async API) |
|
|
233
267
|
| Batch runner | `src/batch.js` |
|
|
234
|
-
| Execution environments | `src/tools/environments/{local,docker,ssh}.js` (modal/daytona/singularity
|
|
235
|
-
| Dashboard | `src/web/{server,index.html}`
|
|
268
|
+
| Execution environments | `src/tools/environments/{local,docker,ssh}.js` (modal/daytona/singularity are explicit residual) |
|
|
269
|
+
| Dashboard | `src/web/{server,app,state,routes,index.html}` — thin mount over `anentrypoint-design` SDK |
|
|
236
270
|
| Auth store | `src/auth.js` (FileAuthStore) + pi-ai key resolution |
|
|
237
271
|
| Context engine | `src/context/engine.js` |
|
|
238
|
-
| Browser tool | `
|
|
239
|
-
|
|
|
240
|
-
|
|
|
241
|
-
|
|
|
242
|
-
| Memory tool | `src/tools/memory.js` |
|
|
243
|
-
| Delegate | `src/tools/delegate.js` |
|
|
244
|
-
| Bundled skills | `skills/` (5 categories, 12 SKILL.md placeholders) |
|
|
245
|
-
| Integration tests | one `test.js` at root per gm policy |
|
|
272
|
+
| Browser tool | `plugins/browser/` (puppeteer-core, lazy) |
|
|
273
|
+
| LLM resolver | `src/agent/llm_resolver.js` (thin shim over `acptoapi.chat`) |
|
|
274
|
+
| Bundled skills | `skills/` (5 categories) |
|
|
275
|
+
| Integration tests | one `test.js` at root |
|
|
246
276
|
|
|
247
277
|
## Cross-project Rust gotchas
|
|
248
278
|
|
|
249
|
-
- **rs-plugkit exec utility verbs
|
|
250
|
-
- **rs-exec timeout alias
|
|
251
|
-
|
|
279
|
+
- **rs-plugkit exec utility verbs**: binary advertises `exec:status`, `exec:close`, `exec:sleep` in hook help. If the Cmd enum is missing variants, use `exec:wait <secs>` for waits and read task output files via `fs.readFileSync` instead of `exec:status`.
|
|
280
|
+
- **rs-exec timeout alias**: both `--timeout` (long-form) and `--timeout-ms` (plugin convention) are accepted on `Cmd::Exec` and `Cmd::Bash`.
|
|
252
281
|
|
|
253
282
|
## Plugsdk integration
|
|
254
283
|
|
|
255
|
-
-
|
|
256
|
-
-
|
|
257
|
-
-
|
|
258
|
-
## Integration test status (2026-04-30)
|
|
259
|
-
|
|
260
|
-
All 21 named integration tests in `test.js` pass (exit 0). Subsystem coverage:
|
|
261
|
-
- agent loop, CLI, gateway, plugins, skills, sessions, cron, batch, dashboard, ACP, web, context-engine, compressor, auth, observability
|
|
262
|
-
|
|
263
|
-
## Windows libuv cleanup caveat
|
|
264
|
-
|
|
265
|
-
`test.js` adds explicit cleanup hooks before exit (`closeDb()`, `closeAll()` for log streams) to prevent libuv handle-teardown crash on Windows. Without these, exit hangs or returns non-zero. Critical for stability on Windows hosts.
|
|
284
|
+
- plugsdk publishes automatically to npm registry on push to main.
|
|
285
|
+
- Freddie installs plugsdk from registry, NOT via `file:` dep — a stale `"link": true` entry in package-lock.json blocks `npm ci`.
|
|
286
|
+
- `src/host/contract.js` re-exports `piAdapter`, `HookType`, `allowResult`, `blockResult`, `modifyResult` from plugsdk and uses `HookType` constants in `FREDDIE_TO_SDK_HOOK` mapping.
|
|
266
287
|
|
|
267
|
-
##
|
|
288
|
+
## opencode CLI shim
|
|
268
289
|
|
|
269
|
-
|
|
270
|
-
- **acptoapi dep pattern** (2026-05-12) — `package.json` now pins `"acptoapi": "^1.0.55"` from the npm registry (CI auto-bumps on each acptoapi push; restore-package.cjs ROOT FIX prevents file: regressions). For local SDK iteration, swap to `file:../acptoapi` temporarily. CJS/ESM boundary bridged via `createRequire(import.meta.url)` in freddie ESM files that import acptoapi CJS exports.
|
|
271
|
-
- **LLM resolver priority** (2026-05-10) — (1) explicit provider+key, (2) acptoapi if `/v1/models` returns 200, (3) `agent.model_preference` config array (ordered failover, sampler-gated), (4) `sdk.buildAutoChain()` env-key scan, (5) throw. `PROVIDER_KEYS` and `PROVIDER_DEFAULTS` imported from `acptoapi` — not maintained in freddie. `sdk.chat()` returns OpenAI `{choices:[{message}]}` format; `sdkChat()` adapter in llm_resolver converts to freddie's `{content, tool_calls, raw}`.
|
|
272
|
-
- **Model sampler — direct acptoapi import** (2026-05-17) — `src/agent/model-sampler.js` was deleted as redundant. Sampler funcs (`isAvailable`, `markFailed`, `markOk`, `resetAvailability`, `getStatus`, `probe`, `startSampler`, `stopSampler`, `createSampler`) come straight from `acptoapi` via `createRequire`. Backoff logic (5-step 30s→480s, createSampler factory, singleton) lives in `c:\dev\acptoapi\lib\sampler.js`.
|
|
273
|
-
- **model_preference config key** (2026-05-10) — `agent.model_preference: []` in `~/.freddie/config.yaml`. Array of `{ provider, model? }` objects; `resolveCallLLM` tries each in order, skipping unavailable (sampler-gated) and marking failures with backoff. Config v2 migration adds the key on upgrade from v1.
|
|
274
|
-
- **acptoapi Claude backend verified** (2026-05-03) — Live agent loop working: start acptoapi server `node bin/agentapi.js --port 4800` (c:\dev\acptoapi), then set `FREDDIE_LLM_URL=http://localhost:4800/v1 + FREDDIE_LLM_MODEL=claude/haiku`. Model prefix `claude/` routes to Claude CLI subprocess. freddie test.js 12/12 green confirming integration production-ready. Dashboard `/api/chat` and `/api/batch` are POST-only; GET returns 404 (correct).
|
|
290
|
+
On Windows, use the npm install: `npm install -g opencode-ai` → `C:\Users\user\AppData\Roaming\npm\opencode.cmd`. The bun-installed shim (`C:\Users\user\.bun\bin\opencode.exe`) is broken — its wrapper looks for `C:\Users\user\node_modules\opencode-ai\bin\opencode` and fails with `MODULE_NOT_FOUND`. Do NOT `bun install -g opencode-ai`.
|
|
275
291
|
|
|
276
|
-
|
|
292
|
+
Start ACP daemon: `& 'C:\Users\user\AppData\Roaming\npm\opencode.cmd' serve --port 4790 --hostname 127.0.0.1`. Verify via `GET http://127.0.0.1:4790/` returning 200. `OPENCODE_SERVER_PASSWORD is not set` warning is harmless for localhost.
|
|
277
293
|
|
|
278
|
-
|
|
294
|
+
## scripts/sync-upstream.mjs
|
|
279
295
|
|
|
280
|
-
|
|
296
|
+
`node scripts/sync-upstream.mjs [--dry-run] [pkg ...]` bumps sibling dep entries (plugsdk, acptoapi, anentrypoint-design, gm-cc) in `package.json` to `^<latest>` from npm registry, then runs `npm install --package-lock-only`. Skips `file:` deps. Wired into `.github/workflows/sync-upstream.yml` (weekly cron + workflow_dispatch); opens a PR via `peter-evans/create-pull-request@v6` on changes.
|
|
281
297
|
|
|
282
|
-
|
|
283
|
-
- 2026-05-04 (session 2): Audit cycle — 5 queries fired (pi-ai env keys, profile safe paths, libsql async debt, browser syntax errors, plugin architecture contract). rs-learn store still returning "No recall results" or off-topic trajectory entries. All 5 recalls failed. 0 items migrated; all AGENTS.md facts retained (safe default). Ingest path confirmed live (4 facts accepted), but retrieval side empty. Likely requires backend indexing rebuild or cross-session propagation.
|
|
284
|
-
- 2026-05-01: 5 items queried (pi-ai keys, profile paths, cache safety, floosie composition, browser errors); rs-learn store unavailable (exec:recall returned no results). 0 items migrated. New facts (anentrypoint-design build, dashboard live-rerender caveat, libuv spawn caveat) ingested directly into rs-learn; audit will retry in future sessions.
|
|
285
|
-
- 2026-05-01 (session 2): 5 items queried (pi-ai env keys, profile safe paths, cache safety, floosie composition, browser syntax errors). rs-learn store still empty. 0 items migrated. Refined anentrypoint-design source/dist skew entry in AGENTS.md to include silent-failure pageerror diagnostic. New fact `reference/anentrypoint-design-dist-rebuild` ingested.
|
|
286
|
-
- 2026-05-03: Pre-rename validation snapshot recorded (all 12 test.js groups, CLI, tools, 284 files, version drift). Baseline stored to isolate post-rename regressions.
|
|
287
|
-
- 2026-05-03 (session 2): Ingested feedback/app-js-size-violation (src/web/app.js 548L violation) into AGENTS.md Substrate gotchas. rs-learn store unavailable (exec:memorize missing binary). 0 migration audit items queried. 1 new fact added.
|
|
288
|
-
- 2026-05-03 (session 3): Added Website theme + YAML caveats section (3 items: structured-YAML rendering, YAML colon-space trap, SSR innerHTML injection). rs-learn store still unavailable (exec:memorize → exit 127, command not found). 0 migration audit items queried. 3 new facts added to AGENTS.md only.
|
|
289
|
-
- 2026-05-03 (session 3): Ingested libsql-async-debt-class into AGENTS.md Substrate gotchas (sessions.js + cron/scheduler.js async callsites; silent TypeError class; test.js passes while CLI broken). rs-learn store still unavailable (exec:memorize/exec:recall not on PATH). 0 migration audit items queried. 1 new fact added.
|
|
290
|
-
- 2026-05-03 (session 4): Plugin-architecture decomposition recorded — added "Plugin architecture" section before Layout, rewrote "Adding a tool" + "Adding a gateway platform" for plugins/<name>/{plugin,handler}.js shape. Ingested 6 facts to rs-learn (project/freddie-plugin-architecture, reference/freddie-host-contract, reference/freddie-plugin-ctx, project/freddie-migrated-subsystems, reference/freddie-thin-shims, project/freddie-plugin-witness). Audit: 5 queries fired (pi-ai env keys, profile safe paths, libsql async debt, browser inline module errors, yaml colon space trap, plus self-test on freddie-plugin-architecture) — all returned "No recall results". rs-learn ingest path live but retrieval side empty for this session (likely needs learn-build propagation). 0 items migrated; AGENTS.md items retained.
|
|
291
|
-
- 2026-05-04: Recorded freddie publish workflow root fix — `.github/workflows/restore-package.cjs` now pins anentrypoint-design via `npm view` + version (`^<latest>`) instead of file: dep; `publish.yml` runs `npm install --package-lock-only` to sync lockfile. Updated "Rebase regression trap" entry with detailed causation + recurrence tell. Added new "GitHub Actions deploy-pages@v5 duplicate artifact rejection" caveat (rerun --failed silently re-uploads; trigger fresh run instead). rs-learn store still unavailable. 0 items migrated; 2 facts added/refined in AGENTS.md.
|
|
292
|
-
- 2026-05-10: 15-provider LLM resolver expansion. Added model-sampler.js (backoff), PROVIDER_KEYS/DEFAULTS expansion, model_preference config key, config v2 migration. Updated "LLM resolver priority" + "15-provider support" + "Model sampler" + "model_preference" entries in AGENTS.md. test.js 12/12 green at exactly 200 lines. rs-learn store still unavailable. 4 new facts added to AGENTS.md.
|
|
293
|
-
- 2026-05-17 (continuation): +2 memorizes for uncommitted working-tree state — freddie-llm-resolver-anthropic-fallback-removed (project) captures the ~95L deletion of @anthropic-ai/sdk fallback in src/agent/llm_resolver.js (aligns with 'acptoapi is THE SDK' policy); freddie-stray-test-files-policy-violation (feedback) flags untracked src/agent/acptoapi-provider.js + test-acptoapi.js as violating the 'one test.js at root' rule. Total session exfiltration: 20 KV keys.
|
|
294
|
-
- 2026-05-17: AGENTS.md exfiltration to learning store. 18 memorize dispatches landed (KV keys mem-1779041362557 through mem-1779041487836, 21KB total) covering: substrate stack, dynamic stack contract, acptoapi SDK surface, plugin architecture, multi-project workspace, 3 gotcha batches (browser/async, codeinsight FPs, exec/CI), dashboard caveats, website YAML, LLM resolver+sampler, trajectory v2 + validation witness, opencode+kilo ACP, gm-cc + sync-upstream, model availability matrix, plugsdk + Rust gotchas, testing/profile/cache policies, layout map. learn-status confirms wasm-via-KV mode. In-session recall still returns empty (consistent with documented retrieval-side gap; index propagation is cross-session). PRD 18/18 resolved; phases PLAN -> EXECUTE -> VERIFY -> COMPLETE.
|
|
295
|
-
- 2026-05-10 (session 2): Deep integration audit between freddie and acptoapi SDK. Moved sampler logic into acptoapi lib/sampler.js (createSampler factory + singleton, 5-step backoff). Added lib/provider-maps.js (PROVIDER_KEYS + PROVIDER_DEFAULTS, 17 providers, derived from BRANDS+auto-chain). freddie: switched to file:../acptoapi dep, model-sampler.js replaced with 13-line re-export shim, llm_resolver.js simplified 131→95L (OPENAI_COMPAT removed, PROVIDER_KEYS/DEFAULTS from SDK, buildAutoChain() for auto-scan, sdkChat() adapter for OpenAI→freddie format). test.js 12/12 green. Both repos pushed. AGENTS.md updated with acptoapi dep pattern, re-export shim pattern, CJS/ESM bridge via createRequire.
|
|
298
|
+
## Trajectory recorder
|
|
296
299
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
- **anentrypoint-design source/dist pattern** (2026-05-10) — No vendor dir in freddie (`src/web/vendor/` removed, commit 52d0732). `server.js` always serves SDK from `node_modules/anentrypoint-design/dist/`. freddie's `package.json` pins `"anentrypoint-design": "file:../anentrypoint-design"` so `npm install` symlinks directly to the live source — no publish cycle needed. Dist is rebuilt by CI on push to the SDK repo; rebuild locally after SDK edits via `node scripts/build.mjs` in `C:/dev/anentrypoint-design` (emits dist/247420.js ~441KB + 247420.css; build ~150ms; warning "[247420] missing css: vendor/rippleui-1.12.1.css" is benign). Skip rebuild before using a new component and the pageerror "component is not a function" kills app mount silently.
|
|
300
|
-
- **Live page rerender caveat** — AppState.body caching (page computed once at navigation, body saved) breaks for live routes like #/chat where AppState is mutated mid-flight (SSE pushes new messages). Fix: detect live routes in rerender(), recompute body: `if (AppState.hash === '#/chat') { Promise.resolve(PAGES['#/chat']()).then(b => { AppState.body = b; _mount() }); return }`. Any future live-streaming pages (cron output, traces) need the same treatment.
|
|
301
|
-
- **libuv spawn caveat** — Spawning createDashboard() from exec:nodejs and keeping process alive triggers libuv UV_HANDLE_CLOSING crash on shutdown. Reliable alternative: boot via `node bin/freddie.js dashboard --port <port>`. Liveness checks: exec:browser → page.goto → window.__debug.dashboard() returns {booted, ts, framework, route}; window.__debug.chat() exposes {messages, streaming, draft}; window.__debug.sendChat(text) drives round-trips.
|
|
302
|
-
- **anentrypoint-design theme token descendant selector fix** (2026-05-04) — SDK CSS scopes light/dark theme variables under descendant selectors `.ds-247420 [data-theme="dark"]` and `.ds-247420 [data-theme="light"]`. Placing both `class="ds-247420"` and `data-theme="dark"` on the same node (e.g. `<html>`) breaks theming: the descendant selector does NOT match when both attributes are on the same element. Fix: scope `class="ds-247420"` on `<html>` and `data-theme="dark"` on `<body>`. Theme toggle must write to `document.body`, not `document.documentElement`. Browser-witnessed commit 17dfce0: pre-fix dark/light backgrounds identical (rgb 26,27,30); post-fix dark=rgb(26,27,30), light=rgb(245,240,228). Symptom: toggle theme in dashboard, page background does not change.
|
|
303
|
-
|
|
304
|
-
## Website theme + YAML caveats (2026-05-03)
|
|
305
|
-
|
|
306
|
-
- **Structured-YAML rendering** — `website/theme.mjs` (164L) renders structured YAML via 247420 design vocabulary, not raw markdown. Consumes `page.hero` (heading/subheading/accent/body/badges/ctas), `page.sections[]` (rotating rail color green→purple→mascot→sun→flame→sky by section index, optional `lede` + per-item `benefit` italic), `page.examples[]` (railed link list with mono numeric ranks + ↗ glyph). Falls back to `page.body` markdown for prose. Style block inlined so rail/dot/chip/btn classes work without ds-247420 SDK CSS loading first. To get a specific rail color, reorder sections. Prefer enriching hero+sections+examples over expanding body markdown; copy existing YAML structure as template for new pages.
|
|
307
|
-
- **YAML colon-space trap** — In `website/content/pages/*.yaml`, any value containing `: ` outside backticks (e.g. `[linux, macos, windows]`, `requiresEnv: ['MY_KEY']` code snippets) MUST be double-quoted. The parser otherwise interprets the embedded colon as a mapping and the file fails to load. Hit twice: tools.yaml line 72, skills.yaml line 40. Fix is wrapping the whole value in `"..."`.
|
|
308
|
-
- **SSR innerHTML injection beats client dispatch** — anentrypoint-design exposes Hero/HomeView/Panel/Row/Section/WorksList (and more), but pre-mounted SSR injection via innerHTML is more reliable than dispatching components client-side at build — avoids depending on SDK loading before the static HTML paints. Emitted HTML carries rail/dot/chip/btn classes with inline styles to be self-sufficient.
|
|
309
|
-
|
|
310
|
-
## Residual complement (NOT ported this session)
|
|
311
|
-
|
|
312
|
-
Genuinely out of session reach, with reasons:
|
|
313
|
-
|
|
314
|
-
- **Real credentials per platform** — adapters work; setup needs you to provide TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, SLACK_BOT_TOKEN, etc. before `start()` succeeds. Listed in each adapter's `getRequiredEnv()`.
|
|
315
|
-
- **Memory provider API accounts** — 8 provider modules call real endpoints. Test runs construct objects but don't hit external APIs without keys (HONCHO_API_KEY etc.).
|
|
316
|
-
- **modal / daytona / singularity environments** — only local/docker/ssh ported. The other three are heavyweight remote-execution deps (Modal SDK, Daytona Cloud, Singularity containers).
|
|
317
|
-
- **Bedrock / codex provider adapters** — `pi-ai` covers Anthropic/OpenAI/Groq. Adding bedrock/codex requires registering custom providers via `pi-ai`'s `registerApiProvider`.
|
|
318
|
-
- **TUI Ink rewrite** — `pi-tui` IS the substrate (architectural choice, not a port).
|
|
319
|
-
- **15k pytest tests** — single `test.js` per gm policy.
|
|
320
|
-
|
|
321
|
-
## Dashboard Agents Section — Design Decision Needed (2026-05-04)
|
|
322
|
-
|
|
323
|
-
User requested "agents section" for dashboard. Exploration result: agent state is **not exposed** via HTTP API. Dashboard is client-side UI consuming only HTTP endpoints. Current endpoints: `/api/sessions`, `/api/tools`, `/api/health`. No `/api/agents`.
|
|
324
|
-
|
|
325
|
-
To implement:
|
|
326
|
-
1. Export agent machine state (xstate snapshot) from `src/agent/machine.js`
|
|
327
|
-
2. Create new HTTP endpoint `/api/agents` returning count, active agent, metrics
|
|
328
|
-
3. Add `#/agents` route + new PAGES entry to `src/web/app.js`
|
|
329
|
-
4. Register `window.__debug.agents()` observability global
|
|
330
|
-
|
|
331
|
-
**Blocked on**: Design decision (what metrics? count only? session associations? perf data?). Deferred pending user clarification.
|
|
332
|
-
|
|
333
|
-
## Trajectory recorder schema v2 (2026-05-12)
|
|
334
|
-
|
|
335
|
-
`src/agent/machine.js::writeTrajectory()` writes one JSON per turn under `<FREDDIE_HOME>/trajectories/<ts>-<slug>.json` whenever `agent.save_trajectories=true` OR `--witness <path>` is set on `freddie exec`. Schema (`schema_version: 2`):
|
|
300
|
+
`src/agent/machine.js::writeTrajectory()` writes one JSON per turn under `<FREDDIE_HOME>/trajectories/<ts>-<slug>.json` when `agent.save_trajectories=true` OR `--witness <path>` is set on `freddie exec`. Schema (`schema_version: 2`):
|
|
336
301
|
|
|
337
302
|
```
|
|
338
303
|
{
|
|
@@ -346,20 +311,11 @@ To implement:
|
|
|
346
311
|
}
|
|
347
312
|
```
|
|
348
313
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
Captured fields per acceptance bar:
|
|
352
|
-
- (a) tool_call args → `tool_calls[].arguments` + `messages[].tool_calls`
|
|
353
|
-
- (b) tool_result → `tool_results[]` + `messages[role:'tool']`
|
|
354
|
-
- (c) LLM call timing/duration/provider/content_length/tool_calls_count → `llm_calls[]`
|
|
355
|
-
- (d) compressor invocations → counted from `messages[role:'system']` matching `[trajectory.compressed]`
|
|
356
|
-
- (e) errors with stack → `error_stack` + per-`llm_call` `stack` field
|
|
314
|
+
`--witness <path>` writes a parallel JSONL stream with one event per line (`session_start`, `message`, `llm_call`, `session_end`). `runTurn({witnessPath})` is the in-code equivalent.
|
|
357
315
|
|
|
358
|
-
|
|
316
|
+
## LLM validation witness
|
|
359
317
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
`.gm/llm-validation.json` is the canonical witness for provider reachability. Generated by an out-of-band validator script (per session) that probes every key in `process.env` matching `<PROVIDER>_API_KEY`, plus the acp-daemon endpoints (kilo on 4780, opencode on 4790) and claude-cli subprocess. Shape:
|
|
318
|
+
`.gm/llm-validation.json` is the canonical witness for provider reachability. Generated by an out-of-band validator that probes every `<PROVIDER>_API_KEY` in `process.env`, plus acp-daemon endpoints (kilo on 4780, opencode on 4790) and claude-cli subprocess. Shape:
|
|
363
319
|
|
|
364
320
|
```
|
|
365
321
|
{
|
|
@@ -370,29 +326,9 @@ Witnessed 2026-05-12: mistral-large 4-iteration loop on penguins repo produced 4
|
|
|
370
326
|
}
|
|
371
327
|
```
|
|
372
328
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
## opencode CLI shim caveat (2026-05-12)
|
|
376
|
-
|
|
377
|
-
`opencode-ai@1.14.48` installs successfully via `npm install -g opencode-ai` and exposes a working binary at `C:\Users\user\AppData\Roaming\npm\opencode.cmd` (Windows). The bun-installed shim at `C:\Users\user\.bun\bin\opencode.exe` is BROKEN — its wrapper looks for `C:\Users\user\node_modules\opencode-ai\bin\opencode` (wrong path) and fails with `MODULE_NOT_FOUND`. **Workflow**: use npm version; do NOT `bun install -g opencode-ai`. To start ACP daemon: `& 'C:\Users\user\AppData\Roaming\npm\opencode.cmd' serve --port 4790 --hostname 127.0.0.1`. Verified by GET http://127.0.0.1:4790/ returning 200 (HTML shell). Warning `OPENCODE_SERVER_PASSWORD is not set` is harmless for localhost use.
|
|
378
|
-
|
|
379
|
-
## gm-cc skill registry (2026-05-12)
|
|
380
|
-
|
|
381
|
-
`plugins/gm-cc/plugin.js` auto-discovers 12 SKILL.md files from npm `gm-cc` package and registers them via `pi.skills.register({name: 'gm:<name>', description, content, source: 'gm-cc'})`. Registered skills: browser, code-search, create-lang-plugin, gm, gm-complete, gm-emit, gm-execute, governance, pages, planning, ssh, update-docs. Inspect with `node bin/freddie.js skills | findstr gm:`.
|
|
382
|
-
|
|
383
|
-
## kilo ACP integration (2026-05-12)
|
|
329
|
+
## Model availability matrix
|
|
384
330
|
|
|
385
|
-
`
|
|
386
|
-
|
|
387
|
-
Note: kilo + opencode ACP backends return content only, no tool_calls (the LLM-side tool layer is opaque to freddie). For multi-iteration tool-using loops, use OpenAI-compatible providers (mistral, openrouter, sambanova, groq) instead.
|
|
388
|
-
|
|
389
|
-
## scripts/sync-upstream.mjs (2026-05-12)
|
|
390
|
-
|
|
391
|
-
`node scripts/sync-upstream.mjs [--dry-run] [pkg ...]` bumps sibling dep entries (plugsdk, acptoapi, anentrypoint-design, gm-cc) in package.json to `^<latest>` from npm registry, then runs `npm install --package-lock-only`. Skips `file:` deps (local-dev pattern). Wired into `.github/workflows/sync-upstream.yml` (weekly cron + workflow_dispatch) which opens a PR via peter-evans/create-pull-request@v6 when changes land. Dry-run validated: detected `acptoapi: ^1.0.52 -> ^1.0.54`.
|
|
392
|
-
|
|
393
|
-
## Model availability matrix (2026-05-13)
|
|
394
|
-
|
|
395
|
-
`scripts/build-model-availability.js` writes `.gm/model-availability.json` — a Cartesian witness of every (provider × model × mode) cell. Per-cell probe with `PER_CELL_TIMEOUT_MS` (15s default) and `MAX_MODELS_PER_PROVIDER` (5 default). Provider keys read from `.env`. Feeds acptoapi sampler: `probeDirect` and `probeAgentLoop` both call `markOk`/`markFailed` on outcome; both skip when `isAvailable(provider)===false` (sampler-backoff respected).
|
|
331
|
+
`scripts/build-model-availability.js` writes `.gm/model-availability.json` — a Cartesian witness of every (provider × model × mode) cell. Per-cell probe with `PER_CELL_TIMEOUT_MS` (15s default) and `MAX_MODELS_PER_PROVIDER` (5 default). Provider keys read from `.env`. Feeds acptoapi sampler: `probeDirect` and `probeAgentLoop` call `markOk`/`markFailed`; both skip when `isAvailable(provider)===false`.
|
|
396
332
|
|
|
397
333
|
Schema:
|
|
398
334
|
|
|
@@ -410,10 +346,15 @@ Schema:
|
|
|
410
346
|
|
|
411
347
|
6 skipped-reasons: `no_api_key_for_provider`, `sampler_backoff_active`, `daemon_not_running:<PORT>`, `mode_mismatch:<detail>`, `claude_cli_not_installed`, `unknown_mode`.
|
|
412
348
|
|
|
413
|
-
|
|
349
|
+
Dashboard endpoints (registered in `plugins/gui-models-discover/plugin.js`):
|
|
414
350
|
- `GET /api/models/availability` → full JSON (404 with `{error,hint}` if file absent)
|
|
415
351
|
- `GET /api/models/availability/summary` → `{timestamp, daemons, summary}` (truncated)
|
|
416
|
-
- `POST /api/models/availability/rebuild` → 202 `{ok, pid, jobId}` (spawns
|
|
352
|
+
- `POST /api/models/availability/rebuild` → 202 `{ok, pid, jobId}` (spawns build; 409 if rebuild in flight)
|
|
353
|
+
|
|
354
|
+
Sampler integration: agent-loop failures feed acptoapi's per-provider backoff (5-step 30s→480s). Provider-scoped granularity only — per-(provider,model) would need an acptoapi sampler schema change.
|
|
417
355
|
|
|
418
|
-
|
|
356
|
+
## Website theme + YAML
|
|
419
357
|
|
|
358
|
+
- `website/theme.mjs` renders structured YAML via 247420 design vocabulary, not raw markdown. Consumes `page.hero` (heading/subheading/accent/body/badges/ctas), `page.sections[]` (rotating rail color green→purple→mascot→sun→flame→sky by section index, optional `lede` + per-item `benefit` italic), `page.examples[]` (railed link list with mono numeric ranks + ↗ glyph). Falls back to `page.body` markdown for prose. Style block inlined so rail/dot/chip/btn classes work without ds-247420 SDK CSS loading first. Prefer enriching hero+sections+examples over expanding body markdown.
|
|
359
|
+
- **YAML colon-space trap**: in `website/content/pages/*.yaml`, any value containing `: ` outside backticks (e.g. `[linux, macos, windows]`, `requiresEnv: ['MY_KEY']` snippets) MUST be double-quoted. The parser otherwise interprets the embedded colon as a mapping and the file fails to load.
|
|
360
|
+
- SSR innerHTML injection beats client dispatch for site pages — emit HTML with rail/dot/chip/btn classes + inline styles so it paints before the SDK loads.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "freddie",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.107",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Open JS agent harness built on pi-mono, floosie, xstate, and anentrypoint-design",
|
|
6
6
|
"bin": {
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"@mariozechner/pi-ai": "^0.70.6",
|
|
27
27
|
"@mariozechner/pi-coding-agent": "^0.70.6",
|
|
28
28
|
"@mariozechner/pi-tui": "^0.70.6",
|
|
29
|
-
"acptoapi": "^1.0.
|
|
30
|
-
"anentrypoint-design": "^0.0.
|
|
29
|
+
"acptoapi": "^1.0.104",
|
|
30
|
+
"anentrypoint-design": "^0.0.127",
|
|
31
31
|
"commander": "^14.0.0",
|
|
32
32
|
"express": "^5.0.0",
|
|
33
33
|
"flatspace": "^1.0.18",
|
|
34
34
|
"floosie": "^0.6.14",
|
|
35
|
-
"gm-
|
|
35
|
+
"gm-skill": "latest",
|
|
36
36
|
"js-yaml": "^4.1.0",
|
|
37
37
|
"libsql-plugkit-client": "^0.0.10",
|
|
38
38
|
"plugsdk": "^1.0.20",
|
|
@@ -7,14 +7,21 @@ const _require = createRequire(import.meta.url)
|
|
|
7
7
|
function resolveSkillMd() {
|
|
8
8
|
const home = process.env.USERPROFILE || process.env.HOME
|
|
9
9
|
if (home) {
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const candidates = [
|
|
11
|
+
path.join(home, '.agents', 'skills', 'gm-skill', 'SKILL.md'),
|
|
12
|
+
path.join(home, '.claude', 'skills', 'gm-skill', 'SKILL.md'),
|
|
13
|
+
]
|
|
14
|
+
for (const p of candidates) {
|
|
15
|
+
if (fs.existsSync(p)) return p
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (const pkg of ['gm-skill', 'gm-cc']) {
|
|
19
|
+
try {
|
|
20
|
+
const pkgPath = _require.resolve(`${pkg}/package.json`)
|
|
21
|
+
const candidate = path.join(path.dirname(pkgPath), 'skills', 'gm-skill', 'SKILL.md')
|
|
22
|
+
if (fs.existsSync(candidate)) return candidate
|
|
23
|
+
} catch {}
|
|
12
24
|
}
|
|
13
|
-
try {
|
|
14
|
-
const pkgPath = _require.resolve('gm-cc/package.json')
|
|
15
|
-
const candidate = path.join(path.dirname(pkgPath), 'skills', 'gm-skill', 'SKILL.md')
|
|
16
|
-
if (fs.existsSync(candidate)) return candidate
|
|
17
|
-
} catch {}
|
|
18
25
|
return null
|
|
19
26
|
}
|
|
20
27
|
|