Joysafeter v2#141
Open
yuzzjj wants to merge 460 commits into
Open
Conversation
…ture Implements two new execution engines following the ExecutionEngine protocol: - CodeEngine: executes user Python code by extracting a StateGraph via execute_code() sandbox, compiling, and streaming results - CopilotEngine: wraps CopilotService streaming and persists copilot events as ExecutionEvents through the event bus Adds dispatch_copilot() to ExecutionOrchestrator with engine override mechanism, POST /v1/copilot/run endpoint, copilot event types, and frontend execution bridge hook for copilot UI integration. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
…yout CodeEditorPage is now a self-contained page (parallel to AgentBuilder) with its own toolbar (Save + Deploy + Run/Stop), execution panel, and run input modal — matching the graph builder UX without coupling. - Enable code option in agent creation and version forms - Show Builder tab for code-mode agents in layout and overview - Add Deploy button that publishes with runtime_kind="code" - Wire Run/Stop to executionStore.startExecution() for execution panel - Simplify CodeEditorToolbar to save-only (run/deploy moved to page) - deploymentAdapter.deploy() accepts optional runtimeKind parameter Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
- Delete unused CodeEditorToolbar (dead code after page refactor) - Remove unused imports: Input, useAgent, useRef from CodeEditorPage - Use individual Zustand selectors instead of bare destructure to prevent re-renders on every execution event - Remove WHAT comments from JSX - Extract hasBuilderSupport() helper to deduplicate definition_kind checks in layout.tsx and agent-overview-tab.tsx Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
- AgentBuilder now checks version.definition_kind instead of variables.graph_mode to decide CodeEditorPage vs canvas - useVersionGraphState returns definitionKind from the version - Slug generation auto-appends suffix on conflict instead of erroring Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
… new execution architecture Copilot was still using the old runService (agent_name + graph_id + message) against a backend that only accepts the new AgentRun schema (release_id + trigger_source + goal). All old endpoints (/snapshot, /events, /active, /ws/runs) were 404s. This rewires the entire Copilot chain to use the already-existing POST /v1/copilot/run → ExecutionOrchestrator.dispatch_copilot → execution_event_bus → /ws/executions → useCopilotExecutionBridge pipeline. - Fix WS field inconsistency: WebSocketSubscriber "data" → "payload" - Add agent_id/trigger_source/status filters to GET /v1/runs - Add "copilot" to TriggerSourceLiteral - Rewrite useCopilotActions to dispatch via copilotService.dispatchRun - Activate useCopilotExecutionBridge in CopilotPanel (closes event loop) - Rewrite useCopilotEffects for session restore via agentRunService - Consolidate executionAdapter to delegate to agentRunService - Delete runService.ts, runWsClient, test_runs_api.py and all dead code Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
- Add use-execution-stream.ts to WS protocol task (was missing onStatus→onCompleted) - Fix route targets: /tasks for param links, /dashboard for back nav only - Add 'artifact' to ExecutionStepType union - Add getWsChatUrl cleanup to dead code deletion task - Simplify component dedup to pure deletion (no imports reference it) Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…docstring Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
…stream endpoint Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
…, remove phantom execution_status Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
…dlers, remove old pipeline Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…ter, eventProcessor, getWsChatUrl Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
…n engine_kind Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
…w 404 Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
Fixes from spec review: 1. Rename ChatMessage → ConversationMessage to avoid name collision with existing ChatMessage in openclaw_chat.py and code_agent/memory.py 2. Add StagedUpload table to bridge file upload (sandbox path) → chat attachment (UUID reference) gap 3. Specify seq generation strategy with SELECT FOR UPDATE locking 4. Add full MessageProjectionSubscriber event→message mapping table 5. Update dispatch_chat signature to accept attachment_ids Also: remove preview_ready (compute on-the-fly), add updated_at column, note max_length change from 4000→10000, clarify ChatResponse schema disambiguation. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Previous versions designed new tables (conversation_messages,
message_attachments, staged_uploads) — wrong direction. ExecutionEvent
is already the source of truth for all conversation content.
New design:
- Zero new tables
- Delete ThreadMessage + MessageProjectionSubscriber (redundant projection)
- One new endpoint: GET /v1/threads/{id}/events (aggregation query)
- Extend ChatRequest with attachments (metadata in USER_MESSAGE payload)
- Frontend ChatPanel is a view layer over execution events
Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
1. Pagination: after_seq → cursor-based (event_id), since sequence_no resets per execution 2. Attachment payload construction: in router not dispatch_chat, with exact code location reference (threads.py lines 185-195) 3. max_length 4000→10000 explicitly called out as intentional 4. ThreadDetailResponse added to deletion scope (embeds MessageResponse) Also: copilot_* filtered server-side, authorization documented, file preview raw-mode code path detailed. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
…, fix Date.now consistency - Remove duplicate generateId import (genId alias) - Remove redundant clearInterrupts() before updateGraphState that already clears - Capture Date.now() once in onCompleted for consistent endTime/duration - Switch if/if/if to switch in handleMessage hot path - Add payload.delta/content fallback for assistant_text events - Remove WHAT comments, keep only WHY comments Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
8 tasks: backend deletion → schema extension → events endpoint → file preview → frontend types/hooks → component system → wiring → verification. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
- agent-overview-tab: add cn import, type ActivityItem.status as AgentRunStatus, include current_execution_id - recent-tasks: replace .toSorted() (ES2023) with .slice().sort() - AgentBuilder/CodeEditorPage: fix import path for deleted graph-builder/components/execution/ - en.ts/zh.ts: rename duplicate chat.untitled key to chat.untitledCode Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…lter syntax 1. Add dependency note: Task 1 removes get_thread_with_messages, Task 2 must update the router handler that calls it 2. Clarify artifact download endpoint already exists (no new task) 3. Fix SQLAlchemy copilot filter: not_(like) instead of ~startswith 4. Add adapter read fallback guidance 5. Explicit ThreadDetail deletion lines in Task 5 Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
…ith attachments Remove the redundant ThreadMessage model, MessageProjectionSubscriber, and all dependent code (ThreadMessageRepository, list_messages endpoint, get_thread_with_messages, MessageResponse/ThreadDetailResponse schemas). ExecutionEvent is now the sole source of truth for chat history. Extend ChatRequest with optional attachments (up to 10 ChatAttachment items) and increase max message length from 4000 to 10000 characters. Attachments are serialized into the USER_MESSAGE event payload. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
storage_ref is the sandbox path from /v1/files/upload response. size_bytes is required (not optional). Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Add ?mode=raw query parameter to GET /v1/files/read/{filename} that
returns raw binary bytes with correct Content-Type header instead of
JSON-wrapped text. This enables inline image/PDF preview in the chat UI
via <img src="/api/v1/files/read/photo.png?mode=raw">.
Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Aggregates execution events across all runs in a thread. Cursor-based pagination via ?after=<event_id>. Filters out copilot_* events server-side. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
- ThreadEvent type replaces ThreadMessage - threadService.listThreadEvents and sendChat replace message methods - useThreadEvents, useChatSend, useChatStream hooks Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
ChatPanel, ChatHistory, ChatEventBubble, ChatInput, ChatFilePreview, AttachmentChip, ThreadSidebar — chat UI rendering execution events as conversation bubbles. Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
… default redirection path to /dashboard
Replaces confusing DefinitionKindLiteral/RuntimeKindLiteral/executor_kind with two clean orthogonal axes: EngineKind (what kernel) and RuntimeKind (where it runs). Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
10-task plan covering contracts, engine registry, models, schemas, services, migration, frontend types and components. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…eKind/RuntimeKind in contracts
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…_kind in models Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…consolidate overrides Fixes missed files (schemas/execution, services/agent_service, agent_version_service, execution_service, frontend execution page) and consolidates executor_kind_override into engine_kind_override in the orchestrator. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Replaces TriggerSourceLiteral with two orthogonal axes: - TriggerMediumLiteral: how the run was triggered (api, scheduler, system, ui) - RunPurposeLiteral: why the run exists (production, draft_test, debug, internal_builder) Updates contracts, models, schemas, repositories, services, and API layer. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…e_kind refactors - 1a2b3c4d5e6f: unify agent_run metadata (trigger_source → trigger_medium + run_purpose) - b2af1f3e0215: refactor agent kinds (definition_kind → engine_kind, executor_kind → engine_kind) Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Removes deprecated assignee_type/assignee_id aliases and TERMINAL_TASK_STATUSES export from frontend task types. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…frontend Renames trigger_source to trigger_medium/run_purpose in: - execution_runner.py EventContext construction - coordinator_tools.py sub-run creation - execution_service.py envelope construction (5 occurrences) - agentRunService.ts list params - useCopilotEffects.ts copilot session restore - executionAdapter.ts run creation Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
…timize metadata migration scripts
…d overhaul observation processing systems
…tend state management
…ontext wiring, and fix React hook dependencies and rendering logic
…on-scoped trace listing
Foundation for Agent multi-turn full-stack tracing. Establishes Thread as the
session root across dispatch, traces, and the debug UI, without yet touching
the container pool or Trace/Collector placement (reserved for P1-P3).
Backend
- executions/debug + agent-runs/draft accept thread_id, propagated through
DispatchService → ExecutionOrchestrator → AgentRun.thread_id
- dispatch_debug sets Trace.session_id = str(thread_id) so multi-turn debug
runs aggregate under one session (fallback "debug-{user}-{version}-{date}"
kept only when thread_id is absent; goes away once thread_id is NOT NULL)
- traces.list_traces: new session_id query param; the frontend was already
sending it but the backend silently ignored it
- ExecutionOrchestrator._require_no_active_run(thread_id): centralized guard
enforcing the "one active run per thread" invariant; hooked into
_create_and_fire and _create_and_fire_draft
- threads/chat: inline active-run check removed; orchestrator is now the sole
enforcement point
Frontend
- executionAdapter / executionStore / types carry threadId end-to-end
- DebugPanel creates a Thread on first turn, reuses it for follow-ups, filters
trace history by session_id, exposes turn count and a "New Session" action
that archives the old thread
- DebugToolbar surfaces turn count, renders trace history as "Turn N — <time>"
when in a multi-turn session
…itional full-stack tracing
Engine-level multi-turn (decision B): same Thread reuses container + CLI
session across turns; Trace/Collector now apply to every dispatch path.
Schema
- Migration c3bf20d4e6a7 "thread_as_session": adds threads.container_id,
cli_session_id, last_active_at (NOT NULL DEFAULT now()), and LRU index;
agent_runs.thread_id tightened to NOT NULL (orphans deleted); tasks gains
thread_id (nullable for this PR; PR3 tightens to NOT NULL alongside task
creation that auto-provisions a Thread).
ContainerPool — rewritten around Thread
- key: thread_id (was release.id)
- API: acquire / release / store_session / evict (old get/put/set_session_id/
remove removed). Old call shapes deleted, not kept as compat shims.
- DB as source of truth: cache miss lazily adopts threads.container_id if the
container is still running via docker inspect; stale bindings cleared.
- LRU eviction only touches idle entries (active_count == 0); running
executions are never evicted. Global max_containers hard cap (default 50).
- On shutdown containers are left running so they can be adopted after restart.
ExecutionRunner
- container_pool.acquire(run.thread_id, create_fn) replaces the release-keyed
get+put+remove dance. Provisioning is a callback so the pool owns the
adopt / cap / race logic.
- CLI session id persisted via store_session(thread_id, sid) after each turn;
next turn on the same thread resumes via --resume.
- _cleanup_container removed — every container now belongs to the pool, so
there is no "destroy on no-release" branch.
Orchestrator
- All dispatch_* thread_id parameters are now required (no Optional = None):
dispatch_chat (already so), dispatch_debug, dispatch_draft, dispatch_direct.
Matching tightening in DispatchService + agent_run / executions schemas.
- dispatch_task: Task lacking a thread_id lazy-provisions one via
_ensure_task_thread (title inherited from the task). Runs bind to that
thread so retries / follow-ups share container + CLI session.
- dispatch_copilot_draft provisions an anonymous Thread so the invariant
"every run has a thread" holds for copilot interactions as well.
- _require_no_active_run: removed the thread_id is None fallback — invariant
is now unconditional.
Trace / Collector unification
- _fire_engine builds the Trace row via _ensure_trace before firing, and
constructs ObservationCollector unconditionally (was gated by debug=True).
session_id = str(run.thread_id), no more debug-{user}-{version}-{date}
fallback strings.
- _create_and_fire_draft.debug parameter removed; dispatch_debug no longer
builds its own Trace.
- Every dispatch path (chat, task, direct, draft, debug, copilot_draft) now
emits a Trace grouped by thread_id — full-stack tracing coverage.
ThreadService.archive_thread
- Refuses to archive a thread with an active run (THREAD_ACTIVE_RUN_EXISTS).
- Calls container_pool.evict(thread_id) so the container is torn down and
the DB binding cleared atomically with the status flip.
Frontend
- executionAdapter StartRunParams / StartDraftRunParams threadId required.
- StartDraftExecutionInput.threadId required.
- CodeEditorPage "Run" path: executionStore mints a fresh Thread per click
so the build-page Run button has a session root (same model as Debug).
- DebugPanel: Thread is provisioned on mount (replacing the per-turn "create
if null" branch). New Session archives the thread — backend evicts the
container — and the useEffect re-provisions a fresh one.
…nous provisioning) Closes the last gap in the Thread-as-Session model. Tasks are no longer allowed to exist without an agent or a thread, so the engine side can assume every dispatch_task call has a complete session root to operate on. Schema (migration d4c031e5f7b8) - Deletes tasks with a NULL agent_id or thread_id (greenfield — nobody should have them after PR2, but the DELETE makes the upgrade idempotent). - tasks.agent_id and tasks.thread_id are ALTERed to NOT NULL. Model + schema - Task.agent_id / Task.thread_id typed as non-optional Mapped[uuid.UUID]. - CreateTaskRequest.agent_id required; TaskSummary exposes thread_id as a required field so callers always see the session root. Service - TaskService.create_task provisions a Thread synchronously in the same transaction as the Task insert. Thread title mirrors task title; agent_id inherits from the task. No lazy path remains. - ExecutionOrchestrator._ensure_task_thread removed (dead after the Task constructor guarantees thread_id). dispatch_task reads task.thread_id directly and forwards it as the run's thread_id. Effect - Task retries/follow-ups now always land on the same Thread → same container → same CLI session, preserving the agent's memory across attempts. Combined with PR2, every run in the system has engine-level multi-turn continuity.
…eplay When the underlying CLI rejects --resume (session expired, container restart, TTL eviction), the run no longer fails. Instead the ExecutionRunner evicts the stale session, rebuilds the prompt from this Thread's completed turns, and retries once without --resume — preserving the agent's apparent memory across CLI-side session loss. Contract - CLIResult gains a session_invalid: bool flag (providers' responsibility to set when their resume id was rejected). This keeps string-matching out of the Runner. CLISessionInvalidError is available in base.py as a sibling signal path for providers that prefer exceptions; not used by Runner today but documents the intent. - claude_code provider detects "session not found" / "no such session" / "session expired" / "session does not exist" in the combined stdout+stderr when the exit was non-zero with a resume id set, and flips session_invalid. Codex and openclaw don't use --resume, so they don't need detection. Reader port - ExecutionReaderPort.load_thread_history(thread_id, before_run_id) returns ordered (role, content) pairs drawn from completed AgentRuns on the Thread. User turns come from AgentRun.goal; assistant turns are concatenated ASSISTANT_TEXT event payloads. - ExecutionReaderAdapter implements it via a straightforward SELECT chain. Runner recovery loop - The provider.execute / drain / await-result path is wrapped in a single retry loop. On session_invalid at attempt 1 with a prior session, the Runner calls container_pool.store_session(thread_id, "") to purge the bad id, logs a "cli_session_recovered" observation (WARNING level) through the collector, reloads thread history, rebuilds the prompt via _rebuild_prompt, and retries with resume_session_id=None. - _rebuild_prompt concatenates prior turns verbatim with "User:" / "Assistant:" markers (raw-text strategy — long sessions eat more tokens on recovery but correctness is preserved and no summariser is needed).
…n browsing
Observation panel now shows a horizontal timeline of turns in the active
Thread, replacing the History dropdown that only surfaced a flat list.
New component
- TurnTimeline: horizontal strip of per-turn chips, ordered chronologically
(Turn 1 leftmost). Active turn is highlighted; the trailing chip pulses
when its execution is still live. Clicking a chip selects that turn.
DebugPanel wiring
- timelineTurns memo: reverses the DESC-sorted /traces response once so the
timeline reads chronologically and Turn N matches the Nth execution.
- activeTraceId state tracks which turn's trace is in the observation panel;
updated in three places:
* handleStartDebug — set to the new execution_id so the in-flight turn
lights up before /traces refetches.
* handleSelectTrace — set to the clicked turn (replay mode).
* handleNewSession — cleared alongside the other session state.
- Auto-switch behavior: starting a new turn immediately jumps the timeline
to that turn (user's chosen behavior) while leaving past turns one click
away.
DebugToolbar cleanup
- Removed the History <select> — TurnTimeline covers the same navigation
more directly. Props trimmed: onSelectTrace, traces no longer needed.
…ies and switch orchestrator to commit trace records
Review agents flagged a mix of correctness, concurrency, and quality issues
across the Thread-as-Session PR chain. This commit folds in the must-fix
items plus the obvious quality wins; performance-only suggestions
(write-amplification debouncing, untracked eviction task handle) and the
deeper refactors (cascade FKs, Thread.kind column) are noted for follow-up.
## must-fix
- **load_thread_history was always returning []** — the filter used
`status == "completed"` but the AgentRun enum is
`pending/running/succeeded/failed/cancelled`. Session recovery would
silently replay without any prior turns. Now uses "succeeded", collapses
the N+1 into a single IN query over all runs' assistant-text events, and
caps at max_turns=20 so very long threads don't blow the prompt budget.
- **Trace cross-session race** — _ensure_trace used to write the Trace row
in the orchestrator's session, but _run_engine opens a fresh
AsyncSessionLocal for everything else. Observations FK'd to a row that
might not be visible yet (or at all, if the caller rolled back). Renamed
to _insert_trace and moved the insert inside _run_engine's session.
Dropped _version_id_for_release — callers have the version loaded already.
- **Active-run race at dispatch** — the SELECT-then-INSERT check could let
two concurrent dispatches both pass and both create pending runs. New
migration e5d14297a8c9 adds a partial unique index
`uq_agent_runs_active_per_thread (thread_id) WHERE status IN
('pending','running')`. The service keeps the SELECT as a fast-path and
now catches IntegrityError as the same THREAD_ACTIVE_RUN_EXISTS error.
## quality
- **ACTIVE_RUN_STATUSES constant** — extracted from the two stringly-typed
literal tuples in the orchestrator and thread_service.
- **ExecutionRunner retry clarity** — "while True + attempt counter that
always breaks after iteration 2" replaced with straight-line first-try /
recover / retry, factored into _run_one_attempt. Container is acquired
once, released once in finally; the comment makes the invariant explicit.
- **DebugPanel redundant state** — activeTraceId was set in three places
alongside mode + executionId/replayTraceId. Derived it now
(`mode==='live' ? executionId : mode==='replay' ? replayTraceId : null`)
and dropped the state.
- **DebugPanel Thread hooks** — useCreateThread / useArchiveThread from
hooks/queries/threads.ts replace the hand-rolled useEffect+cancelled
flag and direct threadService.archive fire-and-forget. Cache
invalidation comes for free.
- **Magic setTimeout(refetchTraces, 1000)** — replaced with
queryClient.invalidateQueries; Trace is now inserted synchronously in
_run_engine so the refetch sees it immediately.
- **Dead DebugToolbar props** — agentId/agentVersionId/workspaceId were
renamed with underscore prefix but never read. Removed from interface and
call site.
- **startExecution in-flight guard** — module-level Set<graphId> prevents
rapid double-clicks from minting two Threads before isExecuting flips.
- **Import consistency** — `datetime.now(timezone.utc)` replaced with the
project's utc_now() in _insert_trace.
- **timelineTurns memo dropped** — ≤20 items reversed per render is cheaper
than the memo overhead.
Second pass on the review punch-list — the items that didn't fit in the
first cleanup commit.
- **Tracked pool eviction tasks** — ContainerPool._spawn_cleanup replaces
the bare asyncio.create_task fire-and-forget. Tasks are retained in a
set, exceptions surface via add_done_callback, and shutdown() awaits
them so we don't write to a DB engine that's tearing down.
- **TurnTimeline uses Button primitive** — swapped the hand-rolled button
for `<Button variant={isActive ? 'default' : 'outline'} size="sm">`
with a height override; the live-pulse dot now inherits foreground
colour so it reads as part of the chip instead of floating in primary.
- **Static threadService import** — lifted `import('@/services/threadService')`
out of the startExecution inline dynamic import; it's not conditional
anymore and there's nothing to code-split here.
Every acquire-hit / release / adopt used to fire a one-row UPDATE on threads.last_active_at. For a chat session that's 2-3 DB round-trips per turn purely for a timestamp — the reaper uses this column at 30-min granularity, so sub-second freshness is wasted I/O. ContainerPool now debounces those flushes to at most one per 60 s per thread. PoolEntry gains a last_flushed wall-clock timestamp; the in-memory last_used still updates synchronously, only the DB write is rate-limited. Provision seeds last_flushed=now because _persist_container just wrote the row; adopt leaves it at 0 so the stale DB timestamp gets refreshed on the first touch after boot. Idle-eviction window (idle_timeout, default 30 min) widened beyond the 60 s debounce, so no entry is prematurely reaped.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.