Commit Graph

38845 Commits

Author SHA1 Message Date
Peter Steinberger 2409df768c fix(kimi): honor K3 thinking off (#109335)
* fix(kimi): honor K3 thinking off

* chore: defer Kimi release note

* test(kimi): narrow live reasoning blocks
2026-07-16 13:50:15 -07:00
tzy-17 543b60092d fix(cli): use startsWith for loopback IPv4 check in container proxy (#106077)
isLoopbackProxyHostname used split(".", 1)[0] === "127" to detect
127.0.0.0/8 addresses. While isIP() already rejects leading-zero octets
before this branch is reached, startsWith("127.") is more direct and
clearly expresses the intent of matching the entire 127.0.0.0/8 range.
2026-07-16 13:40:18 -07:00
Peter Steinberger 093b8859c4 perf(test): fix env-dependent failures, stop verifier lazy-fetch, balance CI stripes (#109332)
* test: fix environment-dependent failures on built and partial-clone checkouts

Canvas A2UI fixtures now pin the resolved root through a new canvas
test-api hook: dev checkouts with a locally built a2ui.bundle.js won
candidate resolution and shadowed the fixture root. Speech-core prefs
tests resolve the canonical tmpdir (macOS /tmp symlink breaks fs-safe
store roots), and the bundled plugin install test asks discovery for
the canonical bundled path instead of hardcoding the source tree.

* fix(release): keep evidence verifier blob reads local-deterministic

git show against a forged or missing SHA lazily fetched from the
promisor remote on partial clones, hanging a security check on network
state (266s observed). GIT_NO_LAZY_FETCH pins verification to local
objects; missing blobs fail closed as before.

* perf(test): pay the command-handler barrel once and merge subagent command tests

commands-name and commands-login imported the full handler barrel
(~1,700 modules) for registration assertions; those now live in a
dedicated registration test, dropping the two files from 12.6s/4.6s to
1.4s/1.0s locally. Five subagent command test files merged into one so
the auto-reply module graph evaluates once instead of five times.

* perf(ci): stripe node test bins by measured file cost

Round-robin striping packed the whale files together, leaving
auto-reply-commands-1 at ~220s while sibling stripes idled at ~30s.
Greedy LPT over advisory per-file seconds hints balances the commands,
cli-runner, and tooling stripes deterministically; group hints for the
restriped bins are equalized at the old totals.
2026-07-16 13:38:56 -07:00
Peter Steinberger 9ac2af50e6 fix(migrate): reject duplicate item ids, fingerprint the full plan, route Hermes memory copies by item (#109323) 2026-07-16 13:31:44 -07:00
mushuiyu886 cb47a0b4cc fix(agents): force-stop stalled tool probes (#109215) 2026-07-16 13:31:10 -07:00
Peter Steinberger b39e1cf66f perf(agents): drain code-mode bridge calls inline within one exec turn (#109290)
Code mode previously suspended to the model with a waiting result for every
awaited tools.search/describe/call, forcing one wait tool turn per await
(turns = tool calls + 1). Bridge calls now resolve inline within the exec
deadline and resume the QuickJS snapshot host-side, matching the namespace
auto-drain path. Restart-safe runs keep their existing no-drain semantics.
Live proof across OpenAI/Anthropic/Google: ~45% fewer turns and ~45% fewer
input tokens at equal success rate.

Also landed with the same contract:
- one shared wall-clock deadline per exec/wait call (initial worker, inline
  drain rounds, and resumed guest interrupt budget all share it)
- park the snapshot instead of resuming when the remaining budget could not
  survive VM restore, so near-deadline settles stay recoverable
- abort now terminates the guest worker, skips resume, and drops suspended
  runs instead of pinning process-global slots until TTL, reported with the
  existing aborted failure code
- exec description omits API.list/MCP/namespace guidance when the run catalog
  has no MCP tools or namespaces, so models stop probing an empty surface
- API.list tolerates a trailing-slash prefix like API.list("mcp/")
2026-07-16 13:30:07 -07:00
Peter Steinberger ea53179025 test: reuse config suite state (#109326) 2026-07-16 13:28:03 -07:00
mushuiyu886 9edd662741 fix(doctor): force-stop stalled TUI process probes (#109243) 2026-07-16 13:17:08 -07:00
Peter Steinberger a9f82837a1 feat: inspect background tasks across web and mobile (#109150)
* feat: add background task details across clients

* fix: refresh background task clients and translations

* fix: keep background task details fresh

* fix: ignore deleted background task details

* refactor: split background task rendering

* fix: use canonical background task ids

* refactor: extract background task view types

* fix(apps): regenerate task localizations

* fix(ios): remove unused task screen import

* fix(apps): refresh task localization inventory
2026-07-16 13:08:42 -07:00
xingzhou 0e1fad711c fix(channels): prevent lifecycle listener buildup (#109108) 2026-07-16 13:08:12 -07:00
Peter Steinberger 54eb03fcf0 fix(migrate): harden importer path resolution and memory copy safety (#109314) 2026-07-16 12:56:43 -07:00
Peter Steinberger ce919593bf fix(ui): unblock rebases with generated i18n conflicts (#109279)
* fix(ui): automate i18n conflict resolution

* fix(ui): preserve i18n cache merge intent

* fix(ui): restrict resolver to generated locales

* fix(ui): prune stale locales before baseline

* fix(ui): handle removed locale conflicts
2026-07-16 12:52:28 -07:00
wings1029 8dfb310b56 fix(infra): bound three warning dedupe caches with createDedupeCache (#101746)
* fix(infra): bound three warning dedupe caches with createDedupeCache

Replace unbounded Set<string> warning dedupe caches with the shared
createDedupeCache helper (ttlMs=0, maxSize=4096) to prevent unbounded
memory growth:

- safeBinTrustedDirWarningCache in invoke-system-run.ts
- chmodWarnedTargets in openclaw-state-db.ts
- clobberCapWarnedPaths in io.clobber-snapshot.ts

All three follow the same anti-pattern: .has()/.add() only, no eviction,
no size cap. Matches the fix pattern from #101696 and #101738.

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: refresh PR body evidence for ClawSweeper re-review

* test(io.clobber-snapshot): add clobber-cap warning deduplication test

Exercises warnClobberCapReached through the production API
(persistBoundedClobberedConfigSnapshot) to prove that
clobberCapWarnedPaths.check() suppresses duplicate warnings
on the same config path.

Co-Authored-By: Claude <noreply@anthropic.com>

* test(infra): tighten warning cache coverage

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 12:45:51 -07:00
wangmiao0668000666 a54623ffde fix(config): bound config I/O warning caches (#109000)
* fix(config): bound config I/O warning and pending-secret caches

* fix(config): drop unused import and unexported constants in io.state

* refactor(config): centralize bounded io cache writes

* fix(config): keep generated owner secrets stable

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 12:36:36 -07:00
Alix-007 8f1c6df748 fix(daemon): bound Node binary lookup (#109239)
* fix(daemon): bound Node binary lookup

* docs(changelog): note bounded daemon Node lookup

Co-authored-by: Alix-007 <li.long15@xydigit.com>

* docs(changelog): keep release notes out of contributor PR

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 12:32:08 -07:00
thomas.szbay b06fe2a673 fix(kill-tree): verify process group leader before using group kill to prevent gateway SIGTERM (#76259) (#94697)
* fix(kill-tree): verify process group leader before group kill to prevent gateway SIGTERM (#76259)

- Add isProcessGroupLeader() to killProcessTree/signalProcessTree: ps -p <pid> -o pgid= primary check with /proc/<pid>/stat fallback on Linux. Group kill only when the PID is its own process group leader; non-leaders fall back to single-pid kill, preventing accidental gateway SIGTERM when a non-detached child shares the gateway's process group.
- Propagate detached: true to all detached-spawn cleanup callers (exec-termination, agent-bundle LSP, mcp-stdio, bash, supervisor pty, agent-core nodejs) so detached group cleanup survives leader exit.
- Gateway/daemon cleanup paths (schtasks, restart-health) keep the leader-checked default (detached omitted).

Closes #76259

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor(process): tighten process-group ownership checks

* refactor(daemon): split restart diagnostics

* refactor(daemon): isolate restart health types

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 12:30:51 -07:00
Peter Steinberger cf24f14c63 test(agents): speed local service startup fixtures (#109296) 2026-07-16 12:24:37 -07:00
Peter Steinberger 734e1ed369 refactor(daemon): split restart port ownership helpers (#109298) 2026-07-16 12:15:48 -07:00
wangmiao0668000666 7b8dd74f66 fix(skills): bound skill command debug dedupe cache (#109164)
* fix(skills): bound skill command debug dedupe cache

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(skills): move test-only reset to testing object and fix type error

* fix(skills): avoid test-only export in command debug cache

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-16 12:15:34 -07:00
Peter Steinberger 6c39e713d4 refactor: move native hook relay registry to SQLite (#109289)
* refactor: store native hook relays in SQLite

* chore: satisfy relay store CI checks
2026-07-16 12:14:06 -07:00
David Gorelik 63b0cac9f0 fix: reply hooks can claim ordinary agent turns (#108353)
* fix: honor reply hooks for user agent turns

* fix: preserve reply hook turn context

* fix: gate CLI reply hooks by trigger

* fix(plugins): run reply hooks once per admitted turn

Co-authored-by: Clive (OpenClaw) <clive@openclaw.local>

* fix(plugins): reconcile reply hooks after rebase

* fix(auto-reply): dispatch hooks for active steers

* test: use valid channel context shape in steer hook test

---------

Co-authored-by: Clive (OpenClaw) <clive@openclaw.local>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 11:50:43 -07:00
NianJiu 42ff5ec754 feat(kimi): add Kimi K3 support (#109202)
* feat(moonshot): add Kimi K3 support

* feat(kimi): add K3 subscription models

---------

Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 11:41:12 -07:00
Shakker f084ab2240 fix: prevent external gateway restart timeouts (#109273) 2026-07-16 19:36:58 +01:00
Alix-007 6bcf071120 fix(infra): bound macOS process start probes (#109064)
* fix(infra): bound macOS process start probes

* docs(changelog): note macOS process probe deadline

Co-authored-by: Alix-007 <li.long15@xydigit.com>

* test(infra): cover process probe timeout

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 11:36:07 -07:00
Peter Steinberger 6175e6a98c refactor: remove stale internal deprecated compatibility (#109266)
* refactor(agents): remove stale deprecated internals

* refactor(channels): remove dead target compat aliases

* refactor(plugins): collapse private deprecated types

* refactor(onboarding): remove stale provider skip alias

* style(agents): format deprecated cleanup

* refactor(agents): internalize sanitizer options
2026-07-16 11:35:34 -07:00
Peter Steinberger 76870ed958 fix(agents): scope tool-terminal completion proof to post-assistant results (#109259)
The continuation guard scanned the whole session snapshot for non-error
toolResults, so a prior turn's result with a model-reused tool-call id
could prove 'completion' for a batch that never dispatched (fresh-eyes
review finding on 2848acbbaa). Scope the scan to snapshot entries after
the terminal assistant — both assistant fields are found on the snapshot
itself, so identity lookup holds; an assistant missing from the snapshot
fails closed to the existing incomplete-turn error. Adds a stale
prior-turn-result regression.
2026-07-16 11:26:34 -07:00
wings1029 c64a16196a fix(resolve-system-bin): cap binary resolution caches with eviction (#108858)
* fix(resolve-system-bin): cap binary resolution caches with eviction

The two module-level Maps grow with every distinct binary name resolved.
A 512-entry cap prevents unbounded memory growth in long-running processes
that resolve many different system binaries.

* fix(resolve-system-bin): share LRU cache pruning

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 11:17:00 -07:00
Dallin Romney 9f0e35c233 test: stabilize UI and gateway auth timing (#109257) 2026-07-16 11:13:42 -07:00
thomas.szbay b85531c62f fix(agents): bound base64 image input before decode in tool-image sanitizer (#105086)
* fix(agents): bound base64 image input before decode in tool-image sanitizer

* fix(agents): lower input-size cap to 10MB for OOM headroom

* fix(agents): align tool-image input-cap comment with 10 MiB ceiling

* fix(agents): typecheck-safe access in tool-image input-cap test

* test(agents): exercise real tool image input cap

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 11:13:27 -07:00
Peter Steinberger 3c1a36380f fix(gateway): read pinned session-extension registry for catalog node commands (#109248)
Session-catalog dispatch read the mutable `activeRegistry`, which a later
standalone plugin load can replace with providers built from default
(unbound) plugin runtimes. Those runtimes hit the unavailable nodes stub, so
Control UI Codex/Anthropic catalogs surfaced `NODE_LIST_FAILED` ("Plugin node
runtime is only available inside the Gateway") even though the gateway binds a
real nodes runtime and CLI `nodes list` works.

Read the pinned session-extension registry instead so catalog providers keep
the gateway-bound runtime across plugin registry churn. Generic across all
session-catalog plugins.
2026-07-16 11:12:38 -07:00
Peter Steinberger 7cf486f657 fix(agents): guard optional provider models when resolving transcript route (#109252)
resolveTranscriptAssistantRoute read providerConfig.models.find without
guarding models, but the runtime config passed here is the raw user config
where a provider entry may omit models (e.g. models.providers.openai =
{ apiKey }). That threw "Cannot read properties of undefined (reading 'find')"
inside the SQLite transcript-append transaction, so every agent turn failed to
persist its transcript (the underlying error was swallowed to {} and surfaced
only as "Turn transcript persistence failed").

Guard the optional models array at the crash site, and fix two identical
raw-config hazards found in the same sweep (cron isolated-agent model api
resolution, microsoft-foundry image provider). Regression test drives
redactTranscriptMessage with a provider that has no models array.
2026-07-16 11:10:59 -07:00
Peter Steinberger 7133305bac fix(config): reject ignored audio transcription settings (#109226)
* fix(config): reject retired audio transcription config

* fix(config): align dead audio cleanup contracts

* fix(doctor): detect retired audio config
2026-07-16 11:00:28 -07:00
wings1029 d1153e4ad8 fix(git-commit): cap commit resolution cache with FIFO eviction (#108856)
* fix(git-commit): cap commit resolution cache with FIFO eviction

The module-level cachedGitCommitBySearchDir Map grows without bound.
A 256-entry FIFO cap prevents unbounded memory growth from repeated
lookups across distinct search directories.

* fix(git-commit): use shared LRU cache pruning

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 10:54:35 -07:00
Sally O'Malley 11c7360af5 fix(chat): keep internal metadata out of transcripts (#109056)
* fix(chat): keep internal metadata out of transcripts

Signed-off-by: sallyom <somalley@redhat.com>

* fix(gateway): harden control reply projection

Co-authored-by: sallyom <somalley@redhat.com>

* chore: leave changelog to release tooling

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 10:47:43 -07:00
Bill 3590f7df7e fix(agents): route opted-in embedded runs through the CLI backend on subscription-only claude-cli auth (#106840)
Embedded runs targeting a CLI runtime provider fall through to the openclaw
harness and call the provider API directly with the runtime's credentials
(cli_runtime_passthrough_openclaw). Anthropic routes direct anthropic-messages
calls on subscription OAuth tokens to metered extra-usage billing — this is
long-standing behavior, not a recent change. Without extra-usage balance,
every such run (e.g. active-memory recall) fails with a billing error; with
extra-usage enabled, the run silently draws paid metered usage instead of the
plan limits the CLI runtime was configured for. Only CLI-backed execution
runs on plan limits for those credentials.

Add an opt-in RunEmbeddedAgentParams.cliBackendDispatch: "subscription-auth"
that dispatches the run through runCliAgent as a one-shot turn when the
provider is claude-cli, a CLI backend is registered, and the ordered auth
profile selection for the passthrough resolves to a subscription (oauth/token)
credential or nothing rather than an API key; resolution stays on stored
credential metadata, with no credential materialization or refresh on the
per-turn path. The dispatch translates toolsAllow into the selectable-backend
surface (native: [], allowlisted loopback MCP tools; wildcard allowlists stay
MCP-only), runs with a fresh CLI process (no live-session reuse; session-
scoped bundle-MCP retirement on run end rather than the process-wide loopback
close), bridges CLI tool result events to onAgentToolResult with native-path
semantics (normalizeToolName + isToolResultError), and drops CLI session
bindings from the result.

The selectable-backend MCP list now also bounds the loopback MCP grant
server-side: the grant carries a per-run gateway tool allowlist enforced in
scoped tool resolution, so tools outside the run's allowlist can be neither
listed nor called even under CLI bypass permission modes where
--allowedTools is advisory.

active-memory recall opts in so recall works on claude-cli subscription-only
instances and stops drawing metered extra usage where it previously could.
Scoped to claude-cli; other CLI runtimes keep the passthrough until their
direct-API contract is verified.

The dispatch also mirrors the run into the run's session transcript through the session
accessor (user turn, tool call/result records as they stream, final assistant
snapshot at run end) so transcript consumers keep parity with embedded runs:
active-memory's persistTranscripts, timeout partial-text salvage, and the
live terminal-search watcher that polls the session file mid-run.

Post-review hardening: canonical anthropic/<model> refs whose configured
agentRuntime is claude-cli resolve through the runtime policy before the
dispatch gate (they previously stayed on the failing passthrough); restricted
dispatches serve an exclusive loopback-only MCP bundle so user/plugin MCP
servers stay outside the run's tool universe; and the transcript recorder
flushes the latest assistant snapshot the moment the run aborts, so timeout
salvage sees partial text even while the killed CLI child is still settling.

Recalls routed to the claude-cli runtime default to a 45s budget (measured
CLI-dispatched runs take 14-20s, over the plain 15s default); explicit
timeoutMs config always wins.

Transcript mirror keeps bare-array tool_result content (claude stream-json
echoes MCP results without a {content} wrapper); dropping it classified every
successful recall as no_relevant_memory.

CLI dispatch resolves inside session/global lane admission so dispatched
runs obey the same lifecycle, placement, and concurrency gates as native
embedded runs.

LOC-ratchet offsets move the loopback grant-context builders to
cli-runner/mcp-grant-context.ts and dedupe the run/prep stage-summary
emitters into attempt-stage-timing.ts; unused type exports dropped and the
now-used stream-message baseline entry removed.

The recall timeout default now consumes the runner's own dispatch
eligibility through a new plugin-runtime seam
(agent.resolveCliBackendDispatchEligibility): API-key and missing-backend
routes keep the passthrough and its plain 15s default.

Eligibility honors an explicitly pinned authProfileId (the credential the
run executes on) before ordered profile selection, in both directions.

Transcript mirror composes buildAssistantMessage + buildUsageWithNoCost
directly; main trimmed the zero-usage wrapper export (ab0ccc244b) before
this branch's usage landed.

Dispatch eligibility is provider-owned: the anthropic plugin's claude-cli
backend declares CliBackendPlugin.subscriptionAuthDispatch and core reads
the registered descriptor instead of a core provider allowlist.

Dispatch fails closed on tool policy (only non-empty named allowlists are
expressible on the CLI surface; deny-all, wildcards, absent allowlists, and
disableTools/modelRun keep the passthrough), threads the pinned
authProfileId into CLI runtime resolution, and emits onExecutionStarted at
the admitted dispatch boundary.
2026-07-16 10:37:54 -07:00
Peter Steinberger cb84dc76fc feat(control-ui): approval attention chip + approval history page (#108776)
Adds the sidebar approval attention chip and a dedicated approval history
page backed by the new approval.history gateway RPC (30-day retention
window). Extracts buildSidebarAttentionItems into its own module so the Lit
component consumes it as a real cross-module dependency, and wires the strict
i18n catalog for the new strings (fallbacks=0).
2026-07-16 10:32:05 -07:00
Peter Steinberger b6e90cbed4 test: raise async wait ceilings that flaked under CI load (#109222)
vi.waitFor in the ACP abort test and the shared Swift waitUntil helper
both timed out on contended runners while passing solo; polling waits
return immediately when healthy, so the higher ceilings cost nothing.
2026-07-16 10:30:50 -07:00
Peter Steinberger 2cd3ee43ee refactor: store workspace setup state in SQLite (#109147)
* refactor(state): move workspace setup state to SQLite

* test(state): track workspace cleanup temp dirs

* fix(state): satisfy workspace migration gates

* docs(state): fix migration list indentation

* fix(state): verify sibling migration claims

* fix(state): remove obsolete claim probe
2026-07-16 10:28:36 -07:00
Peter Steinberger 2db77fb3fb test(media): speed up download timeout coverage (#109238) 2026-07-16 10:24:47 -07:00
heichl_xydigit 1f3b4e86ca fix(agents): keep not_required delivery status when completion_announced_at exists (#108710)
* fix(agents): keep not_required delivery status when completion_announced_at exists for expectsCompletionMessage=false runs

createDeliveryFromTypedColumns unconditionally overrode status to
'delivered' when completion_announced_at was present, even for runs
where expects_completion_message is false. This produced a contradictory
delivery state where a run that does not require completion delivery
was marked as delivered.

Only set status to 'delivered' when expects_completion_message is true.
For not_required runs that still have completion_announced_at recorded
(for informational purposes), preserve announcedAt without overriding
the status.

* test(agents): cover tainted delivery rows

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 10:21:50 -07:00
Peter Steinberger e71ef76e8e feat(onboarding): offer detected Claude Code/Codex/Hermes memory imports across CLI, macOS, and Linux onboarding (#108977)
* feat(migrate): add Hermes memory-only import and a shared memory-import core

* feat(onboarding): offer detected memory imports during CLI setup and guided onboarding

* feat(ui): show a first-run memory-import offer in Control UI onboarding mode

* feat(linux-app): open the first-run dashboard in onboarding mode

* feat(macos): add the onboarding memory-import page

* docs: document the onboarding memory-import page across surfaces

* chore(i18n): translate onboarding memory-import strings for control-ui and native locales

* refactor: keep memory-import internals unexported for deadcode gates

* fix(ci): resolve lint findings in onboarding memory import

* chore(i18n): refresh native inventory after lint refactor

* fix(ci): skip updater artifacts in the unsigned Linux companion PR lane

* chore(i18n): reconcile locale artifacts after rebase
2026-07-16 10:10:01 -07:00
Peter Steinberger 4432016f7f refactor: prune redundant OpenClaw env controls (#109211)
* refactor: prune redundant OpenClaw env controls

* docs: refresh environment reference map
2026-07-16 10:06:39 -07:00
NianJiu 1eda44ee36 fix(acp): show durable notice after gateway disconnect (#108827)
* fix(acp): record disconnect interruptions

* test(acp): tighten disconnect settlement coverage

Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com>

* fix(acp): preserve replay order under backpressure

Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com>

* test(acp): await accepted prompt ledger record

Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com>

* fix(acp): isolate ledger mutation queues

Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com>

---------

Co-authored-by: NianJiuZst <180004567+NianJiuZst@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 10:01:10 -07:00
Monkey-wusky e6ac6c8506 fix(agents,skills): keep session-identity and experience-review transcript truncation UTF-16 safe (#106370)
* fix(agents,skills): keep session-identity and experience-review transcript truncation UTF-16 safe

normalizeCliContextValue (cli-output.ts) and formatSkillExperienceReviewTranscript
(experience-review-prompt.ts) both used raw .slice(0, N) / .slice(-N) to truncate
user-facing strings. When a truncation boundary falls inside a surrogate pair (emoji),
the resulting string contains a lone surrogate — encodeURIComponent throws URIError,
JSON serialisers may reject it, and downstream display surfaces can render garbage.

Replace .slice(0, 200) with truncateUtf16Safe in normalizeCliContextValue.
Replace .slice(0, 6_000) with truncateUtf16Safe and full.slice(-tailBudget) with
sliceUtf16Safe(full, tailStart) in formatSkillExperienceReviewTranscript.

Follows the pattern established by #98644, #102470, #101728 and the broader
UTF-16 safety sweep already applied across the codebase.

* chore: re-trigger CI after PR body update

* test(agents,skills): construct transcripts exceeding 60k chars, verify old slices dangle

Both experience-review fixtures now exceed EXPERIENCE_REVIEW_MAX_TRANSCRIPT_CHARS
so the truncation branch actually fires. Pre-condition assertions prove the old raw
.slice(0,6000) / .slice(tailStart) produce isolated surrogates; the production
functions (formatSkillExperienceReviewTranscript, formatCliOutputError) do not.

cli-output test also adds pre-condition check showing normalizeCliContextValue
with raw .slice(0,200) would split a surrogate pair.

* test: tighten UTF-16 truncation coverage

Co-authored-by: 毛宇豪0668001457 <mao.yuhao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 09:53:58 -07:00
Peter Steinberger 618002c141 refactor(core): reuse keyed promise memoization (#109184) 2026-07-16 09:51:40 -07:00
Peter Steinberger 7e410bdb52 fix(auto-reply): stop minting channel source-turn ids for internal-origin turns (#109201)
PR #108283 started deriving a durable channel source-turn id for gateway
chat.send turns carrying originatingChannel/originatingTo overrides, while
the gateway's pre-created user-turn recorder keys the persisted turn by run
id. The same PR's admission guard then throws 'channel restart recovery
requires source-keyed user-turn admission' for every such turn — breaking
qa-lab suite runs, ACP-bridge flows, and relay integrations (reproduced
live on the codex harness chat-image probe; text-only fails identically).

Internal-origin turns (gateway chat.send stamps the internal channel as the
ingress provider) carry run ids, not provider message ids; gateway turns
already own restart via fingerprint admission and client-side idempotent
retries. Skip the mint for internal ingress at both derivation sites so
admission takes the source-less persist path, while real channel ingress
keeps the new source-keyed recovery. Also surface the full agent.wait
result in the codex harness live test error (how this was found).

Live proof: gateway-codex-harness.live.test.ts chat-image probe on a
Blacksmith Testbox with a real OpenAI key failed with the admission error
twice on main and passes admission with this fix.
2026-07-16 09:48:53 -07:00
wings1029 966e176e04 fix(shell-env): cap login shell env probe cache with eviction (#108862)
* fix(shell-env): cap login shell env probe cache with eviction

The loginShellEnvProbeCache Map keys include the exec function reference
and exec environment, which can vary across calls. A 64-entry eviction
cap prevents unbounded growth.

* refactor(shell-env): share bounded probe cache insertion

Co-authored-by: 陈志强0668000989 <31802073+wings1029@users.noreply.github.com>

* fix(shell-env): retain active probe cache entries

Co-authored-by: 陈志强0668000989 <31802073+wings1029@users.noreply.github.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 09:46:35 -07:00
Peter Steinberger fa2b38c890 test: speed up stuck-session recovery coverage (#109195) 2026-07-16 09:44:46 -07:00
Peter Steinberger 05dd921016 fix: shard OpenAI transport stream tests (#109181)
* test(agents): shard OpenAI transport stream suites

* chore(lint): transfer transport test baselines
2026-07-16 09:43:07 -07:00
Peter Steinberger f871b499b6 fix(infra): drop consumer-less gateway supervision exports
333c4f9a61 and 244f4965dc landed six exports with no production
consumers, failing the hard-zero deadcode gate on every PR: a dead
re-export block in restart-handoff (the contract module's real importer
uses it directly), three result/mode types only referenced inside their
own modules, and two supervision symbols whose only consumers were their
tests. The types and helpers stay defined for the in-flight supervisor
work to re-export alongside real consumers; the supervision test now
proves mode resolution through the public isGatewayExternallySupervised
surface.
2026-07-16 09:31:46 -07:00