Commit Graph

78555 Commits

Author SHA1 Message Date
Peter Steinberger 8c3c8a2ab0 feat(android): show live subagent activity in chat (#121813)
* feat(android): show live subagent activity

* chore(android): mark task event handled

* fix(android): localize activity diff chips

* fix(android): reconcile dropped task activity
2026-08-10 21:08:44 -07:00
Peter Steinberger 45e4c838d0 feat(ui): show live subagent activity rows (#121840)
* feat(ui): show live subagent activity rows

* test(ui): split subagent activity coverage
2026-08-10 21:08:27 -07:00
Peter Steinberger 768f53d345 fix(secrets): resolve default aliases by source (#121630) 2026-08-10 21:05:26 -07:00
Peter Steinberger 39cce52ed2 fix: report message-tool-only runs that never reply (#121822)
* fix: record missing message-tool source replies

Settled message_tool_only runs now record a bounded message-tool-not-called terminal fact when no current-source delivery, silence, or continuation exists. Private final text remains private; long-final recovery and actual deliveries remain unchanged.

* fix: preserve intentional message-tool non-delivery

Reuse the canonical private-final exclusions for policy-denied, heartbeat, and room-event runs before recording message-tool-not-called. Keep stranded retry diagnostics unchanged and cover each lifecycle outcome.

* refactor: remove private-final test-only export

Keep the warning predicate local to its test now that production shares the canonical private-final classifier directly. This satisfies the exact-head dead-code export gate without changing runtime behavior.
2026-08-10 21:04:26 -07:00
Peter Steinberger 29bd7e462f fix(ui): hide agent selectors for single-agent setups (#121826)
* fix(ui): hide redundant agent selectors

* test(ui): split agent selector regression

* test(ui): keep selector e2e fixtures multi-agent
2026-08-10 20:55:54 -07:00
Peter Steinberger 7dbc2916a3 fix: cloud workers work with trusted-proxy browser auth (#121671)
* fix(ui): make cloud recovery scope gateway-owned

Allow trusted-proxy and other authorized browser sessions to use cloud workers without deriving recovery ownership from client-visible credentials or Web Crypto. The Gateway now emits an opaque principal-bound recovery scope.

* test(gateway): type recovery scope fixture

* fix(ui): preserve cloud recovery across upgrades

Keep the shipped credential-derived scope for older v4 Gateways and migrate credential-proven pending recovery rows to the Gateway-owned namespace before publishing readiness.

* perf(ui): lazy-load recovery migration

* fix(ui): preserve canonical recovery promotion

* test(ui): use deterministic provider navigation

* fix(ui): authorize recovery scope migration

* build(plugin-sdk): refresh recovery compatibility baseline

* test(gateway): expect recovery migration authorization
2026-08-10 20:52:55 -07:00
Ayaan Zaidi b65fa6b96a fix(skills): keep specialized skills during cleanup (#121829)
Preserve distinct domain-specific skills with reliable triggers during automatic collection cleanup while retaining junk, duplicate, and stale-fragment removal.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-11 03:49:19 +00:00
Vyctor H. Brzezowski 619f3c9ab1 feat(ui): identify project files in Control UI chat (#121775)
Recognized workspace paths in chat now read as their basename behind a
file-type glyph, so a file reference is identifiable before it is read.
Markdown, package manifests, TypeScript and other code, TSX/JSX components,
config/data, shell scripts, and images each get their own mark; anything else
falls back to a plain document. Paths sharing a basename keep the smallest
trailing suffix that tells them apart, and Windows paths keep their own
separator.

Classification wins over authoring syntax: a path written in backticks is a
file link first, so it drops the inline-code chip and renders exactly like a
bare path beside it. Code spans that are not file links keep the chip.

The full path stays addressable: it drives the file panel, the new tooltip,
and the message Copy action (which returns the original Markdown).
Author-written labels in [label](path) links are never rewritten, and text
that is not a recognizable path stays plain prose.

The glyph is painted as a masked ::before like the sibling GitHub mark, so it
stays out of the accessibility tree and out of copied text and follows the
link color in every theme. Extension classification moves to one shared
resolver that the file preview modal now uses too, replacing its own
code/text extension set.
2026-08-11 00:48:43 -03:00
Peter Steinberger 27be68d2d1 fix(agents): report best-effort delivery failures (#121824)
* fix(agents): report best-effort delivery failures

Keep explicit delivery intent through Gateway and agent preflight so final delivery records a send or concrete failure outcome. Persist replay markers only after a route is prepared.

Refs #121804.

* test(gateway): preserve requested delivery contract

Update the multi-channel Gateway regression to assert that best-effort preflight preserves explicit delivery intent for final outcome reporting.

Refs #121804.
2026-08-10 20:40:34 -07:00
Paul Campbell 6bd83561ad fix(mxc): agent tools fail in Windows workspace subdirectories (#115399)
* fix(mxc): validate agent workdirs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: fc20c06c-e8c0-4649-9efd-ad87fac71a4f

* fix(mxc): treat non-directory parents as unavailable workdirs

validateWorkdir declares that unusable workdirs return null, but only ENOENT was classified as missing. A workdir nested under a file raises ENOTDIR on Linux, which escaped as a raw filesystem error instead of the normal unavailable-workdir result.

The same ENOENT-only classification appeared at three sibling sites in this file, so the shared isMissingPathError guard fixes the whole class rather than the single reported path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d7253887-71c9-4083-a591-91979c981e39

* fix(mxc): sandbox provisioning crashes reading containerWorkdir

MxcFsBridge initialized three fields from this.sandbox, but sandbox is a constructor parameter property. Plugin sources load through jiti, which evaluates field initializers before assigning parameter properties, so this.sandbox was still undefined and every MXC sandbox provisioning failed with SandboxProvisioningError: Cannot read properties of undefined (reading 'containerWorkdir').

Assign the derived fields in the constructor body from the sandbox parameter instead. The repo tsconfig sets useDefineForClassFields=false, so tsgo and Vitest (esbuild) both apply legacy ordering and could not observe the failure; only the jiti-loaded runtime path did.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d7253887-71c9-4083-a591-91979c981e39

---------

Copilot-Session: fc20c06c-e8c0-4649-9efd-ad87fac71a4f
Copilot-Session: d7253887-71c9-4083-a591-91979c981e39
2026-08-11 11:38:34 +08:00
Peter Steinberger b5d5ec340f feat(cloud-workers): add desktop apps and browser autonomy (#121475)
* feat(cloud-workers): add desktop apps and browser autonomy

provider-attested Browser/Terminal launchers, shared visible loopback CDP Browser tool, no MCP/cookies/generic command.

* feat(ui): add cloud desktop app launcher

* docs(gateway): document cloud desktop apps and browser autonomy

* perf(ui): trim desktop launcher startup copy

* refactor(ui): simplify desktop launch feedback

* perf(ui): reuse desktop app labels

* fix(ui): keep desktop launch failures actionable

* fix(crabbox): allow browser bootstrap to finish

* fix(cloud-workers): honor provider provision budgets

* fix(cloud-workers): persist browser screenshot receipts

* fix(cloud-workers): bound browser screenshot lifecycle

* fix(cloud-workers): avoid replaying desktop launches

* test(cloud-workers): isolate browser runtime integration

* refactor(cloud-workers): tighten desktop runtime boundaries

* test(cloud-workers): keep browser runtime mock synchronous

* fix(cloud-workers): break gateway type import cycle

* fix(ci): settle admitted setup sessions in tests

* build(plugin-sdk): refresh desktop app contract

* ci: refresh merge-tree validation

* build(plugin-sdk): regenerate desktop app baseline

* style(gateway): format merged method order test
2026-08-10 20:31:07 -07:00
Peter Steinberger cc9e75efb2 fix(media): accept canonical file URL forms (#121676)
* fix(media): accept canonical file URL forms

* fix(media): normalize file store keys

* fix(media): handle canonical file URLs end to end

* test(media): expose nested store key failure on Windows

Amp-Thread-ID: https://ampcode.com/threads/T-019fed8d-ad78-74f0-9592-a3466bb6a9bd
Co-authored-by: Peter Steinberger <steipete@gmail.com>

* test(media): cover canonical file URL dedupe

Amp-Thread-ID: https://ampcode.com/threads/T-019fed8d-ad78-74f0-9592-a3466bb6a9bd
Co-authored-by: Peter Steinberger <steipete@gmail.com>

* Revert "test(media): cover canonical file URL dedupe"

This reverts commit 63efbbc879.

* fix(media): keep nested store keys portable

* fix(media): complete canonical file URL handling

* fix(media): reject encoded separators in comparison keys

* fix(auto-reply): block canonical file URLs without sandbox

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-10 20:30:37 -07:00
Masato Hoshino 504badbfb1 fix(line): treat a non-positive mediaMaxMb as unset (#121184)
`channels.line.mediaMaxMb` has no range constraint, so a configured `0` or
negative value loads cleanly and then survives the `??` chain in `createLineBot`
into `mediaMaxBytes`. Every non-empty inbound media download is then measured
against a 0-byte budget it cannot satisfy: `saveMediaStream` throws
`Media exceeds 0MB limit`, the LINE handler degrades the attachment to
`[line attachment unavailable]`, and the only trace is a verbose log line that
never names the setting.

Treat a non-positive value as unset at every link of the chain, so it falls back
to the same 10 MB default an unset field already means, and a non-positive
caller override no longer discards a valid account config value. Matrix
(#120466) and Zalo (#120988) resolved the identical contract the same way; LINE
was the last bundled channel resolving this field by hand without the guard.

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-10 22:30:10 -05:00
Peter Steinberger 2715d1a878 refactor(agents): unify dot-family attempt helper names (#121550)
* refactor(agents): add dash-named attempt helpers

* refactor(agents): repoint dot-family attempt imports

* refactor(agents): remove dot-family attempt helpers
2026-08-10 20:27:56 -07:00
Peter Steinberger 69220fc0d4 test(macos): remove body-only view probes (#121820)
* test(macos): remove body-only view probes

* test(macos): drop retired i18n wrapper contract
2026-08-10 20:26:56 -07:00
Peter Steinberger ebfd3ba154 improve: cut CI critical path without more workers (#121807)
* test(ci): remove redundant builds from critical path

Amp-Thread-ID: https://ampcode.com/threads/T-019fee8d-665d-707b-a380-23f2a6a1ce03
Co-authored-by: Peter Steinberger <steipete@gmail.com>

* docs(ci): format runner table

Amp-Thread-ID: https://ampcode.com/threads/T-019fee8d-665d-707b-a380-23f2a6a1ce03
Co-authored-by: Peter Steinberger <steipete@gmail.com>

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-10 20:21:20 -07:00
ffyuuu efe5d8d76e fix(longcat): correct cache pricing and add provider icon (#121369)
* longcat: align baseUrl with docs, fix cacheWrite pricing, add brand icon

- Point baseUrl at the documented https://api.longcat.chat/openai/v1 path
  instead of the undocumented unversioned alias route.
- Fix cacheWrite pricing to 0: the LongCat pricing page has no separate
  cache-write charge (matches deepseek/moonshot/zai catalog conventions).
- Update the pricing page link to the current docs path.
- Add the official LongCat brand icon (provenance recorded in
  ATTRIBUTION.md) and display-name mapping so the Control UI shows proper
  branding instead of the letter-badge fallback.
- Migrate the persisted legacy default baseUrl via a plugin-owned doctor
  configRepair contract: onboarding persists
  models.providers.longcat.baseUrl and the runtime reads the stored value,
  so the contract rewrites exactly the former default to /openai/v1,
  preserves custom endpoints, and warns via a legacy-config rule.

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

* fix(longcat): correct provider metadata

* fix(longcat): migrate persisted stock pricing

* style(longcat): format doctor repair

* fix(longcat): preserve doctor migration after compat cleanup

* fix(longcat): avoid map spread in doctor repair

---------

Co-authored-by: yuzehui02 <yuzehui02@meituan.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-08-10 20:15:18 -07:00
Peter Steinberger 4e22cee056 perf(ui): format sidebar timestamps on demand (#121819)
Amp-Thread-ID: https://ampcode.com/threads/T-019fe957-0e49-707f-859f-9600ef536125

Co-authored-by: Amp <amp@ampcode.com>
2026-08-10 20:09:52 -07:00
Vincent Koc c87f77185d fix(release): preserve frozen candidate identity (#121786)
* commit 'c3e3f276f2b520ffdf28e453eb464a6f4e50dfa0':
  fix(release): preserve frozen candidate identity
2026-08-11 11:08:13 +08:00
Marvinthebored bd897a6143 fix(exec): make timeout unit explicit (#119874)
* fix: exec timeout field does not state its unit, so callers pass milliseconds

`exec.timeout` is in seconds, but its sibling `yieldMs` is in milliseconds and
the `process` tool's identically named `timeout` is also in milliseconds. Those
two tools are used together in one workflow: exec backgrounds a long command,
then process polls it.

The unit exists only in the field description, and code mode deliberately defers
descriptions - the model sees `timeout?: number` and nothing else. A caller that
guesses milliseconds passes 900000 intending 15 minutes and gets ~10 days, so
the command never times out.

Add `timeoutSeconds` as the canonical field, matching the convention already
used by the config default (`tools.exec.timeoutSeconds`) and the sibling
(`yieldMs`). Keep `timeout` as a deprecated alias; `timeoutSeconds` wins when
both are supplied. Project the new field onto `nodeExecSchema`, which
hand-picks its properties.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* fix(ci): satisfy test-type and dependency checks

Two CI failures from the test approach, not the change itself.

- check-test-types: TypeBox's TOptional does not surface `description` on its
  static type, so the schema assertions failed under tsgo. Read it through a
  narrow accessor instead.
- check-dependencies: knip flagged resolveExecTimeoutSeconds as an unused
  export because only the test imported it. Move it beside the schema it
  resolves, where the exec runtime imports it, so it is production-reachable.

Verified with the same commands CI runs: `run-tsgo.mjs -p
test/tsconfig/tsconfig.core.test.json` reports no errors in these files, and
knip --production reports no unused exports.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* fix(ci): fit the exec description budget and satisfy oxlint

- `agent-tools.schema.test.ts` enforces a 550-char budget on the exec tool's
  model-facing descriptions; the two new ones pushed it to 640. Shortened both.
  The rename is what makes this possible: the unit now lives in the field name,
  so the description no longer has to carry it. Extended that budget test to
  pin `timeoutSeconds` alongside `timeout`.
- oxlint `no-base-to-string`: the test's description accessor typed the field as
  `unknown` and stringified it. Typed as `string | undefined` instead.

Verified: budget test passes (137 tests green across both suites), and all six
behavior tests still fail against origin/main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* fix: remove the unit-ambiguous exec timeout field instead of deprecating it

Owner decision on the review's "ambiguous model-visible alias" finding: drop
`timeout` entirely rather than keep it as a deprecated alias.

Keeping it visible defeated the purpose. Code mode renders property names and
types and defers descriptions, so a model would still be offered a bare
`timeout` sitting next to a millisecond-based `yieldMs` and could still pick it.
A deprecation marker in a description the model never sees changes nothing.

Backward compatibility is not required here: every tool call is constructed
fresh by the model from the current schema, and a repository search found no
internal caller passing `timeout` to the exec tool. `timeoutSeconds` is now the
only exec timeout field, on both the main and node-only surfaces.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* fix: migrate the internal exec callers to timeoutSeconds

Removing `timeout` from the exec schema broke three real internal callers that
the earlier survey missed, plus two test files and the runtime's own reads.
`check-test-types` caught it; my local search had filtered `timeout:` matches to
lines mentioning "exec" and discarded these.

- src/auto-reply/reply/bash-command.ts
- src/auto-reply/reply/commands-diagnostics.ts
- src/auto-reply/reply/commands-export-trajectory.ts
  all passed `timeout: timeoutSec` and now pass `timeoutSeconds: timeoutSec`.
- bash-tools.exec-run.ts reads the field through the same typeof narrowing the
  third call site already used; `params` is loosely typed, so a raw read is
  `unknown`.
- Test fixtures updated. A vitest `waitFor({ timeout })` option in
  background-abort.test.ts is NOT an exec param and was left alone.

`run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json` now reports zero errors
in changed files; the 5 remaining `rejectSymlinks` errors are pre-existing on
main. 148 tests pass across the affected suites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* docs: synchronize the exec config table with timeoutSeconds

Addresses the P2 finding. The tools.exec.timeoutSeconds row still described the
per-call override as `timeout` and `timeout: 0`, which no longer exist. The
only remaining bare `timeout` reference in this page is the deliberate
contrast with the process tool's millisecond field.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* fix: migrate the QA lifecycle caller and the shared ExecToolArgs contract

Two findings from the re-review of 6e8c3560e7, both real.

[P1] test/e2e/qa-lab/runtime/openclaw-exec-process-lifecycle.e2e.test.ts:174
still passed `timeout: 0.05`. After the removal that field is accepted and
ignored, so the call silently used the default timeout and could not produce
the asserted 50 ms `overall-timeout` result. Migrated.

[P2] ExecToolArgs in bash-tools.exec-request-preparation.ts:33 still declared
`timeout?: number` while the runtime reads only `timeoutSeconds`, offering
request-preparation users an accepted-but-inert argument. Renamed.

Why the typecheck missed both: ExecToolArgs is
`Record<string, unknown> & {...}`, and that index signature defeats excess
property checking, so a stale member produces no error at any call site. The
e2e file also sits outside the core test tsconfig I had been running locally.

Left alone deliberately: `processTool.execute({ action: "poll", timeout })` is
milliseconds and correct, and `createBashTool` in sessions/tools/bash.ts is a
separate tool surface with its own timeout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* fix(test): update the node exec tool property assertion to timeoutSeconds

src/gateway/tool-resolution.exclude.test.ts:413 asserts the exact property list
of the node-forced exec tool and still expected "timeout". Renamed to match
nodeExecSchema.

Found by checks-node-compact-large-5; my local core-test tsconfig does not cover
src/gateway, so it did not surface there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* fix: accept the legacy exec timeout as an undocumented alias

Removing the field from the schema does not reject it at runtime: the exec
schema accepts unknown properties, so a call built against an older schema was
accepted and then silently ignored, taking the default deadline instead of the
one it asked for.

Route every read through resolveExecTimeoutSeconds, which prefers the canonical
timeoutSeconds and falls back to a numeric timeout. The alias stays out of
execSchema, nodeExecSchema and ExecToolArgs, so no new caller can adopt it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* docs: update the background-process guide to timeoutSeconds

The exec parameter table and the inheritance note still taught `timeout` and
`timeout: 0`, which kept the retired spelling publicly discoverable and
contradicted the exec guide.

`poll`'s own millisecond `timeout` is left alone; it is a different tool and a
different unit, which is the collision this rename removes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQojYRCUdXhcKFeu4QW5Gh

* fix(exec): reject removed timeout field

* chore: regenerate plugin SDK API baseline

---------

Co-authored-by: Marvinthebored <262704729+Marvinthebored@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-08-10 20:02:53 -07:00
Peter Steinberger 00fb85e48f fix(openai): preserve hosted search in Code Mode (#121812)
Keep provider-hosted web_search alongside Code Mode's constrained function surface when OpenAI policy authorizes it. Enforce the same authorization at Responses transport egress.

Refs #121803.
2026-08-10 19:59:51 -07:00
Vincent Koc c3e3f276f2 fix(release): preserve frozen candidate identity 2026-08-11 10:48:08 +08:00
Peter Steinberger 9ae3583375 test(gateway): remove webchat audio test wrapper (#121810) 2026-08-10 19:48:03 -07:00
Peter Steinberger b44c9a8515 refactor(qa): simplify gateway lifecycle state (#121809)
Amp-Thread-ID: https://ampcode.com/threads/T-019fec02-d170-73eb-aec8-b364ab706745

Co-authored-by: Amp <amp@ampcode.com>
2026-08-10 19:47:45 -07:00
Peter Steinberger f5aba54437 refactor(agents): split Claude live sessions by concept (#121566)
* refactor(agents): split claude-live-session into concept modules

* refactor(agents): delete duplicated live-session helpers

* fix(agents): fence live-session close during pending spawn

* chore(lint): ratchet max-lines baseline after live-session split

* style(agents): satisfy lint on live-session split modules

* fix(agents): fence live session close state

* refactor(agents): extract Claude live turn timeouts

* fix(agents): preserve exec policy after Claude live split

* test(agents): complete Claude live policy session fixture
2026-08-10 19:39:16 -07:00
Peter Steinberger 17854d4c0b fix(runtime): restore main validation contracts (#121716)
* fix(runtime): repair main validation contracts

* test(runtime): enforce compact lifecycle payloads

* test(memory): preserve missing extra-path contract

* test(ci): drop repairs already on main

* style(test): align current-main fixture

* fix(memory): preserve missing-error type contract

* fix(memory): reject invalid extra-path parents
2026-08-10 19:35:29 -07:00
Peter Steinberger fada067277 feat(browser): add zero-click Chrome extension bootstrap (#121586)
* feat(browser): add zero-click extension bootstrap

Pre-register deterministic path-derived extension IDs and install a strict native messaging host.

Keep the popup and options UI minimal while removing the obsolete copilot and page-share flows.

* fix(browser): satisfy native bootstrap CI guards

* test(browser): isolate native bootstrap Chrome roots

* test(browser): flush native bootstrap profile before status

* test(browser): seed Linux native bootstrap identity

* fix(browser): preserve native bootstrap upgrade safety

Allow immutable root-owned package inputs while keeping mutable state, manifests, and launchers user-owned. Preserve all retired copilot keys whenever active or unrecognized recovery custody remains.

* fix(browser): preserve pending copilot custody

Retired cleanup now removes copilot state only when the durable registry is exactly empty. Any session, archive, malformed value, future shape, or read failure preserves every retired key.

* fix(browser): guard native bootstrap upgrades

Fail closed while retired copilot custody remains and make discard durable across partial failures.

Require exact launcher-embedded origins and repair full launcher drift without accepting mismatched registrations.

* fix(browser): remove stale layout export

* chore(release): leave changelog to release flow
2026-08-10 19:31:13 -07:00
Peter Steinberger d6f70a96cb fix(plugins): native commands execute the selected plugin (#121544)
* fix(plugins): preserve selected command identity

* test(telegram): use scoped command registries

* test(telegram): isolate command runtime fixtures

* test(telegram): warm native command runtime

* refactor(plugins): keep command metadata private

* fix(plugins): accept synchronous command handlers

* fix(plugins): scope command drain bypass to live execution

* test(telegram): use scoped command registry fixtures

* test(telegram): isolate native menu runtime fixtures

* test(telegram): isolate login session store

* test(telegram): surface login flow failures

* test(telegram): preload native login module

* test(telegram): scope native command registries

* fix(plugins): complete command dispatch contracts

* fix(plugins): break command dispatch import cycles

* fix(plugins): stabilize command dispatch contracts

* fix(channels): keep plugin dispatch options internal

* fix(plugins): keep command dispatch carrier opaque

* test(channels): align delivery adapter fixtures

* test(delivery): align custody ownership coverage

* test(delivery): align latest queue reconciliation

* test(channels): drop obsolete delivery wrappers

* fix(plugins): rebind channel reload starts

* fix(plugins): scope command catalog reloads

* fix(ci): align current runtime contracts

* chore(plugin-sdk): refresh API baseline
2026-08-10 19:30:47 -07:00
Josh Avant bdd35432dd perf(checks): accelerate fresh changed gates (#121805) 2026-08-10 21:30:24 -05:00
Vyctor H. Brzezowski b953cb2597 fix(ui): rename and delete session groups without browser prompts (#121738)
* fix(ui): rename and delete session groups in owned dialogs

The sidebar group menu was the last session surface still asking browser
chrome: Rename group opened window.prompt and Delete group opened
window.confirm. Both are unthemed, unvalidated and unavailable in the
webviews the Control UI also ships in, where they answer silently.

Rename now uses the owned input dialog, prefilled with the name it is
changing and titled with it. showInputDialog gains requireChange, which
holds submission closed while the entry still equals defaultValue, so the
no-op rename the caller used to discard after the fact is not submittable
in the first place. That replaces the caller-side "next === group" bail and
puts the empty check on one predicate shared by the button state and the
submit path.

Delete now uses the owned danger confirm, beside the other destructive
session mutations in the lazily loaded operations module, with Cancel
focused and no opt-out. Its copy said the sessions move to "Ungrouped", a
section id no operator ever sees; the list is labelled Sessions, so it now
says they move back to the session list. Deleting a group really does keep
them: session-groups clears the category on every member rather than
removing anything.

The confirm follows the access check so nobody is asked about a delete the
Gateway would refuse, and the mutation scope is reproven once they answer.

* test(ui): freeze animations in Control UI proof captures

A dialog capture taken during its fade-in shows a half-transparent card over
the page behind it, which proves nothing about the state it was taken for.
Playwright can settle running transitions before the shot.

* fix(ui): stop repeating the group name in the delete confirm

The title said Delete group and the message repeated it with the name, so
the operator read the same three words twice before reaching what actually
happens. The title now carries the group it is about and the message is
left to state the outcome.

* fix(ui): keep stale group deletes retryable

* fix(ui): match the stale delete notice to its sibling

The retry notice led with the negation and used curly quotes the rest of
this dialog family does not, so it read as a different product's copy. It
now states the cause and the next step the same way newGroupStale does for
the same replaced connection.
2026-08-10 23:27:39 -03:00
Peter Steinberger 7bd44eb440 fix(agents): retry failed subagent cleanup with backoff (#121802) 2026-08-10 19:26:30 -07:00
Peter Steinberger 05a9a941f8 test(gateway): remove metadata test facade (#121801) 2026-08-10 19:24:43 -07:00
Peter Steinberger 8595bf7459 test(skills): canonicalize workshop workspace paths (#121800) 2026-08-10 19:23:55 -07:00
Vyctor H. Brzezowski 4ea11b93c1 fix(ui): open the session companion from one header toggle (#121652)
* fix(ui): open the session companion from one header toggle

The collapsed rail was a button floating over the message area whose click
set the display preference to "pill", so opening the companion took three
interactions and the first two landed on surfaces nobody asked for. On an
idle session the pill had no digest to show and degraded to a bare title
with an X and a chevron.

The control moves into the pane header action row beside background tasks,
workspace files, and split view, where it carries aria-expanded and closes
the panel when pressed again. Opening now expands transiently instead of
persisting "card", matching the automatic path from /btw and the transcript
selection actions; the pill stays as the ambient density for a running
digest. Closing drops the manual-open claim so an idle session falls back to
nothing rather than to an empty pill.

The panel's empty state offers three starter questions instead of a sentence
about being read-only, the digest band no longer renders when there is no
digest, the destructive thread clear moves into an overflow menu, and the
composer input is anchored to its footer.

Closes #121440

* test(ui): cover the session rail header action slot

* fix(ui): label the companion composer without the undefined sr-only class

The composer's screen-reader label used class sr-only, which no stylesheet
defines, so "Ask the session companion" rendered as visible text above the
input. An aria-label on the input carries the same information to assistive
technology and does not depend on a missing utility class.

The class is applied by roughly fifteen other Control UI modules and is
tracked separately; this change only removes the rail's dependency on it.

* refactor(ui): read the mirrored rail mode through one accessor

* docs(ui): state why collapsing the rail keeps observer visibility

* fix(ui): dock the session companion as a side column instead of a card

The companion opened as a floating card over the conversation on any pane
narrower than 1080px, which is most of them once a sidebar or a split pane
takes its share. It covered the transcript the operator was reading and read
as a pop-up chat rather than a surface belonging to the pane.

Docking is now the default whenever the column and a readable thread both
fit, measured against the width left after the workspace and task rails take
theirs — the same way the workspace rail decides. The docked rail claims
layout space, so the thread reflows narrower instead of being painted over,
and it drops the shadow, blur, and animation that made it read as an overlay.

Below that width no pane can hold two columns, so the companion becomes a
full-height sheet on the trailing edge, and a full-screen sheet under 768px.
It still covers the thread there, but as a surface that took the pane over
rather than a card hovering above it.

The starters now sit where the first exchange will land; centring stranded
them mid-column once the rail became full height.

* fix(ui): give the companion the app's send button and a bottom-left opener stack

The rail composer had its own ghost submit icon, which made a second send
pattern in a product that already has one. It now renders chat-send-btn with
icons.arrowUp — the same component and classes the main composer uses, so the
brand circle and every state come from one place instead of a lookalike.

The openers move out of the thread and stack against the composer they feed,
left-aligned, so the eye travels chips to input without crossing the thread.
The explainer keeps the thread's empty space to itself, centred on both axes.

Also drops the now-unused submit colour rule and three svg declarations that
strokeIcon already inlines.

* test(ui): bound the rail against its container, not the old card cap

Three browser assertions encoded the floating card's max-height (680px
desktop, 460px mobile). The docked rail and the narrow sheet are deliberately
full height, so those numbers no longer describe anything.

The invariant they protected is still real and still asserted: a long thread
scrolls inside itself instead of the rail outgrowing what contains it. The
bound is now the pane's own height on desktop and the viewport on mobile,
which is what "do not expand to swallow the layout" actually means.

* fix(ui): stop rail metadata inheriting the thread's scroll layout

Removing the duplicate __empty declaration took its closing brace with it, so
the pr-checks, timestamp and hint selectors merged into the thread's rule.
Each would have rendered as a 96px scrolling bordered flex column instead of
a muted line of metadata.

The narrow sheet also kept the base rule's shadow and blur, which are card
cues on a surface that is meant to read as part of the pane; it drops them,
and the docked rule stops repeating resets it now inherits.

The fixture grows a timestamp and a hint so a browser assertion can hold the
metadata and the thread apart — the empty-companion captures never rendered
either node, which is why this reached review.

* fix(ui): reset the undocked sheet's fade-in and cover the metadata layout

The expanded sheet reset the shared shadow and blur but still inherited the
rail's fade-in, so a narrow-width open animated in like the floating card the
redesign replaces. The reset now lives on the expanded rule and the docked
rule stops repeating what it inherits.

Coverage follows the defect rather than the fix: the browser fixture renders
PR checks alongside the timestamp and hint, and the undocked-sheet assertion
now reads computed shadow, blur, and animation instead of geometry alone.

* test(ui): compare rail metadata against the thread, not a literal

min-height computes to auto on these grid items, so asserting 0px failed on
the metadata the guard was meant to protect. The invariant is that the nodes
do not share the thread's rule, so compare against the thread's own value and
let it change without breaking the guard.
2026-08-10 23:23:52 -03:00
Jesse Merhi d90e47783d fix(plugins): remove local dependency denylist (#101813) 2026-08-11 12:21:29 +10:00
Peter Steinberger 36c3f38a3a improve(ui): make warm session switches settle faster (#121792)
* perf(ui): reuse sidebar session projection per update

Amp-Thread-ID: https://ampcode.com/threads/T-019fe957-0e49-707f-859f-9600ef536125

* perf(ui): keep reply callback out of transcript renders

Amp-Thread-ID: https://ampcode.com/threads/T-019fe957-0e49-707f-859f-9600ef536125

* fix(ui): invalidate chat rows on media updates

Amp-Thread-ID: https://ampcode.com/threads/T-019fe957-0e49-707f-859f-9600ef536125

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-10 19:19:14 -07:00
Peter Steinberger 76b6496f34 perf(gateway): defer non-startup runtime imports (#121780)
Amp-Thread-ID: https://ampcode.com/threads/T-019fec00-3abd-710b-b9de-8925c23ad779

Co-authored-by: Amp <amp@ampcode.com>
2026-08-10 19:17:40 -07:00
Vito Cappello 9320bd379e fix(ollama): enable cloud max thinking (#121074)
Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
2026-08-10 19:06:54 -07:00
Peter Steinberger 498433a58a test(ui): remove output-root wrapper seam (#121794) 2026-08-10 19:04:03 -07:00
Vincent Koc e9b687a628 fix(agents): bind plugin registry for direct ingress (#121787) 2026-08-11 10:00:56 +08:00
Peter Steinberger a381aa3e8b fix(memory): restrict multimodal indexing to extra paths (#121627)
* fix(memory): restrict multimodal indexing to extra paths

* fix(state): add lazy secret store schema ensure

* fix(state): bound lazy secret store schema

* chore(plugin-sdk): refresh API baseline
2026-08-10 19:00:45 -07:00
Peter Steinberger 52e3149ed7 feat(secrets): inject store env into agent exec (#121773) 2026-08-10 18:59:32 -07:00
Peter Steinberger 8aa0376f27 refactor(agents): simplify turn latency runtime ownership (#121779)
Amp-Thread-ID: https://ampcode.com/threads/T-019fe95b-d75c-70fc-82b6-4825ca874826

Co-authored-by: Amp <amp@ampcode.com>
2026-08-10 18:58:36 -07:00
Peter Steinberger 58a538767c fix(security): centralize provider diagnostic redaction (#121599)
* fix(security): centralize provider diagnostic redaction

* fix(workers): redact finishing diagnostics

* fix(security): fail closed on malformed diagnostics

* fix(security): redact media wrapper diagnostics

* fix(security): normalize diagnostic field redaction

* fix(security): redact diagnostic media URIs

* fix(ai): preserve diagnostic compatibility in error projection

* refactor(ai): simplify diagnostic error handling

* fix(security): redact credential query diagnostics

* fix(security): redact diagnostic media arrays

* fix(security): redact diagnostic credential headers

* test: align shared expectations with current runtime contracts

* test(ui): align managed media and picker expectations

* fix(security): redact plural media diagnostics

* fix(ai): preserve bracketed provider errors

* fix(security): cover generic diagnostic credentials

* fix(security): redact prefixed diagnostic JSON

* fix(security): redact embedded diagnostic JSON

* test(agents): align cache trace media redaction

* fix(security): redact cookie diagnostics

* refactor(ai): isolate diagnostic host policy

* refactor(ai): narrow diagnostic helper exports

* fix(security): bound diagnostic descriptor reads

* fix(security): bound diagnostic graph traversal
2026-08-10 18:53:45 -07:00
Peter Steinberger ba58803997 fix(models): repair local model no-auth and overflow handling (#121790)
Preserve the internal null Authorization marker when resolving local no-auth provider headers, avoid classifying LM Studio prompt-template guidance as context overflow, and rebuild ai/llm-core package sources during pnpm dev/watch.
2026-08-10 18:52:00 -07:00
Patrick Erichsen abb856ece4 fix(release): restore frozen beta validation (#121791)
Punchcard-Session: coral-summit-brook-8j
2026-08-10 18:45:42 -07:00
Peter Steinberger 595ac6eb28 test(agents): remove aggregate command test facade (#121789) 2026-08-10 18:42:31 -07:00
Peter Steinberger b985d5a1ed feat: stream live subagent progress in task events (#121549)
* feat(tasks): stream live subagent activity

* fix(tasks): correlate edit activity from start events

* fix(gateway): sanitize live task activity

* test(reply): align custody harnesses with lifecycle

* fix(outbound): preserve post-send recovery evidence

* fix(types): align reply harnesses with current contracts

* fix(tasks): preserve activity liveness and SDK parity

* test(sdk): avoid unsafe task summary assertion

* chore(contracts): refresh Plugin SDK API baseline
2026-08-10 18:37:58 -07:00
Peter Steinberger b6b937d6ac chore(types): give test helpers nameable exported types (#121783) 2026-08-10 18:17:50 -07:00
Peter Steinberger dfb4b3658e fix(agents): stop orphan delivery polling and delayed yield wakes (#121782)
* fix(agents): dead-letter expired orphan completion deliveries

* fix(agents): let a fresh yield wake preempt a stale retry timer
2026-08-10 18:17:20 -07:00