summaryrefslogtreecommitdiffhomepage
path: root/packages/transport-http/src/extension.ts
AgeCommit message (Collapse)Author
2026-06-28merge: bring dev crash fixes into feature/workspace-starAdam Malczewski
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.
2026-06-28Merge branch 'dev' into feature/workspace-starAdam Malczewski
2026-06-28feat(workspace-star): starred workspace priority for concurrency limitingAdam Malczewski
2026-06-28fix: disable LSP + change memory telemetry interval to 15sAdam Malczewski
- 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
2026-06-27feat(provider-concurrency): implement per-provider in-memory concurrency ↵Adam Malczewski
limits with oldest-agent-first scheduling
2026-06-27style: reformat heartbeat merge to 2-space indentationAdam Malczewski
2026-06-26feat(heartbeat): resolve [type:name] variables in heartbeat prompts (CR-HB-1)Adam Malczewski
2026-06-26feat(heartbeat): workspace heartbeat loop with configurable AI monitoringAdam Malczewski
2026-06-25feat(ssh): wave 4 — computer HTTP/WS endpoints + chat computerId threadingAdam Malczewski
Wave 4 of transparent SSH support (3 parallel owner-agents on disjoint packages). - transport-http: computer routes — GET /computers, GET /computers/:alias, GET /computers/:alias/status, POST /computers/:alias/test (all delegate to a new ComputerService seam, graceful []/disconnected when ssh not loaded); GET/PUT/DELETE /conversations/:id/computer; PUT /workspaces/:id/default-computer (mirror the cwd/default-cwd routes); /chat threads computerId into the orchestrator. Defines ComputerService interface + computerServiceHandle (defineService<ComputerService>('ssh')) in seam.ts — the seam the ssh package provides via host.provideService in wave 5. - transport-ws: chat.send + chat.queue thread computerId onto the route result (mirrors cwd/workspaceId), forwarded to the orchestrator input. - mcp: CR-1 fix — filterMcpTools now preserves computerId on the returned ToolAssembly (mirrors cwd preservation), so the filter chain stays consistent. - orchestrator: added @dispatch/wire dep to transport-http (build/config, my lane) so its seam.ts Computer/ComputerEntry import resolves. Verified: tsc -b EXIT 0, biome clean, 1641 vitest pass (was 1620, +21). Refs: notes/ssh-support-plan.md (decisions §0.5/§13). No merge or push.
2026-06-25feat(transport-http): add GET /conversations/:id/mcp status endpointAdam Malczewski
Mirrors the existing GET /conversations/:id/lsp route exactly: gates on the persisted then effective cwd (null → empty servers), returns 503 when the MCP service isn't loaded, and maps McpServerStatus → McpServerInfo (conditionally including `error` per exactOptionalPropertyTypes). Wires mcpService into CreateServerOptions + extension activate via a plain host.getService (mirroring lspService; "mcp" added to dependsOn, route added to contributes.routes), adds the @dispatch/mcp workspace dep, and re-exports mcpServiceHandle / McpService / McpServerStatus from seam.ts. Adds 4 tests mirroring the LSP status tests.
2026-06-24feat(system-prompt): wire into turn flow + compaction + API routesAdam Malczewski
session-orchestrator: - Wire systemPromptService as optional dep (lazy via host.getService) - Regular turn: construct on first turn (new conversation), get on subsequent turns, set on providerOpts.systemPrompt (cache-safe) - Compaction: construct (fresh resolve) + append COMPACTION_SYSTEM_PROMPT - 12 new tests (construct/get/service-unavailable/compaction) transport-http: - GET /system-prompt (returns template or DEFAULT_TEMPLATE) - PUT /system-prompt (validate + setTemplate, 503 when unavailable) - GET /system-prompt/variables (static catalog, always available) - 6 new tests system-prompt service: added getTemplate/setTemplate to interface + impl. 1396 vitest pass. typecheck + biome clean.
2026-06-23feat: workspaces — session-orchestrator + transport-http + transport-ws + ↵Adam Malczewski
cli (Wave 2+3) session-orchestrator: workspaceId on StartTurnInput/EnqueueInput; effective cwd resolution (getCwd → getEffectiveCwd); auto-create workspace on turn start; warm parity (same effective cwd). 93 tests (+8). transport-http: workspace routes (GET/PUT/DELETE /workspaces, title, default-cwd); workspaceId threading on POST /chat + queue; ?workspaceId= filter on GET /conversations; DELETE /conversations/:id/cwd (clears explicit cwd); GET /conversations/:id/lsp uses effective cwd; slug validation. 166 tests. transport-ws: workspaceId threading on chat.send + chat.queue. 32 tests. cli: --workspace/-w flag; ConversationMeta test fakes fixed. 123 tests. Full typecheck EXIT 0, biome clean. 1283 vitest + 199 transport bun pass (1 pre-existing tool-shell failure unrelated to workspaces).
2026-06-22feat: context window from model endpoints + percentage-based auto-compactAdam Malczewski
ModelInfo (kernel contract): - Add contextWindow?: number field OpenAI-stream listModels: - Parse contextWindow from common field names (context_length, context_window, max_context_length, max_tokens) Transport-contract: - ModelsResponse: add optional modelInfo map (model name → { contextWindow? }) - Add ModelMetadata type - Rename CompactThresholdResponse → CompactPercentResponse - Rename SetCompactThresholdRequest → SetCompactPercentRequest Credential store: - Add getModelInfo(modelName) method — resolves full ModelInfo (including contextWindow) for a <credential>/<model> string Transport-http: - GET /models now includes modelInfo with contextWindow per model - Rename compact-threshold endpoints → compact-percent Session-orchestrator: - Auto-compact now uses contextSize (not overcounted usage.inputTokens) compared against contextWindow * (percent / 100) - Default percent: 85 (was flat 350000) - resolveModelInfo dep added to look up contextWindow - Passes modelName from the settled turn to the compaction service Conversation store: - Rename getCompactThreshold/setCompactThreshold → getCompactPercent/setCompactPercent - compactThresholdKey → compact-percent key
2026-06-22feat: stop generation mid-turn (POST /conversations/:id/stop)Adam Malczewski
Add stopTurn to the orchestrator: aborts the in-flight turn's AbortController without changing conversation status. The turn seals normally (finishReason: 'aborted'), partial messages are persisted, and the conversation transitions active → idle via the normal settle path. Distinct from closeConversation which marks the conversation closed. - POST /conversations/:id/stop endpoint - dispatch stop <id> CLI command - FE handoff: frontend-stop-generation-handoff.md
2026-06-22feat: conversation compacting (manual + automatic)Adam Malczewski
Implement roadmap item 10: conversation compaction to reclaim context window without losing the thread. Wire (0.11.0): - Add CompactionResult type - Add ConversationCompactedMessage WS event Transport-contract (0.15.0): - Add CompactResponse, CompactThresholdResponse, SetCompactThresholdRequest - Add ConversationCompactedMessage to WsServerMessage union - Re-export CompactionResult Conversation-store: - replaceHistory: delete all chunks, reset seq, append new messages - getCompactThreshold / setCompactThreshold (per-conversation setting) - compactThresholdKey added to keys.ts Session-orchestrator: - CompactionService interface + compactionHandle - conversationCompacted hook descriptor - createCompactionService: load history, split old/recent, call provider to summarize, replaceHistory with [system: summary] + recent N - Auto-trigger: resolveCompaction lazy dep, fires after turn settles (checks threshold, non-blocking) - Hook declared in manifest contributes.hooks + services Transport-http: - POST /conversations/:id/compact (manual trigger) - GET /conversations/:id/compact-threshold (read setting) - PUT /conversations/:id/compact-threshold (set setting) Transport-ws: - Subscribe to conversationCompacted hook - Broadcast conversation.compacted WS message CLI: - dispatch compact <conversationId> command FE handoff: frontend-compaction-handoff.md
2026-06-21feat: standalone build + systemd install (Arch Linux)Adam Malczewski
bin/build: compiles standalone binaries (dispatch-server + dispatch CLI) via bun build --compile, builds the frontend static bundle with VITE_HTTP_PORT=24991 + VITE_WS_PORT=24990, copies to dist/web/. bin/install: installs binaries to /usr/bin/, frontend to /usr/share/dispatch/web/, systemd service to /etc/systemd/system/, config to /etc/dispatch/env, data dirs to /var/lib/dispatch/ + /var/log/dispatch/. Enables + starts the dispatch systemd service. Supports --uninstall and --no-build flags. systemd/dispatch.service: Type=simple, reads /etc/dispatch/env, restarts on failure, logs to journald. systemd/dispatch.env: template config (ports 24991 HTTP + 24990 WS, DISPATCH_WEB_DIR, API key, data paths). transport-http: optional webDir static file serving — unmatched GET requests fall through to Bun.file() serving with SPA index.html fallback. Gated on DISPATCH_WEB_DIR env var (backward compatible).
2026-06-21feat(transport): CLI endpoints + conversation.open broadcast (Wave 2)Adam Malczewski
transport-http: GET /conversations (list with ?q= prefix filter), GET /conversations/:id/last (blocks until turn settles, returns last AI text), POST /conversations/:id/open (emits conversationOpened hook), PUT /conversations/:id/title (set title). emit threaded from host.emit. extractLastAssistantText pure helper. 21 new tests (166 total). transport-ws: subscribes to conversationOpened hook, broadcasts ConversationOpenMessage to all connected WS clients. 2 new tests. session-orchestrator: conversationOpened hook descriptor (exported).
2026-06-21feat(cli): Wave 0 — contracts for conversation list, last message, open tabAdam Malczewski
Additive contract changes for the CLI milestone (roadmap items 2 + 4): @dispatch/wire 0.8.0 → 0.9.0: - ConversationMeta { id, createdAt, lastActivityAt, title } @dispatch/transport-contract 0.12.0 → 0.13.0: - ConversationListResponse, LastMessageResponse, OpenConversationResponse - SetTitleRequest, TitleResponse - WS conversation.open broadcast (additive to WsServerMessage) ConversationStore interface: - listConversations(), getConversationMeta(), setConversationTitle() - Stub implementations in real store + 11 test fakes (Wave 1 fills in) Transport-http manifest: new routes declared (GET /conversations, GET /conversations/:id/last, POST /conversations/:id/open, PUT /conversations/:id/title)
2026-06-21feat(message-queue): per-conversation queue + steering injectionAdam Malczewski
A per-conversation message queue (new message-queue extension) holds user messages enqueued while a turn generates; delivered mid-turn as steering at the tool-result boundary (or carried to a new turn if no tool call fires). - kernel: RunTurnInput.drainSteering callback (generic; kernel stays pure) - wire 0.7.0->0.8.0: QueuedMessage, QueuePayload, TurnSteeringEvent (additive) - transport-contract 0.11.0->0.12.0: POST /conversations/:id/queue + chat.queue WS op - message-queue ext: queue state + per-conversation custom surface (rendererId message-queue) - session-orchestrator: enqueue facade + drainSteering wiring + post-seal carry - transport-http/ws: queue endpoint + chat.queue op (fixes WsClientMessage exhaustive switch) - host-bin: register message-queue 1043 vitest + 199 transport bun pass; tsc/biome clean; boot smoke clean. FE courier: frontend-message-queue-handoff.md.
2026-06-12feat(reasoning-effort): persisted per-conversation + per-turn override, ↵Adam Malczewski
threaded to providers - conversation-store: get/setReasoningEffort (own key space, mirrors cwd) - session-orchestrator: resolveReasoningEffort (override -> stored -> 'high'), StartTurnInput.reasoningEffort, warm() parity (cache-safe) - transport-http: /chat validation (400 on bad level) + GET/PUT /conversations/:id/reasoning-effort - transport-ws: chat.send threading + validation - cli: --effort <low|medium|high|xhigh|max> 993 vitest + 189 bun tests green; typecheck + biome clean.
2026-06-12feat(cache-warming): lifecycle CR-4 — default-off, fresh nextWarmAt, ↵Adam Malczewski
conversation close (+CR-1 table, CR-2 scope) CR-4a: warming defaults OFF (opt-in per conversation); re-enabling restores the persisted interval. CR-4b: re-arm BEFORE surface notify so post-warm updates carry the FUTURE nextWarmAt; turnSettled/turnStarted now also push (fresh schedule after seal, null while generating). CR-4c: POST /conversations/:id/close — per-turn AbortController wired to the kernel runTurn signal (partial persist + normal seal, done.reason "aborted"), new conversationClosed hook, cache-warming disables sync + persists OFF. Disconnect/chat.unsubscribe semantics unchanged. CR-4d: no change needed — initial surface echo already at HEAD (stale up2 boot on the FE probe). CR-1: loaded-extensions emits a single custom rendererId:"table" field (TablePayload exported; Name|Version|Trust|Activation, all trust tiers). CR-2: SurfaceCatalogEntry.scope?: "global"|"conversation" on both surfaces. Contracts: ui-contract 0.1.0→0.2.0, transport-contract 0.8.0→0.9.0 (additive). 907 tests pass (+13); live-verified against bin/up (warms @5s with future nextWarmAt; mid-turn close → abortedTurn:true + done.reason aborted). Courier: frontend-cache-warming-lifecycle-handoff.md.
2026-06-11feat(lsp,cwd): LSP integration + per-conversation cwd; fix cache-warming ↵Adam Malczewski
cache bust LSP + per-conversation CWD feature: - new bundled `lsp` extension: hand-rolled JSON-RPC codec (framing/rpc), lazy one-server-per-(serverID,root), per-cwd config resolution, on-demand `lsp` tool - `conversation-store`: getCwd/setCwd (cwdKey); `session-orchestrator` defaults a turn's cwd from the store - `transport-http`: cwd + lsp status endpoints; wire types in transport-contract - host-bin: register lsp; config wiring Cache-warming fix (the warm read 0% on the first reheat after a message): - warm assembled tools under a different cwd than the real turn (a reheat sends no cwd, and the warm service had no store fallback). The skills filter rewrites the cwd-sensitive `load_skill` description, so the tools block — the first bytes of the prompt-cache prefix — diverged and the cache missed entirely. Warm now resolves cwd as opts.cwd ?? conversationStore.getCwd(), mirroring handleMessage. - capture warm sends as `provider.request` spans flagged `warm:true` (thread a child logger into providerOpts) so warm vs real bodies are diffable (obs §3.1). - kernel logger: span-close now merges child-bound attrs like span-open, so a `warm:true` query finds the closed span (with usage/status), not just the open. Tests: warm forwards a warm-flagged logger; warm falls back to stored cwd; logger open/close attr consistency. Full suite green (873).
2026-06-11feat(cache-warming): manual POST /chat/warm trigger endpointAdam Malczewski
A frontend 'warm now' button (and fast tests) can trigger a warm on demand instead of waiting for the automatic timer. - transport-contract: WarmRequest / WarmResponse wire types - transport-http: POST /chat/warm → cacheWarmHandle.warm(); 200 with cachePct, 409 when the conversation is generating, 400 on missing conversationId Live-verified vs claude haiku: seed turn cacheWrite=6799 → POST /chat/warm returns cacheReadTokens=6799 cachePct=100 (100% hit). 760 vitest + 109 bun green.
2026-06-10feat: per-model throughput (tok/s) tracking + metrics endpointAdam Malczewski
New throughput-store extension records one token-weighted sample per turn (model, output tokens, pure generation time = Σ step genTotalMs) into a day-bucketed KV store, and aggregates per-model tok/s = Σtokens / Σgen-seconds over a day/week/month (server-local boundaries; week = ISO Mon–Sun). transport-http records a sample per turn (logged) and serves GET /metrics/throughput?period=day|week|month&date=<...>. The response is typed as transport-contract's ThroughputResponse, so store/wire drift is a compile error. Pure period + aggregate logic fully unit-tested.
2026-06-06refactor(transport-http,host-bin): transport-http owns its Bun.serve (fix ↵Adam Malczewski
log scope) Make transport-http a full-fidelity extension that runs its own Bun.serve inside activate(host) — symmetric with transport-ws. The Hono app is now built with the extension-scoped host, so all HTTP edge logs are correctly attributed extensionId=transport-http instead of the host-bin __host__ scope (verified live in the journal). - transport-http: createTransportHttpExtension() factory; activate builds the app + Bun.serve, reads host.config httpPort (?? 24203); deactivate stops it. - host-bin: drops the HTTP Bun.serve + createServer call; config.ts maps BACKEND_PORT/PORT -> httpPort. host-bin now serves no transport (both transports self-serve); boot log -> 'Dispatch booted'. - +5 bun lifecycle tests wired into test:bun. No contract change (composition wiring). Verified live: HTTP serves on :24203; journal edge logs now scoped transport-http. typecheck clean, 498 vitest + 89 bun, biome clean.
2026-06-06feat(transport-http,transport-ws): structured edge logging (close coverage ↵Adam Malczewski
gap #2) Both HTTP + WS transport edges now emit structured logs via the injected logger (D7-compliant: no per-AgentEvent/chat.delta frame logging). Verified live — the journal contains the edge records. - transport-ws: connection open/close (debug), chat.send accepted (info), surface-op + malformed-chat.send (warn), abort-on-close (debug). +4 bun tests. Correctly scoped extensionId=transport-ws (owns its Bun.serve). - transport-http: /chat accepted (info) / 400 (warn) / turn-failure (error), GET /conversations read (info), /models + store failure (error). +4 vitest. Known follow-up: transport-http edge logs are attributed to '__host__' (not 'transport-http') because host-bin runs the HTTP server via createServer(getHostAPI()) rather than the extension owning its Bun.serve. Logs are captured + correlated; only the per-extension filter is mis-scoped. Tracked in tasks.md. typecheck clean, 498 vitest + 84 bun, biome clean.
2026-06-06feat(transport-http): GET /conversations/:id?sinceSeq= read-side history ↵Adam Malczewski
endpoint Incremental rehydration endpoint for long-lived clients. Returns ConversationHistoryResponse { chunks: StoredChunk[], latestSeq } — the RAW, append-order, seq-filtered slice from conversation-store.loadSince, NOT reconciled (reconcile conflicts with the per-chunk seq cursor, so it stays on the turn path; the read path is a pure sync primitive). - transport-contract: add ConversationHistoryResponse + StoredChunk re-export. - transport-http: GET /conversations/:id route reaching the log directly via conversationStoreHandle (dependsOn conversation-store); pure parseSinceSeq (absent->0, invalid->400). - build wiring: conversation-store dep + project ref. FE Slice 2 backend prereq (read-side). typecheck clean, 481 vitest, biome clean.
2026-06-05feat(backend): credential-store + model selection/catalog (GET /models) + ↵Adam Malczewski
per-turn cwd through orchestrator/transport/host-bin
2026-06-04feat(core-ext): session-orchestrator + transport-http (parallel); wire into ↵Adam Malczewski
build graph (164 tests)