177631f1234daeee9b8f79e1c8b30c27d94267ac
17
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9d9af7e263 | Improve event stream resilience | ||
|
|
f24e6de21b |
fix: improve event stream reconnect reliability
Recover stalled event streams without dropping the session Wait briefly for reconnection before showing connection lost errors Persist Electron server logs for easier disconnect debugging |
||
|
|
17dd526731 |
fix: eliminate parent-child session desync across reconnect and navigation (#985)
* fix(pipeline): distinguish transport switch from real disconnect WS_FALLBACK errors (e.g. ready timeout → SSE fallback) are transport switches, not disconnections. No events are lost because lastEventId is preserved across the switch. Previously, every WS timeout triggered onDisconnect → onReconnect with a full resyncDirectoryAfterReconnect, which: - Missed idle parent sessions in candidate selection (root cause 1) - Could overwrite in-flight SSE state with stale fetch data (root cause 4) - Caused isConnected to flash false→true Now: WS_FALLBACK fires onTransportSwitch (sets isConnected only). Real disconnections (heartbeat timeout, network error) still fire the full onDisconnect → onReconnect → resync cycle. * fix(sync): relationship-aware reconnect with merge-not-replace Two changes to resyncDirectoryAfterReconnect: 1. Candidate selection now also includes parent sessions of any child sessions in the directory. Previously, if a child completed during the disconnect gap (busy→idle), neither child nor parent was selected because both appeared idle. The parent's task tool part would remain permanently stale. 2. Parent resync merges parts instead of replacing. Previously, the resync deleted parts for messages not in the fetch snapshot, which could erase parts delivered by SSE events that arrived between the fetch and the setState. Now only parts for messages in the snapshot are overwritten; everything else is preserved. * fix(sync): demand-load child session messages on access Bootstrap only loads session metadata — messages are populated exclusively by SSE events. When a user navigates to an old session that spawned subagents, child session messages were never in the store. Add useEnsureSessionMessages hook that detects this gap (session exists in state.session but state.message[sessionID] is absent) and triggers a background API fetch to load messages and parts. ToolPart already calls useSessionMessageRecords(taskSessionId) which returns empty when not loaded. Now it also calls useEnsureSessionMessages to populate the store on first access. * fix(sync): unmount-safe parent resync when child session goes idle When a child session transitions to idle (completes), the sync layer now schedules a targeted parts repair for the parent session's task tool part. Previously this only happened when the ToolPart component was mounted and had observed the child being active (taskChildSeenActive). This covers: - User navigated away while child was running - App restarted with active subagent sessions - SSE reconnect where child completed during disconnect Uses the existing repairSessionParts mechanism with its 5s cooldown to avoid redundant fetches. * fix: type-check fixes for sync-layer parent resync Fix TypeScript errors in Fix 5 implementation: - Convert currentSessionId from null to undefined for resolveFallbackTaskSessionId - Add default empty string for dir parameter in getScopedSdkClient - Use explicit sessionID parameter for scopedClient.session.messages All type-checks now pass. * fix(sync): address PR review feedback on deduplication - Use enqueuePartsRepair for session.idle parent resync instead of direct repairSessionParts call. enqueuePartsRepair already has a 5s cooldown to prevent redundant parallel API calls when multiple child sessions go idle concurrently. - Move useEnsureSessionMessages loading guard from component-scoped React.useRef to a module-level Set keyed by directory:sessionID. Prevents parallel fetches when multiple ToolPart instances mount for the same child session. * fix(sync): add missing semicolon on useEnsureSessionMessages call Address Greptile P2 review comment on ToolPart.tsx:1931. |
||
|
|
5ec7f46105 |
fix: surface disconnect reason, switch health probe to /global/health (#978)
- event-pipeline: onDisconnect now carries a reason tag (ws_closed:code=N, ws_error_frame, ws_closed_before_ready, sse_error) - useConfigStore: store lastDisconnectReason, clear on reconnect - send guards: embed reason in Connection lost toast so we can tell which path tripped - lifecycle: isOpenCodeProcessHealthy hits /global/health (healthy flag) with 5s timeout instead of /session with 2s, avoids false restarts under stream load |
||
|
|
f1b84e3291 |
fix: always signal connected state on first successful connect
markConnected skipped onReconnect when hasConnected was false and there was no prior disconnect. Consumer state (isConnected) starts at false and stayed false — the send button threw "Connection lost" until the HTTP health check in initializeApp happened to race a setState through. Now onReconnect fires on every successful connect; hasConnected is no longer needed and gets removed. |
||
|
|
c2eeadd9a3 |
fix: unstick isConnected when first connect follows a disconnect
markConnected skipped onReconnect on the very first successful connect (hasConnected=false branch), but an earlier failed attempt could have already fired onDisconnect -> isConnected=false. Consumer state stayed stuck at false forever. Fire onReconnect in that case too. Repro: launch app while opencode sidecar is still warming up so the first WS attempt fails. Second attempt succeeds but isConnected stays false; user sees "Connection lost" toast on every send. |
||
|
|
a8a336b0a0 |
perf: halve stream flush cadence from 60hz to 30hz
FLUSH_FRAME_MS 16 -> 33. Delta events keep coalescing, just into larger windows. AssistantTextPart already throttles text at 100ms, so the extra granularity produced no visible benefit — just extra store.setState calls and subscriber notifications per second. |
||
|
|
f5535dcaf1 |
fix: recover from sleep/wake disconnection with connection state tracking and immediate health check (#940)
When the computer sleeps and wakes, the SSE/WS event stream drops silently. Messages appeared sent (optimistic insert) but never reached the OpenCode server, and the user had no indication the system was disconnected. Three fixes: 1. Connection state tracking: add onDisconnect callback to the event pipeline. Stream failures set isConnected=false in useConfigStore; successful reconnect sets isConnected=true. 2. Send guard: optimisticSend, respondToPermission, and respondToQuestion now check isConnected before making API calls, throwing a clear error that surfaces as a toast to the user. The /compact command also checks connection with error feedback. 3. Faster server recovery: add triggerHealthCheck() to the server lifecycle and wire it into the WS event stream runtime. When the upstream OpenCode connection fails, the server immediately checks health and restarts if needed, instead of waiting up to 15s for the periodic health check. |
||
|
|
bee9d19f3a |
feat(web): add WebSocket transport for message event streaming with SSE fallback (#764)
* feat: add websocket message stream transport * fix: avoid false missing session directories in sidebar * fix: re-probe project root session directories * refactor: use button group for message stream transport * fix: resolve chat input hook dependency warning --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com> |
||
|
|
2cedc443a5 | fix(sync): keep deltas after initial part.updated coalescing | ||
|
|
ddc1039d1c | fix(sync): unify live session truth across chat and sidebar | ||
|
|
1656c3bb93 |
perf(sync): optimize multi-session event pipeline with per-directory queues and delta coalescing (#908)
* fix: hide archived section and empty folders when no sessions remain - Only push archived group in useSessionGrouping when there are archived sessions, preventing an empty archived section from rendering - Hide empty folders in archived bucket via shouldKeepFolder check in SessionGroupSection (folders with no sessions and no content in children are filtered out) - Always filter folders through shouldKeepFolder, not just during search * perf: memoize archived folder filtering * perf(sync): per-directory event queues to eliminate cross-session HoL blocking The SSE event pipeline previously used a single global queue and a single flush timer shared across all directories. Under concurrent multi-session workloads, a busy directory's delta storm would block other directories' status and state events from reaching the UI until the next flush tick, producing the "multi-session latency" symptom users report. Split the queue into one DirectoryQueue per directory, each with its own coalesce map, stale-delta set, and flush timer. Directories flush independently so a busy directory can no longer starve a quiet one. Coalesce keys are now scoped to a single directory's queue, so the directory prefix is removed from the key strings. Cross-directory behavior only; same-directory multi-session behavior is unchanged (React 18 auto-batching still collapses a single directory's flush into one render). * perf(sync): coalesce consecutive message.part.delta events per flush window Within a 16ms flush window, consecutive delta events for the same (messageID, partID, field) tuple are string-concatenated into a single accumulated delta rather than being queued individually. This directly addresses same-project multi-session workloads — most notably parent sessions with subagent tasks (child sessions share the same directory queue). Both parties stream deltas concurrently, which previously multiplied raw event count proportionally to the number of active sessions. Coalescing can reduce queue depth by 10-100x during active streaming. Safety: verified against event-reducer.ts — the delta handler is a pure string append (existingValue + props.delta) with no per-event side effects (no time.updated, no notifications, no diff calculations). The merged result is semantically identical to applying each delta separately. The staleDeltas skip mechanism is unaffected: accumulated delta payloads retain their type and identifiers, so message.part.updated supersession still works correctly. * test(sync): cover per-directory queues and delta coalescing Extend event-pipeline.test.js with behavioural coverage for both optimizations landed in 98d013a and 258acf0: P1 (per-directory queues) - Delivers events from two directories without loss - Keeps distinct sessionIDs in the same directory as independent coalesce slots (session.status is not overwritten across sessions) - Collapses repeated session.status for the same session down to latest Option C (delta coalescing) - Accumulates consecutive deltas for the same (messageID, partID, field) into a single dispatched event with concatenated content - Does not merge deltas across different fields on the same part - Does not merge deltas across different parts on the same message - Does not merge deltas across different directories (per-dir queues) - Skips accumulated deltas when message.part.updated is coalesced onto an earlier update, proving staleDeltas still works with C - Leaves non-delta coalescing (session.status replace semantics) intact All 13 tests pass under bun:test. Also adds event-pipeline.bench.js, a runnable synthetic benchmark that reports delta reduction and byte integrity across 8 workload scenarios from "single session, 500 tokens" up to "10 projects × 5 sessions × 1000 tokens". Run with: bun packages/ui/src/sync/__tests__/event-pipeline.bench.js Current numbers on this machine: 99.5% - 99.9% delta event reduction with full byte-level integrity (concatenated delta bytes always equal the input total). * fix(sync): remove staleDeltas — it silently drops delta events --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com> |
||
|
|
964c209ff6 |
fix(sync): remove stale-delta skip and add parts-gap recovery (#889)
The pipeline's stale-delta mechanism incorrectly marked all message.part.delta events as stale when a message.part.updated coalesced, regardless of queue position. This caused valid streaming deltas to be silently dropped, resulting in blank or incomplete assistant messages. Additionally, when part events were dropped by the reducer (missing parts array or partID not found), there was no recovery path — the state stayed permanently out of sync until the next SSE reconnect or manual refresh. Also discovered: message.updated that successfully writes an assistant message but has empty parts would render a blank bubble, with no repair triggered since repair only ran on reducer return false. Changes: - Remove staleDeltas Set and deltaKey from event-pipeline.ts - Coalesce still replaces same-key events, but deltas are never skipped - Add enqueuePartsRepair + repairSessionParts to sync-context.tsx (5s cooldown, deduped, async SDK re-fetch) - Trigger repair on reducer return false for part events - Trigger repair on message.updated return true with empty parts - Add sync debug.ts with gated diagnostic logging - Add pipeline coalescing tests |
||
|
|
f2f57ca281 |
fix: restore live compaction updates in chat
- Show `/compact` command and streamed compaction summary live without refresh - Handle versioned sync event types so message and part updates are processed - Route session/message events via indexed lookup to avoid hot-path store scans |
||
|
|
dd0587501f | fix: recover SSE directory routing in sync pipeline (#830) | ||
|
|
e63450ae2c |
fix: resync session state after SSE reconnect to prevent stuck subagent UI (#817)
* fix: resync session state after SSE reconnect to prevent stuck subagent UI When a subagent completes while the page is in the background (common on mobile PWA and desktop webview), the final SSE events are lost. The UI then stays stuck on 'Waiting for subagent activity...' because: - part.state.status never transitions to 'completed' - session_status is never updated to 'idle' - activeLatched remains true indefinitely Fix: - Add onReconnect callback to event pipeline, fired after SSE reconnect - Add pageshow listener for bfcache restores (mobile PWA back-forward) - On reconnect, re-fetch session list for directories with active sessions - Pass explicit directory to useSessionActivity in ToolPart for subagents to ensure the correct child store is queried Closes #810 * fix(chat): resolve pending subagent task binding before metadata arrives * fix: restore subagent activity and tool visibility after reconnect - Resyncs session status and child session data after SSE reconnect - Ensures child task tool messages are read from the correct directory - Prevents stale assistant fallback from keeping sessions stuck as active --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com> |
||
|
|
c9e31a0e6c |
perf: harden sync architecture and modularize runtimes (#803)
* fix: added desktop app background throttling
* perf: add streaming debug metrics panel
- Show streaming performance metrics in the debug panel
- Auto-enable stream profiling while the panel is open
- Add JSON export for sharing UI and VS Code metrics
* perf: batch streaming updates more aggressively
- Buffer message deltas and metadata updates to cut render churn
- Skip no-op part updates before they touch the message store
- Fix the desktop debug panel shortcut binding
* perf: split streaming event handling and coalesce deltas
- Move streaming content events onto a dedicated fast path
- Defer non-critical stream side effects off the hot path
- Merge repeated message delta events before they reach the UI
* perf: isolate streaming rows from chat rerenders
- Memoize chat rows against render-relevant message changes only
- Read live assistant text directly from store to narrow streaming updates
- Split the active streaming entry from the stable message list path
* perf: streamline chat streaming and SSE proxying
- Reduce chat rerenders around the active streaming path
- Simplify server SSE forwarding to avoid duplicate proxy work
* fix: preserve the first streaming text chunk
- Show the initial text chunk immediately before batched deltas arrive
- Bypass batching for the first text or reasoning part update
- Keep later streaming updates buffered for performance
* perf: align streaming/render hot paths with opencode parity
* perf: harden turn/cache stability and stale delta suppression
* fix: stabilize chat rendering and disable timeline interactions
- Disabled timeline dialog access from shortcuts, commands, and chat input
- Reduced chat render churn by simplifying message list and turn staging behavior
- Improved session-switch stability to prevent update-depth crashes
* perf: track static message rerenders during streaming
* perf: reduce sorted-mode activity rerender fanout
* perf: reduce chat rerender fanout and add active-turn metrics
- Reduced sorted-mode rerender coupling by tightening turn context propagation
- Added a metric for static rerenders outside the active turn during streaming
- Exposed new chat render counters in the debug panel for parity tracking
* fix: keep sorted activity mounted while stream grows
* fix: stabilize session and history scroll rendering
* refactor: decouple server routes from index
* refactor: extract fs module from server index
* refactor: move opencode route ownership into module
* refactor: extract notification route registration
* refactor: extract opencode and notification runtimes from index
* refactor: extract settings runtime and complete server modularization pass
* refactor: modularize server config, skills, icons, and tunnel routes
* refactor: extract server modules from monolithic index.js
Split proxy, routes, runtime helpers, and notification emitter
into dedicated modules under packages/web/server/lib/.
* refactor: replace session/message stores with SSE-driven sync layer
Delete ~9200 lines of old architecture (useEventStream, messageStore,
sessionStore, useSessionStore, questionStore, useTodoStore, client SSE).
New sync layer: event pipeline with coalescing + 16ms flush, pure event
reducer, per-directory child stores with LRU eviction, cursor pagination,
optimistic updates, deferred timeline staging, text throttle.
Migrate all UI consumers to sync hooks (useSessionMessages,
useSessionMessageRecords, useSessionStatus, useSessionPermissions, etc).
Strip session-ui-store to UI-only state, delegate SDK ops to
session-actions with abort-if-busy, optimistic store updates, and
response merging for revert/fork/archive/delete.
Add notification-store for SSE-driven session attention tracking,
cross-directory GlobalSessionStatusStore for sidebar indicators,
client-side diff snapshot sanitization to prevent memory bloat,
and revert message filtering via useVisibleSessionMessages.
* feat: notification store, session actions, activity detection
Add notification-store.ts for SSE-driven attention tracking.
Add sanitize.ts to strip diff snapshot memory bloat.
Add session-actions.ts with optimistic revert/fork/archive/delete.
Improve useSessionActivity with incomplete-message fallback.
Delete useServerSessionStatus polling hook.
* fix: add directory param to all SDK calls, fix command/shell/abort routing
All SDK calls in session-actions.ts now pass directory parameter —
required by OpenCode server to scope session operations. Without it,
abort, commands, revert, fork, and other operations returned 500.
Add routeMessage() in session-ui-store for shell mode (session.shell),
slash commands (session.command), and normal prompts. Command lookup
checks both sync child store and useCommandsStore. Handle /compact
locally via session.summarize().
Implement getContextUsage() to restore header context usage display —
reads token counts from last assistant message in sync store.
* refactor: replace custom API proxy with http-proxy-middleware
Remove ~280 lines of custom proxy code: forwardSseRequest,
forwardGenericApiRequest, collectRequestBodyBuffer, header
manipulation, hop-by-hop filtering, SSE block buffering.
Replace with single createProxyMiddleware() call that handles
SSE streaming, large bodies, and timeouts out of the box.
Dynamic router for OpenCode port changes after restarts.
Auth headers injected via proxyReq hook.
Keep: readiness gate, Windows session merge, API prefix detection.
* perf: targeted event draft cloning to fix streaming render cascade
Event handler was eagerly cloning all state slices on every event,
breaking Zustand selector referential equality. During streaming
(~60 events/sec), this caused every subscriber to re-render regardless
of which slice actually changed.
Now only clones fields the specific event type mutates. Also extracts
StatusRowContainer to isolate high-frequency useAssistantStatus
subscription, removes dead messageStreamStatesMap subscription from
ChatContainer, and narrows useAssistantStatus to only track last
assistant message parts.
MessageList renders: 1972 → 296 per streaming session (-85%).
* fix: null safety for sync state slices
Add defensive ?? {} guards on permission, question, session_status,
and message record access. Prevents crashes when child store state
is partially initialized during bootstrap.
* perf: dedup inflight SDK calls, extract concurrency util, delay PR tracking
Extract mapWithConcurrency to shared lib/concurrency.ts. Add in-flight
dedup for loadProviders/loadAgents to prevent concurrent duplicate SDK
calls. Delay initial PR background tracking by 5s to reduce startup
CPU burst.
* fix: header session lookup across all child stores
Session title and context panel click failed when session belonged to
a different directory than the current child store. Fall back to
getAllSyncSessions() to search all initialized stores.
* chore: bump @opencode-ai/sdk to 1.3.5
* docs: add sync event handling guide
* Optimize session prefetch and improve delete/archive UX
- Add settlement delay to session prefetch to avoid race conditions on
rapid session switches
- Reduce git diff prefetch and session cache limits for better performance
- Implement optimistic UI updates for session delete/archive operations
with proper rollback on failure
- Wire session prefetch hook into SessionSidebar with sync integration
* Add file content cache and sync optimizations
- Wrap FilesAPI with in-memory LRU cache for file content with dual
constraints (entry count and byte size)
- Optimize chat timeline scroll restoration using useLayoutEffect
- Preserve React references in message and part arrays to prevent
unnecessary re-renders when prepending history
- Add session prefetch TTL cache to prevent redundant fetches
- Integrate session prefetch cache clearing with eviction flow
* Improve session sidebar error handling and add diff prefetch filtering
Load active and archived sessions independently using Promise.allSettled
to prevent one failure from blocking the other. Add retry logic to session
API calls and skip large files during diff prefetch to improve performance.
* Replace sendMessage with optimisticSend wrapper
Introduces optimistic UI updates for normal chat messages to provide
instant feedback. Messages appear immediately in the UI while the API
call executes in the background, with automatic rollback on errors.
* perf: split stores, proper optimistic send, fix revert/directory bugs
- split session-ui-store into voice/input/selection/viewport stores
to reduce subscriber re-evaluation during streaming
- wire optimisticSend through useSync shadow Map infrastructure
matching OpenCode's pattern (no heuristic part detection)
- port OpenCode Identifier.ascending ID format for correct sorting
- pass messageID to promptAsync to prevent duplicate messages
- fix worktree directory not propagating to session actions
(dynamic dir() via opencodeClient.getDirectory)
- fix setCurrentSession accepting directoryHint for new sessions
- fix revert not hiding messages (session limit was 5, bumped to match loaded count)
- fix revert optimistic message removal from store
- fix load-more flicker (useLayoutEffect scroll compensation)
- add prefetch TTL cache, file content LRU cache
- add session prefetch for adjacent sessions
- add instant archive/delete (optimistic before SDK call)
- migrate legacy window.__zustand_session_store__ to session-ui-store
- add retry + independent error handling for archived sessions
- add AGENTS.md performance rules
* perf: startup optimization — dedup, caching, light git status, diff rendering gates
- defer diff prefetch to git tab open, reduce concurrency 4→2, skip >500 changed lines
- cap project git checks concurrency (2), directory status probe (3)
- dedup provider/agent loading, github auth, worktree list (in-flight + TTL caches)
- delay PR tracking 5s, cache 403 search failures per-repo
- coalesce settings PUT (200ms debounce), cache settings GET (2s TTL)
- cache canonical directory resolution (60s TTL)
- persist missing directory status to localStorage (10min TTL)
- light/heavy git status: polling skips numstat+line counting+rev-list
- large diff rendering gate (>500 lines → "render anyway" button)
- tokenization degradation for >500KB files in Pierre
- parallelize main.tsx pre-render awaits
- batch sidebar file tree expanded paths restoration (3 at a time)
- remove bare useConfigStore() subscription in AgentsPage
- sync worktree sandboxes to OpenCode SQLite DB
- fix RightSidebarTabs ternary → explicit tab matching
- defensive guards on sync state (session_status, permission, question, message)
* fix: add defensive guards on remaining sync state field accesses
guard session_status, permission, message, todo, part, config with ?? {}
in useDirectorySync selectors, session-cache, and bootstrap
* fix: add missing directory dep to useCallback in use-sync.ts
* fix: preserve diffStats when light-mode polling overwrites status
* perf: optimize startup git status polling and diff rendering
Preserves diff stats when lightweight polling updates repository status
Reduces startup overhead with smarter git polling and store updates
Adds detailed optimization and migration docs for next performance steps
* fix: keep chat diff stats stable during git status updates
Prevents lightweight git polling from dropping diff statistics
Keeps MessageList diff indicators consistent while status refreshes
Improves reliability of git-aware chat rendering
* fix: user animation replay, queued message variant, startup provider loading
- consume animation ID after first play to prevent re-animation
on neighbor assistant message completion
- capture send config (model/agent/variant) at queue time matching
OpenCode's FollowupDraft pattern instead of re-resolving at send time
- replace one-shot startup recovery effect with polling interval
that retries every 2s until providers and agents load
- fix optimistic bridge to avoid re-render loop (stable ref wrappers)
* chore: update tauri to 2.10.3 and all plugins to latest
- tauri 2.9.4 → 2.10.3
- tauri-build 2.5.3 → 2.5.6
- tauri-plugin-dialog 2.4.2 → 2.6.0
- tauri-plugin-log 2.7.1 → 2.8.0
- tauri-plugin-shell 2.3.3 → 2.3.5
- tauri-plugin-updater 2 (floating) → 2.10.0 (pinned)
- @tauri-apps/api ^2.9.0 → ^2.10.1
- wry 0.53.5 → 0.54.4 (transitive)
* refactor: decouple web server index orchestration runtimes
* fix: align VS Code runtime behavior with web and reduce draft view CPU load
- Queue VS Code bridge and SSE startup requests until API readiness to avoid false bootstrap failures
- Make agent manager actions directory-aware and remove real worktrees with safer partial-failure handling
- Replace heavy logo animation path with a lightweight pulse to cut draft-session CPU usage
* fix: restore auto-selected file sending in chat input
- Send server-selected files as proper file URLs in the message payload
- Include server-backed attachments in submit flow instead of dropping them
- Restore queued-message attachments through the refactored input store
* fix: restore session model selection consistently on session switch
- Restore agent, model, and variant from the latest loaded user message for each session
- Wait for session messages before applying restored selections to avoid stale or missing state
- Remove legacy session-choice inference paths that caused overlap and instability
* fix: restore permission replies and auto-accept across sessions
- Scope permission and question replies to the target session directory so answers take effect reliably
- Make permission auto-accept immediately handle pending requests and react to new permission prompts
- Keep parent-session handling working for child-session requests through the shared response path
* feat: add reusable fuzzy branch fuzzy-search helper and dialog integration (#798)
* feat: add reusable fuzzy branch search for worktrees
* chore: drop planning docs from feature branch
* feat: make worktree branch refresh manual
* feat: add configurable session retention action
* refactor: centralize global session state in ui store
* fix: cancel debounced permission push after reply
* docs: clarify global and directory session store architecture
* docs: refine agent development rules and session activity guidance
- Clarify agent code of conduct and durable development patterns
- Add explicit shared-store rerender and live-state guidance
- Narrow session activity fallback to avoid stale working state
* chore: updated .gitignore
---------
Co-authored-by: Iuliia Ivashko <yulia.ivashko@gmail.com>
|