Skip to content

Conversation

@vtemian
Copy link
Owner

@vtemian vtemian commented Jan 16, 2026

Summary

Integrates octto (interactive browser-based brainstorming) into micode as a separate primary agent alongside the existing brainstormer.

  • Adds browser-based UI for interactive Q&A sessions
  • Supports 15+ question types (pick_one, confirm, slider, rank, rate, etc.)
  • Branch-based exploration with automatic follow-up question generation
  • State persistence to thoughts/brainstorms/

Changes

Infrastructure (src/octto/)

  • session/: HTTP server + WebSocket for browser communication
  • state/: Brainstorm state persistence
  • ui/: Self-contained HTML/CSS/JS bundle for browser UI
  • types.ts: 15+ question type configurations

Tools (src/tools/octto/)

  • brainstorm.ts: create_brainstorm, await_brainstorm_complete, end_brainstorm
  • questions.ts: 15 question type tools
  • responses.ts: get_answer, get_next_answer, list_questions
  • session.ts: start_session, end_session
  • processor.ts: Answer processing with probe agent integration

Agents (src/agents/)

  • octto.ts: Primary orchestrator for brainstorm sessions
  • bootstrapper.ts: Creates 2-4 exploration branches from request
  • probe.ts: Evaluates branch Q&A and generates follow-up questions

Config

  • Added octto section to src/utils/config.ts with timeouts and limits
  • ID generators in octto/session/utils.ts (single source of truth)

Test plan

  • bun run build passes
  • bun test passes (200 tests, 1 pre-existing failure unrelated to octto)
  • Manual test: invoke octto agent, verify browser opens with brainstorming UI

Summary by cubic

Add octto: a browser-based brainstorming agent with branch-based exploration, interactive Q&A, and state saved to thoughts/brainstorms/. Also adds milestone artifact indexing and search plus safer startup through model validation and error handling.

  • New Features

    • Octto primary agent with browser UI, 15+ question types, and branch-based flow; sessions via HTTP+WS with persistence.
    • Tools for start/end session, asking questions, getting answers, and full brainstorm orchestration with a probe subagent.
    • Milestone artifact indexing and search in SQLite (classifier + ingest + tool).
    • Centralized config, logger, and error utilities used across hooks and tools.
  • Bug Fixes

    • Validate agent model overrides at startup; warn and fall back to defaults instead of crashing.
    • Fix context injection by switching to camelCase filePath; add tests.
    • PTY spawn now throws clear errors when the command is missing.
    • Reduce unnecessary confirmation prompts in core agents for smoother autonomous execution.

Written for commit ec46a3a. Summary will update on new commits.

The context-injector hook was using snake_case 'file_path' but OpenCode
tools use camelCase 'filePath', causing directory context injection to
silently fail for all file read/edit operations.

- Change input.args?.file_path to input.args?.filePath
- Add tests for context injection behavior
Wrap spawn() call in try-catch to provide descriptive error messages
when command not found or other spawn failures occur.

- Add try-catch around bun-pty spawn() call
- Throw descriptive error with command name and original message
- Add test for command not found scenario
- Note: bun-pty silently ignores invalid workdir (library limitation)
Add src/utils/config.ts with compile-time constants organized by domain:
- compaction: threshold, cooldown, timeout settings
- contextWindow: warning/critical thresholds
- tokens: estimation settings
- paths: ledger dirs, context files, patterns
- timeouts: btca, toast durations
- limits: file sizes, PTY buffer, cache settings

Add comprehensive tests in tests/utils/config.test.ts (29 tests)
When a user configures an agent with an unavailable provider/model in
micode.json, instead of crashing at runtime, we now:

- Validate models at plugin startup against available providers
- Log a warning for invalid models
- Remove the invalid model from config, letting the agent use its default

Handles edge cases: empty strings, malformed models (no slash), and
models with multiple slashes in the ID.

Closes #10
Update hooks and tools to use the new utility modules:
- auto-compact: config.* for thresholds/timeouts, extractErrorMessage
- context-injector: config.paths.* and config.limits.*
- context-window-monitor: config.contextWindow.*
- ledger-loader: config.paths.ledgerDir/ledgerPrefix
- token-aware-truncation: config.tokens.*
- artifact-auto-index: log.error instead of console.error

Part of tech debt remediation (Phase 3).
Integrate octto (interactive browser-based brainstorming) into micode
as a separate primary agent alongside the existing brainstormer.

Infrastructure (src/octto/):
- session/: HTTP server + WebSocket for browser communication
- state/: Brainstorm state persistence to thoughts/brainstorms/
- ui/: Self-contained HTML/CSS/JS bundle for browser UI
- types.ts: 15+ question type configurations

Tools (src/tools/octto/):
- brainstorm.ts: create_brainstorm, await_brainstorm_complete, end_brainstorm
- questions.ts: 15 question type tools (pick_one, confirm, slider, etc.)
- responses.ts: get_answer, get_next_answer, list_questions
- session.ts: start_session, end_session
- processor.ts: Answer processing with probe agent integration

Agents (src/agents/):
- octto.ts: Primary orchestrator for brainstorm sessions
- bootstrapper.ts: Creates 2-4 exploration branches from request
- probe.ts: Evaluates branch Q&A and generates follow-up questions

Config:
- Added octto section to src/utils/config.ts with timeouts and limits
- ID generators moved to octto/session/utils.ts (single source of truth)
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

20 issues found across 53 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/octto/ui/bundle.ts">

<violation number="1" location="src/octto/ui/bundle.ts:733">
P1: Hard-coding `ws://` makes the UI fail to connect when served over HTTPS because browsers block mixed-content WebSocket calls. Derive the protocol from `window.location.protocol` and use `wss://` when the page is loaded via HTTPS.</violation>

<violation number="2" location="src/octto/ui/bundle.ts:1263">
P2: Rate answers are recorded incorrectly: `setRating` marks every star up to the chosen value as `.selected`, but `submitRate` only reads the first `.selected` button, so higher ratings are never captured. Store the chosen value explicitly (e.g., via a data attribute) or select the highest `.selected` element before submitting.</violation>
</file>

<file name="src/octto/state/store.ts">

<violation number="1" location="src/octto/state/store.ts:155">
P1: `deleteSession` bypasses the per-session lock, so concurrent writes can recreate a session immediately after it is deleted.</violation>
</file>

<file name="src/octto/session/sessions.ts">

<violation number="1" location="src/octto/session/sessions.ts:208">
P1: `getAnswer` marks the entire question as TIMEOUT when a single wait call times out, so later user responses are silently dropped because `handleWsMessage` only processes PENDING questions.</violation>
</file>

<file name="src/config-loader.ts">

<violation number="1" location="src/config-loader.ts:122">
P2: `override.model` is assumed to be a string, but malformed configs can make it a non-string, causing `.trim()` to throw during validation.</violation>
</file>

<file name="src/octto/types.ts">

<violation number="1" location="src/octto/types.ts:184">
P2: `ShowPlanConfig.sections` is required even though `markdown` is documented as an alternative; make `sections` optional so callers can supply only `markdown`.</violation>
</file>

<file name="src/octto/constants.ts">

<violation number="1" location="src/octto/constants.ts:11">
P2: `DEFAULT_MAX_QUESTIONS` is hard-coded to 15 instead of being re-exported from `config.octto`, contradicting the stated single source of truth and risking divergence when the config changes.</violation>
</file>

<file name="src/octto/session/waiter.ts">

<violation number="1" location="src/octto/session/waiter.ts:60">
P2: Ensure the waiter is removed even when the callback throws; otherwise a failing waiter stays registered and blocks future notifications for that key.</violation>

<violation number="2" location="src/octto/session/waiter.ts:77">
P2: Wrap the notification loop in a `try/finally` so the waiter list is always cleared even when a callback throws; otherwise the key remains registered forever after an exception.</violation>
</file>

<file name="src/tools/octto/session.ts">

<violation number="1" location="src/tools/octto/session.ts:18">
P2: The `start_session` schema blocks valid octto question types (rank, rate, ask_image, etc.), so sessions containing those questions cannot be started even though the system supports them.</violation>
</file>

<file name="src/tools/milestone-artifact-search.ts">

<violation number="1" location="src/tools/milestone-artifact-search.ts:20">
P1: `searchMilestoneArtifacts` is never implemented on `ArtifactIndex`, so this tool throws before returning any results.</violation>
</file>

<file name="src/agents/octto.ts">

<violation number="1" location="src/agents/octto.ts:43">
P2: Design documents are instructed to be written to `thoughts/shared/plans/...` even though brainstorming outputs must live under `thoughts/shared/designs/`, so this prompt would misfile every design artifact.</violation>
</file>

<file name="src/octto/session/server.ts">

<violation number="1" location="src/octto/session/server.ts:19">
P2: Bind the brainstorming server to the loopback interface (e.g., hostname 127.0.0.1) so brainstorming sessions are not exposed on all network interfaces.</violation>
</file>

<file name="thoughts/shared/designs/2026-01-16-milestone-artifact-indexing-design.md">

<violation number="1" location="thoughts/shared/designs/2026-01-16-milestone-artifact-indexing-design.md:34">
P3: The metadata section is duplicated (“Metadata Fields” and “Metadata Fields (Explicit)”) with identical content, which makes the design doc harder to read and suggests there might be a missing distinction.</violation>
</file>

<file name="src/index.ts">

<violation number="1" location="src/index.ts:117">
P2: `octtoSessionsMap` is never populated, so the new session-deletion cleanup never ends octto sessions, leading to leaked browser/WebSocket sessions for each user session.</violation>
</file>

<file name="src/tools/octto/formatters.ts">

<violation number="1" location="src/tools/octto/formatters.ts:9">
P2: `escapeXml` does not escape quotes, so attribute values containing `"` or `'` will break the generated XML and allow injection. Extend the helper to encode quotes before reusing it for attributes.</violation>
</file>

<file name="src/tools/octto/questions.ts">

<violation number="1" location="src/tools/octto/questions.ts:122">
P2: rate tool omits the `labels` configuration defined in `RateConfig`, preventing callers from supplying the min/max labels the UI supports.</violation>

<violation number="2" location="src/tools/octto/questions.ts:193">
P2: ask_image ignores the `accept` whitelist from `AskImageConfig`, so callers cannot restrict allowed image types and the frontend can’t enforce MIME/extension safety.</violation>

<violation number="3" location="src/tools/octto/questions.ts:451">
P2: slider tool never accepts the `labels` object defined in `SliderConfig`, so callers can’t provide min/mid/max labels and the UI cannot display them.</violation>
</file>

<file name="tests/utils/logger.test.ts">

<violation number="1" location="tests/utils/logger.test.ts:72">
P2: `require` is not available in this ESM test file, so deleting `require.cache[...]` will throw and break the debug test. Use an ESM-friendly cache-busting approach instead (e.g., dynamic import with a query string).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

execute: async (args) => {
try {
const index = await getArtifactIndex();
const results = await index.searchMilestoneArtifacts(args.query, {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: searchMilestoneArtifacts is never implemented on ArtifactIndex, so this tool throws before returning any results.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tools/milestone-artifact-search.ts, line 20:

<comment>`searchMilestoneArtifacts` is never implemented on `ArtifactIndex`, so this tool throws before returning any results.</comment>

<file context>
@@ -0,0 +1,48 @@
+  execute: async (args) => {
+    try {
+      const index = await getArtifactIndex();
+      const results = await index.searchMilestoneArtifacts(args.query, {
+        milestoneId: args.milestone_id,
+        artifactType: args.artifact_type,
</file context>

const ratings = {};
for (const opt of (q.config.options || [])) {
const container = document.getElementById('rate_' + questionId + '_' + opt.id);
const selected = container.querySelector('.rate-star.selected');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Rate answers are recorded incorrectly: setRating marks every star up to the chosen value as .selected, but submitRate only reads the first .selected button, so higher ratings are never captured. Store the chosen value explicitly (e.g., via a data attribute) or select the highest .selected element before submitting.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/octto/ui/bundle.ts, line 1263:

<comment>Rate answers are recorded incorrectly: `setRating` marks every star up to the chosen value as `.selected`, but `submitRate` only reads the first `.selected` button, so higher ratings are never captured. Store the chosen value explicitly (e.g., via a data attribute) or select the highest `.selected` element before submitting.</comment>

<file context>
@@ -0,0 +1,1599 @@
+      const ratings = {};
+      for (const opt of (q.config.options || [])) {
+        const container = document.getElementById('rate_' + questionId + '_' + opt.id);
+        const selected = container.querySelector('.rate-star.selected');
+        if (selected) {
+          ratings[opt.id] = parseInt(selected.dataset.value);
</file context>

- created_at
- tags

# Metadata Fields (Explicit)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The metadata section is duplicated (“Metadata Fields” and “Metadata Fields (Explicit)”) with identical content, which makes the design doc harder to read and suggests there might be a missing distinction.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At thoughts/shared/designs/2026-01-16-milestone-artifact-indexing-design.md, line 34:

<comment>The metadata section is duplicated (“Metadata Fields” and “Metadata Fields (Explicit)”) with identical content, which makes the design doc harder to read and suggests there might be a missing distinction.</comment>

<file context>
@@ -0,0 +1,65 @@
+- created_at
+- tags
+
+# Metadata Fields (Explicit)
+- milestone_id (stored in metadata for filtering and retrieval)
+- artifact_type
</file context>

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 15 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="tests/indexing/search/milestone-search.test.ts">

<violation number="1" location="tests/indexing/search/milestone-search.test.ts:56">
P2: `index.close()` is only called on the happy path; if any assertion throws, the SQLite handle stays open and can make tests flaky. Wrap the test body in try/finally (or move the close to afterEach) so cleanup always runs.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

- Read available models from opencode.json synchronously during init
- Validate user model overrides in micode.json against available models
- Invalid models fall back to opencode default with warning
- Remove hardcoded model from agents (use opencode default)
- Update @opencode-ai/plugin to 1.1.23
- Fix plugin entry point to use dist/index.js
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 19 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/config-loader.ts">

<violation number="1" location="src/config-loader.ts:123">
P2: Model overrides are always rejected when opencode.json cannot be loaded. If loadAvailableModels returns an empty set (missing/invalid config), the code treats every model as invalid and strips the override. Consider skipping validation when no models are available so user overrides still work in that case.</violation>
</file>

<file name="package.json">

<violation number="1" location="package.json:5">
P1: `module`/`main` now point to `dist/index.js`, but the package publish allowlist excludes `dist/`, so the entrypoint will be missing from the published package.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Root cause: prompt had conflicting instructions - meta-rules said "be
proactive" but specific phase instructions said "ask ONE question" and
"wait for feedback". Models follow specific instructions over meta-rules.

Changes:
- Add <identity> section framing agent as senior engineer
- Rewrite exploring phase: "wait for feedback" -> "MAKE THE DECISION"
- Remove all "ask question" patterns throughout
- Update presenting phase to proceed without asking for approval
- Fix description: "collaborative questioning" -> "decisive collaboration"
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/config-loader.ts">

<violation number="1" location="src/config-loader.ts:112">
P2: Model override validation is effectively disabled when mergeAgentConfigs is called without availableModels (the common path), so invalid model IDs will be accepted and applied. This contradicts the documented behavior and can lead to invalid model configuration at runtime.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@vtemian vtemian merged commit d40e4b6 into main Jan 18, 2026
2 checks passed
@vtemian vtemian deleted the feat/octto-integration branch January 18, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants