Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
</td>
<td valign="top" width="50%">
<strong>Persistent admin and time travel</strong><br />
When something breaks, admin mode gives you a stable control plane, and Git-backed history lets you roll back user or group changes without taking everyone down with you.
Enter Space opens the native workspace shell. Admin mode gives you the stable control plane, and Git-backed history lets you roll back user or group changes without taking everyone down with you.
</td>
</tr>
</table>
Expand Down
2 changes: 1 addition & 1 deletion app/L0/_all/mod/_core/admin/views/agent/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Current behavior:
- the admin HuggingFace panel should let users either enter a compatible repo id directly or pick from the shared saved-model list, then load or unload that selection directly from the modal while reusing the same progress block and current-model status area as the routed sidebar
- the admin local-provider panel should show the selected model separately from the currently loaded model, so an unloaded but configured selection is visible immediately instead of looking stuck on `None loaded`
- when no Hugging Face model is selected and the shared saved-model list has entries, the admin local-provider panel should preselect the browser-wide last successfully loaded saved model from `_core/huggingface/manager.js`, falling back to the first saved entry if that last-used entry was discarded
- when no Hugging Face model is selected, no model is loaded, and the shared saved-model list is empty, the admin local-provider panel should prefill the model field with the same default used by the routed testing page: `onnx-community/gemma-4-E4B-it-ONNX`
- when no Hugging Face model is selected, no model is loaded, and the shared saved-model list is empty, the admin local-provider panel should prefill the model field with the same default used by the routed testing page: `onnx-community/Qwen3-0.6B-ONNX`
- admin-local provider inputs mounted through the shared sidebar components should write back through explicit `store.js` setter methods instead of depending on implicit nested `x-model` mutation across component boundaries
- discarding a HuggingFace repo from the routed testing harness removes it from the shared browser-side saved-model list too, so it disappears from the admin saved-model shortcut selector until it is loaded again
- admin should subscribe to `_core/huggingface/manager.js` directly, so the modal and send flow read the same live worker state, saved-model options, loading, and streaming behavior as the routed Hugging Face surface within the current browser context
Expand Down
53 changes: 32 additions & 21 deletions app/L0/_all/mod/_core/admin/views/agent/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -803,17 +803,6 @@ const model = {
return false;
}

const preferredSavedModel = huggingfaceManager.refreshPreferredSavedModelSelection();

if (preferredSavedModel?.modelId && preferredSavedModel?.dtype) {
this.settingsDraft = {
...this.settingsDraft,
huggingfaceDtype: preferredSavedModel.dtype,
huggingfaceModel: preferredSavedModel.modelId
};
return true;
}

const snapshot = huggingfaceManager.getSnapshot();
const hasSavedModels = Array.isArray(snapshot.savedModels) && snapshot.savedModels.length > 0;
const activeModelId = normalizeHuggingFaceModelInput(snapshot.activeModelId || "");
Expand Down Expand Up @@ -1633,21 +1622,31 @@ const model = {
},

openSettingsDialog() {
const provider = config.normalizeAdminChatLlmProvider(this.settings.provider);

this.settingsDraft = {
...this.settings,
promptBudgetRatios: clonePromptBudgetRatios(this.settings.promptBudgetRatios)
};
this.syncHuggingFaceFromManager();
this.prefillSettingsDraftDefaultHuggingFaceModel();

if (!String(this.settingsDraft.huggingfaceDtype || "").trim()) {
this.settingsDraft.huggingfaceDtype = DTYPE_OPTIONS[0]?.value || config.DEFAULT_ADMIN_CHAT_SETTINGS.huggingfaceDtype;
if (provider === config.ADMIN_CHAT_LLM_PROVIDER.LOCAL) {
this.prefillSettingsDraftDefaultHuggingFaceModel();

if (!String(this.settingsDraft.huggingfaceDtype || "").trim()) {
this.settingsDraft.huggingfaceDtype = DTYPE_OPTIONS[0]?.value || config.DEFAULT_ADMIN_CHAT_SETTINGS.huggingfaceDtype;
}
} else {
this.settingsDraft.huggingfaceModel = "";
this.settingsDraft.huggingfaceDtype = "";
}

void this.warmSettingsDraftLocalProvider()
.catch((error) => {
this.reportError("warming the local-provider settings draft", error);
});
if (provider === config.ADMIN_CHAT_LLM_PROVIDER.LOCAL) {
void this.warmSettingsDraftLocalProvider()
.catch((error) => {
this.reportError("warming the local-provider settings draft", error);
});
}
openDialog(this.refs.settingsDialog);
},

Expand All @@ -1656,9 +1655,17 @@ const model = {
},

setSettingsProvider(provider) {
const nextProvider = config.normalizeAdminChatLlmProvider(provider);

this.settingsDraft = {
...this.settingsDraft,
provider: config.normalizeAdminChatLlmProvider(provider)
huggingfaceDtype: nextProvider === config.ADMIN_CHAT_LLM_PROVIDER.LOCAL
? this.settingsDraft.huggingfaceDtype
: "",
huggingfaceModel: nextProvider === config.ADMIN_CHAT_LLM_PROVIDER.LOCAL
? this.settingsDraft.huggingfaceModel
: "",
provider: nextProvider
};

if (this.isSettingsDraftUsingLocalProvider) {
Expand Down Expand Up @@ -1877,8 +1884,12 @@ const model = {
this.settings = {
apiEndpoint: (this.settingsDraft.apiEndpoint || "").trim(),
apiKey: (this.settingsDraft.apiKey || "").trim(),
huggingfaceDtype: (this.settingsDraft.huggingfaceDtype || "").trim(),
huggingfaceModel: normalizeHuggingFaceModelInput(this.settingsDraft.huggingfaceModel || ""),
huggingfaceDtype: provider === config.ADMIN_CHAT_LLM_PROVIDER.LOCAL
? (this.settingsDraft.huggingfaceDtype || "").trim()
: "",
huggingfaceModel: provider === config.ADMIN_CHAT_LLM_PROVIDER.LOCAL
? normalizeHuggingFaceModelInput(this.settingsDraft.huggingfaceModel || "")
: "",
localProvider,
maxTokens,
model: (this.settingsDraft.model || "").trim(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ The settings and prompt-history dialogs reuse the shared `_core/visual/forms/dia
Caught overlay runtime errors are logged through `console.error` and shown through the shared toast stack from `_core/visual/chrome/toast.js`. The composer placeholder still belongs to ready-state and lightweight status guidance, so raw exception text should not be pushed into the textarea placeholder.
Overlay execution transcripts now use the shared YAML-first formatter for both console logs and returned values, emitting block headers such as `log↓`, `warn↓`, `error↓`, and `result↓` so structured telemetry stays complete across the thread and execution cards. Queued follow-up submissions wait behind any just-finished assistant reply that contains `_____javascript`; the runtime must execute the block and append the `execution-output` turn before sending the queued draft, so the next model request sees the execution result in history. Immediately before those execution results are serialized back into the `execution-output` follow-up turn, the overlay also runs the shared assistant-message evaluation seam; the current first-party hook from `_core/agent-chat` prepends synthetic loop warnings when the exact same assistant message reappears, using `info` on the 2nd send, `warn` on the 3rd send, and `error` on the 4th send onward.

The settings dialog now has two provider tabs named `API` and `Local`. `API` keeps the OpenAI-compatible endpoint, model, and key fields. `Local` mounts the shared Hugging Face config sidebar in onscreen mode, so the overlay reads the same saved-model list and live WebGPU worker state as the routed Local LLM page and the admin chat. Opening the Local tab should refresh saved-model shortcuts without booting the worker; saving local settings persists the selected repo id and dtype, then starts background model preparation. When no local model is selected and saved models exist, the Local panel preselects the browser-wide last successfully loaded saved model from `_core/huggingface/manager.js`, falling back to the first saved entry if that last-used entry was discarded. When no local model is selected, no local model is loaded, and the shared saved-model list is empty, the Local panel prefills the Hugging Face model field with the same testing-page default: `onnx-community/gemma-4-E4B-it-ONNX`.
The settings dialog now has two provider tabs named `API` and `Local`. `API` keeps the OpenAI-compatible endpoint, model, and key fields. `Local` mounts the shared Hugging Face config sidebar in onscreen mode, so the overlay reads the same saved-model list and live WebGPU worker state as the routed Local LLM page and the admin chat. Opening the Local tab should refresh saved-model shortcuts without booting the worker; saving local settings persists the selected repo id and dtype, then starts background model preparation. When no local model is selected and saved models exist, the Local panel preselects the browser-wide last successfully loaded saved model from `_core/huggingface/manager.js`, falling back to the first saved entry if that last-used entry was discarded. When no local model is selected, no local model is loaded, and the shared saved-model list is empty, the Local panel prefills the Hugging Face model field with the same testing-page default: `onnx-community/Qwen3-0.6B-ONNX`.

The API-key composer blocker applies only to the default API-provider configuration with no API key, where the composer shows a centered `Set LLM API key` action over the disabled textarea. Local Hugging Face mode can send without an API key and falls back to loading the selected local model on the first message if background preparation has not finished.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ The admin settings modal now starts with a provider switch:

Below those provider-specific sections, the shared settings area also exposes `max_tokens`, prompt-budget ratios for `system`, `history`, and `transient`, plus the separate single-history-message ratio used by the shared trimming path. Those values are persisted in `prompt_budget_ratios` and feed the same prompt-budget builder used by the onscreen agent: prepared entries and prompt items reuse cached token counts, single live history messages are capped first, contributor-level trims must each be at least `250` tokens, and `system` or `transient` falls back to one combined section-body trim when smaller contributor cuts would otherwise be required.

When no local model is selected and saved models exist, the admin local panel preselects the browser-wide last successfully loaded saved model from `_core/huggingface/manager.js`, falling back to the first saved entry if that last-used entry was discarded. When no local model is selected, no local model is loaded, and the shared saved-model list is empty, the admin local panel prefills the Hugging Face model field with the same testing-page default: `onnx-community/gemma-4-E4B-it-ONNX`.
When no local model is selected and saved models exist, the admin local panel preselects the browser-wide last successfully loaded saved model from `_core/huggingface/manager.js`, falling back to the first saved entry if that last-used entry was discarded. When no local model is selected, no local model is loaded, and the shared saved-model list is empty, the admin local panel prefills the Hugging Face model field with the same testing-page default: `onnx-community/Qwen3-0.6B-ONNX`.

The stored config keeps both API settings and the selected local provider state:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The page owns:
- a simple testing chat with system prompt, user messages, streamed assistant replies, stop, and clear-chat
- compact response metrics inline under each assistant reply

When admin or onscreen chat local settings have no selected model and the browser has saved Hugging Face models, the shared sidebar preselects the browser-wide last successfully loaded saved model from local storage. If that entry was discarded, it falls back to the first saved model. When the browser has no saved local Hugging Face models and no persisted auto-reload target, the routed testing-page model input prefills `onnx-community/gemma-4-E4B-it-ONNX` as the empty-state suggestion; admin and onscreen chat local settings reuse that same default only when there is no preferred saved-model selection. The default generation cap is `16384` max new tokens unless the user changes it.
When admin or onscreen chat local settings have no selected model and the browser has saved Hugging Face models, the shared sidebar preselects the browser-wide last successfully loaded saved model from local storage. If that entry was discarded, it falls back to the first saved model. When the browser has no saved local Hugging Face models and no persisted auto-reload target, the routed testing-page model input prefills `onnx-community/Qwen3-0.6B-ONNX` as the empty-state suggestion; admin and onscreen chat local settings reuse that same default only when there is no preferred saved-model selection. The default generation cap is `16384` max new tokens unless the user changes it.

This is not a general agent surface. It does not expose tool execution, queueing, attachments, persisted conversations, or backend orchestration.

Expand Down
2 changes: 1 addition & 1 deletion app/L0/_all/mod/_core/huggingface/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Current route contract:
- `manager.js` exposes startup explicitly through `isWorkerBooting`; routed, admin, and onscreen consumers should treat an unbooted idle manager as `Idle`, not as `Starting`, and should reserve `Starting` for an actual in-flight worker boot
- `manager.js` snapshots and subscription payloads must stay plain-data and clone-safe; do not leak raw browser host objects or rely on generic `structuredClone(...)` fallbacks that can fail on reactive proxies
- when the admin or onscreen local-provider draft has no selected model and saved Hugging Face models exist in browser storage, the sidebar should preselect the last successfully loaded saved model from browser-wide `localStorage`; if that stored model is no longer in the saved list, it should fall back to the first saved entry
- when the browser has no saved models, no active model, and no persisted auto-reload target, the routed testing-page model input should prefill `onnx-community/gemma-4-E4B-it-ONNX` as the empty-state suggestion; admin and onscreen chat sidebars should reuse that same empty-state default only when their selected local model is blank and there is no preferred saved-model selection
- when the browser has no saved models, no active model, and no persisted auto-reload target, the routed testing-page model input should prefill `onnx-community/Qwen3-0.6B-ONNX` as the empty-state suggestion; admin and onscreen chat sidebars should reuse that same empty-state default only when their selected local model is blank and there is no preferred saved-model selection
- the sidebar should surface the currently loaded model first, inside a slightly more prominent rounded panel with a larger model label, a compact right-aligned state badge, and an unload control beside the model name
- while a model is loading, that action switches to `Stop`; stopping a Hugging Face load or unload resets the shared singleton-managed worker and boots a fresh one instead of leaving stale partial state behind
- the load progress area should show a debounced aggregate download status below the bar, with total transferred bytes appended such as `Downloading model files (412 MB / 1.8 GB)` instead of rapidly alternating per-file names
Expand Down
6 changes: 3 additions & 3 deletions app/L0/_all/mod/_core/huggingface/config-sidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ <h2>Models</h2>
:value="$store.huggingface.modelInput"
@input="$store.huggingface.setModelInput($event.target.value)"
:disabled="$store.huggingface.isGenerating"
placeholder="onnx-community/gemma-4-E4B-it-ONNX or https://huggingface.co/..."
placeholder="onnx-community/Qwen3-0.6B-ONNX or https://huggingface.co/..."
/>
</label>

Expand Down Expand Up @@ -228,7 +228,7 @@ <h3>Advanced</h3>
type="text"
:value="$store.adminAgent.settingsDraft.huggingfaceModel"
@input="$store.adminAgent.handleSettingsHuggingFaceModelInput($event.target.value)"
placeholder="onnx-community/gemma-4-E4B-it-ONNX"
placeholder="onnx-community/Qwen3-0.6B-ONNX"
/>
</label>

Expand Down Expand Up @@ -342,7 +342,7 @@ <h3>Advanced</h3>
type="text"
:value="$store.onscreenAgent.settingsDraft.huggingfaceModel"
@input="$store.onscreenAgent.handleSettingsHuggingFaceModelInput($event.target.value)"
placeholder="onnx-community/gemma-4-E4B-it-ONNX"
placeholder="onnx-community/Qwen3-0.6B-ONNX"
/>
</label>

Expand Down
19 changes: 17 additions & 2 deletions app/L0/_all/mod/_core/huggingface/helpers.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const DEFAULT_SYSTEM_PROMPT = "You are a helpful assistant.";
export const DEFAULT_DTYPE = "q4";
export const DEFAULT_MODEL_INPUT = "onnx-community/gemma-4-E4B-it-ONNX";
export const LEGACY_DEFAULT_MODEL_INPUT = "onnx-community/gemma-4-E4B-it-ONNX";
export const DEFAULT_MODEL_INPUT = "onnx-community/Qwen3-0.6B-ONNX";
export const DEFAULT_MAX_NEW_TOKENS = 16384;
export const COMPATIBLE_MODELS_URL = "https://huggingface.co/onnx-community/models";
export const HUGGINGFACE_SAVED_MODELS_STORAGE_KEY = "space.huggingface.saved-models";
Expand Down Expand Up @@ -209,9 +210,23 @@ export function readSavedModelEntries() {
return [];
}

return parsedValue
const nextEntries = parsedValue
.map((entry) => createSavedModelEntry(entry))
.filter((entry) => entry?.modelId !== LEGACY_DEFAULT_MODEL_INPUT)
.filter(Boolean);

if (nextEntries.length !== parsedValue.length) {
try {
globalThis.localStorage?.setItem(
HUGGINGFACE_SAVED_MODELS_STORAGE_KEY,
JSON.stringify(nextEntries)
);
} catch {
// Ignore storage write failures and still return the sanitized list.
}
}

return nextEntries;
} catch {
return [];
}
Expand Down
6 changes: 6 additions & 0 deletions app/L0/_all/mod/_core/huggingface/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
DEFAULT_DTYPE,
DEFAULT_MAX_NEW_TOKENS,
DEFAULT_MODEL_INPUT,
LEGACY_DEFAULT_MODEL_INPUT,
discardCachedModelEntries,
describeModelSelection,
DTYPE_OPTIONS,
Expand Down Expand Up @@ -273,6 +274,11 @@ function readPersistedModelSelection() {
return null;
}

if (modelId === LEGACY_DEFAULT_MODEL_INPUT) {
globalThis.localStorage?.removeItem(PERSISTED_MODEL_STORAGE_KEY);
return null;
}

return {
dtype: String(parsedValue.dtype || DEFAULT_DTYPE).trim() || DEFAULT_DTYPE,
maxNewTokens: normalizeMaxNewTokens(parsedValue.maxNewTokens),
Expand Down
Loading