Custom connections are gone: the CLI talks to the LLeMbas server it is logged in to

LLeMbas CLI 2.0 is LLeMbas-server-specific (CLI rule 2). Removed: the
anthropic, gemini, ollama and responses clients, context discovery and
llama-swap unloading (discover.ts), the Connection schema with its dialect,
auth, key_cmd, discover, unload_url and one_model_at_a_time, the model
`fallback` (it only ever moved to another server), effort_map and cache, and
the multi-connection refs. Kept: openai-chat, http, sse, think, effort,
tokens and all of src/lembas.

- connections.yaml holds the login (`type: webui`, as lembas login writes
  it). A custom entry from 1.x is read past with a warning that says how to
  log in, so an old file still starts; with several logins left, the latest
  is used and the others named.
- A model's ref is the id the server serves it under; the older forms
  (`<login>/<id>`, `<login>/<model>`) still resolve and config.yaml is
  rewritten once. Without a login there is no model, and the error (and the
  first-run screen, now the sign-in alone) says `lembas login <address>`.
- A login to another server signs this machine out of the previous one.
- The window is the server's word (no discovery); a capacity group comes
  from the server's `capacity.group` alone; embeddings use the server's
  /v1/embeddings.
- `lembas models` lists the server's models; install.sh's starter files, the
  README and the JSON schemas say the same.

Tests: every fixture is a login now (the fake server lists models, answers
other GETs with 404, and can hand one model to another script, as a server
with several backends); the dialect suites and the cross-connection tests are
gone with their code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
HomerandClaude Opus 5.5 committed 2026-10-27 21:00:05 +00:00
1 parent f20e8c4307
commit 28ff205ed0
80 files changed
+587 -2575

No files matched your search

+2 -19
View File
@@ -14,7 +14,7 @@ import { fillPrompt } from "../prompt/fill.ts"
import { splitPurpose, toSpec, ToolError, type Tool, type ToolContext, type ToolResult } from "../tool/tool.ts"
import { fill, promptText } from "../prompt/assemble.ts"
import { resolveCall } from "../tool/names.ts"
import { contextWindow } from "../provider/discover.ts"
import { contextWindow } from "../provider/index.ts"
import { summaryTurns, type Store } from "./store.ts"
import { estimateTokens, estimateValue } from "../provider/tokens.ts"
import { duration } from "../duration.ts"
@@ -43,9 +43,6 @@ export interface EngineOptions {
compaction?: { autoAt?: number }
/** Starts a subagent for the task tool; absent in a subagent. */
spawn?: ToolContext["spawn"]
/** The model to switch to when this one's server cannot be reached (its `fallback` list), or
* undefined when there is none left to try. */
fallback?: (current: ResolvedModel, tried: Set<string>) => { model: ResolvedModel; client: Client; effort: Effort | null } | undefined
/** What a message sent mid-turn starts with: the user's words for a session, the main agent's for
* a subagent (loop.json jobs.marks to_subagent). */
steerMark?: string
@@ -264,7 +261,7 @@ export class Engine {
}
private droppedThinking(): number {
if (!this.usage || this.model.connection.dialect !== "openai-chat") return 0
if (!this.usage) return 0
const reply = this.messages[this.usageAt - 1]
if (reply?.role !== "assistant") return 0
const estimate = reply.parts.reduce((n, p) => n + (p.type === "reasoning" ? estimateTokens(p.text) : 0), 0)
@@ -468,7 +465,6 @@ export class Engine {
let ranOut: { limit: string; said: string } | undefined
let nudges = 0
let midRetries = 0
const tried = new Set<string>()
for (let step = 1; ; step++) {
// Checked between steps, never mid-reply: a reply cut off mid-sentence is worth nothing.
@@ -519,19 +515,6 @@ export class Engine {
bus.emit({ type: "done", reason: "cancelled" })
return "cancelled"
}
// The server is not there at all: the model's fallback, if it has one, for the rest of the
// session — said, and shown in the status bar.
if (e instanceof ProviderError && e.unreachable && this.o.fallback) {
tried.add(this.model.ref)
const next = this.o.fallback(this.model, tried)
if (next) {
bus.emit({ type: "notice", message: `${e.message.split("\n")[0]!.slice(0, 160)} — switching to ${next.model.ref}, its fallback` })
this.setModel(next.model, next.client, next.effort)
bus.emit({ type: "setting", key: "model", value: next.model.ref })
step--
continue
}
}
// A server failure part-way through a reply (llama.cpp failing to parse gpt-oss's own
// output is the one seen) is worth two more goes. What was already shown is discarded.
if (e instanceof ProviderError && !e.beforeOutput && (e.status ?? 0) >= 500 && midRetries < 2) {