Skip to content

fix(cli): prevent stalled agent streams - #12249

Merged
iscekic merged 13 commits into
mainfrom
fix/subagent-session-hang
Jul 21, 2026
Merged

fix(cli): prevent stalled agent streams#12249
iscekic merged 13 commits into
mainfrom
fix/subagent-session-hang

Conversation

@iscekic

@iscekic iscekic commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Replace the default AI SDK chunk timeout with a Kilo-owned, tool-aware model-stream idle watchdog. Stalled root and child streams still return control after 60 seconds, while local commands and foreground subagents may run longer without being aborted. Explicit request, model, agent, and provider overrides continue to win, and chunkTimeout: false disables the watchdog.

Context

AI SDK merges chunkMs into the abort signal supplied to local tools, so the previous implementation canceled a parent and its foreground child after exactly 60 seconds. The watchdog now observes raw stream events, pauses while non-provider-executed tools are active, resumes after their results, and aborts the underlying provider stream only on genuine inactivity.

@iscekic iscekic self-assigned this Jul 15, 2026
Comment thread packages/opencode/src/kilocode/session/llm.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Reviewed the incremental changes since the last automated review (commit c42e3c578d). This revision only touches packages/opencode/test/kilocode/session-stream-watchdog.test.ts.

It adds an explicit config.shell: "bash" override to tests A and C, fixing a real latent issue: without it, the bash tool's defaultShell() falls back to cmd.exe on Windows, which can't execute the POSIX bashGate script (touch, [ -f ... ], while ... done), so touch silently failed and the readiness marker never appeared. Test B doesn't need the override since it never runs a real bash command. With the root cause fixed, the compensating Windows-only timeout margins are correctly reduced: waitForFile's win32 duration from 30s to 15s, and the per-test timeout from a win32-only 90s back down to a flat 30s for both tests. The updated comments accurately describe the fix and match the code (shell: "bash", src/shell/shell.ts login-shell behavior, packages/core/src/tool/bash.ts fallback).

No production code changed in this increment, and the change is well-scoped to CI reliability on Windows. No new correctness, security, or fork-hygiene issues found in the changed lines.

The previously flagged items (global 60s watchdog default discussion in llm.ts, swallowed rm cleanup error in session-prompt-permission-refresh.test.ts) are on files not touched in this increment and are not carried forward as open findings.

Files Reviewed (1 file changed since last review)
  • packages/opencode/test/kilocode/session-stream-watchdog.test.ts - Adds shell: "bash" override for tests A/C to fix Windows cmd.exe fallback breaking POSIX bash syntax; reduces compensating Windows-only timeout margins now that the root cause is fixed
Previous Review Summaries (6 snapshots, latest commit c42e3c5)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit c42e3c5)

Status: No Issues Found | Recommendation: Merge

Reviewed the incremental changes since the last automated review (commit 50a311cc3e). This revision only touches packages/opencode/test/kilocode/session-stream-watchdog.test.ts: it revises the explanatory comment for the Windows waitForFile margin (correctly attributing the slowdown to git-bash's login-shell startup rescanning PATH, matching the bash -l -c invocation in src/shell/shell.ts) and bumps the Windows-only timeouts from 15s/60s to 30s/90s to give that startup more headroom. No production code changed in this increment, and the change is well-scoped to CI reliability on Windows.

No new correctness, security, or fork-hygiene issues found in the changed lines. The previously flagged items (global 60s watchdog default discussion, swallowed rm cleanup error in session-prompt-permission-refresh.test.ts) remain resolved/outdated discussion threads per GitHub and are not carried forward as open findings.

Files Reviewed (1 file changed since last review)
  • packages/opencode/test/kilocode/session-stream-watchdog.test.ts - Comment/timeout adjustment for Windows git-bash login-shell startup latency, no logic changes elsewhere

Previous review (commit 50a311c)

Status: No Issues Found | Recommendation: Merge

Reviewed the incremental changes since the last automated review (commit 7e7a3d583c). This revision only touches packages/opencode/test/kilocode/session-stream-watchdog.test.ts: it adds a posixPath helper that normalizes path.join-produced paths to forward slashes before embedding them in the git-bash bashGate script, fixing a real bug where backslash-separated Windows paths could be silently mangled inside a double-quoted bash string (backslash is an escape character there). The surrounding comment explaining the fix is clear and the change is well-scoped to the test file. No production code changed in this increment.

No new correctness, security, or fork-hygiene issues found in the changed lines. The previously flagged items (global 60s watchdog default discussion, swallowed rm cleanup error in session-prompt-permission-refresh.test.ts) remain resolved/outdated discussion threads per GitHub and are not carried forward as open findings.

Files Reviewed (1 file changed since last review)
  • packages/opencode/test/kilocode/session-stream-watchdog.test.ts - Windows path-normalization fix for the bash gate script, no logic changes elsewhere

Previous review (commit 7e7a3d5)

Status: No Issues Found | Recommendation: Merge

Reviewed the incremental changes since the last automated review (commit ca8950a26c). This revision only touches the integration test file: it doubles two waitForFile polling timeouts and two it.live test timeouts on Windows (process.platform === "win32") to account for git-bash's slower startup on Windows CI runners, mirroring the existing platform-aware pattern in test/kilocode/background-process.test.ts, plus minor whitespace/formatting cleanup. No production code changed in this increment.

No new correctness, security, or fork-hygiene issues found in the changed lines. The previously flagged items (global 60s watchdog default discussion, swallowed rm cleanup error in session-prompt-permission-refresh.test.ts) remain resolved/outdated discussion threads per GitHub and are not carried forward as open findings.

Files Reviewed (1 file changed since last review)
  • packages/opencode/test/kilocode/session-stream-watchdog.test.ts - Windows CI timeout adjustments only, no logic changes

Previous review (commit ca8950a)

Status: No Issues Found | Recommendation: Merge

Reviewed the incremental changes since the last automated review (commit 4f89293a). The PR has since been substantially reworked: the chunk-idle timeout is no longer passed through to the AI SDK's built-in timeout.chunkMs; instead, KiloLLM now wraps the raw provider fullStream with a Kilo-owned per-event idle watchdog (resolveIdleMs / watchdogAsyncIterable) that resets on every stream event and suspends while non-provider-executed tool calls are in flight. This directly addresses the blocking regression flagged by human reviewers on the previous revision (subagent/tool-heavy turns being aborted after exactly 60s), and the final commit additionally fixes a cancellation hang by replacing the async-generator wrapper with a hand-rolled AsyncIterator whose return() doesn't wait on an abandoned stalled pull.

The new logic is covered by extensive unit tests (test/kilocode/session/session-stream-watchdog.test.ts) and integration tests (test/kilocode/session-stream-watchdog.test.ts) exercising root/child/foreground-subagent long-running-tool scenarios, disablement via chunkTimeout: false, and the cancellation-hang regression. No new correctness, security, or fork-hygiene issues found in the changed lines.

The previous suggestion about the swallowed rm cleanup error in session-prompt-permission-refresh.test.ts and the WARNING about the global 60s default remain only as resolved discussion threads (GitHub reports both as outdated/no longer attached to current diff lines) and are not carried forward as open findings.

Files Reviewed (9 files changed since last review)
  • .changeset/chunk-idle-timeout-default.md - updated to describe the tool-aware watchdog and chunkTimeout: false disable knob
  • packages/core/src/v1/config/provider.ts - chunkTimeout schema now accepts false to disable the watchdog
  • packages/opencode/src/kilocode/session/llm.ts - new resolveIdleMs / watchdogStream / watchdogAsyncIterable watchdog implementation
  • packages/opencode/src/session/llm.ts - wires the watchdog around the raw AI SDK fullStream in place of the old timeout.chunkMs option
  • packages/opencode/test/kilocode/session-stream-watchdog.test.ts - new integration tests (root/foreground-child/nested-child long tool calls)
  • packages/opencode/test/kilocode/session/llm.test.ts - updated unit tests for resolveIdleMs
  • packages/opencode/test/kilocode/session/session-stream-watchdog.test.ts - new unit tests for the watchdog stream wrapper, including the cancellation-hang regression test
  • packages/sdk/js/src/v2/gen/types.gen.ts - regenerated to reflect the schema change
  • packages/sdk/openapi.json - regenerated to reflect the schema change

Previous review (commit 4f89293)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts 1016 New cleanup finalizer swallows rm errors with .catch(() => {}), inconsistent with sibling finalizers in the same file that let cleanup failures surface
Files Reviewed (1 file changed since last review)
  • packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts - 1 issue (adds a cleanup finalizer for a manually-created temp skill dir, widens awaitWithTimeout/pollWithTimeout timeouts to 15s, and doubles the overall test timeout to 30s to stabilize flaky global-skill permission tests)

Fix these issues in Kilo Cloud

Previous review (commit 6d48404)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/opencode/src/kilocode/session/llm.ts 31 New global 60s default chunk-idle timeout applies to every agent/subagent stream; may false-positive abort slow/reasoning models that go silent >60s before emitting output
Files Reviewed (3 files)
  • .changeset/chunk-idle-timeout-default.md - no issues
  • packages/opencode/src/kilocode/session/llm.ts - 1 issue
  • packages/opencode/test/kilocode/session/llm.test.ts - no issues

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5 · Input: 34 · Output: 8.6K · Cached: 804.2K

Review guidance: REVIEW.md from base branch main

Comment thread packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts Outdated
@shssoichiro

shssoichiro commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Goat. I was getting ready to tackle exactly this problem because I've been seeing the issue more frequently with GPT 5.6, but luckily Kilo found this PR.

@shssoichiro

shssoichiro commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

This change appears to be having a very unwanted side effect. Running a subagent causes the session to be killed after 60 seconds. This is a huge breaking bug and will need to be fixed before this is merged.

Here is the information that my agent was able to determine which will hopefully help guide a fix:

Diagnosis

High confidence: commit c6215488e3 (fix(cli): prevent stalled agent streams) introduced the regression.

It changed the default AI SDK stream configuration from no chunk timeout to:

timeout: { chunkMs: 60_000 }

Failure chain

  1. The parent model emits a task tool call.
  2. The task starts a foreground subagent.
  3. While the subagent runs, the parent AI SDK stream emits no chunks.
  4. AI SDK’s chunk timer remains active during local tool execution.
  5. After 60 seconds, AI SDK aborts the parent tool’s AbortSignal.
  6. TaskTool propagates that abort to the child session.
  7. The interrupted task is finalized as an orphan, producing:
    loop exit with orphaned interrupted tool.

Relevant paths:

  • Timeout introduced at packages/opencode/src/kilocode/session/llm.ts:10,26-34
  • Applied to every normal AI SDK stream at packages/opencode/src/session/llm.ts:353-392
  • AI SDK combines the chunk controller into the signal passed to tools at ai/src/generate-text/stream-text.ts:525-543
  • Timer resets for stream chunks at stream-text.ts:1497-1518,1735
  • Timer is only cleared after tool execution finishes at stream-text.ts:2078-2080
  • Child cancellation propagation is in packages/opencode/src/tool/task.ts:405-439

Timing evidence

The apparent 14.58s delay is measured from the child’s last model request, not from task creation. Parsing the complete local logs produced:

ses_0980620...  60s
ses_0981e3f...  60s
ses_0981a45...  60s
ses_097fe75...  60s
ses_097fe75...  60s

This includes Kilo/Anthropic and OpenAI models. The parallel subagents launched during this investigation were both created at 22:59:19 and canceled together at 23:00:19.

Why cancel appears twice

TaskTool has overlapping cancellation mechanisms:

  • An abort event listener calls ops.cancel(child).
  • Interrupt cleanup also calls cancellation and cancels the background-job wrapper.
  • The running child job has its own onInterrupt(() => ops.cancel(child)).

Therefore, the duplicate session.prompt ... cancel entries are a cleanup fan-out symptom, not the initiating cause.

Alternatives considered

Candidate Assessment
Network/provider failure Ruled out by exact 60-second lifetime across providers and machines
A configured 15-second provider timeout Ruled out; no matching loaded config and actual lifetime is 60 seconds
Manual/session abort No initiating parent cancellation event; timing repeats exactly
Background-job timeout Foreground background.wait() has no timeout
OS process/resource killing Logs show application-level AbortSignal, not a process signal/OOM
Orphan cleanup logic Downstream consequence only
New default chunk timeout Confirmed by commit and timing evidence

The existing timeout unit test passes (7 pass, 0 fail) but only verifies configuration output. It does not cover a tool running longer than chunkMs.

@marius-kilocode marius-kilocode left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iscekic I think @shssoichiro might be right here.

Blocking: the reviewer is correct. The new default at packages/opencode/src/kilocode/session/llm.ts:10,31 is passed to AI SDK streamText at packages/opencode/src/session/llm.ts:392. In AI SDK 6.0.168, chunkMs:
Starts/reset on normalized stream chunks.
Is merged into the abort signal supplied to local tools.
Remains active while the tool is executing.
Is only cleared after the tool result arrives.
A parent receives a task tool-call chunk and then waits silently while the foreground subagent runs. After exactly 60 seconds, the timer aborts the tool signal. Kilo maps that signal directly to Tool.Context.abort at packages/opencode/src/session/tools.ts:61-64, and TaskTool cancels the child at packages/opencode/src/tool/task.ts:405-438. Cleanup then marks the task interrupted at packages/opencode/src/session/processor.ts:1069-1100, producing the reported orphaned-tool exit.

This affects any local tool taking more than 60 seconds without preliminary output, not only subagents.

High: the default cannot reliably be disabled through documented provider configuration. chunkTimeout only accepts positive integers at packages/core/src/v1/config/provider.ts:118-121. Although KiloLLM.timeout() treats 0 as disabled, provider..options.chunkTimeout: 0 is rejected by the public schema. Users can only raise the timeout, not disable the new default through the documented provider setting.

High: the test only verifies object construction. packages/opencode/test/kilocode/session/llm.test.ts:34 checks that { chunkMs: 60_000 } is returned. It does not exercise a tool or subagent running longer than chunkMs, which is exactly where the regression occurs.

@marius-kilocode

marius-kilocode commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Maybe we need a bit more complexity to solve this. Something that observes the idle state?

@iscekic

iscekic commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

Hey, this was my overnight bot submitting the PR. I think both of you are right, I'll play with this a bit more today and push an update.

iscekic added 2 commits July 16, 2026 17:31
…hang

# Conflicts:
#	packages/opencode/test/kilocode/session-prompt-permission-refresh.test.ts
@iscekic
iscekic requested a review from marius-kilocode July 16, 2026 16:47
@iscekic

iscekic commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

(bot) Addressed the blocking regression on head 5ae487b523. The default no longer uses AI SDK timeout.chunkMs; Kilo now observes raw stream events, suspends the idle watchdog while local Bash/TaskTool calls run, resumes after settlement, and accepts chunkTimeout: false. Added deterministic unit coverage plus real root-command, root-child, and nested child-command regressions. Actual source CLI E2E also passed stalled-stream retry and all three long-running tool/session flows.

An async generator's return() cannot preempt an in-flight internal
await; when suspended mid-await it only applies once that await
settles on its own. For a genuinely stalled stream that await never
settles, so interrupting a session mid-stream (e.g. aborting while a
local tool call is pending) hung instead of cancelling.

Replace the generator with a hand-rolled AsyncIterator whose return()
runs immediately and forwards to the source's return() without
waiting on any outstanding pull, matching how interruption already
behaves for the unwrapped upstream iterator.

Fixes CI failures in test/session/processor-effect.test.ts and
test/session/prompt.test.ts that hung/timed out on this branch.
@iscekic

iscekic commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

(bot) Pushed ca8950a26c, which fixes a real regression the CI failures were flagging: the idle watchdog's async-generator implementation couldn't propagate cancellation (return()) while suspended on a stalled/never-resolving pull, so interrupting a session mid-stream (e.g. aborting while a local tool call was pending) hung instead of completing. Replaced it with a hand-rolled AsyncIterator whose return() runs immediately and forwards to the source, matching how interruption already worked for the unwrapped upstream stream before this PR. Verified test/session/processor-effect.test.ts and test/session/prompt.test.ts (both previously hanging on this branch) now pass, plus the full non-CLI and CLI unit suites.

Also replied in the open discussion thread on packages/opencode/src/kilocode/session/llm.ts with the exact doc location backing the 15–30s recommendation cited earlier, and why the 60s global default doesn't affect actively-reasoning models — the watchdog resets on every raw stream event, not just text.

@marius-kilocode could you take another look and re-review when you have a chance?

iscekic added 4 commits July 16, 2026 22:31
git-bash on Windows CI runners spawns and writes the readiness marker
file noticeably slower than the Unix shells this suite otherwise
runs under, so tests A and C's 5s file-poll and 30s scenario timeout
were too tight there and failed with 'readiness marker never
appeared' even though the tool was already running. Double both on
win32, matching the existing platform-aware timeout doubling in
test/kilocode/background-process.test.ts.
path.join() yields backslash-separated paths on Windows. Embedded
inside a double-quoted git-bash string, a literal backslash is an
escape character, so the ready/release marker paths could resolve to
the wrong file (or nothing) instead of erroring, making 'touch' and
the '[ -f ... ]' poll silently miss each other. Normalize to forward
slashes before interpolating into the script; git-bash/MSYS accept
them natively on every platform this suite runs on.

This is the actual root cause of the 'readiness marker never
appeared' failures on Windows shards; the previous commit's timeout
doubling was only masking symptoms.
The production bash tool runs every command through a login shell
(bash -l -c ..., src/shell/shell.ts) so ~/.bashrc/aliases behave like
an interactive terminal. Git for Windows' login-shell startup rescans
the full Windows PATH and is known to take several seconds on CI
hardware, well past the previous 15s/60s Windows margins, before the
script's own touch ever runs. Extend waitForFile to 30s and the two
affected scenario timeouts to 90s on win32.
…n Windows

Root cause, finally isolated: without a config-level shell field, the
bash tool defaultShell() falls back to cmd.exe on Windows (see
packages/core/src/tool/bash.ts). cmd.exe cannot run bashGate POSIX
syntax (touch, test -f, while/done), so touch failed instantly and
silently and the readiness marker never appeared - no timeout was
ever going to fix that, which is why the previous two commits margin
increases did not help. Set shell to bash in tests A and C config so
the bash tool resolves real git-bash via src/shell/shell.ts on
Windows, and drop the speculative timeout inflation back to the
original values plus a small, now-accurate margin for git-bash slower
login-shell startup.
@iscekic

iscekic commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

(bot) Final status on head c128eefdf8:

CI: all checks green, including every previously failing job (test (linux), unit (linux), unit (linux, 1/2), unit (linux, 2/2), unit (macos), unit (windows, 2/4), unit (windows, 3/4)), plus all four Windows shards under the current balanced sharding.

Root causes fixed:

  1. packages/opencode/src/kilocode/session/llm.ts — the idle watchdog's async-generator return() couldn't preempt an in-flight await on a stalled pull, so interrupting a session mid-stream while a local tool call was pending could hang instead of completing. Replaced with a hand-rolled AsyncIterator whose return() runs immediately. This is what fixed test/session/processor-effect.test.ts and test/session/prompt.test.ts, which were timing out/hanging on this branch.
  2. test/kilocode/session-stream-watchdog.test.ts (new in this PR) — its Windows-only "readiness marker never appeared" failures were caused by the bash tool falling back to cmd.exe on Windows (no explicit shell in the test config), which can't run the POSIX bashGate script. Fixed by setting shell: "bash".

Marius's concern: replied in the discussion thread on llm.ts with the exact doc location (packages/kilo-docs/pages/code-with-ai/agents/custom-models.md) backing the 15–30s per-provider recommendation, and why the 60s global default doesn't affect actively-reasoning models (watchdog resets on every raw stream event). Requested re-review in a separate comment — his human re-approval is the one item I cannot do myself.

Local E2E: verified directly against the PR-branch CLI build (dev-local run against this worktree's backend): a subagent-spawning session and a plain session both completed and streamed to completion without hanging — the exact failure mode this PR fixes — including one run where the CLI paused on a real permission prompt mid-turn and resumed/completed normally afterward. Mobile mirroring of this specific local worktree's CLI sessions showed "Session terminated" for reasons unrelated to this PR (session-ingest's Durable Object reported cliSockets: 0 for this fresh worktree stack even while the CLI process was demonstrably alive and responsive in its own pane); mirroring worked normally for other worktrees' sessions in the same mobile app, isolating this to a local dev-environment connectivity gap rather than a regression from this diff.

Outstanding: @marius-kilocode's human re-approval on the current head.

@iscekic
iscekic enabled auto-merge (squash) July 20, 2026 22:26
@iscekic
iscekic merged commit cd205d8 into main Jul 21, 2026
30 checks passed
@iscekic
iscekic deleted the fix/subagent-session-hang branch July 21, 2026 16:16
iscekic added a commit that referenced this pull request Jul 21, 2026
…#12438)

PR #12249 was reverted (open PR #12435) because its test imported three
modules that v1.17.4 compat (2855ebb) removed/moved:

- Reference/RepositoryCache from ../../src/reference/* (now packages/core)
- Ripgrep from @opencode-ai/core/filesystem/ripgrep

Re-land the production fix by pointing RepositoryCache and Ripgrep at
@opencode-ai/core/{repository-cache,ripgrep} and dropping the now-gone
Reference.defaultLayer from the layer stack, matching the current
full-stack sibling (session-prompt-compaction-safety.test.ts).

Typecheck passes; all 3 watchdog tests pass.
crisbanh688 pushed a commit to crisbanh688/kilocode that referenced this pull request Jul 24, 2026
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
* fix(cli): prevent stalled agent streams

* docs(cli): clarify stream timeout scope

* test(cli): stabilize global skill permission timing

* fix(cli): make stream timeout tool-aware

* fix(cli): let the idle watchdog cancel a stalled pull immediately

An async generator's return() cannot preempt an in-flight internal
await; when suspended mid-await it only applies once that await
settles on its own. For a genuinely stalled stream that await never
settles, so interrupting a session mid-stream (e.g. aborting while a
local tool call is pending) hung instead of cancelling.

Replace the generator with a hand-rolled AsyncIterator whose return()
runs immediately and forwards to the source's return() without
waiting on any outstanding pull, matching how interruption already
behaves for the unwrapped upstream iterator.

Fixes CI failures in test/session/processor-effect.test.ts and
test/session/prompt.test.ts that hung/timed out on this branch.

* test(cli): give Windows more time for the watchdog integration bash gate

git-bash on Windows CI runners spawns and writes the readiness marker
file noticeably slower than the Unix shells this suite otherwise
runs under, so tests A and C's 5s file-poll and 30s scenario timeout
were too tight there and failed with 'readiness marker never
appeared' even though the tool was already running. Double both on
win32, matching the existing platform-aware timeout doubling in
test/kilocode/background-process.test.ts.

* test(cli): use POSIX-style paths in the watchdog bash gate script

path.join() yields backslash-separated paths on Windows. Embedded
inside a double-quoted git-bash string, a literal backslash is an
escape character, so the ready/release marker paths could resolve to
the wrong file (or nothing) instead of erroring, making 'touch' and
the '[ -f ... ]' poll silently miss each other. Normalize to forward
slashes before interpolating into the script; git-bash/MSYS accept
them natively on every platform this suite runs on.

This is the actual root cause of the 'readiness marker never
appeared' failures on Windows shards; the previous commit's timeout
doubling was only masking symptoms.

* test(cli): extend Windows margins further for the watchdog bash gate

The production bash tool runs every command through a login shell
(bash -l -c ..., src/shell/shell.ts) so ~/.bashrc/aliases behave like
an interactive terminal. Git for Windows' login-shell startup rescans
the full Windows PATH and is known to take several seconds on CI
hardware, well past the previous 15s/60s Windows margins, before the
script's own touch ever runs. Extend waitForFile to 30s and the two
affected scenario timeouts to 90s on win32.

* test(cli): give the watchdog bash gate an explicit shell so it runs on Windows

Root cause, finally isolated: without a config-level shell field, the
bash tool defaultShell() falls back to cmd.exe on Windows (see
packages/core/src/tool/bash.ts). cmd.exe cannot run bashGate POSIX
syntax (touch, test -f, while/done), so touch failed instantly and
silently and the readiness marker never appeared - no timeout was
ever going to fix that, which is why the previous two commits margin
increases did not help. Set shell to bash in tests A and C config so
the bash tool resolves real git-bash via src/shell/shell.ts on
Windows, and drop the speculative timeout inflation back to the
original values plus a small, now-accurate margin for git-bash slower
login-shell startup.
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
…Kilo-Org#12438)

PR Kilo-Org#12249 was reverted (open PR Kilo-Org#12435) because its test imported three
modules that v1.17.4 compat (7a1394e) removed/moved:

- Reference/RepositoryCache from ../../src/reference/* (now packages/core)
- Ripgrep from @opencode-ai/core/filesystem/ripgrep

Re-land the production fix by pointing RepositoryCache and Ripgrep at
@opencode-ai/core/{repository-cache,ripgrep} and dropping the now-gone
Reference.defaultLayer from the layer stack, matching the current
full-stack sibling (session-prompt-compaction-safety.test.ts).

Typecheck passes; all 3 watchdog tests pass.
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants