| Age | Commit message (Collapse) | Author |
|
Removed 40+ markdown files that were cluttering the repo root:
- frontend-*-handoff.md (28 files) — historical API contract handoffs, features all implemented
- backend-to-fe-handoff.md, backend-to-fe-handoff-2.md — old handoff docs
- broken-chat-repair-handoff.md — old repair handoff
- PLAN-mcp.md, PLAN-per-edit-diagnostics.md — old planning docs
- ai-review-report.md, crash-review-report.md — one-time review reports
- tasks.md, HANDOFF.md — outdated status docs (git log is the source of truth)
Kept: AGENTS.md, GLOSSARY.md, ORCHESTRATOR.md, README.md
Also: gitignored ai-review-report.md so future Gemini reviews don't commit it
|
|
Kimi was trying to use Python tools to analyze images rather than just
describing them. Updated both vision system prompts (consult_vision and
image compaction) to explicitly instruct: do not use any tools unless
specifically asked to — just use your vision to see the image and describe
it directly.
|
|
reconcile drops thinking-only messages
Root cause of tool-calls-in-thinking bug: append() assigns msgIdx as a LOCAL
index (reset to 0 per call), but load() grouped chunks by msgIdx alone. Since
the orchestrator persists messages one at a time (append([user]) at turn start,
then append([assistant, ...toolResults]) per step), all single-message appends
share msgIdx=0 and collapse into one giant user-role message. The model loses
its prior assistant responses and tool-call history, falls back to text-based
tool-call syntax inside reasoning_content, and the turn ends with finish_reason
stop (no structured tool_calls detected).
Fix 1 (store.ts load()): split message boundaries on role change too, not just
msgIdx. Handles the alternating user/assistant/tool pattern correctly.
Fix 2 (reconcile.ts hasContent): include thinking chunks as valid content so
thinking-only assistant messages are not silently dropped on load. The buggy
seq-14 output (assistant, thinking-only) was being deleted by reconcile,
destroying evidence of the bug.
Verified: load() on the affected conversation now produces 9 correct messages
(was 3 merged). All 1999 tests pass. See notes/tool-call-in-thinking-bug.md.
|
|
|
|
workspace-star
|
|
# Conflicts:
# packages/provider-concurrency/src/concurrency-manager.ts
# packages/provider-concurrency/src/extension.ts
|
|
safety + persist auto-reduce notice
Bug 1 (overshoot): acquire() fast-path now queues while a recycle-poll is in
flight (gatePolling), and the recycle defers its inFlight decrement until the
usage poll resolves — holding inFlight inflated through the poll window so a
concurrent caller cannot sneak through before upstream confirms room. The gated
path admits exactly one waiter per fresh poll (grantOne), robust against stale
upstream counts. Common-case throughput preserved: the fast-path still grants
immediately when no poll is in flight.
Bug 2 (unhandled rejection): fetchUsage is wrapped in safeFetchUsage — a
throwing getUsage() is caught, treated as undefined (cooldown-only fallback),
and reported via the new onUsagePollError opt (warn-level). No unhandled
promise rejection can crash the process.
Bug 3 (lost notice): loadLimits now calls restoreLimit (a new lower-level
state-seeding method that does NOT clear autoReduced) instead of setLimit
(which is a manual user action that clears the notice). The auto-reduce marker
(autoReducedFrom) is persisted under auto-reduce:<providerId> and restored on
activate via loadAutoReduce, so the frontend banner survives a restart.
+6 tests covering each bug.
|
|
concurrency service absent
Bug 1 (MEDIUM): In-memory starred cache leaks IDs for deleted workspaces.
The starredWorkspaces Set in the concurrency manager never cleaned up when a
workspace was deleted. FIX: the DELETE /workspaces/:id route now calls
concurrencyService.notifyWorkspaceStarred(id, false) after deleting, so the
deleted workspace ID is removed from the in-memory cache (preventing stale
IDs and preventing a re-created workspace with the same slug from inheriting
the old starred state).
Bug 2 (MEDIUM): Stale in-memory priority when concurrency extension is absent.
If provider-concurrency is not loaded, the star toggle persisted but the
in-memory priority cache was never updated (the optional chaining ?. silently
skipped the call). Already-queued agents kept their old priority until
restart. FIX: the star/unstar routes now check if concurrencyService is
defined and log a warning when it is absent, making the degraded behavior
visible. The starred state still persists correctly — it just does not
affect in-memory scheduling until the extension is loaded.
Tests: +5 (star round-trip with concurrency notification, invalid slug 400,
delete cleans up cache, absent-service warning log). All 1970 tests pass.
|
|
|
|
Merge dev to restore production-critical crash fixes that were committed
after the branch was cut: SSH pool permanent error listener, uncaughtException/
unhandledRejection guards in host-bin, MemoryMax=24G circuit breaker,
memory telemetry logging, LSP disable precaution, and crash investigation notes.
The provider-wrapper.test.ts:102 test passes — it was already fixed by the
acquire() signature change (adding workspaceId parameter) that realigned
the test arguments. No additional test fix needed.
Verification: typecheck 0 errors, 1965 tests pass, biome clean.
|
|
|
|
|
|
|
|
|
|
uncaughtException/unhandledRejection guards
Root cause of the live production crash (exit-1 'Timed out while waiting for
handshake'): the pooled ssh2.Client had no permanent 'error' listener after
connect, so a post-connect ssh2 error escaped as an uncaught EventEmitter
'error' with no process-level guard. See notes/crash-investigation-findings.md §1.
- packages/ssh/src/pool.ts: attach a permanent 'error' listener to the pooled
client in buildConnection that sets state=error, logs (alias, message, level),
and does not throw; cleanup() no longer removes it.
- packages/host-bin/src/main.ts: add process.on('uncaughtException') (graceful
shutdown after logging) and process.on('unhandledRejection') (log + continue),
both logging message/stack, memory snapshot, activeConversations count, and
timestamp so the failure site is observable.
|
|
|
|
- Disable LSP extension (import + CORE_EXTENSIONS) due to crashes
- Make transport-http tolerate LSP being absent (optional getService)
- Remove lsp from transport-http dependsOn manifest
- Change memory telemetry sample interval from 60s to 15s
|
|
|
|
service
Hands the user a one-command script to apply the systemd MemoryMax/MemoryHigh
cgroup limits to the live dispatch.service without a full reinstall.
Patches in the real user (same as bin/install), daemon-reloads, and restarts.
|
|
The server has a memory leak (~2.5 GB/h) that eventually triggers a Bun
runtime segfault. These cgroup limits turn the uncontrolled crash into a
controlled OOM-kill → clean restart via Restart=on-failure.
MemoryHigh=20G (soft throttle) + MemoryMax=24G (hard cap).
Machine has 33.24 GB total; 24G leaves OS headroom.
|
|
|
|
background files
|
|
(document lifecycle) + leaked init promises
|
|
# Conflicts:
# packages/session-orchestrator/src/extension.ts
# packages/session-orchestrator/src/orchestrator.ts
|
|
When a non-vision model (e.g. GLM) calls consult_vision, the new Kimi
consultation tab now shows 'IMAGE - <question>' instead of the bare
question-derived title, making image-consultation tabs visually
distinguishable from normal conversation tabs.
- Add formatConsultationTitle(question) pure helper (pure.ts): prefixes
'IMAGE - ' and truncates the question to 80 chars (matching the
conversation store's TITLE_MAX) with an ellipsis.
- Add setConversationTitle dep to VisionHandoffDeps, wired in the
extension to the conversation store's setConversationTitle.
- Call it in consultVision BEFORE the turn starts so the title is
correct from the first moment (the store keeps a non-'Untitled'
title on first message append). Best-effort: a title-write failure
logs a warning but does not break the consultation.
- Tests: 4 pure + 2 service (title set + optional-dep graceful).
|
|
purge on compaction/close
|
|
host storage
|
|
blocks on acquire, re-emit "active" when slot granted
|
|
settings
|
|
|
|
auto-transcription
|
|
rejects temperature: 0)
The vision handoff hardcoded temperature: 0 for the transcription sub-call,
but the Moonshot/Kimi vision model only allows temperature: 1 (or omitted),
causing an HTTP 400 "invalid temperature: only 1 is allowed for this model"
that blocked the entire image analysis for non-vision models like GLM 5.2.
Fix: omit temperature entirely so each vision provider uses its own default —
the truly universal, provider-agnostic approach (different providers have
different temperature constraints).
|
|
overshoot from provider accounting lag
|
|
|
|
|
|
|
|
vision handoff
|
|
limits with oldest-agent-first scheduling
|
|
|
|
# Conflicts:
# packages/host-bin/package.json
# packages/host-bin/src/main.ts
# packages/session-orchestrator/src/orchestrator.ts
# packages/system-prompt/src/service.test.ts
# packages/system-prompt/src/service.ts
# packages/system-prompt/src/types.ts
# packages/transport-contract/package.json
# packages/transport-http/package.json
# packages/transport-http/src/app.test.ts
# packages/transport-http/src/app.ts
# packages/transport-http/src/extension.ts
# packages/transport-http/tsconfig.json
# tsconfig.json
|
|
|
|
backend, dispatch-web → frontend)
|
|
|
|
|
|
|
|
|
|
|
|
The script previously required sudo for the entire script (id -u check),
which meant bin/build ran as root and created root-owned dist/ files.
On the next build, the normal user couldn't overwrite them (EACCES).
Now the script runs without a sudo prefix: the build step runs as the
normal user (dist/ files are user-owned), and sudo is used only on the
specific lines that write to system directories (/usr/bin, /etc,
/usr/share) or call systemctl.
|
|
bin/build was compiling the binary directly from stale dist/*.js files
without first recompiling the TypeScript packages. Since package.json
main fields point to dist/index.js, source edits to .ts files were
silently lost in the compiled binary.
Now tsc --build runs first (composite project references rebuild all
packages in dependency order), then bun build --compile bundles the
fresh dist/ output.
|
|
|