Commit Graph

777 Commits

Author SHA1 Message Date
YeonGyu-Kim cfc3f33f8f feat(agent): add getAgentListDisplayName utility for OpenCode list ordering
Add helper function that returns runtime-facing agent name with sort

prefixes for consistent OpenCode agent list display ordering.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-11 22:01:16 +09:00
YeonGyu-Kim d62e3eacb1 Merge pull request #3342 from code-yeongyu/fix/migration-sidecar-transaction-order
fix(migration): write config before sidecar updates
2026-04-11 21:34:43 +09:00
YeonGyu-Kim 6f21e74d56 fix(migration): write config before sidecar updates
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-11 21:27:21 +09:00
YeonGyu-Kim e2f304f1a8 fix(telemetry): validate activity state shape
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-11 21:25:27 +09:00
YeonGyu-Kim e4bb60ba07 Merge pull request #3339 from Qiiks/fix/shell-detection-shell-before-psmodulepath
fix(shell-env): check SHELL before PSModulePath for shell detection
2026-04-11 21:14:02 +09:00
YeonGyu-Kim a442976d77 Fix invisible characters in agent names 2026-04-11 20:44:57 +09:00
Qiiks f5a95203a4 fix(shell-env): check SHELL before PSModulePath for shell detection
On Windows, PSModulePath is always set by the system even when the
active shell is Git Bash (via SHELL env var). This caused the
non-interactive-env hook to generate PowerShell syntax ($env:VAR=val)
for bash shells, resulting in 15+ errors per command.

The fix prioritizes SHELL over PSModulePath since an explicit SHELL
variable indicates the user's chosen shell, while PSModulePath is just
a system-wide module path that doesn't indicate the active shell.

Fixes: #3338
2026-04-11 16:06:42 +05:30
YeonGyu-Kim c30b058b7e fix(agents): use runtime names for default_agent to match ordered agent list 2026-04-11 14:15:00 +09:00
YeonGyu-Kim 280a8a34e1 feat(telemetry): add PostHog client factory with activity state tracking 2026-04-11 14:14:24 +09:00
YeonGyu-Kim 5493ae226c fix(migration): persist fullMigrationSet in-config when sidecar write fails
Addresses Cubic review: configs without prior _migrations now get the
full migration set written to config as fallback when sidecar write
fails, preventing migration tracking loss.
2026-04-10 19:01:59 +09:00
YeonGyu-Kim 68c8701d34 fix(migration): preserve _migrations in config when sidecar write fails 2026-04-10 18:47:18 +09:00
YeonGyu-Kim e7f5d0d5fd test(shared): update shared utility tests
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-10 15:53:37 +09:00
YeonGyu-Kim bd910ad011 fix(shared): improve compaction marker and tmux server health
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-10 15:53:09 +09:00
YeonGyu-Kim 046ab1060d feat(analytics): add PostHog analytics module
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-10 15:53:00 +09:00
YeonGyu-Kim 33ac57ba7a refactor: update remaining modules to use plugin-identity constants
Update various modules to use centralized constants from plugin-identity:
- get-local-version/formatter: Use PUBLISHED_PACKAGE_NAME
- run/session-resolver: Use PUBLISHED_PACKAGE_NAME
- background-agent/task-poller: Use PUBLISHED_PACKAGE_NAME
- mcp-oauth/provider: Use PUBLISHED_PACKAGE_NAME
- auto-update-checker/constants: Use ACCEPTED_PACKAGE_NAMES
- comment-checker/downloader: Use PUBLISHED_PACKAGE_NAME
- legacy-plugin-toast/hook: Use PLUGIN_NAME
- shared/data-path: Use CACHE_DIR_NAME
- shared/external-plugin-detector: Use ACCEPTED_PACKAGE_NAMES
- shared/logger: Use LOG_FILENAME
- tools/ast-grep/downloader: Use PUBLISHED_PACKAGE_NAME
- tools/call-omo-agent/tools: Use PUBLISHED_PACKAGE_NAME
- tools/delegate-task/category-resolver: Use PUBLISHED_PACKAGE_NAME
- tools/grep/constants: Use PUBLISHED_PACKAGE_NAME
- tools/grep/downloader: Use PUBLISHED_PACKAGE_NAME
- tools/lsp/lsp-client-wrapper: Use PUBLISHED_PACKAGE_NAME

🤖 Generated with assistance of OhMyOpenCode
2026-04-10 11:16:16 +09:00
YeonGyu-Kim 5000d1e37d feat(plugin-identity): add config basename constants for migration
Add CONFIG_BASENAME and LEGACY_CONFIG_BASENAME constants to support
migrating from oh-my-opencode.json to oh-my-openagent.json naming.

🤖 Generated with assistance of OhMyOpenCode
2026-04-10 11:15:42 +09:00
YeonGyu-Kim 7a1f121fdd fix(ci): isolate model-resolution-pipeline test to prevent mock contamination
The resolveModelPipeline test was consistently failing on CI
(resolveModelPipeline > does not return unused explicit user config
metadata in override result) while passing locally. Root cause is the
same mock.module contamination pattern as #3243: when bun runs all test
files in a single process, mock.module calls from other files (e.g.
model-resolver.test.ts) leak into this file's module scope.

Add mock.module('./logger', ...) so run-ci-tests.ts auto-detects the
file and runs it in its own isolated bun process.

This unblocks the v3.16.1 publish workflow which hit this flake twice.
2026-04-09 14:19:17 +09:00
YeonGyu-Kim 0d5b087440 Revert "Merge pull request #3260 from code-yeongyu/fix/remove-zwsp-sort-prefixes"
This reverts commit c3be4c2793, reversing
changes made to d2bb5d57d1.
2026-04-09 12:21:02 +09:00
YeonGyu-Kim 57ebb11e68 Merge pull request #3262 from code-yeongyu/fix/migration-sidecar
fix(migration): track applied migrations in sidecar so user reverts stick
2026-04-09 11:01:02 +09:00
YeonGyu-Kim 00a4f318ef fix(migration): track applied migrations in sidecar so user reverts stick
Users who auto-migrated from `openai/gpt-5.3-codex` to `openai/gpt-5.4`
and then reverted their config back to `gpt-5.3-codex` by hand had the
migration re-apply on every startup in an infinite loop. Discord bug
report pointed at the exact symptom: "i deleted the migrations and they
kept coming back".

The old migration tracking lived on the config body itself as a
`_migrations` string array. The skip-already-applied check relied on
the user not touching that field. But users hit by the unwanted
migration naturally reached for the JSON file to roll their model back,
and the natural human reaction to an incomprehensible internal field
next to their config is to delete it. That wiped the migration memory
and let the same migration re-apply at the next startup.

This PR introduces a sidecar state file that lives next to the config
as `<configPath>.migrations.json` and tracks applied migrations
outside the user's hand-editable config body. The migration pipeline:

  1. Reads applied migrations from BOTH the sidecar AND the legacy
     in-config `_migrations` field, unioning them. This keeps old
     configs that still carry `_migrations` working without forcing
     a reset.

  2. Writes the updated migration set to the sidecar, never to the
     config body.

  3. Strips the legacy `_migrations` field out of the config body on
     the first write after the sidecar takes over. Users stop seeing
     the mystery internal field in their own config from that point
     forward.

If the user also deletes the sidecar (explicit fresh-start gesture)
the migrations run again - that is intentional.

Tests (TDD, all new tests written before implementation):

  - src/shared/migration/migrations-sidecar.test.ts - 11 unit tests
    covering read/write/round-trip, malformed-payload resilience,
    parent-directory creation, sorted output for stable diffs, and
    non-string entry filtering.

  - src/shared/migration.test.ts - 6 new integration tests under the
    "migrateConfigFile with migration tracking via sidecar" block
    covering: no-op path, sidecar-only write, sidecar skip after user
    revert, legacy _migrations mirroring + strip, sidecar + legacy
    union with dedupe, and partial-history append. Existing
    "preserves existing _migrations and appends new ones" test was
    rewritten to assert the new sidecar-based contract.

  - Also fixes a latent test-hygiene bug: the shared
    /tmp/nonexistent-path-for-test.json config path used by many
    migrateConfigFile tests did not clean up its companion sidecar
    between tests, letting state from one test bleed into the next.
    Added afterEach that unlinks the sidecar.

Verified:

  - bun test src/shared/migration/ -> 11 new sidecar tests pass
  - bun test src/shared/migration.test.ts -> 82 pass, 0 fail
  - bun run typecheck -> clean
  - bun run script/run-ci-tests.ts -> 4458 pass, 0 fail (full suite)
2026-04-09 10:54:25 +09:00
YeonGyu-Kim 8b418ea38a fix(agents): remove ZWSP sort prefixes from display name helper (#3259)
AGENT_LIST_SORT_PREFIXES prepended U+200B Zero Width Space characters
to the four core agent display names so they would sort ahead of user
agents in the Tab cycle. Two problems with that approach surfaced:

  1. Some terminal emulators (Ghostty, certain Windows Terminal
     builds) render ZWSP as a visible box or extra space, producing a
     visible black gap in the status bar before "Sisyphus" and
     misaligning the layout (#3259).

  2. The prefixes leaked into the plugin API surface via config.agent
     keys, breaking prompt_async consumers that received
     ZWSP-contaminated agent names (#3238).

#3242 already removed every call site of getAgentListDisplayName() in
production code. That made the sort prefixes dead code: the constant
table was still defined but nothing read it. This PR finishes the
cleanup by:

  - Deleting the AGENT_LIST_SORT_PREFIXES constant entirely
  - Turning getAgentListDisplayName() into a thin alias over
    getAgentDisplayName() for BC with external importers
  - Keeping stripAgentListSortPrefix() as a legacy data migration for
    users upgrading from v3.14.0-v3.16.0 whose config.agent keys may
    still have ZWSP baked in from the old code path
  - Documenting the history on stripAgentListSortPrefix() so future
    maintainers understand why the stripper has to stay even after
    the injector is gone

Sort ordering is preserved via JS object insertion order in
reorderAgentsByPriority() plus the `order` field it injects on the
four core agents. Both mechanisms are already in place and both
pre-date this PR; the ZWSP prefix was an older third layer that was
only meant to work around alphabetical sorting in legacy OpenCode
before the `order` field landed upstream.

Tests: 4445 pass, 0 fail. Added 3 new assertions to
agent-display-names.test.ts verifying that getAgentListDisplayName
returns plain names containing no zero-width characters. Updated
chat-message.test.ts to use a literal ZWSP string instead of the
helper so the defensive-strip path still has coverage.

Closes #3259
2026-04-09 10:10:12 +09:00
YeonGyu-Kim 6943b6d4d8 fix(ci): isolate model-resolver and prometheus-config tests from mock.module contamination
model-resolver.test.ts and prometheus-agent-config-builder.test.ts use
spyOn(shared, 'log') but do not own the logger module. When other test
files in the same bun test process call mock.module('../shared/logger'),
the import cache is poisoned and the spyOn targets a stale binding.

Add a lightweight mock.module call at the top of each file so the
auto-detection in run-ci-tests.ts picks them up as isolated targets.
This ensures each file gets its own module instance and the spy
captures all calls correctly.

Fixes the flaky CI failure pattern where resolveModelWithFallback and
buildPrometheusAgentConfig tests pass locally (separate bun process)
but fail in the shared CI batch.
2026-04-08 23:39:43 +09:00
YeonGyu-Kim 47283f9238 fix(agents): remove ZWSP prefixes from config.agent keys (#3238)
Agent names in the config.agent object (which becomes the /agent API
response) contained invisible Zero-Width Space (U+200B) characters
baked in by getAgentListDisplayName(). These ZWSP prefixes were used
for TUI sort ordering, but they leaked into the public API surface.

Impact: any prompt_async consumer that discovered agent names via the
/agent endpoint and passed them back to prompt_async without manual
ZWSP stripping got silent message drops — the agent name didn't match.
hy-pony's feishu-bridge integration went dark after upgrading to 3.16.0
with no error, no warning, and no indication that invisible Unicode
characters in agent names were the cause.

Fix: switch all four callsites from getAgentListDisplayName() (which
prepends \u200B×N) to getAgentDisplayName() (clean names):

- agent-key-remapper.ts: config keys → display names (was the primary
  injection point)
- agent-priority-order.ts: CORE_AGENT_ORDER lookup (must agree with
  the keys emitted by the remapper)
- command-config-handler.ts: command agent field normalization
- tool-config-handler.ts: agent config lookup (simplified fallback
  chain since the primary lookup is now clean)

Sort ordering is preserved by:
1. JS object insertion order from reorderAgentsByPriority()
2. The injected `order` field (1-4) added by injectOrderField()

getAgentListDisplayName() is marked @deprecated with a link to #3238.
AGENT_LIST_SORT_PREFIXES and stripAgentListSortPrefix() are kept for
any internal callers that strip prefixes from legacy data.

Closes #3238
2026-04-08 22:51:59 +09:00
YeonGyu-Kim ed16dc0608 fix(chat-params): complete maxOutputTokens migration in session prompt params 2026-04-08 17:14:17 +09:00
YeonGyu-Kim 4394129667 fix(compaction): harden continuation directive markers 2026-04-08 15:58:02 +09:00
YeonGyu-Kim 78e6d780eb fix(plugin): verify event hook compatibility with v1.4.0
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-08 13:08:03 +09:00
YeonGyu-Kim e52dd340c6 fix(plugin): migrate chat.params to maxOutputTokens for v1.4.0
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-08 13:02:51 +09:00
YeonGyu-Kim 317e2c6465 fix(zwsp): strip zero-width chars in start-work-hook
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-08 13:01:51 +09:00
YeonGyu-Kim a00fe13122 fix(test): update normalizeAgentForPrompt expectations to dash format
Test expected legacy parenthesized names but display names now use
dash format (Sisyphus - Ultraworker, not Sisyphus (Ultraworker)).
This was the flaky CI failure on dev.
2026-04-07 16:09:37 +09:00
YeonGyu-Kim 6eb527c914 fix: remove ZWSP from agent display names (#3146) 2026-04-07 15:44:31 +09:00
YeonGyu-Kim b77c256943 fix(jsonc): strip BOM before parsing (#3164) 2026-04-07 15:39:51 +09:00
YeonGyu-Kim bf2c104c9e Merge pull request #3172 from code-yeongyu/fix/bom-strip-jsonc
fix(jsonc-parser): strip UTF-8 BOM before parsing
2026-04-07 14:51:00 +09:00
YeonGyu-Kim daae6ed05b fix(jsonc-parser): strip UTF-8 BOM before parsing to fix Windows "InvalidSymbol at offset 0" errors
Windows editors often save UTF-8 files with a BOM (Byte Order Mark: \uFEFF).
When this is present, jsonc-parser reports InvalidSymbol at offset 0 because
the BOM is not valid JSON/JSONC syntax.

This commit strips the BOM before parsing, fixing issues #3164 where Windows
users report their opencode.jsonc file fails to parse even though it appears
to start with a valid '{' character.

Fixes: #3164
Co-authored-by: Jobdori <agent@yeongyu.kim>
2026-04-07 14:48:30 +09:00
YeonGyu-Kim f8c626086e fix(agent-names): use HTTP-header-safe display names and config keys for API calls (#3138)
Display names with parentheses like 'Atlas (Plan Executor)' cause HTTP
header validation errors in x-opencode-agent-name. This was blocking
Atlas/Prometheus from working via /start-work and auto-retry.

Changes:
- Display names: parens -> dashes ('Atlas - Plan Executor')
- Hooks (start-work, no-hephaestus-non-gpt, no-sisyphus-gpt): use
  config keys ('atlas', 'sisyphus', 'hephaestus') for agent API fields
- auto-retry: use config key instead of display name for promptAsync
- agent-override-protection: handle dash-suffix normalization
- Updated all test expectations to match new format

Closes #3138
2026-04-07 10:08:04 +09:00
YeonGyu-Kim 178b635d72 feat(agents): add ZWSP stable-sort prefixes for core agent list ordering
Populate AGENT_LIST_SORT_PREFIXES for sisyphus/hephaestus/prometheus/atlas
so the TUI agent list renders in canonical order. Update dependent tests
to use getAgentListDisplayName() instead of hardcoded display strings.

🤖 Generated with [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) assistance
2026-04-06 18:07:07 +09:00
YeonGyu-Kim 61083d499d fix(oauth+errors): OAuth silent refresh, quota STOP patterns, compaction loop cap
Bug fixes:
1. OAuth token refresh (#3149): buildHttpRequestInit() now attempts silent refresh
   via refresh_token before triggering full browser re-auth. Added refresh() method
   to McpOAuthProvider. Includes test isolation fix for discovery mock.

2. Quota error STOP (#3126): Added STOP_MESSAGE_PATTERNS in model-error-classifier
   that take precedence over RETRYABLE_MESSAGE_PATTERNS. Message-only quota errors
   now non-retryable. Runtime-fallback: quota_exceeded with 'retrying in' signal
   still triggers fallback (provider-managed auto-retry). Restored removed patterns.

3. Compaction loop (#3127): MAX_RECOVERY_ATTEMPTS=3 cap + additional suppression
   guard from opencode session in degradation monitor.

Also: refactored extractAutoRetrySignal to auto-retry-signal.ts, new regression
tests for quota classifier and compaction degradation monitor.
2026-04-06 17:40:12 +09:00
YeonGyu-Kim 6c4e0b69a5 fix(agents): enforce Sisyphus->Hephaestus->Prometheus->Atlas assembly order
Extract hephaestus into agentConfig before prometheus so the config
handler naturally assembles agents in the canonical order instead of
relying solely on reorderAgentsByPriority to fix the wrong intermediate
order. Also fix test data that had atlas/prometheus swapped and add
missing hephaestus to agent-config-integration test lists.
2026-04-06 17:18:47 +09:00
YeonGyu-Kim f69234ab7e fix(error-classifier): quota/billing errors are non-retryable STOP (fixes #3126)
Add STOP_MESSAGE_PATTERNS that take precedence over RETRYABLE_MESSAGE_PATTERNS.
Message-only quota errors (no error name) now correctly return false from
isRetryableModelError, preventing unnecessary fallback retries on exhausted quotas.

- quota will reset after...
- quota exceeded
- usage limit has been reached
- free usage limit / billing limit / plan limit / subscription limit
- out of credits / credits exhausted / insufficient credits / insufficient balance

Also add 4 regression tests covering message-only quota STOP cases.
2026-04-06 16:56:17 +09:00
YeonGyu-Kim 22d0895992 Merge pull request #3136 from andrescera/fix/atlas-zwsp-header-leak
fix: remove ZWSP sort prefix that leaks into x-opencode-agent-name header
2026-04-06 13:55:08 +09:00
YeonGyu-Kim de6c74bfb4 fix(todo-continuation): normalize agent name to config key before promptAsync (#3149)
The todo-continuation-enforcer was passing raw agent names (which could be
display names like 'Sisyphus (Ultraworker)') to promptAsync. These names
contain spaces/parentheses that violate HTTP header specs, causing the
x-opencode-agent-name header validation to fail with 'unknown error' toast.

Added normalizeAgentForPromptKey() that converts display names to config keys
(e.g., 'Sisyphus (Ultraworker)' -> 'sisyphus') before API calls.

TDD: Added regression test that verifies config key is sent to promptAsync.
2026-04-06 11:44:52 +09:00
Andres Cera b482e6c609 fix: remove ZWSP sort prefix from Atlas agent name
The \u200B (zero-width space) in AGENT_LIST_SORT_PREFIXES leaked into
the x-opencode-agent-name HTTP header, causing invalid header errors.

Agent ordering is handled by agent-priority-order.ts via numeric order
fields and insertion order — the ZWSP prefix was redundant.
2026-04-05 00:43:05 -05:00
YeonGyu-Kim d7c2b6249b fix(config): fall back to legacy path when migration fails and use canonical basename everywhere (#3133)
Root cause: loadPluginConfig() unconditionally switched userConfigPath to the
canonical name after calling migrateLegacyConfigFile(), even when migration
failed (e.g. file lock on Windows, permission denied). This left the config
path pointing to a non-existent file, so the plugin config silently loaded
as empty defaults.

Additionally, several fallback/default paths were hardcoded to the legacy
'oh-my-opencode' basename instead of using CONFIG_BASENAME ('oh-my-openagent'),
causing CLI config commands (writeOmoConfig, detectCurrentConfig) to write to
the wrong filename.

Changes:
- plugin-config.ts: check migrateLegacyConfigFile() return value; only switch
  to canonical path if migration succeeded OR the canonical file already exists
- opencode-config-dir.ts: use CONFIG_BASENAME for omoConfig path in
  getOpenCodeConfigPaths()
- config-context.ts: getOmoConfigPath() now uses detectPluginConfigFile() to
  find whichever name variant actually exists on disk
- plugin-config.ts: default fallback paths use CONFIG_BASENAME instead of
  hardcoded legacy name
- Added test: loadPluginConfig still loads config when migration fails
  (read-only directory simulation)
2026-04-05 14:27:24 +09:00
YeonGyu-Kim 7b7526e2f6 docs(src): update counts across tools, features, config, cli AGENTS.md
- tools: 15 -> 16 dirs, writing model kimi-k2p5 -> gemini-3-flash
- features: builtin skills 6 -> 8 (+review-work, +ai-slop-remover)
- config: schema files 24 -> 27 (+git-env-prefix, model-capabilities, openclaw)
- cli: commands 5 -> 6 (+refresh-model-capabilities)
- plugin-handlers: 13 -> 14 non-test files
- shared: 95+ -> 100+ utility files
2026-04-05 14:12:31 +09:00
YeonGyu-Kim 33b17519e5 test(config-dir): align opencode config basename
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-05 13:42:37 +09:00
YeonGyu-Kim 852859ed9c refactor(config-manager): remove legacy config path detection and migration logic
🤖 Generated with assistance of OhMyOpenCode
2026-04-05 11:24:10 +09:00
YeonGyu-Kim f22935cb28 Config-manager: migrate legacy OMO config path 2026-04-05 11:24:10 +09:00
YeonGyu-Kim 0cbfdec76e fix(model): treat 'bad request' as retryable error for GitHub Copilot fallback (#3130)
GitHub Copilot performs rolling model updates which sometimes return
'400 Bad Request' when a model is temporarily unavailable. This error
was not in the retryable message patterns, causing model fallback to
not trigger and users getting stuck.

Changes:
- Added 'bad request' to RETRYABLE_MESSAGE_PATTERNS in model-error-classifier.ts
- Added test cases for 'bad request' pattern matching

Fixes #3130
2026-04-05 10:18:15 +09:00
YeonGyu-Kim 130f4ac080 fix: resolve #3124 #3125 #3127 session tools, cache priming, and compaction loop
- #3124: Session tools now merge SDK and file-backed sessions for SQLite backend
- #3125: Cache priming fixed for OpenCode >=1.3.14 empty workspace
- #3127: Activity-based progress detection prevents infinite compaction on Kimi/Minimax

All 29 new tests pass, 4885 total tests passing.
2026-04-05 09:30:19 +09:00
YeonGyu-Kim aeb9c97c30 fix(model): quota errors should STOP not retry 2026-04-05 09:13:14 +09:00
YeonGyu-Kim aa7e2aa07f test: isolate migrate-legacy-plugin-entry.test.ts to prevent mock contamination 2026-04-04 21:32:52 +09:00