Skip to content

feat(kiro-extension): self-contained floating Doraemon companion - #34

Merged
AlleyBo55 merged 4 commits into
masterfrom
kiro/desktop-companion-extension
Jul 28, 2026
Merged

AlleyBo55 merged 4 commits into
masterfrom
kiro/desktop-companion-extension

Conversation

@AlleyBo55

Copy link
Copy Markdown
Owner

Ships Doraemon as a Kiro extension with a bundled 792 KB native companion, so one install gives a transparent always-on-top desktop mascot with nothing else to download.

Companion (kiro-extension/companion, Rust + tao/wry)

  • Borrows the OS webview instead of bundling Chromium: 792 KB vs Electron's 274 MB
  • Small transparent window that Rust repositions as the mascot walks, rather than a fullscreen click-through overlay. Removes the need for mouse-event forwarding, which Electron only supports on macOS and Windows.
  • Serves sprites over a custom dora:// protocol from the extension's media dir
  • Lifetime tied to the IDE's pid, so a hard kill leaves no orphaned window
  • macOS transparency needs wry's transparent feature and no window shadow

Extension

  • Three surfaces sharing one engine: bundled companion, detached editor window, sidebar. Default auto resolves companion then window, so the mascot appears outside the IDE on first install with no configuration.
  • 33 activity signals from first-party editor APIs
  • Kiro agent state via hooks writing .kiro/doraemon/agent-state.json, installed automatically with platform-correct shell commands. Kiro only loads hooks/**/*.kiro.hook, so .json hooks are silently ignored.
  • "Awaiting approval" is inferred from the agent going quiet; Kiro exposes no such trigger and a slow tool is indistinguishable from outside
  • Clicking the bubble raises the IDE, which an extension cannot do for itself
  • Reuses shimeji.ts, sprites.ts and the thought pools by import, not by copy

Desktop app

  • New env-gated DORAEMON_EXTENSION_MODE renders mascot only, starting no OpenClaw, gateway, memory, experience, voice or MCP subsystems. Default behaviour is unchanged.

Drag regression fix

  • update() checked the animation lock before applyPhysics, so a dropped mascot never fell. Drag now emits lean states, carries throw momentum, and releases the lock. Covered by 24 engine tests.

Also

  • CI matrix builds and packages darwin-arm64/x64, win32-x64, linux-x64 and publishes per-target to Open VSX on a kiro-extension-v* tag
  • electron-builder mac packaging was failing on an empty build/icons directory

Ships Doraemon as a Kiro extension with a bundled 792 KB native companion,
so one install gives a transparent always-on-top desktop mascot with nothing
else to download.

Companion (kiro-extension/companion, Rust + tao/wry)
- Borrows the OS webview instead of bundling Chromium: 792 KB vs Electron's 274 MB
- Small transparent window that Rust repositions as the mascot walks, rather
  than a fullscreen click-through overlay. Removes the need for mouse-event
  forwarding, which Electron only supports on macOS and Windows.
- Serves sprites over a custom dora:// protocol from the extension's media dir
- Lifetime tied to the IDE's pid, so a hard kill leaves no orphaned window
- macOS transparency needs wry's `transparent` feature and no window shadow

Extension
- Three surfaces sharing one engine: bundled companion, detached editor window,
  sidebar. Default `auto` resolves companion then window, so the mascot appears
  outside the IDE on first install with no configuration.
- 33 activity signals from first-party editor APIs
- Kiro agent state via hooks writing .kiro/doraemon/agent-state.json, installed
  automatically with platform-correct shell commands. Kiro only loads
  hooks/**/*.kiro.hook, so .json hooks are silently ignored.
- "Awaiting approval" is inferred from the agent going quiet; Kiro exposes no
  such trigger and a slow tool is indistinguishable from outside
- Clicking the bubble raises the IDE, which an extension cannot do for itself
- Reuses shimeji.ts, sprites.ts and the thought pools by import, not by copy

Desktop app
- New env-gated DORAEMON_EXTENSION_MODE renders mascot only, starting no
  OpenClaw, gateway, memory, experience, voice or MCP subsystems. Default
  behaviour is unchanged.

Drag regression fix
- update() checked the animation lock before applyPhysics, so a dropped mascot
  never fell. Drag now emits lean states, carries throw momentum, and releases
  the lock. Covered by 24 engine tests.

Also
- CI matrix builds and packages darwin-arm64/x64, win32-x64, linux-x64 and
  publishes per-target to Open VSX on a kiro-extension-v* tag
- electron-builder mac packaging was failing on an empty build/icons directory
All four platform builds failed on typecheck with:

  error TS2688: Cannot find type definition file for 'node'

tsconfig sets `types: ["node", "vscode"]` but only @types/vscode was declared.
Locally tsc walks up to the parent repo's node_modules and finds @types/node,
so it passed here and failed everywhere else. CI installs kiro-extension only.

- Declare @types/node 20.19.30, matching the version the parent already resolved
- Add scripts/verify-deps.mjs, run as part of typecheck: asserts every tsconfig
  `types` entry has a matching @types package, and every bare import in src/ and
  scripts/ is declared here rather than hoisted from the parent

Verified by running the full CI pipeline against a fresh clone with no parent
node_modules present: npm ci, typecheck, build, cargo companion, verify-sprites,
smoke, package all pass. The guard was also confirmed to fail with exit 1 when
@types/node is removed again.
An audit of the animation table against reactions.ts found 11 of the 40 generated
sprite sets had nothing that could ever trigger them. They shipped in the VSIX
and never played, which is invisible by definition.

Six action sets had no route at all:
- action_research      -> reviewing code (12+ line selection), replacing coding_thinking
- action_gadget_surprise -> a file being created
- action_eating / action_hungry -> break reminder, picked at random
- action_take_copter   -> git push and deploy commands, recognised in describeCommand
- action_explain_gadget -> agent finished, presenting the result
- action_walk          -> window blurred, he wanders off
- action_angry         -> a terminal command exiting non-zero
- action_chat_answer   -> a task passing, reporting back

Five emotions were never set, so their sets were only reachable through engine
idle aliases rather than any editor event:
- confusion  -> 1-2 errors, turning the error reaction into a ladder
                (1-2 confused, 3-4 concerned, 5+ frustrated)
- hope       -> task started, before the outcome is known
- awe        -> the whole project going error-free
- longing    -> window blurred
- gratitude  -> agent finished

Adds scripts/verify-coverage.mjs, run as part of build: fails if any generated
set has no route to the screen. Counterpart to verify-sprites, which checks the
other direction that every referenced frame exists on disk.

Behaviour changes, all deliberate and covered by updated assertions:
- window blur reads as longing/action_walk rather than calm/action_rest
- agent completion reads as gratitude/action_explain_gadget rather than
  pride/action_chat_answer
- 1-2 errors read as confusion rather than concern
- task start reads as hope, project-clean as awe
Four problems from the first CI run.

win32-x64 failed: release.mjs shells out with spawnSync('npx', ...), but npx is
npx.cmd on Windows and spawnSync cannot exec a .cmd without a shell, so the job
died on a bare ENOENT. Passes shell:true on win32.

darwin-x64 hung: macos-13 is a deprecated image that frequently sits queued.
darwin-x64 is now cross-compiled from the macos-14 arm64 runner via
--target x86_64-apple-darwin, removing that runner from the matrix entirely.
Verified locally: the staged binary reports "Mach-O 64-bit executable x86_64".

Every push ran the matrix twice: `push: branches: ['**']` plus `pull_request`
both fire for a branch with an open PR. Push is now limited to the default
branch and release tags, with pull_request covering branches. Adds a concurrency
group that supersedes in-flight runs but never cancels a tag.

Publishing waited for all four platforms because a separate job used
`needs: build`. Package, upload and publish now all happen inside each matrix
job, so a platform ships the moment it is ready and one broken or slow target
cannot hold up a healthy one.

Also:
- matrix.rust-target would have been evaluated as subtraction in a GitHub
  expression; renamed to matrix.rustTarget
- build-companion.mjs and release.mjs read DORAEMON_RUST_TARGET and
  DORAEMON_VSIX_TARGET, so one `npm run build` serves both native and
  cross-compiled jobs without extra steps
- timeout-minutes: 40 so a hung runner fails instead of blocking the queue
@AlleyBo55
AlleyBo55 merged commit ef6cf65 into master Jul 28, 2026
5 checks passed
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.

1 participant