Commit Graph

752 Commits

Author SHA1 Message Date
YeonGyu-Kim 12bd658079 refactor(prompt-async-gate): remove deprecated dispatch wrappers
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-17 17:15:54 +09:00
YeonGyu-Kim 1bbe065c60 refactor(prompt-callers): migrate shared and cli dispatch
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-17 17:09:04 +09:00
YeonGyu-Kim 989ab7171d refactor(hooks): use unified internal prompt dispatch
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-17 17:07:35 +09:00
YeonGyu-Kim a42f894f88 refactor(prompt-async-gate): collapse dispatch into mode-based entrypoint
Use one dispatchInternalPrompt surface with mode: async | sync so source, settle, hold, timeout, status checks, reservations, and release semantics stay in one runner. Keep the old helper names temporarily so caller migration can land atomically in follow-up commits.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-17 16:37:59 +09:00
YeonGyu-Kim a7b7ace7ed fix(prompt-gate): block prompts into pending tool turns 2026-05-17 15:42:58 +09:00
YeonGyu-Kim 38702f6e85 Merge pull request #4094 from code-yeongyu/fix/opus-4.7
fix(dynamic-truncator): bound session.messages fetch to stop forever-hang on Read (#4086)
2026-05-17 03:57:28 +09:00
YeonGyu-Kim 67ead7bf6d fix(dynamic-truncator): bound session.messages fetch to stop forever-hang on Read (#4086)
Root cause: `getContextWindowUsage` caches the *promise* of
`fetchContextWindowUsage` in a per-session WeakMap keyed by client. When
`ctx.client.session.messages({ path: { id: sessionID } })` never settles
(observed once `service=session.processor ... error=Aborted process`
takes hold), the cached pending promise wedges every concurrent and
later caller in the same session. The five hooks that share one
`createDynamicTruncator(ctx)` -- directory-agents-injector,
directory-readme-injector, rules-injector, tool-output-truncator, plus
indirect callers -- all await that same poisoned promise on every Read,
so the user-facing tool chain hangs forever and ESC cannot break it.
Reporters in #4086 land on this path consistently when reading AGENTS.md
files (which trigger directory-agents-injector via the directory walk).

Fix: race the underlying `session.messages` call against a 5s timeout
through a new `withFetchTimeout` helper. On timeout the catch block logs
the failure and returns `null`, which `dynamicTruncate` already treats
as the "context usage unavailable" signal and falls back to the static
truncation budget. Successful responses still cache as before. The
`message.updated finish=true` invalidation hook still clears poisoned
caches on the next completed turn so retries are clean.

Tests:
- Add a never-settling `session.messages` mock with a 50 ms override via
  the new `_setContextWindowUsageFetchTimeoutMsForTesting` hook (matches
  the established `_setXxxForTesting` pattern in `opencode-http-api.ts`
  and `prompt-async-gate.ts`).
- Three new BDD cases pin the fix: (1) single caller returns null fast,
  (2) parallel concurrent callers all unblock on the same cached promise
  instead of hanging, (3) invalidate + retry rehydrates cleanly.
- All 8 pre-existing tests in the file still pass (happy paths, cache
  reuse, invalidation, env/model fallback).

Verification:
- `bun test src/shared/dynamic-truncator.test.ts` -- 11 pass.
- `bun test src/shared/prompt-async-route-audit.test.ts` -- 6 pass
  (added log import, no raw prompt route added).
- `bun test` (full suite) -- 7009 pass, 1 skip, 1 pre-existing flake in
  `closeTmuxPane` mock.module test (reproduces on dev without this
  change; isolated run passes).
- `bun run typecheck` -- clean.
- `bun run build` -- clean (esm bundle + tsc + schema).
- Manual harness `.debugging/manual-qa.ts` (uncommitted) drives the same
  shape as the real hook chain and resolves the hang scenario in 51 ms.
2026-05-17 03:51:56 +09:00
YeonGyu-Kim 2613de522f fix(prompt-async-gate): timeout isSessionActive to prevent infinite hang on stale SDK status
- Wrap isSessionActive in withDispatchTimeout (capped at 5s) so a
  stuck OpenCode SDK status() call cannot block internal prompts forever.
- Catch the timeout and treat session as inactive so the prompt can
  proceed rather than hanging indefinitely.
- Add regression test: session.status that never resolves now times out
  and allows dispatch instead of hanging the test (and production).

Refs: AGENTS.md internal-message-injection safety note
2026-05-17 03:41:08 +09:00
YeonGyu-Kim 169e61f775 test(audit): allowlist build-team-idle-wake-hint-client.ts in prompt route audit
Red: a43215f24 introduced plugin/build-team-idle-wake-hint-client.ts which
    accesses session.promptAsync for method binding. The audit test flagged it
    as a raw prompt route offender, breaking CI on dev.

Green: Add the narrow client facade to RAW_PROMPT_ALLOWLIST with the same
       justification pattern used for event.ts and recover-unavailable-tool.ts.
       The facade binds SDK methods back to the Session instance and performs
       no direct dispatch itself; all downstream calls flow through the shared
       prompt-async gate.

Verification: bun test src/shared/prompt-async-route-audit.test.ts passes
(6 pass, 0 fail, offenders list empty).
2026-05-17 03:10:02 +09:00
YeonGyu-Kim 25d8054192 Merge pull request #4074 from code-yeongyu/fix/delegate-task-spawn
fix(delegate-task): start child prompts reliably
2026-05-17 01:00:03 +09:00
YeonGyu-Kim 761f682add refactor(delegated-bootstrap): accept optional system and tools
Add optional system and tools fields to DelegatedChildSessionBootstrap
so callers can stash the original delegated context alongside retry
parts. Backward compatible - existing callers stay unchanged.
2026-05-17 00:08:19 +09:00
YeonGyu-Kim b5992b13ec test(shared): stabilize port utility interface check 2026-05-16 18:28:33 +09:00
YeonGyu-Kim cdac0d69bb fix(workspace): report only the active notepad change 2026-05-16 18:12:50 +09:00
YeonGyu-Kim 240a4a17ad fix(workspace): harden omo migration review issues 2026-05-16 18:02:54 +09:00
YeonGyu-Kim f10f796318 fix(workspace): keep omo and legacy rules compatible 2026-05-16 17:41:49 +09:00
YeonGyu-Kim 36e373cdbb feat(workspace): point planning guardrails at omo 2026-05-16 17:41:35 +09:00
YeonGyu-Kim 5dca1a5742 feat(workspace): migrate legacy sisyphus state to omo 2026-05-16 17:39:54 +09:00
YeonGyu-Kim 982fa81367 fix(delegate-task): start child prompts reliably
Preserve delegated child prompt/bootstrap metadata for early runtime fallback before OpenCode has persisted the first user turn. Bind prompt gate calls to the SDK session receiver and keep completed background task lookup visible across plugin manager instances.
2026-05-16 16:27:56 +09:00
YeonGyu-Kim a20540579e Merge pull request #4068 from code-yeongyu/feat/pre-publish-fix-v420
v4.2.0: pre-publish review fixes (BLOCKER-1..3, HIGH-5..10, MID-11/12)
2026-05-16 14:50:54 +09:00
YeonGyu-Kim eba17441cf test(mock-module-audit): require lifecycle cleanup for mock.module
New AST-based audit walks all *.test.ts files under src/ and asserts every mock.module(...) call is paired with cleanup. Existing offenders are documented in MOCK_MODULE_LIFECYCLE_ALLOWLIST with TODO references.

Closes HIGH-10
2026-05-16 02:13:11 +09:00
YeonGyu-Kim 102d067022 fix(model-suggestion-retry): release reservation on async error path
The promptWithModelSuggestionRetry async variant did not release the
post-dispatch reservation when the wrapped promptAsync threw. Callers
that immediately retry (such as sendSyncPrompt error toast paths) hit
the gate as reserved and surfaced 'promptAsync skipped by gate: reserved'
instead of the underlying error.

Mirrors the existing sync variant fix from ff1b15d53.

Closes regression introduced by BLOCKER-2 hardening
2026-05-16 01:56:04 +09:00
YeonGyu-Kim 4848017219 test(mock-module-audit): require lifecycle cleanup for mock.module
Walk all test files, parse with TypeScript Compiler API, assert every
mock.module(path, factory) invocation has a paired afterEach/afterAll
cleanup. Existing offenders are allowlisted with TODOs for v4.2.1 work.

Closes H10
2026-05-16 01:37:28 +09:00
YeonGyu-Kim c096a596eb test(mock-module-audit): require lifecycle cleanup for mock.module
Closes H10

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-16 01:35:10 +09:00
YeonGyu-Kim ff1b15d533 fix(model-suggestion-retry): release reservation before retry attempt
After BLOCKER-2's post-dispatch hold landed (the gate now keeps the
reservation through the hold window regardless of whether the dispatch
threw), the synchronous retry path inside promptSyncWithModelSuggestionRetry
hit 'reserved' on its own second attempt because the first attempt's
post-dispatch hold was still active.

The first attempt's failure is ProviderModelNotFoundError, which is a
synchronous SDK rejection - the prompt never reached the server, so
there is no durable session state worth protecting from a duplicate
injection. Release the post-dispatch reservation hold explicitly before
the suggested-model retry so the second attempt can dispatch immediately.

Fixes test regression introduced by the gate hardening (BLOCKER-2 fix).
2026-05-16 00:53:15 +09:00
YeonGyu-Kim 8c4cc09de7 test(prompt-async-route-audit): migrate to TypeScript AST walker
Replaces the previous regex-based audit (6 line-prefix patterns) with a
TypeScript Compiler API AST walker that detects raw client.session.prompt
and client.session.promptAsync access in any access shape:
- direct call (existing): client.session.promptAsync(...)
- property access reference: const x = client.session.promptAsync
- bracket access: client['session']['promptAsync']
- optional chaining: client.session?.promptAsync
- type cast aliasing: (client.session as { promptAsync }).promptAsync
- destructuring: const { promptAsync } = client.session

RAW_PROMPT_ALLOWLIST captures two legitimate callers that route through
the gate but reference promptAsync as a property value:
- src/plugin/event.ts wires a client facade for team-idle-wake-hint
- src/hooks/session-recovery/recover-unavailable-tool.ts guards capability
before dispatching through promptAsyncAfterSessionIdle.

Each allowlist entry carries a justification string so future contributors
understand why the exception exists.

Closes HIGH-5

Co-authored-by: audit-ast (deep / gpt-5.3-codex high)
2026-05-16 00:49:50 +09:00
YeonGyu-Kim b333a52800 fix(prompt-async-gate): add dispatch timeout, shared runner, harden prefix release
BLOCKER-1 (dispatch deadlock): wrap session.promptAsync / session.prompt in
withDispatchTimeout() that uses Promise.race with a default 30s timeout.
Stalled upstream responses no longer hold the reservation forever.

BLOCKER-2 (post-dispatch failure released too early): collapse the
holdReservationAfterDispatch flag into a dispatchAttempted state so the
post-dispatch hold runs in the finally block regardless of whether
promptAsync resolved or threw. AGENTS.md's documented race window where
promptAsync 'returns before durably accepted, later failures arrive as
session.error' is now covered.

HIGH-6 (sync/async protocol duplicated): extract dispatchAfterSessionIdle
internal runner. promptAsyncAfterSessionIdle and promptAfterSessionIdle
become thin wrappers passing client.session.promptAsync vs prompt as
the dispatch callback. Future reservation semantics fixes apply once.

HIGH-7 (releasePromptAsyncReservation prefix foot-gun, partial): tighten
reservationSourceMatches to require prefix strings to end in ':' so
release cannot accidentally free reservations whose source merely starts
with the same identifier characters. Symbol token verification is still
internal-only as the audit invariant prevents external callers from
bypassing the gate.

Closes BLOCKER-1, BLOCKER-2, HIGH-6
Refs HIGH-7 (prefix hardened; token-required release deferred to follow-up)

Co-authored-by: gate-correctness (deep / gpt-5.3-codex high)
2026-05-16 00:49:28 +09:00
YeonGyu-Kim d02cca4422 test(tmux): align pane replace placeholder expectations 2026-05-16 00:18:33 +09:00
YeonGyu-Kim 54a7256a71 test(tmux): align placeholder command expectations 2026-05-16 00:14:06 +09:00
Disaster-Terminator 91f1cf5fbc fix(tmux): pin pane commands to /bin/sh 2026-05-16 00:08:51 +09:00
Disaster-Terminator 8c5ca73634 fix(tmux): sweep suffixed stale isolated sessions 2026-05-16 00:07:57 +09:00
Disaster-Terminator 0c8e546c57 fix(tmux): support manager-scoped isolated session names 2026-05-16 00:06:58 +09:00
Disaster-Terminator c63108d55b fix(tmux): track placeholder panes before attach readiness 2026-05-16 00:02:02 +09:00
Disaster-Terminator 688bb551b2 fix(tmux): defer subagent attach until pane focus 2026-05-15 23:57:07 +09:00
YeonGyu-Kim ad10450b5c Merge pull request #3841 from Momentum96/fix/background-manager-tmux-ordering 2026-05-15 23:35:29 +09:00
YeonGyu-Kim c580b8f2ce fix(session): ignore internal synthetic turns 2026-05-15 23:16:05 +09:00
Momentum96 efb862ce9f fix(tmux): prefer real tmux when session env exists 2026-05-15 23:12:56 +09:00
YeonGyu-Kim 3e9b125fd5 test(runtime-fallback): cover localized provider errors 2026-05-15 22:25:20 +09:00
wjiuxing c206b16865 feat: add Chinese error patterns to model-error-classifier 2026-05-15 22:23:06 +09:00
YeonGyu-Kim 27788b4ab0 fix(session-recovery): audit raw prompt aliases 2026-05-15 21:53:05 +09:00
YeonGyu-Kim 2bd4944bad fix(prompt-gate): scope reservation releases 2026-05-15 21:52:29 +09:00
YeonGyu-Kim e66d60f4c5 Merge pull request #3773 from cailgarrisk-collab/fix/glm-rate-limit-fallback-statuscode
fix(model-fallback): add HTTP statusCode check for GLM rate limit fallback
2026-05-15 20:04:22 +09:00
YeonGyu-Kim 3c7d1299a7 Revert "Merge pull request #3825 from tw-yshuang/fix/delegated-child-session-early-failure-fallback"
This reverts commit cd33f3a397, reversing
changes made to 521c99cf0b.
2026-05-15 19:13:48 +09:00
YeonGyu-Kim cd33f3a397 Merge pull request #3825 from tw-yshuang/fix/delegated-child-session-early-failure-fallback
fix(delegate-task): harden child-session first-prompt fallback recovery
2026-05-15 19:06:43 +09:00
YeonGyu-Kim a02686e729 test(ci): remove suite-order mock coupling 2026-05-15 18:21:04 +09:00
YeonGyu-Kim 8dcbccf063 fix(tmux): inject pane action dependencies 2026-05-15 17:54:43 +09:00
YeonGyu-Kim 462b55efc7 Merge pull request #4040 from code-yeongyu/cleanup/typescript-ai-slop-20260515
Refactor TypeScript cleanup patterns
2026-05-15 16:42:10 +09:00
YeonGyu-Kim a9886ccbb7 refactor(plugin): remove metadata assertions
Guard optional plugin metadata and pane identifiers before passing them to cleanup and warning paths.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-15 16:31:13 +09:00
YeonGyu-Kim d8f52aae7f test: run suite without split runner 2026-05-15 16:26:57 +09:00
YeonGyu-Kim 9f6d0d2281 docs(agents-md): refresh hierarchical knowledge base for v4.1.2
- Bump root AGENTS.md header: 2026-05-14 → 2026-05-15, commit 5ffbe0e2453a740636, release v4.1.1 → v4.1.2
- Update file counts: 2034 (1337+697) → 2041 (1340+701), LOC ~292k → ~294k
- Fix STRUCTURE: openclaw lives at src/openclaw/ (not src/features/); list more accurate feature modules in the parenthetical
- Clarify interactive_bash gate: tmux binary on PATH via isInteractiveBashEnabled() (not 'tmux enabled')
- Fix docs/reference/features.md hook counts: Tool Guard 14→16, Total base 52→54, total with team-mode 59→61
- Bump 'Generated' date on all 43 subdir AGENTS.md files to 2026-05-15
- Preserve promptAsync injection cautions verbatim (per request)
2026-05-15 13:48:11 +09:00
YeonGyu-Kim c2aa180e7e fix(prompt-gate): pin duplicate prompt dispatches
Keep prompt reservations briefly after successful dispatch so rapid idle/message/error transitions cannot inject the same follow-up twice.

Route all production session prompt calls through the shared gate, restore skipped background resume state, release holds after abort/recovery paths, and preserve Ralph/ULW loop state when a dispatch is deferred.

Add regression coverage for session routing, static prompt route auditing, team-mode live messaging, model suggestion retries, call-omo-agent reuse, background parent wakes, runtime fallback, compaction recovery, Atlas, and Ralph/ULW loops.
2026-05-15 13:19:10 +09:00