Commit graph

1627 commits

Author SHA1 Message Date
Ramnique Singh
d2263759ef
Merge pull request #709 from rowboatlabs/runtime-refactor
refactor(x): agent assembly — registry, extracted composer, capability records
2026-07-09 23:18:05 +05:30
Ramnique Singh
a9a342653f fix(x): review fixes — prototype-safe registry lookup, shared parsing, slimmer records
Findings from an independent review of the branch:

- loadAgent's plain-object lookup traversed Object.prototype, so a
  user-defined agent named "constructor"/"toString" threw a TypeError
  instead of falling through to the agents repo as it did before the
  registry (Object.hasOwn now guards both lookups; regression test
  pins the fallthrough without depending on the environment).
- agentFromRaw now uses the same parseFrontmatter helper FSAgentsRepo
  uses, so builtin and user agents can never drift on one file format.
- The fused workspace-context capability splits into agent-notes and
  work-directory records — the composer's own separator joining makes
  the output byte-identical, so the fusion bought nothing.
- Mode records drop their write-only title/summary (optional on the
  base record, still required on skills where the catalog renders
  them), the composer body is reindented and iterates a hoisted
  PROMPT_CAPABILITIES list, and the two single-consumer re-export
  shims in the legacy runtime file are deleted (consumers retargeted).

Golden snapshots unchanged throughout: composed prompts remain
byte-identical. Deferred to the follow-up PR with availability wiring:
discriminated-union typing on activation + fencing resolveSkill, and
unifying the three mirrored composition shapes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 23:10:58 +05:30
Ramnique Singh
9df3630423 Show token usage in chats 2026-07-09 22:47:25 +05:30
Ramnique Singh
f2a874a214 refactor(x): workspace context becomes an always-activated capability
The agent-notes and work-directory prompt blocks move out of the
composer into WORKSPACE_CONTEXT_CAPABILITY (activation: 'always'),
whose fragment is pure over the resolved inputs — the resolver still
loads notes/work-dir only for agents with the workspaceContext trait
and passes null otherwise, so trait gating is unchanged. The composer
now iterates one capability list (workspace + modes) in fixed order;
golden snapshots pass unchanged, byte-identical output. The composer's
own body is down to: base instructions + hidden-user-context + the
capability loop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 22:40:27 +05:30
Ramnique Singh
e194e32a23 refactor(x): modes become app-activated capabilities
The six mode blocks (voice input, video, coach, voice output
summary/full, search, code mode) move out of the composer's if-chain
into capability records (capabilities/modes.ts), each owning its
fragment text as a pure function of the composition context — the
code-mode fragment keeps its chip/cwd parameterization. The composer
now iterates MODE_CAPABILITIES in declared order, which is the fixed
total order that keeps composed prompts byte-stable; the 13 golden
snapshot tests pass unchanged, proving byte-identical output. Fragment
text was extracted from the if-chain programmatically (not retyped) so
the bytes could not drift. Adding a mode is now one record in one
file instead of a flag threaded through the resolver plus a concat
site in the composer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 22:38:06 +05:30
Ramnique Singh
484a9f0495 refactor(x): capability record — skills become the model-activated subset
capabilities/types.ts introduces the assembly unit: id/title/summary +
lazy guidance + owned tools (what skills already were), plus an
activation axis ('model' | 'app' | 'always') and an eager
promptFragment(ctx) for app/always entries — pure over a persisted
composition context so composed prompts stay byte-identical.
SkillDefinition is now CapabilityDefinition & {content}: bundled skills
are unchanged model-activated entries, the loadSkill catalog filters to
that subset, and disk skills remain structurally limited to it (eager
fragments and app activation are bundled-only powers — a disk file must
never gain a standing system-prompt injection channel).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 22:35:41 +05:30
Ramnique Singh
bd34531305 refactor(x): extract the system-prompt composer from the legacy runtime file
composeSystemInstructions (+ the hidden-user-context block) moves
verbatim to agents/compose-instructions.ts, owned by the assembly
layer; the legacy engine and old import paths keep working via
re-export. New golden-bytes snapshot tests pin the composed output for
a 12-case composition matrix plus the block ordering — the safety net
for folding the mode blocks into capability records: prefix caching
and snapshot inheritance require byte-identical prompts, so any
restructuring must keep these snapshots green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 22:32:47 +05:30
Ramnique Singh
b3330c09c4 refactor(x): agent registry — one table instead of the loadAgent ladder
Built-in agent identity moves to agents/registry.ts: a table of
definitions with builders, an alias (rowboatx → copilot) expressed as
shared entries, and declared traits. One prompt-file loader replaces
the five copy-pasted frontmatter-parsing blocks, and the stringly
"copilot" || "rowboatx" comparisons in the resolver and the legacy
runtime become hasWorkspaceContext(agentId) trait lookups. loadAgent
keeps its signature and its legacy import path via re-export; the
user-agents repo fallback resolves the container lazily so the
registry adds no static edge into the DI graph.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 22:31:02 +05:30
Gagan
5dd8c85ce4 style(x): chat empty state, message bubbles, sidebar hover gap
- Chat empty state: icon-free heading, single suggestion list with
  leading icons and hover arrows, aligned to the composer column
- User message bubbles: rounded-2xl with flattened top-right corner,
  hover-only copy button under the bubble's bottom-right
- Sidebar: hairline gap between menu items so adjacent active/hover
  highlights don't merge
2026-07-09 18:42:47 +05:30
Gagan
6def1bf7d3 style(x): icon-free up-next hero on home tab 2026-07-09 18:14:21 +05:30
Gagan
602889e340 style(x): unify tab headers and clean up Brain/Home UI
- Shared header treatment across Apps, Background tasks, Workspace,
  Meetings, Brain: 24px/650 heading, no leading icons, no divider line,
  centered 1120px column with fixed 34px/30px padding
- Apps-style page background on BG tasks, Workspace, Meetings
- Brain files view: monochrome then icon-free rows, inline note-count
  meta line instead of pill chips, semibold item names, uniform row
  heights, breadcrumb-only navigation, ghost quick actions
- Bases toolbar flush with table edge
- Meetings: full-width Coming up section
- Home: drop Connections/Ask-anything icon tiles, real Slack logo
- Sidebar: Apps above Background agents
2026-07-09 18:11:51 +05:30
Ramnique Singh
51b11101f0 test(x): cover useTurn and useAgentRunTranscript hook behavior
The follower's join protocol was already unit-tested, but the hooks
grew stateful behavior of their own: useTurn's reset-on-turnId-change
vs keep-while-disabled, the snapshotFailed signal and feed-event
recovery, and useAgentRunTranscript's legacy runs:fetch fallback and
loading/error derivation. Tests drive the real turn-feed singleton
through a stubbed window.ipc preload surface.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 15:56:45 +05:30
Ramnique Singh
876bc35e9e refactor(x): one delivery path for turn events — chat, channels, deltas
Completes the turn-spine unification. Chat's SessionChatStore now
consumes turns:events like every other surface, joining live turns by
file offset (drop covered, append contiguous, refetch on gap) instead
of blind-appending session-bus events. Text/reasoning deltas cross IPC
only for turns a window subscribed to (turns:subscribe/unsubscribe, a
per-webContents registry that also survives window teardown), so
headless pipeline chatter never reaches windows that aren't watching.
The channels bridge settles turns off the turn event bus instead of
filtering the session broadcast. With no turn-event consumers left,
SessionsImpl stops forwarding entirely (it drains its execution streams
and keeps outcome/index handling) and sessions:events shrinks to
index-changed entries — one channel for turn events, one for session
metadata.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 15:47:21 +05:30
Ramnique Singh
f916cb1d70 refactor(x): finish turn-spine adoption for headless surfaces
Background-task and live-note transcript views move from fetch-once to
the shared useAgentRunTranscript hook (useTurn over the turns:events
spine, with a one-shot legacy runs:fetch fallback for pre-migration run
ids), so an in-flight run's transcript now streams live. The headless
runner drains its execution stream — delivery rides the turn event bus,
and an unconsumed HotStream buffered every durable event until settle.
Deletes the caller-less runHeadlessTurn duplicate (HeadlessAgentRunner
is the implementation; session-design.md now says so) and the orphaned
web-search skill file that was never registered.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 14:59:22 +05:30
Ramnique Singh
12a7118445 feat(x): turn event spine — one bus for every turn's events
TurnRuntime now publishes every turn's events (durable ones tagged with
their 1-based file offset, deltas without) to an injected TurnEventHub,
regardless of who started the turn. Main forwards durable events to all
windows on one turns:events channel; the renderer's new useTurn(turnId)
hook joins live turns gap/duplicate-free via the offset protocol. The
sub-agent card drops its 1s polling for push updates, and the nine
per-channel window fan-out loops collapse into one broadcastToWindows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 14:19:53 +05:30
Ramnique Singh
61344effea
Merge pull request #703 from rowboatlabs/ci-cd
Improve apps/x CI coverage
2026-07-09 11:55:56 +05:30
Ramnique Singh
55d76f32c9 Improve apps/x CI coverage 2026-07-09 11:52:49 +05:30
Ramnique Singh
2ab3e4e751 feat(x): regroup copilot prompt guidance into skills
Capability guidance now co-locates with the tools that skills own:

- Register the previously orphaned slack skill in the catalog (excluded
  when Slack isn't connected, like composio-integration) and shrink the
  prompt's Slack block to a routing line + followed-channels hint — the
  agent-slack command patterns already live in the skill body.
- Compress the per-capability routing directives (meeting-prep,
  create-presentations, doc-collab, app-navigation, background-task,
  apps, browser-control, notify-user) to when-to-load signals; the
  how-to detail loads with each skill.
- Slim the knowledge-graph access examples to the essential patterns.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 11:03:29 +05:30
Ramnique Singh
2e45035ece feat(x): skill-scoped contextual tool loading for the copilot
Skills now own their tools. The copilot attaches only a small hardcoded
base set (~16 tools instead of all ~42); loading a skill via loadSkill
attaches its declared tools as native tool definitions from the very
next model step, and they stay attached for the rest of the session.

- tools_extended: new durable turn event (the one sanctioned exception
  to per-turn tool immutability — explicit, replayable, never silent).
  Reducer tracks extensions per model-call index; effectiveTools()
  composes base + extensions for both the live loop and inspect.
- Runtime: sync tool results may carry metadata.toolAdditions; dedupe
  and the durable append run atomically on the serialized commit chain
  (safe under concurrent sync tools). Crash recovery rebuilds the
  extended toolset from the log.
- Skills declare tools (SkillDefinition.tools; SKILL.md tools:/
  allowed-tools frontmatter); catalog entries list them; loadSkill
  returns them via a reserved $toolAdditions key the registry lifts
  into result metadata. builtin-tools skill = escape hatch, derived as
  "every non-base builtin" at module init.
- Sessions derive composition.activeSkills from the previous turn's
  request + its tools_extended events; the resolver attaches active
  skills' tools on top of the base set (stable order, so snapshot
  inheritance keeps working).
- Legacy code-mode path keeps working on the base set (which includes
  code_agent_run/launch-code-task) and strips $toolAdditions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 11:00:13 +05:30
Ramnique Singh
06fe337520 fix(x): sub-agent card says what it's doing when collapsed
The collapsed card read just "Sub-agent:" — zero information, and the
expanded view showed the task twice (the task chip plus the child
transcript's opening user message, which IS the task).

The title is now "<Humanized name>: <task>" ("London weather: Find the
current weather…"), truncated by the header with the full text on
hover; "Agent" when the model gave no name. The redundant task chip is
gone — the child transcript's first message carries the task.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 06:29:35 +05:30
Ramnique Singh
f546d1ad59 feat(x): teach copilot when to spawn sub-agents; fence shared surfaces
The spawn-agent tool shipped with mechanics but no judgment layer —
the only guidance was the tool description. This adds the ambient
decision heuristics and closes a child-profile hole.

- Copilot system prompt gains a "Sub-Agents (parallel & heavy work)"
  section (same strong/anti signal structure as Background Tasks):
  the organizing principle is context hygiene — a sub-agent's reads
  and fetches never enter the main conversation, only its distilled
  answer. Research-shaped requests (catch-me-up, dig-into, meeting
  prep) now route through sub-agents by default, with copilot as
  synthesizer. Anti-signals: single quick lookups, journeys-as-answer,
  anything needing mid-task user input, driving shared surfaces.
- Background-task agent gets a matching (shorter) note — children
  have their own model-call budgets, so a bg-task covers more ground
  per run by fanning out.
- The inline default child profile now also excludes app-navigation
  and browser-control: both drive shared visible surfaces (the UI the
  user is watching; the single embedded browser pane), which headless
  or parallel children would corrupt, not just clutter. Still
  available via explicit tools selection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 06:20:38 +05:30
Ramnique Singh
3822bda1f7 fix(x): spawn-agent works from task alone — instructions now optional
First real-world use showed the model calling spawn-agent with
{name, tools, task} and no instructions — a complete spec by any
reasonable reading — and burning a correction round-trip on the
"exactly one of agent_id or instructions" rejection (both children
then succeeded on retry).

Requiring instructions bought nothing: the task is the spec. Omitting
both agent_id and instructions now spawns a general-purpose worker
with a default headless prompt; only supplying BOTH remains an error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 06:08:10 +05:30
Ramnique Singh
b62ed6a2f4 feat(x): spawn-agent — run sub-agents as headless child turns
Adds the agent-as-tool capability deferred in turn-runtime-design §29.2:
a `spawn-agent` builtin that runs a sub-agent in its own standalone
headless turn and returns its final answer to the parent. Multiple
spawn calls in one assistant batch run concurrently (previous commit).

- RequestedAgent is now a union: by-id (unchanged shape) | inline
  {name, instructions, model?, tools?}. Inline definitions persist
  verbatim in turn_created and resolve to the same immutable snapshot.
- Agent resolution splits by variant: DispatchingAgentResolver narrows
  the union once; InlineAgentResolver materializes inline specs
  (builtin catalog validation, headless default profile when tools are
  omitted); RealAgentResolver keeps the by-id path byte-identical. The
  builtin→ToolDescriptor conversion is extracted to a shared helper.
- The spawn handler (RealToolRegistry branch → runSpawnedAgent) runs
  the child via HeadlessAgentRunner on the parent's model by default,
  clamps the model-call budget at 20, cascades the parent's abort
  signal, and records {kind:"subagent", childTurnId} as durable tool
  progress — the only parent→child link; no parentTurnId is added to
  the schema. Task-level failures return as conversational isError
  results, never terminal.
- Depth is capped at 1: both resolvers strip spawn-agent from children
  (inline always; by-id via the new subagent composition flag) and the
  handler refuses child-shaped parents outright.
- Renderer: spawn-agent calls render as a SubAgentBlock — a collapsed
  status card that expands to the child's live transcript
  (CompactConversation over sessions:getTurn, polled at 1s while
  running; standalone child turns don't reach the session bus).
- The BuiltinTools entry gives copilot (and other catalog-attached
  agents) the tool automatically; its execute is the degraded legacy
  path only, since the turn runtime intercepts builtin:spawn-agent.

Schema note: RequestedAgent widened under schemaVersion 1 (pre-release)
— requires wiping ~/.rowboat/storage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 23:31:41 +05:30
Ramnique Singh
d2b68a4684 feat(x): execute sync tools concurrently within a turn batch
Sync tools in one assistant batch now run via Promise.all instead of
sequentially — a tool pending on I/O no longer blocks its siblings.
Three coordinated changes keep the event-sourced runtime sound:

- executeAllowedTools is two-phase: invocation events are appended
  serially in source order (durable before any side effect, deterministic
  log prefix), then all sync executions run concurrently, each appending
  progress/results as they land. Per-call error and cancel semantics are
  unchanged (moved to executeSyncTool).
- append() commits through an internal queue: persist → reduce → stream
  runs to completion per batch, so file order, in-memory order, and
  stream order stay identical even while executions overlap. A failed
  commit rejects only its caller; the chain survives for siblings.
- Abort-registry state is scoped per tool call (turnId:toolCallId) via a
  wrapper, fixing two latent races: createForRun destroying a running
  sibling's tracked processes, and cleanup tearing down the turn-wide
  force-kill scope when the first tool finished.

Wire ordering is untouched: model requests already reference tool
results by the assistant message's source order, pinned by a new test.
No concurrency cap and no per-tool serialization by design; tools that
share state must tolerate racing (file edits already reject stale
writes via their search/replace precondition).

Spec §4.5/§10.5 updated. New runtime tests cover overlap (deadlock
unless concurrent), progress interleaving, sibling failure isolation,
mid-batch cancellation, and crash recovery with multiple open
invocations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 22:51:51 +05:30
Arjun
d3aeeb87d7 feat(x): expand curated Composio toolkits and fix broken Microsoft slugs
Expand the curated toolkit whitelist from 26 to 67 entries, adding
well-known services that support Composio-managed OAuth2 (the only
auth scheme the app's connect flow supports): Zoom, Discord, ClickUp,
monday.com, Confluence, GitLab, Bitbucket, Supabase, Sentry, PagerDuty,
Stripe, Square, QuickBooks, Mailchimp, Google Ads/Analytics/Search
Console, Figma, Canva, YouTube, Instagram, Facebook, Box, SharePoint,
and more. Adds three new categories: design, marketing, finance.

Also fix two entries that were never connectable: 'microsoft_outlook'
and 'onedrive' do not exist in Composio's catalog (the API returns 404,
so initiateConnection always failed). The real slugs are 'outlook' and
'one_drive'.

Excluded per curation policy: Composio's own utility toolkits
(composio_search, codeinterpreter, browser_tool), no-auth toolkits
(Hacker News, OpenWeatherMap), API-key-only toolkits the app cannot
connect (Firecrawl, Tavily, Exa, PostHog, etc.), and niche/low-quality
entries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 00:33:32 +05:30
arkml
3261d64dd4
Merge pull request #691 from rowboatlabs/feat/caffeinate-toggle
feat(x): Caffeinate toggle to keep the system awake
2026-07-08 00:10:03 +05:30
Arjun
94727a238b feat(x): add Caffeinate toggle to keep the system awake
Adds a Caffeinate switch (Settings → Mobile channels) that uses
Electron's powerSaveBlocker ('prevent-app-suspension', equivalent to
caffeinate -i) to stop the machine from idle-sleeping so the
WhatsApp/Telegram bridge stays connected. While active, an amber
coffee icon in the titlebar shows the state and can be clicked to
turn it off; a power:caffeinateChanged push keeps the titlebar
indicator and the settings switch in sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 00:07:14 +05:30
Ramnique Singh
24a87ea71a
Merge pull request #689 from rowboatlabs/runtime_optimizations
token optimizations
2026-07-07 21:11:41 +05:30
Ramnique Singh
cda925ad9d feat(core): Anthropic prompt-cache breakpoints in the model registry
Anthropic caching is opt-in, and nothing sent breakpoints: observed
sessions show 0% cache hits on Claude models (~1.27M of 1.36M sampled
input tokens billed at full rate) vs ~82% implicit hits on Gemini. Two
ephemeral breakpoints fix that: the system prompt (whose cache prefix
also covers the tool schemas — both immutable per turn by construction)
and the last message (Anthropic's incremental-conversation pattern).
Conservative simulation on the sampled traffic floors the saving at 44%
with no cross-turn reuse; realistic reuse lands 70-85%.

Applied in the model registry bridge just before streamText, gated by
provider flavor or model id (covers direct Anthropic, OpenRouter, and
the gateways — the installed OpenRouter provider reads the same
providerOptions.anthropic key). Transport-only: nothing is persisted,
message content is untouched, and non-Anthropic requests pass through
byte-identical. Verify with cachedInputTokens on model_call_completed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 21:03:35 +05:30
Ramnique Singh
7000f34c48 test(core): fix type errors in the elision test suite
The resolver helper's inline policy type predated middlePaneContent and
no longer satisfied ElisionPolicy; use the exported type. Coerce message
content to string where the preview assertions call string methods.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 21:02:03 +05:30
Gagan
fd91f4ea22 feat(apps): star-ranked catalog, star from Rowboat, delete local apps
- catalog is ranked by GitHub star count (per-repo lookup with a 10-min
  cache; unauthenticated works, the publish token is used when present)
- star button on each catalog card stars/unstars the app's repo as the
  signed-in user (public_repo scope covers it), optimistic with revert
  and a sign-in hint when signed out
- the info panel now offers Delete for local apps (apps:delete already
  existed and cleans up app-owned agents; only installed apps had a
  removal button before)
2026-07-07 20:59:02 +05:30
Ramnique Singh
30570a9325 test(core): fill elision coverage gaps
The original suite covered the tool-result path well but left holes:
loadElisionPolicy had no tests at all (now: missing file, full config,
partial merge, unparseable JSON, and the all-or-nothing malformed-key
behavior — via an injectable config path so tests stay off the real
WorkDir), images/note policies had no idempotency or determinism tests
(the properties prefix caching depends on), the note floor boundary was
unpinned, resolveAgent delegation was unverified, and nothing exercised
a multi-turn reference chain or the per-resolve hot config reload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:43:27 +05:30
Ramnique Singh
4b4e6af2ea docs(core): surface the config-relative recomposition caveat
Elision reads config/context.json at compose time, so the composed
payload is no longer a pure function of the durable log — inspecting an
old turn after a config edit can show different prefix bytes than were
transmitted. Document the exception against §8.3 and make the inspect
CLI print the policy in effect so divergence is visible instead of
silent. The gold fix (recording the applied policy on the turn) is
noted for when exact-bytes replay becomes a hard requirement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:43:27 +05:30
Ramnique Singh
8a952a4b7e fix(core): guard against constructing an undecorated context resolver
TurnRepoContextResolver was still freely constructible, so a future
call site could silently lose elision. The factory now accepts an
injectable policy loader (also keeps tests off the machine's real
context.json), the raw class carries a do-not-construct note, and
factory-level tests pin both the decorator wiring and the default
policy behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:43:27 +05:30
Ramnique Singh
331a5eda4e feat(core): lower default tool-result elision threshold to 2500 chars
Real session data shows ~5k-char skill bodies are the most common
oversized historic result; the 10k default replayed them on every model
call for the life of the session. With the head preview in place the
model retains enough scent to re-load on demand, so the aggressive
default is the right trade. Still tunable via config/context.json.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:43:27 +05:30
Ramnique Singh
04348dcc41 feat(core): keep a head preview in elided tool results
A bare placeholder tells the model only the tool name and size; the
first 400 characters tell it what the output actually was (a skill
guide reads very differently from a fetched page), which is what it
needs to judge whether re-running the tool is worth it. The preview is
capped at the threshold so small thresholds still shrink content, and
the transform stays a pure per-message function (byte-stable prefixes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:43:27 +05:30
Arjun
4fbca2dbad feat(core): elide historic middle-pane note snapshots from model context
Every user message sent while a note is open carries a full snapshot of
that note in userMessageContext, so a long chat over one open note
resends N full copies on every model call. The elision decorator now
rewrites prior-turn user messages to keep the pane kind and path but
replace note content above a small floor with a placeholder pointing at
the still-readable file. The current message's snapshot is untouched,
so "summarize this" keeps working; the system prompt already tells the
model later middle-pane context overrides earlier. Config:
elideHistoricMiddlePaneContent, default on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:43:27 +05:30
Arjun
05d7fa41cf feat(core): elide historic video-mode frames from model context
Video-mode webcam and screen-share frames matter for the response they
were captured for; afterwards the assistant's own text carries the
takeaway and fresh frames arrive with every new call message, yet each
message's frames (~10k tokens) were resent on every subsequent model
call. The elision decorator now strips image parts from prior-turn user
messages, leaving a text part recording how many frames of each kind
were dropped. The current turn's just-captured frames are always sent
verbatim, and the policy generalizes the existing config
(elideHistoricImages, default on).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:43:26 +05:30
Arjun
99f196e16c feat(core): elide oversized historic tool results from model context
Tool results from prior turns (skill loads, file reads, HTTP fetches)
dominate resent context and are rarely needed verbatim. A decorator over
the context resolver now replaces prior-turn tool results above a size
threshold with a short placeholder telling the model to re-run the tool
if it needs the output. The current turn's in-flight results are always
sent verbatim, the durable log is untouched, and elision is a pure
per-message function so resolved prefixes stay byte-stable for provider
prefix caching.

Policy lives in config/context.json (elideHistoricToolResults, default
on; elideHistoricToolResultsThresholdChars, default 10000). The inspect
CLI composes through the same decorated resolver so debug output still
matches transmitted bytes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 20:43:26 +05:30
Ramnique Singh
b659c75e4d fix(x): separate reasoning from turn activity
Show a generic animated Working status whenever an active turn can be stopped, and reserve the Thinking shimmer for model reasoning_start/reasoning_end windows.

Keep human-wait state separate so permission and ask-human controls remain interactive, and add renderer state and component coverage for the new behavior.
2026-07-07 19:26:31 +05:30
Ramnique Singh
50beb9f33e
Merge pull request #686 from rowboatlabs/dev
Dev changes
2026-07-07 18:56:10 +05:30
Arjun
abf54d4250 chore(x): drop the CI-trigger comment from main.ts
Removes the placeholder comment added in #684 to trigger the
path-filtered 'test' check; doubles as the trigger for this PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:45:25 +05:30
PRAKHAR PANDEY
678130ab40
Merge pull request #651 from prakhar1605/feat/disk-skills
feat: disk-backed agent skills with live-reload
2026-07-07 17:17:36 +05:30
Arjun
37d4b8d13a chore(x): trigger apps/x tests on this PR
Adds a harmless comment under apps/x/ so the path-filtered required
'test' check runs on this otherwise README-only PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:54:41 +05:30
Arjun
0ccce7f1e2 feat(models): switch signed-in default model to Gemini 3.5 Flash
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:32:03 +05:30
Gagan
3f7e915c4d feat(apps): show installed state on catalog cards
Cards for apps already installed from the catalog show a green
Installed badge (matched via the install record's registry name) that
opens the app, instead of a second Install button.
2026-07-07 15:22:27 +05:30
Gagan
043cdc7cb2 fix(apps): stop reinstall from yanking the user out of the agent dialog
Two races around the post-install prompt:

- AppsView kept a stale selectedFolder after the open app was
  uninstalled; on reinstall the 4s apps:list poll matched it again and
  instantly swapped in the app frame, unmounting the catalog and the
  enable-agents dialog mid-choice. Clear the selection when its app
  disappears from the list.

- Bundled agents materialized on the NEXT apps:list poll, not at
  install — so the dialog's bg-task:get/patch hit a task that didn't
  exist yet if the user acted within ~4s. Install handlers now call
  syncAppAgents synchronously.
2026-07-07 14:58:47 +05:30
Gagan
ec4ae4d5eb feat(apps): model picker in the post-install agent prompt
The enable dialog now lists the user's available models (models:list),
preselecting the host-pinned default from materialization. Turning the
agents on patches the chosen model/provider onto the task along with
active — so installers can route a bundled agent to a specific model
at the moment they opt in, without digging into bg-tasks.
2026-07-07 14:49:58 +05:30
Gagan
3fc4621dc1 fix(apps): pin the host default model on materialized bundled agents
Bundled agents can't ship a model override (the author's providers are
not the installer's), so materialized tasks ran on the bg-task category
default — gemini flash lite — which reliably mangles large app-set-data
payloads (invalid JSON string → contract rejection → the installed app
never gets data; observed with pr-dashboard's refresh agent).

Materialization now resolves getDefaultModelAndProvider() on the host
and pins that on the new task — the installer's machine picks the
strong model, the package still pins nothing. Existing tasks keep
their user-set model (update path unchanged).
2026-07-07 14:14:05 +05:30
Gagan
78defccb29 feat(apps): post-install prompt to enable bundled agents
Bundled agents install disabled (§8.3), but nothing told the installer
they exist — the app opened empty with the refresher buried in
bg-tasks. After an install that materialized agents, offer to turn
them on: 'Turn on & run now' activates each task and fires a first run
so the app opens with data; 'Not now' keeps them off.
2026-07-07 13:58:59 +05:30
Gagan
715d92e4ba fix(apps): route 'Publish update' through apps:publishUpdate
The detail panel's Publish update button opened the same dialog as
first publish, which hardcoded apps:publish — so updating an already
published app always failed with name_taken from the registry check.

The dialog now takes a published flag: it runs apps:publishUpdate with
a version-bump picker (patch/minor/major), shows a simple progress
state (the update path emits no step events), and links the new
release on success.
2026-07-07 13:48:13 +05:30
Gagan
a9a47e5a28 fix(apps): make agent bundling mandatory in the copilot apps skill
The skill treated mirroring the bg-task into agents/<slug>.yaml as
optional ('if the app should ship the agent'), so the copilot built
agent-backed apps (e.g. pr-dashboard) with the refresher as a loose
personal bg-task only. Publishing such an app ships a dead UI: data/
is user state and never packaged, and installers get no agent.

Bundling is now a required step with the failure mode spelled out.
2026-07-07 13:42:29 +05:30
Arjun
e367ec889d build(x): graft vscode-jsonrpc onto langium via packageExtensions
langium imports vscode-jsonrpc/lib/common/events.js without declaring the
dependency, which fails to resolve under pnpm's strict layout and broke the
renderer's production (vite) build during forge packaging.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 02:45:09 +05:30
Arjun
8ce56c84ab fix(tour): correct apps stop copy — built by you, shareable, same tools
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 02:45:02 +05:30
Arjun
037ba76245 add tour to onboarding 2026-07-07 02:22:07 +05:30
Prakhar Pandey
3fffe12978 Merge remote-tracking branch 'upstream/dev' into feat/disk-skills 2026-07-06 22:37:30 +05:30
gagan
84c71b37af
Merge pull request #676 from rowboatlabs/feature/apps-m3
Rowboat Apps V1 — M3: publish, catalog, install, update
2026-07-06 21:14:10 +05:30
Gagan
b55417e97c fix(apps): pace device-flow polls in core — slow_down made auth spin forever
GitHub rate-limits the device-code token endpoint: a poll arriving even
slightly under the flow interval returns slow_down and permanently raises
the required interval. The renderer polled on a fixed 5s timer (exactly
the limit), so one jittery request tripped slow_down and every poll after
it was 'too fast' — GitHub answered slow_down forever and the dialog sat
on 'Waiting for authorization' even after the user approved.

Core now tracks lastTokenPollAt and skips the request until the flow's
current interval (base +1s margin) has elapsed, so slow_down bumps take
effect and the flow recovers. The renderer timer is just a heartbeat (2s).
2026-07-06 21:07:13 +05:30
Gagan
5a7fd1506c fix(apps): drop iframe auto-retry, add reopen hint to stuck overlay 2026-07-06 21:00:45 +05:30
Gagan
f2bc01ffa9 fix(apps): eliminate blank-app window on launch and add crash visibility
- start the apps server before createWindow instead of after the full
  service-init chain: the Apps view was reachable ~10s before port 3210
  was listening, so every app iframe opened to connection-refused
- retry EADDRINUSE binds (quick relaunch races left the server disabled
  for the whole session with no retry)
- app frame: auto-retry the iframe instead of a dead manual Retry, and
  surface when the apps server itself is down (new apps:serverStatus)
- log render-process-gone/child-process-gone reasons; renderer helper
  crashes were previously invisible in packaged builds
2026-07-06 20:52:31 +05:30
Gagan
5e0fc059e7 feat(apps): publish button in app toolbar + published links in detail
- toolbar (local apps): 'Publish' button; turns into a green 'Published'
  badge-check once published (click opens the detail panel)
- detail Source section: clickable repo + release links for published apps
2026-07-06 20:12:25 +05:30
Gagan
8e4f234d69 fix(apps): take bootstrap commit sha from the PUT response
Re-reading refs/heads/main immediately after the bootstrap commit can 409 on
a stale replica (GitHub eventual consistency) — the Contents API response
already carries the commit sha, so use it directly.
2026-07-06 20:07:09 +05:30
Gagan
1ed9cced59 fix(apps): re-report completed steps on publish resume
Resumed publishes skipped already-done steps without emitting progress, so
the dialog showed stale spinners for steps that finished in a prior attempt.
2026-07-06 20:04:43 +05:30
Prakhar Pandey
b8b4589ff2 test: unit tests for disk skills loader + catalog merge (11 cases) 2026-07-06 19:42:00 +05:30
Prakhar Pandey
43590ca6d9 Merge remote-tracking branch 'upstream/dev' into feat/disk-skills
# Conflicts:
#	apps/x/apps/main/src/main.ts
#	apps/x/packages/core/src/application/assistant/instructions.ts
2026-07-06 19:11:36 +05:30
Gagan
2bbb502661 chore(apps): drop unused STEPS array (lint) 2026-07-06 16:17:58 +05:30
Gagan
beaf6ebf69 fix(apps): bootstrap empty repos before the Git Data push
GitHub's Git Data API (blobs/trees/commits) returns 409 'Git Repository is
empty' on a repo with no commits — it cannot create the first commit. Ensure
main exists via a Contents API bootstrap commit (the generated README), then
chain the publish commit onto it.
2026-07-06 16:17:26 +05:30
Gagan
684ec0b390 fix(apps): device-flow diagnostics + bounded identity retries
Verified the wire calls are correct (both endpoints behave with this client
id from a plain node run), so instrument the runtime path instead of
guessing:

- log every poll outcome ([GitHubAuth] lines) at the token exchange, the
  identity fetch, and the IPC handler
- identity failures now retry at most 3 polls then fail loudly with the HTTP
  status — never silently degrade to 'pending' forever
- explicit User-Agent on all GitHub calls (defensive)
2026-07-06 16:12:53 +05:30
Gagan
502aae3c14 fix(x): resolve baileys build approval placeholder blocking package build
The mobile-channels feature added baileys, whose install-time scripts
require build approval. A placeholder value ("set this to true or
false") was left in the allowBuilds block of pnpm-workspace.yaml. pnpm
11 treats an unresolved build decision as a hard error, causing
pnpm install to exit non-zero and abort Electron Forge's generateAssets
hook during npm run package.

Set baileys: true to match the onlyBuiltDependencies entry.
2026-07-06 16:11:38 +05:30
Gagan
0ca3606be5 fix(apps): device-flow poll stuck on 'Waiting for authorization'
GitHub's OAuth endpoints take form-encoded params; the JSON token request
produced an unrecognized error that fell through to 'pending' forever, so the
dialog never advanced after the user authorized.

- form-encode device/code and access_token requests
- unknown token-endpoint errors now fail loudly instead of spinning;
  incorrect_device_code maps to expired (restart offered)
- issued-token is remembered so a transient identity-fetch failure retries on
  the next poll instead of burning the consumed device code
- dialog catches hard poll failures and surfaces them
2026-07-06 15:54:06 +05:30
Gagan
815eac43db feat(apps): M3 UI — catalog, D18 install dialog, publish dialog, detail actions
- Catalog tab: registry search/list, stale-cache refresh, install flow with
  the D18 capability-disclosure dialog (plain-language capability lines +
  bundled agents, explicit confirm), install-from-URL with preview and
  'updates unavailable' notice for non-GitHub sources
- Publish dialog: GitHub device-flow sign-in (user code + polling), §11.2
  step progress via apps:progress pushes, success links, name_taken
  rename-and-retry hint
- App detail actions: check-for-update / update (new_capabilities and
  modified_files confirmation flows), rollback, uninstall (names data/ and
  bundled agents), publish / publish update
- docs/publishing-apps.md (§11.5): bundle format, two-asset requirement, tag
  convention, registry record, monorepo latest-release constraint
2026-07-06 15:34:54 +05:30
Gagan
35c781b8e4 feat(apps): M3 publisher — guided publish, updates, register-existing
- publisher (§11): resumable state machine persisted in .rowboat-publish.json
  (packaged → repo_created → source_pushed → release_created →
  assets_uploaded → registered → published); Git Data API single-commit
  source push with generated README/LICENSE/.gitignore when absent; both
  release assets uploaded (bundle + standalone manifest); fork+PR registry
  registration with rejected:<code> parsing and rename-retry surface
- publish update (§11.3): semver bump, package, push, release — no registry
- register existing release (§11.5) with client-side asset probe
- IPC: apps:publish/publishUpdate/registerExisting + apps:progress pushes
2026-07-06 15:08:36 +05:30
Gagan
6dea528fb4 feat(apps): M3 installer — catalog/URL installs, updates, rollback, uninstall
- installer (§12): streaming download with size cap, extraction guards
  (zip-slip, symlink entries, entry-count, uncompressed cap), bundle-identity
  check, D18 capability-mismatch check against the previewed manifest,
  defaults->data on first install, pinned per-file sha256, folder suffixing
- URL installs (§12.5): two-phase preview from the bundle's own manifest,
  10-min retained staging, GitHub-provenance detection for later updates
- update (§12.3): D18 scoped to the diff (new_capabilities), modified-file
  warning against pinned hashes, .previous/ swap with one-step rollback
- uninstall (§12.4) deletes app-owned bg-tasks; startup tmp cleanup
- IPC: apps:catalogIndex/Search/Detail, install, installFromUrl, uninstall,
  checkUpdate, update, rollback + analytics events
2026-07-06 15:02:00 +05:30
Gagan
d7af51c10b feat(apps): M3 groundwork — packager, GitHub device-flow auth, registry client
- env: GITHUB_OAUTH_CLIENT_ID (device flow enabled on the Rowboat OAuth app;
  overridable via ROWBOAT_GITHUB_CLIENT_ID)
- packager (§4.4): allowlist-only .rowboat-app ZIP (yazl), sorted entries,
  symlink skip, sha256
- github-auth (§10): device-code start/poll, identity fetch, token stored
  0600 with safeStorage encryption injected from main (core stays
  electron-free); githubAuth:* IPC + external open of the verification page
- registry client (§9.2): unauthenticated tarball index with 5-min cache and
  stale fallback, raw-record resolve, substring search, quota-free
  latestManifest via release-asset redirect with name-mismatch guard
- registry repo contents (docs/apps-registry): record JSON schema +
  validate-and-merge Action implementing §9.3 checks 1-7 with rejected:<code>
  comments and per-name concurrency
- fix: host-api copilot-run adapted to dev's ModelSelection {provider,model}
  (this is the same fix dev needs for Ramnique's packaging break)
- pnpm 11: blockExoticSubdeps=false (electron-forge has a git subdep)
2026-07-06 14:49:52 +05:30
gagan
b7d1019538
Merge pull request #665 from rowboatlabs/feature/apps-v1
Rowboat Apps V1 — M1 + M2 (spec implementation)
2026-07-06 14:18:11 +05:30
PRAKHAR PANDEY
1a71b38f2e
Merge pull request #637 from prakhar1605/feat/out-of-credits-warning
feat: add out-of-credits warning to sidebar plan badge
2026-07-06 14:11:18 +05:30
Ramnique Singh
1050614295
Merge pull request #674 from rowboatlabs/code-mode-fixes
Code mode fixes
2026-07-06 14:06:41 +05:30
Ramnique Singh
3bbed55735 feat(x): ephemeral CodeRunFeed for codex streaming + settle-time durable batch
The runs->turns migration broke codex live streaming in copilot chat: the
turns bridge's publish shim forwarded only tool-output-stream and silently
dropped code-run-event / code-run-permission-request (the latter would
deadlock a turn under policy 'ask'). An interim fix persisted every stream
event as durable tool_progress, but that wrote each text chunk to the turn
file — unsustainable.

Final architecture — live and durable paths split:

- Live (ephemeral, bypasses the turn runtime): code_agent_run broadcasts
  each ACP event on a new CodeRunFeed (core DI singleton), forwarded by
  main over a dedicated codeRun:events channel, buffered per toolCallId in
  a module-level renderer store and rendered by CodingRunBlock. The buffer
  survives session switches; nothing is persisted.

- Durable (one line per run): when the run settles (success, error, or
  cancel), code_agent_run publishes a single code-run-events-batch with
  the whole ordered timeline, consecutive same-role message chunks
  coalesced (display-lossless — the timeline concatenates them anyway).
  The turns bridge maps it to tool_progress {kind:'code-run-events'};
  turn-view derives the replay timeline from it, so reloads keep history.

- Permissions stay durable per-ask (request + resolved marker): the
  renderer overlay resets on session switch, so an ephemeral-only ask
  would strand a blocked turn with no card to answer. Pending = requests
  minus resolutions (handles concurrent asks), cleared on tool result.

The legacy code-section path (runs bus per-event) is untouched; its
per-event ctx.publish remains and is a no-op under the turns shim.

Also: repaired the two code_agent_run tests broken by the earlier cwd
existence check (they used a nonexistent /repo), and added coverage for
feed broadcast, batch coalescing, partial-batch-on-failure, bridge
durability routing, and pending-permission derivation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:04:33 +05:30
PRAKHAR PANDEY
c75d55ed31
Merge branch 'dev' into feat/out-of-credits-warning 2026-07-06 13:54:20 +05:30
Arjun
fd49334c33 move local-model concurrency to a turn-level defer lock
Review feedback: drop the model-layer scheduler (per-call-site
interactive/classifier/background priorities, local-provider hostname
heuristic) in favor of app-level orchestration:

- deferBackgroundTasks flag in models.json, surfaced as a settings
  toggle; auto-enabled once (UI logic) when the user connects Ollama
- ChatActivity counter marked by both chat runtimes (sessions layer and
  legacy AgentRuntime.trigger)
- startWhenPossible/runWhenPossible wrappers around the headless agent
  runner; all background invocations (knowledge pipeline, live notes,
  background tasks, scheduled + prebuilt agents) go through them and
  wait for chat-idle when the flag is set

createLanguageModel keeps only the Ollama context-window middleware;
the LM Studio capability probe now keys off the provider flavor instead
of hostname sniffing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:38:39 +05:30
Arjun
c1fd4e6221 allow byok even when logged in 2026-07-06 13:32:13 +05:30
Arjun
0b55dc3300 fix ollama model effort level 2026-07-06 13:31:13 +05:30
Arjun
db7df256ca fix ollama model selection issue 2026-07-06 13:31:13 +05:30
Arjun
799d7584b8 make local models work well 2026-07-06 13:31:13 +05:30
Ramnique Singh
7c06af04a7 fix(x): unblock codex code-mode (bad cwd + revoked engine cert)
code_agent_run failed with a misleading "spawn <Electron> ENOENT" that
read as "Codex isn't installed". Two stacked causes:

1. cwd wasn't expanded/validated: a `~` or non-existent path was passed
   straight to child_process.spawn, which reports ENOENT against the
   command (Electron) rather than the missing directory. Now expandHome +
   resolve + existence-check with a clear error.

2. Apple revoked the signing cert on the pinned @openai/codex@0.128.0, so
   macOS Gatekeeper trashed the binary on launch and the ACP adapter died
   mid-handshake. Bump the ACP stack off the revoked build:
   codex-acp 0.0.44 -> 1.1.0, claude-agent-acp 0.39 -> 0.55, sdk 0.22 ->
   1.1, regen engine-manifest (codex 0.142.5 / claude 0.3.198). Removed the
   two now-obsolete patches (contextCompaction upstreamed; the codex
   windowsHide patch targeted bin/codex.js, which we bypass via CODEX_PATH).

Bump fallout handled:
- client.ts setModel: sdk 1.x dropped unstable_setSessionModel; model is now
  a config option -> setSessionConfigOption({configId:'model'}).
- engine-provisioner: codex 0.142 moved its binary (codex/ -> bin/) and rg
  (path/ -> codex-path/); probe both layouts.
- pnpm override vscode-jsonrpc to 8.2.0: codex-acp 1.1 pulls jsonrpc 9.x
  whose restrictive exports break langium's deep import in the renderer
  (blank screen). codex-acp is the only 9.x consumer and works on 8.x
  (handshake verified), so pin the workspace to 8.x.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 12:57:02 +05:30
Ramnique Singh
c1b5291f7d approve baileys build 2026-07-06 10:56:34 +05:30
Ramnique Singh
4a2fe30153 fix(x): surface coding tool errors 2026-07-06 10:56:23 +05:30
Arjun
adb8b16a3c Add in-call mic mute: pauses all input (mic audio + frame capture)
A mute button on both call surfaces (full-screen call and floating
popout) that pauses everything going to the assistant while keeping the
call alive — for stepping away to talk to someone in the room without
ending and restarting the call.

- Mic audio stops reaching Deepgram (user mute OR'd into the existing
  thinking/speaking setPaused; KeepAlives keep the socket warm so
  unmute is instant)
- Camera/screen frames stop being sampled (new setCapturePaused in
  useVideoMode); collectFrames() returns nothing while muted, so typed
  messages during a mute carry no frames either
- Devices stay acquired for instant resume; mute resets at call
  start/end; assistant output is unaffected (Stop handles that)
- Honest UI: "Muted" status chip replaces the green "Listening" pulse,
  muted badge on the user tile, pill's share badge flips to "Sharing
  paused"; new toggle-mic popout action + micMuted in popout state

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 00:28:34 +05:30
arkml
1c380c33ed
Merge pull request #671 from rowboatlabs/fix_chat_history
fix race condition in chat history
2026-07-05 23:36:39 +05:30
Arjun
930089b218 fix race condition in chat history 2026-07-05 23:29:11 +05:30
Arjun
098378170d default model shows correctly along with the dropdown 2026-07-05 23:23:38 +05:30
Arjun
b493baf689 use native gmail in copilot 2026-07-05 22:44:06 +05:30
arkml
6b2120d376
Merge pull request #666 from rowboatlabs/graph2
Improvements to graph and email labeling
2026-07-05 11:38:31 +05:30
Arjun
0d14220239 labeling improvements 2026-07-05 11:09:18 +05:30
Arjun
c88a45ee7e fix issue with sent emails showing up 2026-07-05 03:14:45 +05:30
Arjun
9d31d25046 learns email importance from preference actions 2026-07-05 02:36:01 +05:30
Arjun
258f157b39 improvements to graph building 2026-07-05 02:10:46 +05:30
Gagan
dc28b0b868 merge dev into feature/apps-v1
Union resolutions — dev's changes kept, apps-v1 additions appended:
- use_case.ts: dev's meeting_prep + our app_llm_generate/app_copilot_run
- ipc.ts: dev's HttpAuthRequestSchema import + our AppSummarySchema
- shared/index.ts: dev's channels export + our rowboatApp export
- builtin-tools app-navigation: dev's read-view/open-item actions + our open-app
- chat-conversation: dev's read-view/open-item labels + our open-app labels
- sidebar: dev's nav-workspaces tour id + our Apps entry
- App.tsx: dev's open-item handler + our open-app handler
2026-07-05 01:08:35 +05:30
Gagan
7c9fe7214d fix(apps): stop task.yaml rewrite races + blank-load watchdog + loop-lag monitor
Investigating 'apps blank while a bg task runs': measured serving latency
through a real scheduled bg-task run — 1-3ms on both loopbacks throughout,
so the server is not the bottleneck. Found and fixed the likely renderer-era
culprit plus made the failure observable:

- agent sync (apps:list, polled every 4s) rewrote each bundled agent's
  task.yaml unconditionally — racing the bg runner's own patches mid-run and
  spamming watcher events; now writes only when the definition changed
- AppFrame load watchdog: if the iframe hasn't loaded in 6s, show a visible
  'taking too long' state with Retry instead of a silent blank pane
- main event-loop lag monitor: stalls >300ms are logged so future blank
  reports can be tied to the offending work
2026-07-05 00:45:30 +05:30
Gagan
9173097715 style(apps): subtler light-mode page background 2026-07-05 00:22:15 +05:30