Skip to content

Commit ebbd7aa

Browse files
committed
Fix PR review issues and validation coverage
1 parent 479c2e1 commit ebbd7aa

50 files changed

Lines changed: 1496 additions & 152 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

actions/EventFlowSystem.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,12 @@ class EventFlowSystem {
16471647
return !hasChatFields;
16481648
}
16491649

1650+
isCounterEventPayload(message) {
1651+
if (!this.isMetaOnlyPayload(message)) return false;
1652+
const eventType = String(message.event || '').trim().toLowerCase();
1653+
return ['viewer_update', 'likes_update', 'follower_update', 'subscriber_update'].includes(eventType);
1654+
}
1655+
16501656
isObsEventPayload(message) {
16511657
return !!(message && typeof message === 'object' && (message.type || '').toLowerCase() === 'obs' && message.event);
16521658
}
@@ -1696,8 +1702,9 @@ class EventFlowSystem {
16961702
// Automatic User Memory resets happen before flows evaluate the stream event.
16971703
await this.resetUserMemoriesForEvent(message);
16981704

1699-
// Ignore meta-only payloads (e.g., viewer/follower count updates) so they never trigger flows
1700-
if (this.isMetaOnlyPayload(message)) {
1705+
// Counter payloads may continue only to explicit matching event triggers.
1706+
// Other meta-only traffic remains excluded from Event Flow.
1707+
if (this.isMetaOnlyPayload(message) && !this.isCounterEventPayload(message)) {
17011708
return message;
17021709
}
17031710

@@ -2068,8 +2075,9 @@ class EventFlowSystem {
20682075
// console.log(`[EvaluateTrigger] Node: ${triggerNode.id}, Type: ${triggerType}, Config: ${JSON.stringify(config)}, Message: ${message.chatmessage}`);
20692076
let match = false;
20702077

2071-
// Skip meta-only payloads so metric updates can't trigger flows
2072-
if (this.isMetaOnlyPayload(message)){
2078+
// Known counters may match only an explicit Event Type trigger. This keeps
2079+
// them out of Any Message and other generic chat-oriented triggers.
2080+
if (this.isMetaOnlyPayload(message) && (triggerType !== 'eventOther' || !this.isCounterEventPayload(message))){
20732081
return false;
20742082
}
20752083

ai.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,7 @@ let tmpModelFallback = "";
10731073
let localBrowserLLMClient = null;
10741074
let localBrowserActiveRequestState = null;
10751075
let localBrowserLLMQueue = Promise.resolve();
1076-
const LOCAL_BROWSER_WORKER_VERSION = '17';
1076+
const LOCAL_BROWSER_WORKER_VERSION = '18';
10771077

10781078
function getLocalBrowserWorkerPath() {
10791079
if (typeof chrome !== 'undefined' && chrome.runtime?.getURL) {

cohost.html

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,7 +1772,7 @@
17721772
const GEMINI_AUDIO_TOKENS_PER_SECOND = 25;
17731773
const GEMINI_VIDEO_TOKENS_PER_SECOND = 258;
17741774
const LOCAL_BROWSER_DEFAULT_REMOTE_HOST = "https://largefiles.socialstream.ninja/";
1775-
const LOCAL_BROWSER_WORKER_PATH = "local-browser-model-worker.js?v=17";
1775+
const LOCAL_BROWSER_WORKER_PATH = "local-browser-model-worker.js?v=18";
17761776
const CUSTOM_OPENAI_DEFAULT_ENDPOINT = "http://127.0.0.1:1234/v1";
17771777
const CUSTOM_OPENAI_DEFAULT_MODEL = "";
17781778
const CONFIGURED_LLM_BRIDGE_CHUNK_SIZE = 12000;
@@ -1859,6 +1859,9 @@
18591859
const providerKey = String(providerConfig?.key || "")
18601860
.trim()
18611861
.toLowerCase();
1862+
if (providerKey === "localgemma") {
1863+
return ["config.json", "generation_config.json", "tokenizer.json", "tokenizer_config.json", "preprocessor_config.json", "onnx/embed_tokens_q4.onnx", "onnx/embed_tokens_q4.onnx_data", "onnx/decoder_model_merged_q4.onnx", "onnx/decoder_model_merged_q4.onnx_data", "onnx/vision_encoder_q4.onnx", "onnx/vision_encoder_q4.onnx_data", "onnx/audio_encoder_q4.onnx", "onnx/audio_encoder_q4.onnx_data"];
1864+
}
18621865
if (providerKey.startsWith("localqwen")) {
18631866
return ["config.json", "generation_config.json", "tokenizer.json", "tokenizer_config.json", "preprocessor_config.json", "onnx/embed_tokens_q4.onnx", "onnx/embed_tokens_q4.onnx_data", "onnx/decoder_model_merged_q4.onnx", "onnx/decoder_model_merged_q4.onnx_data", "onnx/vision_encoder_q4.onnx", "onnx/vision_encoder_q4.onnx_data"];
18641867
}
@@ -1986,24 +1989,27 @@
19861989
if (providerKey === "localgemma") {
19871990
return {
19881991
key: "localgemma",
1989-
label: "Local Gemma 4 E2B (Browser, text-only)",
1990-
providerLabel: "Local Gemma 4 (Browser, text-only)",
1992+
label: "Local Gemma 4 E2B (Browser, self-hosted)",
1993+
providerLabel: "Local Gemma 4 (Browser, self-hosted)",
19911994
modelId: "gemma4-e2b-it-onnx",
19921995
localPath: "thirdparty/models/gemma4-e2b-it-onnx",
19931996
remoteHost: LOCAL_BROWSER_DEFAULT_REMOTE_HOST,
19941997
runtime: {
1995-
modelClass: "Gemma4ForCausalLM",
1998+
modelClass: "Gemma4ForConditionalGeneration",
19961999
requiresWebGPU: true,
19972000
dtype: {
19982001
embed_tokens: "q4",
1999-
decoder_model_merged: "q4"
2002+
decoder_model_merged: "q4",
2003+
vision_encoder: "q4",
2004+
audio_encoder: "q4"
20002005
},
20012006
generation: {
2002-
text: { temperature: 1.0, topP: 0.95, topK: 64 }
2007+
text: { temperature: 1.0, topP: 0.95, topK: 64 },
2008+
vision: { temperature: 1.0, topP: 0.95, topK: 64 }
20032009
}
20042010
},
2005-
supportsVision: false,
2006-
defaultPrompt: "You are a concise, friendly social chat co-host. Keep answers short, natural, and relevant to the ongoing conversation."
2011+
supportsVision: true,
2012+
defaultPrompt: "You are a concise, friendly social chat co-host. You can reference visible context when it helps, but do not narrate the scene unless asked."
20072013
};
20082014
}
20092015
if (providerKey === "localqwen2b") {
@@ -9095,7 +9101,7 @@
90959101
<option value="gemini">Google Gemini Live</option>
90969102
<option value="localqwen">Local Qwen 3.5 0.8B (Browser)</option>
90979103
<option value="localqwen2b">Local Qwen 3.5 2B (Browser, quality)</option>
9098-
<option value="localgemma">Local Gemma 4 E2B (Browser, text-only)</option>
9104+
<option value="localgemma">Local Gemma 4 E2B (Browser, self-hosted)</option>
90999105
<option value="configuredllm">SSN Configured LLM</option>
91009106
<option value="customopenai">OpenAI-Compatible (Custom)</option>
91019107
<option value="chatgpt">OpenAI Realtime</option>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Standalone App Sitemap
2+
3+
Status: generated folder sitemap on 2026-07-22 for docs/agents/04-standalone-app.
4+
5+
Use this file to navigate this folder without scanning the filesystem.
6+
7+
- [Agent docs sitemap](../SITEMAP.md)
8+
- [Master agent index](../99-agent-index.md)
9+
10+
## Files
11+
12+
- [Standalone App Deep Docs Index](../04-standalone-app/index.md) - Entry point for detailed Electron-app docs; explains the ssapp-repo path convention.
13+
- [Main Process Lifecycle](../04-standalone-app/main-process-lifecycle.md) - Use this page for startup order, window types and lifecycle, tray/headless behavior, single-instance rules, and shutdown.
14+
- [Social Stream Loading And Injection](../04-standalone-app/social-stream-loading.md) - Use this page for how the app resolves background/popup/source pages, the fallback bundle, content-script injection mechanics, and the chrome.runtime emulation bridge.
15+
- [IPC Reference](../04-standalone-app/ipc-reference.md) - Use this page when you need an ipcMain channel name, its direction, or its handler location.
16+
- [Control API And MCP](../04-standalone-app/control-api-and-mcp.md) - Use this page for the localhost control HTTP API, the MCP stdio server and tool list, token auth/lifecycle, SSE events, and the legacy `/exec` security caveat.
17+
- [Packaging, Updates, And State](../04-standalone-app/packaging-updates-and-state.md) - Use this page for electron-builder targets, signing, the lack of auto-update, settings persistence files, and portable mode.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Standalone App Control API And MCP Server
2+
3+
Status: deep extraction pass on 2026-07-22 from `ssapp/main.js`, `ssapp/resources/electron-control-api.js` (API version 1.1.3), `ssapp/resources/ssapp-mcp.js` (MCP 1.0.1), `ssapp/index.html` (renderer bridge), and `docs/skills/control-social-stream/`. Source-backed; endpoints verified against the ssapp e2e tests (`tests/electron/llm-control-headless-e2e.js`, `ssapp-mcp-e2e.js`).
4+
5+
## Purpose
6+
7+
Use this page for how external programs — scripts, LLM agents, MCP clients — observe and control the standalone app. For the operational how-to (token hygiene, workflows), use `../../skills/control-social-stream/SKILL.md`; this page is the implementation-level reference. For in-app AI chat features (Ollama, chatbots), use `../09-api-and-integrations/ai-features.md` — that is a separate surface.
8+
9+
## Architecture At A Glance
10+
11+
```
12+
MCP client (Claude etc.)
13+
└─ stdio JSON-RPC → ssapp/resources/ssapp-mcp.js (adapter, no SDK)
14+
└─ HTTP → 127.0.0.1:17777 control API
15+
Python/script agents ──────HTTP──┘ (X-SSAPP-Token auth)
16+
└─ main.js router → electron-control-api.js
17+
└─ commands → renderer bridge
18+
(window.SSAppStreamDeckBridge.handleCommand,
19+
index.html:7452-7480)
20+
```
21+
22+
## Enabling The API
23+
24+
- Off by default. Enable via:
25+
- Menu: File → "AI / LLM Control" → Enable Control API (persists `controlApi.enabled`, needs restart) (`main.js:16883-16950`).
26+
- CLI: `--ssapp-control-api`, or `--ssapp-headless-control` (also hides all windows), or legacy `--remote-control`.
27+
- Env: `SSAPP_CONTROL_API`, `SSAPP_HEADLESS_CONTROL` (`main.js:1875-1886`).
28+
- Port: default 17777, `--ssapp-control-port` / `SSAPP_CONTROL_PORT` (`main.js:1887-1895`). Bound to loopback only (`server.listen(port, '127.0.0.1')`, `main.js:2554`).
29+
30+
## Authentication And Token Lifecycle
31+
32+
- Every request (except `/ping`) needs the token via `X-SSAPP-Token` header or `?token=` query param (`main.js:2214-2222`), compared with `crypto.timingSafeEqual` (`main.js:1926-1930`).
33+
- Token precedence: token file → env `SSAPP_CONTROL_TOKEN` → CLI flag → stored/generated (`main.js:1896-1921`). Auto-generated 64-hex token persisted in electron-store `controlApi.token`.
34+
- Rotation: File → "AI / LLM Control" → Rotate Control Token (`main.js:16927-16943`). "Copy Control Connection" copies `{url, token, ssappVersion}` JSON for agent config.
35+
- Query-param token exists for SSE `EventSource` clients; the credential-leak risk is tracked in `../issues/ISSUE-006-control-api-token-in-query-param.md`.
36+
37+
## HTTP Endpoints
38+
39+
Versioned router in `resources/electron-control-api.js:211-247`:
40+
41+
| Endpoint | Purpose |
42+
| --- | --- |
43+
| `GET /api/v1/capabilities` | Advertised commands + versions. |
44+
| `GET /api/v1/status` | App/source status snapshot. |
45+
| `GET /api/v1/events` | SSE stream; resumable via `Last-Event-ID`, 15 s heartbeat, 500-event ring buffer (`:122-145`). |
46+
| `GET /api/v1/operations/{id}` | Async operation result polling. |
47+
| `POST /api/v1/command` | Body `{"action": "...", "value": {...}}`. |
48+
49+
Every response includes `apiVersion`, `ssappVersion`, `requestId` (`:95-109`). Mutations return operation IDs (`op_<uuid>`) and publish `operation.started/completed/failed` + `status.changed` events on the SSE stream (`:185-206`).
50+
51+
### Command actions (`electron-control-api.js:11-34`)
52+
53+
| Class | Actions |
54+
| --- | --- |
55+
| Read-only | `getCapabilities`, `getSources`, `getSource`, `getSettings`, `getOperation` |
56+
| Mutating | `addSource`, `updateSource`, `startSource`, `stopSource`, `restartSource`, `removeSource`, `setSourceMute`, `toggleSourceMute`, `setSourceVisibility`, `toggleSourceVisibility`, `setSourceConnectionMode`, `startAllSources`, `stopAllSources`, `restartAllSources`, `updateSettings` |
57+
| Confirm-gated (`confirm:true` required, `:170-172`) | `removeSource`, `stopAllSources`, `restartAllSources`, `reloadApp`, `shutdownApp` |
58+
59+
Renderer-side execution and allowlists live in `index.html`: global settings allowlist `:6936-6946` (`betaMode`, `youtubeAutoAdd`, `youtubeAutoCleanup`, `youtubeCheckInterval`, `forceTikTokClassic`, `preferTikTokLegacy`, `lastTikTokMode`), platform/mode definitions `:6957-6971`, URL building restricted to http/https `:6983-7014`. Normalized sources deliberately omit stored `url` (may contain credentials).
60+
61+
What the API **cannot** do: read chat messages, send chat messages, or drive overlays — no such commands exist. Chat data still flows only through the normal SSN session transport.
62+
63+
## Legacy Endpoints (`--remote-control` only)
64+
65+
Gated at `main.js:2252`: `/ping` (always available, `:2242`), `/queue-file-selection` (`:2257`), `/youtube-auth` (`:2273`), `/twitch-auth` (`:2341`), `/kick-auth` (`:2348`), `/create-youtube-source` (`:2281-2339`), `/create-kick-source` (`:2356`), and **`/exec` arbitrary JS execution** (`:2521-2544`).
66+
67+
`/exec` contradicts the documented "no arbitrary JavaScript execution" guarantee — tracked as `../issues/ISSUE-005-exec-arbitrary-js-endpoint.md`. Do not claim the app never exposes JS execution without the legacy-mode caveat.
68+
69+
## MCP Server (`resources/ssapp-mcp.js`)
70+
71+
- Dependency-free stdio MCP server (newline-delimited JSON-RPC 2.0, `:177-229`); Node stdlib only. Run with `npm run mcp` (`package.json:32`) or `node resources/ssapp-mcp.js`.
72+
- Protocol version `2025-06-18`, serverInfo `{name: 'social-stream-ninja', version: '1.0.1'}` (`:189-192`). Capabilities: `tools` only (no resources/prompts).
73+
- Config via env: `SSAPP_CONTROL_URL`, `SSAPP_CONTROL_TOKEN` or `SSAPP_CONTROL_TOKEN_FILE` (`:86-95`).
74+
- Methods: `initialize`, `tools/list`, `tools/call`; everything else → `-32601`.
75+
- 12 tools (`:14-53`), thin wrappers over the HTTP commands: `ssapp_get_status`, `ssapp_get_capabilities`, `ssapp_list_sources`, `ssapp_add_source`, `ssapp_update_source`, `ssapp_start_source`, `ssapp_stop_source`, `ssapp_reload_source`, `ssapp_remove_source`, `ssapp_get_settings`, `ssapp_update_settings`, `ssapp_shutdown`.
76+
- Tools are filtered at runtime against the app's advertised capabilities (`:124-152`), and every result embeds `ssappVersion`/`apiVersion` (`:168-174`) — so an older app simply exposes fewer tools.
77+
78+
## Companion Skill And Tests
79+
80+
- `docs/skills/control-social-stream/` (this repo): SKILL.md + `references/control-api.md` + `scripts/ssapp_control.py`. The Python script is a thin client for this exact API (`GET /api/v1/capabilities` `:51`, `GET /api/v1/status` `:53`, `POST /api/v1/command` `:61`, header `X-SSAPP-Token` `:18`).
81+
- ssapp's own e2e test runs that exact script against a headless app (`llm-control-headless-e2e.js:251-259`) — contract parity is enforced in CI.
82+
- `ssapp/AGENTS.md:55-59` mandates the skill be updated whenever the API changes; version history lives in `docs/skills/control-social-stream/references/version-log.md`.
83+
84+
## Do Not Overclaim
85+
86+
- The control API is loopback-only; do not describe it as remote/LAN reachable without user reconfiguration (there is no supported non-loopback bind).
87+
- The MCP adapter is not an SDK-based server and exposes no resources/prompts — tools only.
88+
- The settings allowlist is small by design; do not promise arbitrary settings control.
89+
- `/exec` exists in legacy mode (ISSUE-005); the "no arbitrary JS" claim only holds for the versioned `/api/v1/*` surface.
90+
91+
## Follow-Up Extraction Needs
92+
93+
- Settings-allowlist growth per API version (currently only in version-log.md bullets).
94+
- Whether `--remote-control` legacy endpoints are slated for removal.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Standalone App (ssapp) Deep Docs
2+
3+
Status: deep extraction pass on 2026-07-22 from the live `ssapp` repo (`C:\Users\steve\Code\ssapp`, a separate repository, not a subfolder of this one). Source-backed orientation with file:line anchors; not a replacement for in-app/e2e testing.
4+
5+
## Purpose
6+
7+
This folder holds the detailed Electron-app docs that `../04-standalone-app-architecture.md` and `../04-standalone-app-source-windows.md` explicitly deferred: main-process lifecycle, IPC reference, control API + MCP surface, packaging/state, and how Social Stream code is loaded and injected.
8+
9+
Path convention: anchors prefixed `ssapp/...` refer to the separate ssapp repository on disk (`C:\Users\steve\Code\ssapp`). Anchors without a prefix refer to this social_stream repository.
10+
11+
## Pages
12+
13+
| Page | Use it when |
14+
| --- | --- |
15+
| [Main Process Lifecycle](main-process-lifecycle.md) | Startup order, window types, tray/headless modes, shutdown, single-instance behavior. |
16+
| [Social Stream Loading And Injection](social-stream-loading.md) | How the app resolves background/popup/source pages (remote → cache → fallback), injects content scripts, and emulates `chrome.runtime`. |
17+
| [IPC Reference](ipc-reference.md) | Channel names, direction, and purpose for the ~90 ipcMain channels. |
18+
| [Control API And MCP](control-api-and-mcp.md) | Localhost control HTTP API, MCP stdio server, token auth/lifecycle, SSE events, legacy `/exec` caveat. |
19+
| [Packaging, Updates, And State](packaging-updates-and-state.md) | electron-builder targets, code signing, no auto-updater, savedSync.json/electron-store schema, portable mode. |
20+
21+
## Related docs outside this folder
22+
23+
- `../04-standalone-app-architecture.md` - layer map and source-root loading rules (backbone pass).
24+
- `../04-standalone-app-source-windows.md` - source-state vs source-window mental model and app-vs-extension parity.
25+
- `../08-platform-sources/tiktok-standalone-app.md` - TikTok connector modes, signing, fallbacks.
26+
- `../08-platform-sources/tiktok-app-event-payload-map.md` - TikTok app event → SSN payload field mapping.
27+
- `../../skills/control-social-stream/` - operational skill for driving the app via the control API (Python client + MCP).
28+
- `../issues/` - bugs found during documentation passes, including ssapp defects.

0 commit comments

Comments
 (0)