freddie 0.0.123 → 0.0.124
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/package.json +5 -8
- package/src/agent/acptoapi-bridge.js +9 -4
- package/src/agent/codex_responses_adapter.js +3 -1
- package/src/agent/gemini_native_adapter.js +3 -1
- package/src/agent/image_gen_provider.js +3 -1
- package/src/agent/llm_resolver.js +22 -4
- package/src/agent/model-discovery.js +3 -1
- package/src/host/index.js +20 -14
- package/src/agent/__browser_shims/alias-plugin.mjs +0 -24
- package/src/agent/__browser_shims/build.mjs +0 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "freddie",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.124",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Open JS agent harness built on pi-mono, floosie, xstate, and anentrypoint-design",
|
|
6
6
|
"bin": {
|
|
@@ -18,16 +18,14 @@
|
|
|
18
18
|
"start": "node bin/freddie.js",
|
|
19
19
|
"test": "node test.js",
|
|
20
20
|
"build:browser": "vite build --config vite.browser.config.js",
|
|
21
|
+
"build:browser-shim": "node scripts/browser-shim-build/build.mjs",
|
|
21
22
|
"link-local-design": "node scripts/link-local-design.mjs"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
24
|
-
"@anentrypoint/libsql-plugkit-client": "github:AnEntrypoint/libsql-plugkit-client",
|
|
25
25
|
"@libsql/client": "^0.5.0",
|
|
26
|
-
"@mariozechner/pi-agent-core": "^0.70.6",
|
|
27
26
|
"@mariozechner/pi-ai": "^0.70.6",
|
|
28
|
-
"@mariozechner/pi-coding-agent": "^0.70.6",
|
|
29
27
|
"@mariozechner/pi-tui": "^0.70.6",
|
|
30
|
-
"acptoapi": "^1.0.
|
|
28
|
+
"acptoapi": "^1.0.144",
|
|
31
29
|
"anentrypoint-design": "latest",
|
|
32
30
|
"commander": "^14.0.0",
|
|
33
31
|
"express": "^5.0.0",
|
|
@@ -36,10 +34,8 @@
|
|
|
36
34
|
"gm-plugkit": "^2.0.1535",
|
|
37
35
|
"gm-skill": "latest",
|
|
38
36
|
"js-yaml": "^4.1.0",
|
|
39
|
-
"libsql-plugkit-client": "^0.0.10",
|
|
40
37
|
"plugsdk": "^1.0.20",
|
|
41
|
-
"xstate": "^5.31.0"
|
|
42
|
-
"zod": "^4.0.0"
|
|
38
|
+
"xstate": "^5.31.0"
|
|
43
39
|
},
|
|
44
40
|
"optionalDependencies": {
|
|
45
41
|
"@libsql/darwin-arm64": "0.3.19",
|
|
@@ -85,6 +81,7 @@
|
|
|
85
81
|
"xstate"
|
|
86
82
|
],
|
|
87
83
|
"devDependencies": {
|
|
84
|
+
"esbuild": "^0.28.0",
|
|
88
85
|
"vite": "^8.0.13"
|
|
89
86
|
}
|
|
90
87
|
}
|
|
@@ -11,10 +11,15 @@ const envVal = (k) => { try { return (typeof process !== 'undefined' && process.
|
|
|
11
11
|
const ACPTOAPI_TIMEOUT_MS = Number(envVal('FREDDIE_LLM_TIMEOUT_MS')) || 240000
|
|
12
12
|
|
|
13
13
|
export function getAcptoapiUrl() {
|
|
14
|
-
//
|
|
15
|
-
// (the dashboard health row, CLI banner)
|
|
16
|
-
//
|
|
17
|
-
|
|
14
|
+
// Returns the configured dialable acptoapi URL, or null when unset. Most
|
|
15
|
+
// callers (the dashboard health row, CLI banner) treat this as display/logging
|
|
16
|
+
// only -- there is no listening port required for the in-process callLLM()
|
|
17
|
+
// path below. However codex_responses_adapter.js, gemini_native_adapter.js,
|
|
18
|
+
// image_gen_provider.js, and model-discovery.js still fetch() this value as a
|
|
19
|
+
// live HTTP base and DO require a real dialable URL (FREDDIE_LLM_URL set) --
|
|
20
|
+
// they must guard against a null return rather than building a request
|
|
21
|
+
// against a placeholder string.
|
|
22
|
+
return envVal('FREDDIE_LLM_URL') || null
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
export function getAcptoapiModel() {
|
|
@@ -4,7 +4,9 @@ import { isCodexModel } from '../cli/codex_models.js'
|
|
|
4
4
|
|
|
5
5
|
export async function chat({ input, model = 'o3-mini', tools = [], reasoning_effort = 'medium' } = {}) {
|
|
6
6
|
if (!isCodexModel(model)) console.warn('[codex_responses] non-codex model: ' + model)
|
|
7
|
-
const
|
|
7
|
+
const url = getAcptoapiUrl()
|
|
8
|
+
if (!url) throw new Error('FREDDIE_LLM_URL must be set for this adapter (acptoapi is in-process only otherwise)')
|
|
9
|
+
const base = url.replace(/\/v1\/?$/, '')
|
|
8
10
|
const r = await fetch(base + '/v1/responses', {
|
|
9
11
|
method: 'POST',
|
|
10
12
|
headers: { authorization: 'Bearer none', 'content-type': 'application/json' },
|
|
@@ -3,7 +3,9 @@ import { getAcptoapiUrl } from './acptoapi-bridge.js'
|
|
|
3
3
|
import { adaptToolForGemini, adaptMessagesForGemini } from './gemini_schema.js'
|
|
4
4
|
|
|
5
5
|
export async function chat({ messages, model = 'gemini-2.5-flash', tools = [] } = {}) {
|
|
6
|
-
const
|
|
6
|
+
const acptoapiUrl = getAcptoapiUrl()
|
|
7
|
+
if (!acptoapiUrl) throw new Error('FREDDIE_LLM_URL must be set for this adapter (acptoapi is in-process only otherwise)')
|
|
8
|
+
const base = acptoapiUrl.replace(/\/v1\/?$/, '')
|
|
7
9
|
const url = `${base}/v1beta/models/${model}:generateContent`
|
|
8
10
|
const body = { contents: adaptMessagesForGemini(messages), ...(tools.length ? { tools: [{ function_declarations: tools.map(adaptToolForGemini) }] } : {}) }
|
|
9
11
|
const r = await fetch(url, {
|
|
@@ -5,7 +5,9 @@ const PROVIDERS = ['openai', 'replicate', 'stability']
|
|
|
5
5
|
|
|
6
6
|
export async function generate({ provider = 'openai', prompt, size, model } = {}) {
|
|
7
7
|
if (!PROVIDERS.includes(provider)) throw new Error('unknown image provider: ' + provider)
|
|
8
|
-
const
|
|
8
|
+
const url = getAcptoapiUrl()
|
|
9
|
+
if (!url) throw new Error('FREDDIE_LLM_URL must be set for this adapter (acptoapi is in-process only otherwise)')
|
|
10
|
+
const base = url.replace(/\/v1\/?$/, '')
|
|
9
11
|
const body = provider === 'replicate'
|
|
10
12
|
? { version: model || 'black-forest-labs/flux-schnell', input: { prompt } }
|
|
11
13
|
: { model: model || 'gpt-image-1', prompt, size }
|
|
@@ -5,6 +5,22 @@ import { parseTextToolCalls } from './tool_call_text.js'
|
|
|
5
5
|
import * as sdkNs from 'acptoapi'
|
|
6
6
|
export { matrixUsable } from './model-matrix.js'
|
|
7
7
|
|
|
8
|
+
// Reachability memoization: bridgeReachable() now performs a REAL LLM call
|
|
9
|
+
// (acptoapi-bridge.js's isReachable() sends a live 'ping' completion), so
|
|
10
|
+
// calling it on every turn doubles LLM cost/latency. Cache the result for a
|
|
11
|
+
// short TTL so a burst of turns within the window reuses one probe. Does NOT
|
|
12
|
+
// touch acptoapi-bridge.js's exported isReachable -- health-check/dashboard
|
|
13
|
+
// callers still need a live, uncached probe.
|
|
14
|
+
let _lastReachable = { at: 0, ok: false }
|
|
15
|
+
const REACHABLE_TTL_MS = 5000
|
|
16
|
+
async function cachedReachable() {
|
|
17
|
+
const now = Date.now()
|
|
18
|
+
if (now - _lastReachable.at < REACHABLE_TTL_MS) return _lastReachable.ok
|
|
19
|
+
const ok = await bridgeReachable()
|
|
20
|
+
_lastReachable = { at: now, ok }
|
|
21
|
+
return ok
|
|
22
|
+
}
|
|
23
|
+
|
|
8
24
|
// `acptoapi` is externalized by vite (browser) so the host environment
|
|
9
25
|
// supplies it (thebird ships docs/lib/acptoapi-browser.js via importmap).
|
|
10
26
|
// Node CLI gets the real CJS package. Defensive `|| {}` keeps the bundle
|
|
@@ -78,7 +94,7 @@ async function buildModel({ provider, model, inputModel }) {
|
|
|
78
94
|
const keyed = Array.isArray(auto) ? auto.filter(l => { const p = l.model.split('/')[0]; const env = PROVIDER_KEYS[p]; return env && process.env[env] }) : []
|
|
79
95
|
if (keyed.length) return keyed.map(l => l.model).join(', ')
|
|
80
96
|
// No local provider keys — delegate to acptoapi if reachable.
|
|
81
|
-
if (await
|
|
97
|
+
if (await cachedReachable()) return process.env.FREDDIE_LLM_MODEL || 'auto'
|
|
82
98
|
return null
|
|
83
99
|
}
|
|
84
100
|
|
|
@@ -87,12 +103,12 @@ export function resolveCallLLM({ provider, model } = {}) {
|
|
|
87
103
|
const m = await buildModel({ provider, model, inputModel: input.model })
|
|
88
104
|
if (!m) {
|
|
89
105
|
const status = typeof sdk.getStatus === 'function' ? sdk.getStatus().map(s => `${s.provider}(ok=${s.ok},fails=${s.failCount})`).join(', ') : ''
|
|
90
|
-
throw new Error('no LLM backend reachable: set a provider API key or
|
|
106
|
+
throw new Error('no LLM backend reachable: set a provider API key or FREDDIE_LLM_MODEL' + (status ? ' | sampler: ' + status : ''))
|
|
91
107
|
}
|
|
92
108
|
try {
|
|
93
109
|
const isSimple = typeof m === 'string' && !m.includes(',') && !/^queue\//.test(m)
|
|
94
110
|
|
|
95
|
-
if (isSimple && await
|
|
111
|
+
if (isSimple && await cachedReachable()) {
|
|
96
112
|
return await bridgeCall({ ...input, model: m })
|
|
97
113
|
}
|
|
98
114
|
|
|
@@ -105,7 +121,9 @@ export function resolveCallLLM({ provider, model } = {}) {
|
|
|
105
121
|
if (m.includes(',') || /^queue\//.test(m)) opts.matrixSource = process.env.FREDDIE_MATRIX_URL || MATRIX_FILE
|
|
106
122
|
|
|
107
123
|
if (typeof sdk.chat !== 'function') {
|
|
108
|
-
// Browser context:
|
|
124
|
+
// Browser/no-sdk context: fall back to acptoapi-bridge's in-process
|
|
125
|
+
// call (may be a no-op/broken in true browser bundles since acptoapi
|
|
126
|
+
// is externalized for vite -- unverified post-rewrite, see build:browser).
|
|
109
127
|
return await bridgeCall({ ...input, model: m })
|
|
110
128
|
}
|
|
111
129
|
const r = await sdk.chat(opts)
|
|
@@ -17,7 +17,9 @@ export function listKnownProviders() {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export async function discoverModels({ provider } = {}) {
|
|
20
|
-
const
|
|
20
|
+
const url = getAcptoapiUrl()
|
|
21
|
+
if (!url) throw new Error('FREDDIE_LLM_URL must be set for this adapter (acptoapi is in-process only otherwise)')
|
|
22
|
+
const base = url.replace(/\/v1\/?$/, '')
|
|
21
23
|
try {
|
|
22
24
|
const r = await fetch(base + '/v1/models', {
|
|
23
25
|
headers: { authorization: 'Bearer none' },
|
package/src/host/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { getFreddieHome } from '../home.js'
|
|
|
5
5
|
import { applyActiveProjectFromRegistry } from '../projects.js'
|
|
6
6
|
|
|
7
7
|
let _host = null
|
|
8
|
-
let
|
|
8
|
+
let _loadPromise = null
|
|
9
9
|
|
|
10
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
11
|
const REPO_PLUGINS = path.resolve(__dirname, '..', '..', 'plugins')
|
|
@@ -16,18 +16,24 @@ export function host() {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export async function bootHost(extraRoots = []) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
// Memoize the IN-FLIGHT promise, not a boolean flag: a boolean set true
|
|
20
|
+
// before the awaits below complete let concurrent callers observe a
|
|
21
|
+
// partially-loaded host. Returning the same promise means every caller
|
|
22
|
+
// (including ones that arrive mid-load) awaits the exact same completion.
|
|
23
|
+
if (_loadPromise) return _loadPromise
|
|
24
|
+
_loadPromise = (async () => {
|
|
25
|
+
const h = host()
|
|
26
|
+
if (!process.env.FREDDIE_HOME && !process.env.FREDDIE_PROFILE) applyActiveProjectFromRegistry()
|
|
27
|
+
const roots = [REPO_PLUGINS, path.join(getFreddieHome(), 'plugins'), path.join(process.cwd(), '.freddie', 'plugins'), ...extraRoots]
|
|
28
|
+
const plugins = await discoverPlugins(roots)
|
|
29
|
+
await h.load(plugins)
|
|
30
|
+
const ccRoots = [path.join(getFreddieHome(), 'cc-plugins'), path.join(process.cwd(), '.freddie', 'cc-plugins')]
|
|
31
|
+
await h.loadCcPlugins(ccRoots)
|
|
32
|
+
const extra = (process.env.FREDDIE_EXTRA_CC_ROOTS || '').split(path.delimiter).filter(Boolean)
|
|
33
|
+
for (const r of [__dirname, process.cwd(), ...extra]) await h.loadCcFromNodeModules(r)
|
|
34
|
+
return h
|
|
35
|
+
})()
|
|
36
|
+
return _loadPromise
|
|
31
37
|
}
|
|
32
38
|
|
|
33
|
-
export function resetHostForTests() { _host = null;
|
|
39
|
+
export function resetHostForTests() { _host = null; _loadPromise = null }
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import path from 'path';
|
|
3
|
-
const F='C:/dev/freddie';
|
|
4
|
-
const R=p=>path.resolve(p);
|
|
5
|
-
const ALIAS={
|
|
6
|
-
[R(F+'/src/host/index.js')]: R(F+'/src/agent/__browser_shims/host.js'),
|
|
7
|
-
[R(F+'/src/toolsets.js')]: R(F+'/src/agent/__browser_shims/toolsets.js'),
|
|
8
|
-
[R(F+'/src/agent/llm_resolver.js')]: R(F+'/src/agent/__browser_shims/llm_resolver.js'),
|
|
9
|
-
[R(F+'/src/observability/log.js')]: R(F+'/src/agent/__browser_shims/log.js'),
|
|
10
|
-
[R(F+'/src/config.js')]: R(F+'/src/agent/__browser_shims/config.js'),
|
|
11
|
-
[R(F+'/src/home.js')]: R(F+'/src/agent/__browser_shims/home.js'),
|
|
12
|
-
};
|
|
13
|
-
export default {
|
|
14
|
-
name: 'freddie-browser-alias',
|
|
15
|
-
setup(build){
|
|
16
|
-
build.onResolve({filter: /.*/}, async args=>{
|
|
17
|
-
if(args.path.startsWith('node:') || (!args.path.startsWith('.') && !path.isAbsolute(args.path))) return null;
|
|
18
|
-
const resolved = path.resolve(args.resolveDir||'', args.path);
|
|
19
|
-
const candidates=[resolved, resolved+'.js', resolved+'/index.js'];
|
|
20
|
-
for(const c of candidates) if(ALIAS[c]) return { path: ALIAS[c] };
|
|
21
|
-
return null;
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import * as esbuild from 'esbuild';
|
|
3
|
-
import alias from './alias-plugin.mjs';
|
|
4
|
-
await esbuild.build({
|
|
5
|
-
entryPoints: ['C:/dev/freddie/src/agent/__browser_shims/entry.js'],
|
|
6
|
-
bundle: true,
|
|
7
|
-
format: 'esm',
|
|
8
|
-
platform: 'browser',
|
|
9
|
-
conditions: ['browser','module','import'],
|
|
10
|
-
outfile: 'C:/dev/thebird/docs/freddie-runtime.js',
|
|
11
|
-
plugins: [alias],
|
|
12
|
-
legalComments: 'none',
|
|
13
|
-
minify: false,
|
|
14
|
-
sourcemap: false,
|
|
15
|
-
logLevel: 'info',
|
|
16
|
-
external: [],
|
|
17
|
-
});
|
|
18
|
-
console.log('bundle ok');
|