- Removed auto-invoked syncIndexes() blocks from all 11 model files
(api-client, api-client-log, csrf-token, drone-monitor,
drone-registration, email-log, email-verification, ide-session,
user, web-token, web-visit) — no longer needed
- Fixed drone-session.test.ts by mocking ChatSession.updateOne(),
VectorStoreService.ingestTurn(), and MessageQueue to prevent
real DB connections during tests
- Removed unused mongoose Types import from drone-session test
- All 129 tests now pass across all packages
The qwen3-embedding:4b model defaults to 2560-d vectors. Both
Ollama (client.embed()) and OpenAI support a dimensions parameter
to request a specific output size. This change threads the configured
qdrant.vectorSize through the AI provider layer so the model returns
vectors matching the Qdrant collection dimensions.
- AiApi.embeddings() now accepts optional dimensions parameter
- Ollama provider: switched from client.embeddings() to client.embed()
- OpenAI provider: passes dimensions to embeddings.create()
- VectorStoreService.getEmbedding() passes env.qdrant.vectorSize
- Added unit tests for dimension mismatch detection, collection
creation, and search guards
Adds vector-based semantic search across all chat sessions using Qdrant.
When a ChatTurn finishes, its content is chunked, embedded, and upserted
to a Qdrant collection. A search API and UI components enable searching
at user, project, and session scope.
Phase 1 — Configuration & Dependencies
- Add port/apiKey to GadgetCodeConfig.qdrant type
- Uncomment and update qdrant section in YAML config example
- Add qdrant config passthrough in env.ts
- Add @qdrant/js-client-rest dependency
Phase 2 — AI Embedding API (@gadget/ai)
- Add IAiEmbeddingResponse interface and abstract embeddings() to AiApi
- Implement embeddings() in OllamaAiApi (client.embeddings)
- Implement embeddings() in OpenAiApi (client.embeddings.create)
- Export IAiEmbeddingResponse from package index
Phase 3 — Backend Vector Store Service
- Create VectorStoreService (ingestTurn, search, removeTurnPoints)
- Hook fire-and-forget ingest after turn.save() in drone-session
- Register VectorStoreService in service startup/shutdown
Phase 4 — Backend Search API
- Create POST /api/v1/search controller with userId enforcement
- Batch-hydrate results from MongoDB (user, project, session, turn)
- Register search route in v1 API router
Phase 5 — Frontend Search Components
- SearchInput: debounced input with lucide-react icons
- ChatSearchResults: modal with score badges, metadata, loading states
- DroneSelectionModal: drone picker for sessions without a drone
- Add searchApi and ISearchResult to API client
- Add search to Home (global), ProjectManager (project), ChatSessionView (session)
- Add id=turn-{turnId} to ChatTurn for scroll targeting
- Scroll-to-turn from search result selection and router state
- Show DroneSelectionModal when no drone available
- Add Select Drone button in ChatSessionView sidebar
- Log the SDK response object immediately after client.chat.completions.create()
in both generate() and readStreamingChatCompletion()
- These debug-level logs capture the response at the call site before any
iteration or processing
- All downstream info-level logs continue to dump raw chunk/response objects
- Add rawUsage field to OpenAiChatIterationResult to carry the
original OpenAI SDK usage object through the call chain
- All downstream chat() logs now dump rawUsage via
JSON.parse(JSON.stringify()) instead of our cooked
{ promptTokens, completionTokens }
- This exposes completion_tokens_details.reasoning_tokens and
any other fields OpenAI returns that we weren't capturing
Phase 1: OpenAI API Token Extraction
- Add stream_options: { include_usage: true } to all streaming API calls
- Capture chunk.usage from final streaming chunks and response.usage from non-streaming
- Extend OpenAiChatIterationResult with optional usage field
- Update buildStats() to accept and return real token counts from usage data
- Wire iteration.usage through chat() to both buildStats() call sites
Phase 2: Agent Loop Stats Propagation
- Rename inputTokens/outputTokens to masterInputTokens/masterOutputTokens, add masterThinkingTokens
- Accumulate response.stats.tokenCounts after each master AI call
- Delete all Math.ceil(length/4) crude approximations (master and subagent loops)
- Track startTime/durationMs and emit IWorkOrderCompleteStats with workOrderComplete
- Subagent loop uses response.stats?.tokenCounts instead of Math.ceil
Phase 3: Database Model Changes
- Add contextWindowUsage field to IChatSession, ChatSessionSchema, and frontend ChatSession
- Initialize contextWindowUsage: 0 on session creation
Phase 4: Persist Stats on Turn Completion
- drone-session: accept IWorkOrderCompleteStats, persist turn stats, walk subagent records for aggregate
- drone-session: $inc session stats and contextWindowUsage, add formatDurationLabel() helper
- code-session: accept and forward stats, update in-memory session stats
- message-queue: Redis replay handles 4th stats arg
- Update WorkOrderCompleteMessage type in @gadget/api to accept stats parameter
Phase 5: UI — Context Window Fuel Gauge
- Add contextWindowUsage prop to SessionPanel
- Add fuel gauge bar with E→F labels, green/yellow/red zones, token count display
- Visible for ALL provider types (not gated by apiType)
Phase 6: Frontend Streaming State
- Add IWorkOrderCompleteStats interface to frontend api.ts
- handleWorkOrderComplete accepts stats, updates turn stats and session contextWindowUsage
- Pass contextWindowUsage prop to SessionPanel
New chat tool: chat_export
- Export current ChatSession and all finished ChatTurn records to disk
- Two formats: markdown (human-readable) and json (machine-readable)
- JSON exports always include a companion -readme.md with author credits,
Gadget Code version/link, AI provider/model/parameters
- Output to .gadget/exports/ with sanitized session name + timestamp
- Security-first: excludes emails, API keys, baseUrls, system prompts,
gitUrls, and other PII/infrastructure details from all exports
- Only finished/aborted/error turns included (processing excluded)
- Follows SubagentTool setter-injection pattern for session data
- Available in all chat session modes (plan/build/test/ship/dev)
Files:
- packages/ai-toolbox/src/chat/export.ts (NEW - 729 lines)
- packages/ai-toolbox/src/chat/index.ts (barrel update)
- gadget-drone/src/tools/index.ts (re-export update)
- gadget-drone/src/services/agent.ts (import, register, getExportData)
- auto-generate name test: mock drone to accept work order so success callback runs
- session lock test: make async, mock TabLock, use vi.waitFor for async callback chain
- Add detectWorkspace() utility in packages/api/src/lib/workspace-detector.ts
that walks up from process.cwd() looking for .gadget/workspace.json
- Update gadget-drone to detect workspace at startup, change to workspace
directory, and restore original startup directory on shutdown
- Update gadget-tasks with same workspace detection pattern
- Both tools now fail fast if started outside a managed workspace
- Prevents creating invalid workspaces when started from subdirectories
Plan tools extended to all modes; recent chat sessions added to
Authenticated Home view; drone locking and chat session startup factored
out of Project Manager into Chat Session view and made universal; update
for AGENTS.md.
- README.md: Added gadget-tasks and @gadget/ai-toolbox to projects table,
added Scheduled Tasks architecture section with diagram, updated
monorepo structure, added gadget-tasks to dev server instructions,
added doc link
- docs/agent-toolbox.md: Updated to reflect @gadget/ai-toolbox extraction
from @gadget/ai. Changed IAiEnvironment → GadgetToolboxEnvironment,
AiTool → GadgetTool, updated import paths, config examples, and added
Tool Categories section. Clarified gadget-tasks is NOT a consumer.
- docs/gadget-tasks.md: New documentation covering architecture, per-task
execution flow, configuration, startup/shutdown sequences, concurrency,
work order tracking, heartbeat, error recovery, and getting started.
Converts gadget-tasks from a duplicated codebase with direct MongoDB access
to a headless IDE client that uses gadget-code's REST API and Socket.IO
protocol to submit and process task prompts through the existing pipeline.
Deleted (no longer needed):
- gadget-tasks/src/models/ (5 Mongoose models — duplicated from gadget-code)
- gadget-tasks/data/prompts/ (11 prompt templates — duplicated from gadget-code)
- gadget-tasks/src/services/executor.ts (629-line AWL loop — duplicated from gadget-drone)
- gadget-tasks/src/services/ai.ts (direct AI API calls — drones do this now)
- gadget-tasks/src/services/workspace.ts (workspace management — drones do this now)
Added:
- gadget-tasks/src/services/platform.ts — REST API + Socket.IO headless IDE client
with session lock, workspace mode, prompt submission, work order tracking
Rewritten:
- gadget-tasks/src/gadget-tasks.ts — new startup: user login → auth → drone
selection → Socket.IO connect → schedule tasks (no MongoDB)
- gadget-tasks/src/services/scheduler.ts — delegates to PlatformService.executeTask()
- gadget-tasks/src/config/env.ts — platform.baseUrl config, no mongodb/google
gadget-code changes:
- Added PATCH /api/v1/projects/:projectId/tasks/:taskId/lastRun route
- Added ProjectService.updateTaskLastRun() for atomic task field update
Config changes:
- GadgetTasksConfig: replaced mongodb with platform.baseUrl, removed google.cse
- Dependencies: removed mongoose, @gadget/ai, @gadget/ai-toolbox, dayjs,
simple-git, nanoid; added socket.io-client, @inquirer/prompts