12 Commits

Author SHA1 Message Date
9f3d7dc6a9 Merge pull request 'docs: record live LiteLLM validation' (#55) from codex/issue-53-live-handoff into codex/production-intelligence-terminal
All checks were successful
Codex Template Compliance / template-compliance (push) Successful in 5s
Release Dry Run / release-dry-run (push) Successful in 17s
Build / test-and-image (push) Successful in 26s
Merge pull request #55: record live LiteLLM validation
2026-07-04 10:35:10 +00:00
f10bff9ba4 docs: record live LiteLLM validation
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 9s
Build / test-and-image (pull_request) Successful in 22s
2026-07-04 12:34:09 +02:00
14d9276c30 Merge pull request 'fix: persist LLM predictions without stable ID shadowing' (#54) from codex/issue-53-prediction-stable-id into codex/production-intelligence-terminal
All checks were successful
Codex Template Compliance / template-compliance (push) Successful in 5s
Release Dry Run / release-dry-run (push) Successful in 17s
Build / test-and-image (push) Successful in 32s
Merge pull request #54: fix LLM prediction persistence
2026-07-04 10:29:42 +00:00
84b2c9ebc9 test: assert hashed prediction identifier format
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 9s
Build / test-and-image (pull_request) Successful in 51s
2026-07-04 12:27:57 +02:00
9263157a9e fix: persist LLM predictions without stable ID shadowing
Some checks failed
Codex Template Compliance / template-compliance (pull_request) Successful in 5s
Build / test-and-image (pull_request) Failing after 22s
2026-07-04 12:25:58 +02:00
f7b527763d Merge pull request 'fix: respect configured LLM generation limits' (#52) from codex/issue-51-llm-timeout-config into codex/production-intelligence-terminal
All checks were successful
Codex Template Compliance / template-compliance (push) Successful in 5s
Release Dry Run / release-dry-run (push) Successful in 15s
Build / test-and-image (push) Successful in 32s
Merge pull request #52: respect configured LLM generation limits
2026-07-04 10:13:16 +00:00
dda1d23a30 fix: respect configured LLM generation limits
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 7s
Build / test-and-image (pull_request) Successful in 55s
2026-07-04 12:11:28 +02:00
ebe2906d1c Merge pull request 'docs: synchronize README with production configuration' (#50) from codex/issue-46-readme-sync into codex/production-intelligence-terminal
All checks were successful
Codex Template Compliance / template-compliance (push) Successful in 5s
Release Dry Run / release-dry-run (push) Successful in 14s
Build / test-and-image (push) Successful in 30s
Merge pull request #50: synchronize README with production configuration
2026-07-03 21:56:32 +00:00
6d78c119c0 docs: synchronize README with production configuration
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 9s
Build / test-and-image (pull_request) Successful in 47s
2026-07-03 23:54:58 +02:00
374340d71a Merge pull request 'docs: record LiteLLM image publication' (#49) from codex/issue-47-release-handoff into codex/production-intelligence-terminal
All checks were successful
Codex Template Compliance / template-compliance (push) Successful in 6s
Release Dry Run / release-dry-run (push) Successful in 15s
Build / test-and-image (push) Successful in 24s
Merge pull request #49: record LiteLLM image publication
2026-07-03 21:12:41 +00:00
28c5e7955a docs: record LiteLLM image publication
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 5s
Build / test-and-image (pull_request) Successful in 23s
2026-07-03 23:11:36 +02:00
5c4bf80eb0 Merge pull request 'feat: add LiteLLM provider and publish Code-Inc image' (#48) from codex/issue-47-litellm-provider into codex/production-intelligence-terminal
All checks were successful
Codex Template Compliance / template-compliance (push) Successful in 6s
Release Dry Run / release-dry-run (push) Successful in 15s
Build / test-and-image (push) Successful in 42s
Merge pull request #48: add LiteLLM provider and Code-Inc image target
2026-07-03 21:08:45 +00:00
8 changed files with 181 additions and 41 deletions

View File

@@ -29,6 +29,7 @@ OPENROUTER_APP_NAME=Intelligence Terminal
# Local OpenAI-compatible examples # Local OpenAI-compatible examples
# LiteLLM: LLM_PROVIDER=litellm, LLM_BASE_URL=https://llm.example.com/v1, LLM_API_KEY=your-proxy-key, LLM_MODEL=your-model-alias # LiteLLM: LLM_PROVIDER=litellm, LLM_BASE_URL=https://llm.example.com/v1, LLM_API_KEY=your-proxy-key, LLM_MODEL=your-model-alias
# Local 20B+ models may need LLM_TIMEOUT_MS=300000 for full intelligence sweeps.
# LM Studio: LLM_PROVIDER=lmstudio, LLM_BASE_URL=http://host.docker.internal:1234/v1, LLM_MODEL=local-model # LM Studio: LLM_PROVIDER=lmstudio, LLM_BASE_URL=http://host.docker.internal:1234/v1, LLM_MODEL=local-model
# Ollama: LLM_PROVIDER=ollama, LLM_BASE_URL=http://host.docker.internal:11434, LLM_MODEL=llama3.1:8b # Ollama: LLM_PROVIDER=ollama, LLM_BASE_URL=http://host.docker.internal:11434, LLM_MODEL=llama3.1:8b
# Generic: LLM_PROVIDER=openai-compatible, LLM_BASE_URL=http://host.docker.internal:8000/v1, LLM_MODEL=your-model # Generic: LLM_PROVIDER=openai-compatible, LLM_BASE_URL=http://host.docker.internal:8000/v1, LLM_MODEL=your-model

View File

@@ -85,7 +85,7 @@ npm run dev
> ``` > ```
> This bypasses npm's script runner, which can swallow errors on some systems (particularly PowerShell on Windows). You can also run `node diag.mjs` to diagnose the exact issue — it checks your Node version, tests each module import individually, and verifies port availability. See [Troubleshooting](#troubleshooting) for more. > This bypasses npm's script runner, which can swallow errors on some systems (particularly PowerShell on Windows). You can also run `node diag.mjs` to diagnose the exact issue — it checks your Node version, tests each module import individually, and verifies port availability. See [Troubleshooting](#troubleshooting) for more.
The dashboard opens automatically at `http://localhost:3117` and immediately begins its first intelligence sweep. This initial sweep queries all 27 sources in parallel and typically takes 3060 seconds — the dashboard will appear empty until the sweep completes and pushes the first data update. After that, it auto-refreshes every 15 minutes via SSE (Server-Sent Events). No manual page refresh needed. The server starts at `http://localhost:3117` and immediately begins its first intelligence sweep. Browser auto-open is disabled by default; open the URL yourself or explicitly set `AUTO_OPEN_BROWSER=true` for a supported desktop environment. The initial sweep queries all 27 sources in parallel and typically takes 3060 seconds — the dashboard will appear empty until the sweep completes and pushes the first data update. After that, it auto-refreshes every 15 minutes via SSE (Server-Sent Events). No manual page refresh is needed.
**Requirements:** Node.js 22+ (uses native `fetch`, top-level `await`, ESM) **Requirements:** Node.js 22+ (uses native `fetch`, top-level `await`, ESM)
@@ -157,9 +157,13 @@ ACLED_EMAIL=
ACLED_PASSWORD= ACLED_PASSWORD=
CLOUDFLARE_API_TOKEN= CLOUDFLARE_API_TOKEN=
BLS_API_KEY= BLS_API_KEY=
REDDIT_CLIENT_ID=
REDDIT_CLIENT_SECRET=
TELEGRAM_BOT_TOKEN= TELEGRAM_BOT_TOKEN=
TELEGRAM_CHAT_ID= TELEGRAM_CHAT_ID=
TELEGRAM_POLL_INTERVAL=5000
TELEGRAM_CHANNELS=
DISCORD_BOT_TOKEN= DISCORD_BOT_TOKEN=
DISCORD_CHANNEL_ID= DISCORD_CHANNEL_ID=
DISCORD_GUILD_ID= DISCORD_GUILD_ID=
@@ -174,6 +178,7 @@ LLM_PROVIDER=litellm
LLM_BASE_URL=https://llm.example.com/v1 LLM_BASE_URL=https://llm.example.com/v1
LLM_API_KEY=your-litellm-api-key LLM_API_KEY=your-litellm-api-key
LLM_MODEL=your-model-alias LLM_MODEL=your-model-alias
LLM_TIMEOUT_MS=300000
# LM Studio # LM Studio
LLM_PROVIDER=lmstudio LLM_PROVIDER=lmstudio
@@ -436,14 +441,18 @@ Reddit is OAuth-only in this fork. If the Reddit credentials are missing or reje
### LLM Provider (optional, for AI-enhanced ideas) ### LLM Provider (optional, for AI-enhanced ideas)
Set `LLM_PROVIDER` to one of: `anthropic`, `openai`, `gemini`, `codex`, `openrouter`, `minimax`, `mistral`, `grok` Set `LLM_PROVIDER` to one of: `litellm`, `openrouter`, `openai-compatible`, `lmstudio`, `ollama`, `anthropic`, `openai`, `gemini`, `codex`, `minimax`, `mistral`, or `grok`.
| Provider | Key Required | Default Model | | Provider | Key Required | Default Model / Requirement |
|----------|-------------|---------------| |----------|--------------|-----------------------------|
| `litellm` | `LLM_API_KEY` | Explicit `LLM_BASE_URL` and `LLM_MODEL` required |
| `openrouter` | `LLM_API_KEY` | `openrouter/free` |
| `openai-compatible` | Endpoint-dependent | `local-model`; set `LLM_BASE_URL` |
| `lmstudio` | No | `local-model` |
| `ollama` | No | `llama3.1:8b` |
| `anthropic` | `LLM_API_KEY` | claude-sonnet-4-6 | | `anthropic` | `LLM_API_KEY` | claude-sonnet-4-6 |
| `openai` | `LLM_API_KEY` | gpt-5.4 | | `openai` | `LLM_API_KEY` | `gpt-4o-mini` |
| `gemini` | `LLM_API_KEY` | gemini-3.1-pro | | `gemini` | `LLM_API_KEY` | gemini-3.1-pro |
| `openrouter` | `LLM_API_KEY` | openrouter/auto |
| `codex` | None (uses `~/.codex/auth.json`) | gpt-5.3-codex | | `codex` | None (uses `~/.codex/auth.json`) | gpt-5.3-codex |
| `minimax` | `LLM_API_KEY` | MiniMax-M2.5 | | `minimax` | `LLM_API_KEY` | MiniMax-M2.5 |
| `mistral` | `LLM_API_KEY` | mistral-large-latest | | `mistral` | `LLM_API_KEY` | mistral-large-latest |
@@ -631,9 +640,22 @@ All settings are in `.env` with sensible defaults:
| `STALE_DATA_MAX_AGE_MINUTES` | `60` | Data age threshold for stale health state | | `STALE_DATA_MAX_AGE_MINUTES` | `60` | Data age threshold for stale health state |
| `STALE_ALERT_COOLDOWN_MINUTES` | `60` | Minimum time between repeated operator stale-data alerts | | `STALE_ALERT_COOLDOWN_MINUTES` | `60` | Minimum time between repeated operator stale-data alerts |
| `DASHBOARD_URL` | local URL | Dashboard URL included in operator alerts | | `DASHBOARD_URL` | local URL | Dashboard URL included in operator alerts |
| `AUTO_OPEN_BROWSER` | `false` | Open the dashboard in a host browser; keep disabled in Docker |
| `TERMINAL_ACTIONS_ENABLED` | environment-dependent | Enable guarded dashboard actions such as sweep and brief |
| `SWEEP_TOKEN` | disabled | Shared token required for remote action requests |
| `SSE_HEARTBEAT_INTERVAL_MS` | `25000` | Heartbeat interval for reverse-proxy SSE connections |
| `TERMINAL_ACTION_RATE_LIMIT_WINDOW_MS` | `60000` | Terminal action rate-limit window |
| `TERMINAL_ACTION_RATE_LIMIT_MAX` | `10` | Maximum terminal actions per client/window |
| `BRIEF_VERBOSITY` | `standard` | Briefing detail level |
| `LLM_PROVIDER` | disabled | `litellm`, `openrouter`, `openai-compatible`, `lmstudio`, `ollama`, `anthropic`, `openai`, `gemini`, `codex`, `minimax`, `mistral`, or `grok` | | `LLM_PROVIDER` | disabled | `litellm`, `openrouter`, `openai-compatible`, `lmstudio`, `ollama`, `anthropic`, `openai`, `gemini`, `codex`, `minimax`, `mistral`, or `grok` |
| `LLM_API_KEY` | — | API key (not needed for codex) | | `LLM_BASE_URL` | provider default | API base URL; required for LiteLLM and custom endpoints |
| `LLM_API_KEY` | — | Provider or proxy API key; required for LiteLLM |
| `LLM_MODEL` | per-provider default | Override model selection | | `LLM_MODEL` | per-provider default | Override model selection |
| `LLM_TEMPERATURE` | `0.2` | Sampling temperature for OpenAI-compatible providers |
| `LLM_MAX_TOKENS` | `2000` | Maximum completion token budget |
| `LLM_TIMEOUT_MS` | `90000` | LLM request timeout in milliseconds |
| `OPENROUTER_SITE_URL` | repository URL | OpenRouter attribution URL |
| `OPENROUTER_APP_NAME` | `Intelligence Terminal` | OpenRouter application title |
| `TELEGRAM_BOT_TOKEN` | disabled | For Telegram alerts + bot commands | | `TELEGRAM_BOT_TOKEN` | disabled | For Telegram alerts + bot commands |
| `TELEGRAM_CHAT_ID` | — | Your Telegram chat ID | | `TELEGRAM_CHAT_ID` | — | Your Telegram chat ID |
| `TELEGRAM_CHANNELS` | — | Extra channel IDs to monitor (comma-separated) | | `TELEGRAM_CHANNELS` | — | Extra channel IDs to monitor (comma-separated) |

View File

@@ -1,17 +1,22 @@
# Agent Handoff # Agent Handoff
Last updated: 2026-07-03 Last updated: 2026-07-04
## Current Work ## Latest Completed Work
- Canonical repository: `https://git.wilkensxl.de/Code-Inc/intelligence-terminal` - Canonical repository: `https://git.wilkensxl.de/Code-Inc/intelligence-terminal`
- Production baseline before this work: `c159c83a0768486c8c6f445b458b760dba4ba385` - LiteLLM implementation merge: `5c4bf80eb0c19bd59080f5432a2a344798d7a3ce`
- Active branch: `codex/issue-47-litellm-provider` - Merged PR: `#48 feat: add LiteLLM provider and publish Code-Inc image`
- Issue: `#47 Add first-class LiteLLM provider and publish updated image` - Completed issue: `#47 Add first-class LiteLLM provider and publish updated image`
- LiteLLM is implemented through the OpenAI-compatible `/chat/completions` API and requires `LLM_BASE_URL`, `LLM_API_KEY`, and `LLM_MODEL`. - LiteLLM is implemented through the OpenAI-compatible `/chat/completions` API and requires `LLM_BASE_URL`, `LLM_API_KEY`, and `LLM_MODEL`.
- The build workflow now targets `git.wilkensxl.de/code-inc/intelligence-terminal` and publishes only from the production branch, not from pull requests. - The build workflow now targets `git.wilkensxl.de/code-inc/intelligence-terminal` and publishes only from the production branch, not from pull requests.
- Runner build/test/image verification and the first `code-inc` registry publication must be recorded here after the branch is pushed and merged. - Gitea Actions runs 231-235 passed for the PR and production merge, including unit tests, Compose validation, Docker build, release dry-run, and template compliance.
- Related maintenance: issue #21 tracks the failing security scan, #45 tracks the dependency workflow, and #46 tracks remaining namespace/handoff cleanup. - The first `code-inc` registry publication was verified through the Gitea Package API on 2026-07-03.
- PR #52 / issue #51 removed the hard-coded 90-second/4096-token idea-generation override. LLM ideas now respect `LLM_TIMEOUT_MS` and `LLM_MAX_TOKENS`.
- PR #54 / issue #53 fixed prediction persistence after successful LLM generation and added a SQLite-backed regression test.
- Live Dockge verification on 2026-07-04 used `LLM_TIMEOUT_MS=300000` and `LLM_MAX_TOKENS=4096` with the `heim-llm` LiteLLM alias. The completed sweep produced six parsed ideas, reported `ideasSource=llm`, persisted memory, and had no `lastSweepError`.
- Production implementation commit: `14d9276c30e06cafcaee8177ba7377fdf5f26277`.
- Issues #47, #51, and #53 are complete. Issue #21 tracks the failing security scan and #45 tracks the dependency workflow.
## Repository State ## Repository State
@@ -236,27 +241,41 @@ README includes:
## Registry And Images ## Registry And Images
Target registry image after the current production merge: Production registry image:
```text ```text
git.wilkensxl.de/code-inc/intelligence-terminal git.wilkensxl.de/code-inc/intelligence-terminal
``` ```
The legacy `mrsphay` package remains available. Verify these new `code-inc` tags after the current runner publication: The legacy `mrsphay` package remains available. Verified `code-inc` tags from the LiteLLM production merge:
```text ```text
latest latest
YYYYMMDD 20260703
<production-commit-sha> 5c4bf80eb0c19bd59080f5432a2a344798d7a3ce
``` ```
Required pull verification after publication: Operator pull command:
```bash ```bash
docker pull git.wilkensxl.de/code-inc/intelligence-terminal:latest docker pull git.wilkensxl.de/code-inc/intelligence-terminal:latest
``` ```
Record the resulting digest after the runner push. Published manifest digest:
```text
sha256:5e29483ebfd9baae368673adc790789f02aed2d5d5d3a550fe55a4b71b5b62dd
```
Relevant successful runner executions:
```text
PR build: https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/231
PR template check: https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/232
Production publish: https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/233
Release dry-run: https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/234
Template compliance: https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/235
```
## Gitea Actions ## Gitea Actions
@@ -285,12 +304,12 @@ template-compliance.yml on codex/production-intelligence-terminal: success
Relevant run URLs: Relevant run URLs:
```text ```text
https://git.wilkensxl.de/MrSphay/intelligence-terminal/actions/runs/23 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/23
https://git.wilkensxl.de/MrSphay/intelligence-terminal/actions/runs/24 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/24
https://git.wilkensxl.de/MrSphay/intelligence-terminal/actions/runs/25 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/25
https://git.wilkensxl.de/MrSphay/intelligence-terminal/actions/runs/26 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/26
https://git.wilkensxl.de/MrSphay/intelligence-terminal/actions/runs/27 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/27
https://git.wilkensxl.de/MrSphay/intelligence-terminal/actions/runs/28 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/actions/runs/28
``` ```
Repository secret expected by the registry publish workflow: Repository secret expected by the registry publish workflow:
@@ -320,28 +339,28 @@ The following Gitea issues were created for real remaining work:
```text ```text
#1 Reddit source must stop unauthenticated .json scraping #1 Reddit source must stop unauthenticated .json scraping
https://git.wilkensxl.de/MrSphay/intelligence-terminal/issues/1 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/issues/1
#2 Send operator alerts when dashboard data remains stale #2 Send operator alerts when dashboard data remains stale
https://git.wilkensxl.de/MrSphay/intelligence-terminal/issues/2 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/issues/2
#3 ACLED credentialed integration needs regression test and diagnostics #3 ACLED credentialed integration needs regression test and diagnostics
https://git.wilkensxl.de/MrSphay/intelligence-terminal/issues/3 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/issues/3
#4 Complete memory and prediction loop beyond Phase-1 SQLite #4 Complete memory and prediction loop beyond Phase-1 SQLite
https://git.wilkensxl.de/MrSphay/intelligence-terminal/issues/4 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/issues/4
#5 Remove old inline dashboard snapshot from production builds #5 Remove old inline dashboard snapshot from production builds
https://git.wilkensxl.de/MrSphay/intelligence-terminal/issues/5 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/issues/5
#6 Harden Terminal Actions for public reverse-proxy deployments #6 Harden Terminal Actions for public reverse-proxy deployments
https://git.wilkensxl.de/MrSphay/intelligence-terminal/issues/6 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/issues/6
#7 Replace ADS-B stub with real disabled/degraded source handling #7 Replace ADS-B stub with real disabled/degraded source handling
https://git.wilkensxl.de/MrSphay/intelligence-terminal/issues/7 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/issues/7
#8 Clean inherited public-demo and upstream marketing references #8 Clean inherited public-demo and upstream marketing references
https://git.wilkensxl.de/MrSphay/intelligence-terminal/issues/8 https://git.wilkensxl.de/Code-Inc/intelligence-terminal/issues/8
``` ```
## Verification Already Performed ## Verification Already Performed
@@ -371,7 +390,7 @@ Audit result:
0 high vulnerabilities 0 high vulnerabilities
``` ```
Docker build and smoke test were performed locally earlier: Docker build and smoke test were performed locally earlier against the now-legacy `mrsphay` image namespace. Current deployments must use the `code-inc` image documented above:
```bash ```bash
docker build -t git.wilkensxl.de/mrsphay/intelligence-terminal:latest . docker build -t git.wilkensxl.de/mrsphay/intelligence-terminal:latest .
@@ -425,7 +444,7 @@ origin/main
1. Clone the Gitea repository: 1. Clone the Gitea repository:
```bash ```bash
git clone https://git.wilkensxl.de/MrSphay/intelligence-terminal.git git clone https://git.wilkensxl.de/Code-Inc/intelligence-terminal.git
cd intelligence-terminal cd intelligence-terminal
git checkout codex/production-intelligence-terminal git checkout codex/production-intelligence-terminal
``` ```
@@ -490,5 +509,5 @@ docker pull git.wilkensxl.de/code-inc/intelligence-terminal:latest
For a pinned deployment: For a pinned deployment:
```bash ```bash
docker pull git.wilkensxl.de/code-inc/intelligence-terminal:YYYYMMDD docker pull git.wilkensxl.de/code-inc/intelligence-terminal:20260703
``` ```

View File

@@ -205,7 +205,7 @@ export class IntelligenceStore {
_recordPredictions(data, timestamp) { _recordPredictions(data, timestamp) {
for (const idea of data.ideas || []) { for (const idea of data.ideas || []) {
const title = idea.title || 'Untitled idea'; const title = idea.title || 'Untitled idea';
const stableId = stableId('prediction', title, idea.type || '', idea.ticker || '', idea.horizon || ''); const predictionId = stableId('prediction', title, idea.type || '', idea.ticker || '', idea.horizon || '');
const evidence = Array.isArray(idea.signals) ? idea.signals : []; const evidence = Array.isArray(idea.signals) ? idea.signals : [];
this.db.prepare(`INSERT INTO predictions ( this.db.prepare(`INSERT INTO predictions (
stable_id, created_at, updated_at, title, type, hypothesis, evidence_json, confidence, stable_id, created_at, updated_at, title, type, hypothesis, evidence_json, confidence,
@@ -217,7 +217,7 @@ export class IntelligenceStore {
confidence=excluded.confidence, confidence=excluded.confidence,
evidence_json=excluded.evidence_json, evidence_json=excluded.evidence_json,
payload_json=excluded.payload_json`).run( payload_json=excluded.payload_json`).run(
stableId, predictionId,
timestamp, timestamp,
timestamp, timestamp,
title, title,

View File

@@ -43,7 +43,13 @@ Output ONLY valid JSON array. Each object:
}`; }`;
try { try {
const result = await provider.complete(systemPrompt, context, { maxTokens: 4096, timeout: 90000 }); const maxTokens = Number.isFinite(provider.maxTokens) && provider.maxTokens > 0
? provider.maxTokens
: 4096;
const timeout = Number.isFinite(provider.timeoutMs) && provider.timeoutMs > 0
? provider.timeoutMs
: 90000;
const result = await provider.complete(systemPrompt, context, { maxTokens, timeout });
const ideas = parseIdeasResponse(result.text); const ideas = parseIdeasResponse(result.text);
if (ideas && ideas.length > 0) { if (ideas && ideas.length > 0) {
return ideas; return ideas;

View File

@@ -12,7 +12,7 @@
"brief:save": "node apis/save-briefing.mjs", "brief:save": "node apis/save-briefing.mjs",
"diag": "node diag.mjs", "diag": "node diag.mjs",
"test": "npm run test:unit", "test": "npm run test:unit",
"test:unit": "node --test test/llm-openrouter.test.mjs test/llm-ollama.test.mjs test/llm-openai-compatible.test.mjs test/llm-litellm.test.mjs test/fetch-utils.test.mjs test/reddit-source.test.mjs test/acled-source.test.mjs test/mojibake-text.test.mjs test/adsb.test.mjs test/dashboard-geotagging.test.mjs", "test:unit": "node --test test/llm-openrouter.test.mjs test/llm-ollama.test.mjs test/llm-openai-compatible.test.mjs test/llm-litellm.test.mjs test/llm-ideas.test.mjs test/intelligence-store.test.mjs test/fetch-utils.test.mjs test/reddit-source.test.mjs test/acled-source.test.mjs test/mojibake-text.test.mjs test/adsb.test.mjs test/dashboard-geotagging.test.mjs",
"compose:config": "docker compose config", "compose:config": "docker compose config",
"clean": "node scripts/clean.mjs", "clean": "node scripts/clean.mjs",
"fresh-start": "npm run clean && npm start" "fresh-start": "npm run clean && npm start"

View File

@@ -0,0 +1,44 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { IntelligenceStore } from '../lib/intelligence-store.mjs';
test('records LLM ideas as stable predictions', async (t) => {
const directory = mkdtempSync(join(tmpdir(), 'intelligence-store-'));
t.after(() => rmSync(directory, { recursive: true, force: true }));
const store = await new IntelligenceStore(join(directory, 'intelligence.db')).init();
if (!store.available) {
t.skip(`node:sqlite unavailable: ${store.reason}`);
return;
}
store.recordRun({
meta: {
timestamp: '2026-07-04T10:17:51.011Z',
sourcesOk: 22,
sourcesDegraded: 7,
sourcesFailed: 0,
},
ideasSource: 'llm',
ideas: [{
title: 'Gold safe-haven hedge',
type: 'HEDGE',
ticker: 'GLD',
confidence: 'MEDIUM',
rationale: 'Geopolitical risk remains elevated.',
risk: 'Risk appetite recovers.',
horizon: 'Weeks',
signals: ['geopolitical escalation'],
source: 'llm',
}],
}, { summary: { direction: 'risk-off' } });
const result = store.listPredictions({ limit: 10 });
assert.equal(result.available, true);
assert.equal(result.predictions.length, 1);
assert.equal(result.predictions[0].title, 'Gold safe-haven hedge');
assert.match(result.predictions[0].stable_id, /^[a-f0-9]{24}$/);
});

48
test/llm-ideas.test.mjs Normal file
View File

@@ -0,0 +1,48 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { generateLLMIdeas } from '../lib/llm/ideas.mjs';
const response = JSON.stringify([{
title: 'Test idea',
type: 'WATCH',
ticker: 'SPY',
confidence: 'LOW',
rationale: 'Test rationale',
risk: 'Test risk',
horizon: 'Days',
signals: ['test'],
}]);
test('idea generation respects provider token and timeout configuration', async () => {
let capturedOptions;
const provider = {
isConfigured: true,
maxTokens: 2000,
timeoutMs: 300000,
async complete(_systemPrompt, _context, options) {
capturedOptions = options;
return { text: response };
},
};
const ideas = await generateLLMIdeas(provider, {}, null, []);
assert.deepEqual(capturedOptions, { maxTokens: 2000, timeout: 300000 });
assert.equal(ideas.length, 1);
assert.equal(ideas[0].source, 'llm');
});
test('idea generation keeps safe defaults for providers without limits', async () => {
let capturedOptions;
const provider = {
isConfigured: true,
async complete(_systemPrompt, _context, options) {
capturedOptions = options;
return { text: response };
},
};
await generateLLMIdeas(provider, {}, null, []);
assert.deepEqual(capturedOptions, { maxTokens: 4096, timeout: 90000 });
});