Skip to content

fix: route then_run through run_command - #248

Merged
liuedcson merged 1 commit into
mainfrom
fix/then-run-command-parity-20261014
Oct 14, 2026
Merged

liuedcson merged 1 commit into
mainfrom
fix/then-run-command-parity-20261014

Conversation

@MelodyVAR

Copy link
Copy Markdown
Collaborator

Fused edit commands bypassed run_command, leaving their default timeout unset and losing complete output when the Step character cap truncated it. Route then_run through the registered run_command.execute path so working directory, timeout, output retention and cancellation follow the same contract.

Preserve successful file edits when their subsequent check fails, and retain full log paths in details.thenRun.fullOutputPath. Clarify the tool description, prompt and integration docs. This restores the execution contract described in #217.

Validation:

  • 12 new command-parity regressions (8 failed before the fix).
  • 178 relevant tool, permission, SDK, workflow and prompt tests passed.
  • Full pnpm run check passed, including TypeScript and browser checks.

@liuedcson
liuedcson merged commit 7a0382b into main Oct 14, 2026
2 of 3 checks passed
Hylouis233 added a commit to Hylouis233/Step-Code that referenced this pull request Oct 15, 2026
* feat(compaction): use model-managed CLM by default

Keep canonical history while validating bounded edits to a persisted working
view. Reuse compatible actor prefixes for maintenance and retain native
compaction for failures, overflow recovery, and explicit requests.

Honor explicit projection settings and disabled auto-compaction, defer unused
maintenance after final answers, and cover default and native behavior in the
session, CLI, persistence, and usage regressions.

* fix(cli): retain explicit context projection after resource loading

Apply the CLI mode after resource discovery reloads settings, so an explicit
off selection takes precedence over the default CLM mode. Exercise the real
CLI startup path with a local streaming endpoint and keep the config unchanged.

* feat: add Full Access and simplify permission tiers

Introduce Ask, Approve for Me, and Full Access with session-only or remembered risk acknowledgment. Reset legacy permission settings to Ask and enable bounded model-error continuation in both automatic tiers. Preserve explicit tool overrides and scope consent to the active session.

Route /exit through the existing /quit shutdown path, remove duplicate extension registration, and cover permission migration, consent lifecycle, and exit behavior.

* fix(shell): discover native Git Bash on Windows instead of WSL

On Windows, a Git for Windows install in a non-default directory only puts
<Git>\cmd on PATH, so shell discovery fell through to `where bash.exe` and
picked C:\Windows\System32\bash.exe, silently running commands inside WSL.

- Look for Git Bash in Program Files, Program Files (x86),
  %LOCALAPPDATA%\Programs\Git, then relative to each git.exe on PATH, then
  native bash.exe on PATH. Resolve symlinks and drop the where.exe dependency.
- Never select the WSL launcher automatically; an explicit shellPath to it
  keeps its stdin command transport.
- Add the Git installation's bin and usr/bin to the Git Bash child PATH only.
- On Windows, pipe background command output into the log file instead of
  handing the file descriptor to MSYS, which exited 1 with an empty log.
- Generate candidates lazily so a standard install returns without scanning
  PATH on every command or permission check.

Fixes stepfun-ai#136

Co-authored-by: ZXJC-niusile <2632590648@qq.com>

* fix(shell): use Git Bash child PATH for config value commands

Config values written as `!command` were run with a direct spawnSync on the
resolved shell, bypassing spawnShellChild, so on Windows they did not get the
Git installation's bin and usr/bin on PATH. Export withGitBashPath and apply
it there too so these commands see the same tools as run_command.

* fix(skills): emit readable single-line diagnostics for SKILL.md frontmatter errors

* fix(skills): make frontmatter diagnostic hints syntax-aware

Inspect recovered YAML structure to avoid quoting hints for comments and block scalars. Escape parser control characters and tolerate absent error values. Cover both skill loaders and CLI diagnostic rendering.

* fix: stop repeating Ultracode capability reminders (stepfun-ai#241)

* fix: announce Ultracode mode only when context state changes

Keep capability guidance in the workflow tool description instead of appending a tutorial to every user turn. Announce session-mode transitions after real user input and reuse the last runtime-authored state in the active context. Preserve per-turn opt-in and reconcile missing or changed mode state before provider requests.

Cover provider-visible message order, unchanged turns, mode toggles, compaction, restricted profiles, and streaming revocation.

* fix: keep workflow opt-in guidance accurate across tool profiles

Explain unavailable workflow tools for per-turn requests and make standing consent conditional on the current tool profile. Cover restricted per-turn requests and changing tool availability without repeated state notices.

* fix: avoid persisting an unchanged retry default

Skip the native retry setter when automatic permission mode finds retry already enabled. This prevents startup from adding a redundant retry section to config.toml and preserves runtime-only CLI overrides.

---------

Co-authored-by: liuedcson <332840681+liuedcson@users.noreply.github.com>

* fix(skills): quiet startup skill conflicts for duplicated shared skill dirs

Users who run both Codex and Claude Code often have the same skills
copied into ~/.agents/skills and ~/.claude/skills. Each copy produced a
collision entry, flooding startup even though the shadowed copy is never
loaded.

- Skip a colliding skill silently when its SKILL.md is byte-identical to
  the winner, the same way symlinked duplicates are already skipped.
- Without the full resource listing, collapse remaining skill/prompt
  collisions into one line per (skipped dir, winning dir) pair; --verbose
  still lists each one. Warnings and errors are shown in full as before.

* feat: persist Ultracode mode per session (stepfun-ai#245)

* feat: persist Ultracode mode per session

Record the latest Ultracode on/off choice in the current session journal and restore it when that session is reopened. Keep new sessions opted out and avoid global or project settings.

* fix: restore Ultracode status after session reopen

---------

Co-authored-by: liuedcson <332840681+liuedcson@users.noreply.github.com>

* docs: add guidelines for platform and provider packages

Document configuration migrations, host contracts, provider adapters, telemetry, and terminal rendering.

Define dependency boundaries and module-specific validation commands.

* docs: define agent runtime and coding product boundaries

Describe Agent Loop, session, context projection, and compaction ownership.

Keep coding-agent guidance at package scope and document tools, extensions, orchestration, and Step integrations against the current implementation.

* docs: document CLI hosting and UI layer responsibilities

Define startup, mode routing, process lifecycle, and protocol output boundaries.

Document host-to-runtime-to-view dependencies, input handling, redraws, and focused validation.

* docs: establish repository and support directory guidelines

Connect module guidance through the repository architecture and ownership map.

Add documentation, script, release, and plugin conventions while preserving the existing commit attribution policy and CLAUDE.md reference.

* fix: harden workflow lifecycle, concurrency, and retries with structured errors (stepfun-ai#249)

* fix: harden workflow lifecycle, concurrency, and retries with structured errors

Supersedes stepfun-ai#247 as a single change. It keeps that PR's lifecycle, concurrency,
and error-propagation fixes, and replaces the parts that inferred behavior
from text.

Structured provider errors (no text matching for known failures)
- Add AssistantMessage.errorKind: rate_limit | quota | auth | transient |
  invalid_request. Providers set it in their catch blocks from the HTTP
  status, the provider's machine-readable error type/code (insufficient_quota,
  GoUsageLimitError, rate_limit_exceeded, overloaded_error, ...), and transport
  error codes (APIConnectionError, ECONNRESET, ...). Mid-stream error events
  (Anthropic SSE `error`, OpenAI Responses `error`/`response.failed`) now throw
  ProviderStreamError carrying the code, and ProviderRetryDelayError keeps the
  original 429 as its cause, so both stay classifiable.
- The agent loop classifies errors thrown by a provider stream and marks a
  stream that closes without a terminal event as transient.
- isRetryableAssistantError trusts errorKind when present; the old message
  patterns remain only for failures that arrive with no status or code.
- Session retries pick the separate 429 budget, and workflow congestion
  control throttles the admission window, only for errorKind === "rate_limit".
  A 400 that mentions "429" is no longer a rate limit, and a 429 whose code
  reports exhausted quota is terminal. The regex-based isRateLimitError and
  isTerminalQuotaError are removed. Subagent children forward the kind in
  their messages; workflow runners report it as WorkflowAgentRunResult.errorKind.

Intent is the model's call, not a regex
- Do not add stepfun-ai#247's counted fan-out detector (COUNTED_AGENT_LAUNCH, REFUSAL,
  META_QUESTION); ultraloop-opt-in.ts stays as on main. The detector missed
  common requests ("开10个子agent分别审查" hit the "别" in 分别, "…看看怎么回事"
  hit the "怎么" question filter, and verbs like 用/让 were not listed) while
  trying to rule out negations and questions with word lists. Only the
  explicit ultraloop/ultracode keyword, the fixed opt-in phrases, and
  /ultracode session mode are recognized by the harness; the model judges
  everything else from the existing tool descriptions. No intent guidance is
  added to the system prompt.

One admission window for subagents and workflows
- Parallel subagent batches take slots from the same process-wide admission
  window as workflow nodes, and feed its AIMD control with their model-request
  events. Running both at once can no longer exceed the host ceiling, and a
  429 seen by either lowers the window for both.
- Parallel subagent batches also honor STEP_WORKFLOW_CONCURRENCY_LIMIT, so the
  configured provider limit applies without a workflow running, and an
  admitted child holds its slot until its process exits (awaitExit), so a
  child still shutting down or cancelling cannot overlap queued work.

Content-addressed workflow resume
- resumeFromRunId replays any completed call whose prompt+options hash
  matches, independent of its sequence number. A failed, inserted, or
  reordered call (pipeline stages finish in nondeterministic order) now
  re-runs only itself instead of invalidating every later call. Identical
  calls are consumed in their original order, one cached result each.
  Nothing after a malformed journal line is trusted.
- A replayed call is journaled under the resumed run's own seq, with the
  schema-cleaned value the script received, so sequence numbers stay unique
  and chained resumes see what was consumed.
- A cached result must still satisfy the call's JSON schema, otherwise the
  call runs live. Calls with a runtime (safeParse) schema never replay: such
  schemas hash to one placeholder, so two different ones are
  indistinguishable.
- The cache closes on the first write, as ZCode's amend-resume does. The
  default runner forwards each child tool_execution_start (emitted before the
  tool's permission check and execution) and reports toolActivityObserved
  only when the child exited normally, since a killed child can lose events.
  classifyWorkflowToolEffect maps tools to none (clarify_user, find_tools),
  read (file reads/searches, search_web), or mutate (file writes, shell
  commands, and any unrecognized tool such as MCP). Completed entries record
  worldAccess. Once a live call starts a mutating tool, or a live call cannot
  vouch for its tool activity, only cached entries with worldAccess === false
  still replay; entries from older journals count as having touched the
  environment. Calls issued together before any live write, such as a resumed
  fan-out where a few nodes had failed, still replay in full.
- Resume wording in the workflow tool description, its guidance, and the
  system prompt's workflow section now describes this behavior.

Kept from stepfun-ai#247
- Workflow runtime: cancel and drain running and queued children before
  publishing a terminal state, seal terminal states, and abort the run scope
  on exit. Clamp requested concurrency to a CPU-derived ceiling (optionally
  lowered by STEP_WORKFLOW_CONCURRENCY_LIMIT) and report clamping as a notice.
  Share a process-wide admission window with a 25% cut per congestion epoch,
  recovery after four successes, a Retry-After cooldown capped at 30s, and
  60s retention of a throttled window between back-to-back workflows.
- Record parallel()/pipeline() callback failures as taskFailures (including
  nested workflows), accept promise arrays in parallel(), keep node failures
  out of the callback channel and count them once, and fail runs that return
  with unawaited operations. Report partial_failed/failed with the failed
  nodes in the final tool result.
- Node timeouts cover execution only (not queue time) and come from our own
  timer, not a substring match. Tokens a cancelled or timed-out attempt
  reports while settling are still charged. A runner that ignores
  cancellation keeps its shared slot until it settles.
- Failure recording, progress.json writes, and live-update listeners are best
  effort and cannot replace a node's real error. A failed progress write no
  longer poisons the write queue.
- Subagents: accept batches above the concurrency window (up to 1000) and
  queue the excess, wait for workflow children to exit before releasing their
  slot, mark tool results with failed or aborted children as isError, and
  report rate-limit retries and request start/success over RPC.
- Session retries: an independent 429 budget (retry.rateLimitMaxRetries) that
  honors Retry-After, falls back to jittered backoff (1s to 30s), and can be
  cancelled.
- Agent loop: honor AgentToolResult.isError. Finalize a partial assistant
  message when a provider stream throws, ends early, or fails during
  construction, without reclassifying consumer/persistence errors or emitting
  a second terminal message.
- Providers: preserve Retry-After when the delay cap rejects a wait, and treat
  an empty or non-numeric retry-after-ms as absent.

* test: make subagent concurrency checks independent of host CPU count

---------

Co-authored-by: liuedcson <332840681+liuedcson@users.noreply.github.com>

* fix: route then_run through run_command (stepfun-ai#248)

* docs: define development and testing practices

* docs: map repository structure and engineering rules

* docs: standardize English commits and pull request reviews

* fix: provision StepPage on Windows via the published bundle

provisionBuiltinPlugin() skipped win32 entirely and ensureStepPageReady()
refused to install without a healthy executable, so the built-in steppage
plugin could never set up steppage-mcp on Windows and every launch reported
"automatic installation is currently supported on macOS and Linux" (stepfun-ai#219).

Windows cannot run the POSIX installer script, so install the published
bundle directly: download steppage-mcp.mjs, write a steppage-mcp.cmd wrapper
next to it under ~/.local/bin, and verify the result. Executable probes run
through `cmd /d /c` on Windows so both bare PATHEXT lookups and explicit
.cmd files resolve on every Node runtime.

The lock, Node.js >= 20 check, credential handling and failure reporting
stay shared with the POSIX path; only the download-and-execute step is
platform-specific. The Windows failure hint points at `step mcp prepare`
instead of the POSIX-only `curl | sh` pipeline.

* ci: run existing checks on the StepPage fix branch

* ci: add workflow_dispatch so CI runs can be dispatched manually

Restore the canonical push trigger (the branch was only added to work around
the missing manual entry) and add workflow_dispatch so the CI workflow can be
triggered from the Actions tab without pushing a no-op commit.

* ci: retrigger checks for this PR

CI had no workflow run attached to this PR. Actions is confirmed working, so
this empty commit fires the pull_request synchronize event against main.

* ci: retrigger PR checks

Actions only started accepting event triggers after the first manual dispatch;
this forces a pull_request synchronize event now that it is live.

---------

Co-authored-by: youwang <61931019+MelodyVAR@users.noreply.github.com>
Co-authored-by: liuedcson <332840681+liuedcson@users.noreply.github.com>
Co-authored-by: edcsonzzz <liuedcson@gmail.com>
Co-authored-by: xuyunfang <xuyunfang@stepfun.com>
Co-authored-by: ZXJC-niusile <2632590648@qq.com>
Co-authored-by: Hylouis233 <Hylouis233@users.noreply.github.com>
Co-authored-by: Uking-xxx <xuklaus1@gmail.com>
Co-authored-by: ZouR-Ma <2605315944@qq.com>
Co-authored-by: ZouR Ma <37286405+ZouR-Ma@users.noreply.github.com>
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.

2 participants