mirror of
https://github.com/lobehub/lobehub.git
synced 2026-09-20 04:56:13 +08:00
fix/refer-topic-message-rendering
77 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
585fa593a5 |
🔖 chore(cli): bump version to 0.0.55 (#19618)
Ships the hetero `/goal` conversation flow (#19588), the operation-token renewal for long hetero exec runs (#19549), the wrapped artifact share URL (#19536) and readable task slugs (#19503). Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
15c5a6fb05 |
🔖 chore(cli): bump version to 0.0.54 (#19451)
Ships `lh artifact publish` (#19360). Claude-Session: https://claude.ai/code/session_019jVhraxkznG9MtkXaux657 |
||
|
|
6b608fa53e |
🐛 fix(cli): declare url-join and vitest so the standalone install builds (#19408)
* 🐛 fix(cli): declare url-join and vitest so the standalone install builds `apps/cli` installs against its own workspace, but `src/utils/agentStream.ts` imports `url-join` and the tests import `vitest` while neither is declared there — both resolve only through the root install's hoisting. CI does the root install first, so this stayed invisible until someone builds the CLI on its own: Could not resolve 'url-join' in src/utils/agentStream.ts `src/runtime/*` also imports `@lobechat/agent-runtime` and `@lobechat/conversation-flow` undeclared, and those are deliberately left alone: registering them pulls their workspace closure — 16 more packages including `@lobechat/database` and a dozen builtin tools — into an install kept slim on purpose. Declaring them properly is a bigger decision than this fix. Verified by removing node_modules and running `pnpm install` + `bun run build` inside `apps/cli` with no root install. Claude-Session: https://claude.ai/code/session_01558yVdNvoxocvnWAs11DGT * 🔒 chore(desktop): sync lockfile with the CLI dependency declarations apps/desktop lists ../cli as a workspace member and keeps a lockfile, so declaring url-join and vitest in apps/cli left it outdated and failed Test Desktop App with ERR_PNPM_OUTDATED_LOCKFILE. Claude-Session: https://claude.ai/code/session_01558yVdNvoxocvnWAs11DGT |
||
|
|
b1a9979c5f |
✨ feat(cli): publish a local HTML file as an artifact (#19360)
* ✨ feat(cli): publish a local HTML file as an artifact `lh artifact publish <file>` gathers the page's local references, packs them, and publishes the result. The page's identity is a deployment id, recorded in `.lobehub/artifacts.json` next to the project and committed with it, so a publish from CI updates the same site a developer created locally. Republishing adds a revision and keeps the public URL; `--new` makes a separate site and rebinds; `--deployment <id>` targets one for a single run without touching the manifest. A bound id that no longer resolves is an error — falling back to create would silently strand the URL the caller meant to update. Assets are read straight off the filesystem, so `--root` (default: the working directory) sets how far up a page may reach for them. That replaces the app's sandbox boundary, which does not apply here: the CLI reads with the user's own permissions, and a page under `dist/` referencing `../assets/logo.png` is ordinary rather than an escape attempt. A create carries an idempotency key, written to the manifest *before* the request. If the response is lost, the retry reuses the key and the server returns the deployment it already made instead of minting a second site and burning another quota slot. Updates are addressed by id and deliberately carry no key: a retry costs a spare revision, never a stray site. Claude-Session: https://claude.ai/code/session_01558yVdNvoxocvnWAs11DGT * 📝 docs(acceptance): record what the CLI artifact-publish round had to learn Two judgment rules and two probe recipes from verifying `lh artifact publish` against a local cloud → market → artifact-host chain. L-E21 is the one an acceptance-checker caught mid-round: the case asserting `--new` leaves the original site untouched published two sites with identical bytes and a request line that echoed only the response. It read as a pass while proving nothing, because requesting the wrong site would have printed the same output. The rule generalises — make the two objects distinguishable before capturing, and echo the target in the request. L-S22 covers the quota the round consumes itself: each publish leaves an active deployment, the Free tier caps at three, and the third run fails in a way that reads as a regression. Compounded by clearing the cap for the wrong account — the CLI publishes as the seeded runtime user, not the smoke's default id. P84 records how to authenticate the CLI against lobehub-cloud's dev runtime at all (seed an api_keys row; `dev:runtime:auth` only writes browser cookies), plus the port-override that every runtime subcommand needs. P85 records how to simulate a publish whose response was lost, which cannot be produced by killing the process because the manifest is what carries the recovery state. Claude-Session: https://claude.ai/code/session_01558yVdNvoxocvnWAs11DGT * 🐛 fix(cli): register @lobechat/html-artifact in the CLI's own workspace `apps/cli` installs standalone against its own `pnpm-workspace.yaml`, which enumerates the packages it may link rather than globbing them. The root install resolved the new dependency, so this only showed up as CI's "Install CLI deps" step failing: "@lobechat/html-artifact@workspace:*" is in the dependencies but no package named "@lobechat/html-artifact" is present in the workspace Claude-Session: https://claude.ai/code/session_01558yVdNvoxocvnWAs11DGT * 🐛 fix(desktop): register @lobechat/html-artifact in the desktop workspace too `apps/desktop/pnpm-workspace.yaml` lists `../cli` as a member, so the CLI's new dependency has to be resolvable from the desktop install as well — its "Install deps" step failed the same way the CLI's did. Unlike the CLI workspace this one sets `lockfile: true`, so the regenerated lockfile ships with it. Claude-Session: https://claude.ai/code/session_01558yVdNvoxocvnWAs11DGT |
||
|
|
9dfe9242ec |
🐛 fix(cli): keep workspace packages inside the embedded CLI bundle (#19391)
The desktop app builds `apps/cli` from the workspace it installs at `apps/desktop`. pnpm 12 writes those out-of-root member links with two extra `../` segments, so `apps/cli/node_modules/@lobechat/device-control` and `@lobechat/device-identity` dangle. Rolldown only warns, marks both imports external and reports a successful build, so canary 2.2.17-canary.18 onwards shipped a `lobe-cli.js` that dies at startup with `ERR_MODULE_NOT_FOUND: @lobechat/device-control`, even for `--version`. Repair the dangling links before bundling, and turn an unresolved import into a build failure so a broken bundle can never ship silently again. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b65aae96b6 |
👷 build: migrate workspace to pnpm 12 (#19324)
* 👷 build: migrate workspace to pnpm 12 pnpm 12 no longer reads the package.json "pnpm" field, only honours auth and registry keys from .npmrc, and requires allowBuilds instead of onlyBuiltDependencies. Move every project setting into pnpm-workspace.yaml and keep the previously effective overrides and build allowlist. * 👷 build: resolve desktop and cli workspaces on pnpm 12 pnpm 12 no longer accepts './'-prefixed workspace package globs, so the desktop stub path is now relative without the prefix. Migrate the remaining package.json "pnpm" fields into the desktop and cli pnpm-workspace.yaml files. * 👷 build: list protobufjs in the desktop build allowlist pnpm 12 fails the install with ERR_PNPM_IGNORED_BUILDS when a script-bearing dependency is missing from allowBuilds. protobufjs only appears in the desktop graph on Linux, so it was absent from the previous allowlist. * 👷 build: relink root workspace members before the desktop typecheck pnpm 12 writes broken relative symlinks for workspace members that live outside the desktop workspace root (../../packages/*), so tsgo cannot resolve sibling packages from source. Installing the root workspace after the desktop install rewrites those links correctly. |
||
|
|
c209af302f |
🔖 chore(cli): bump version to 0.0.53 (#19340)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
675e98554e |
🐛 fix(cli): wait for daemon startup readiness (#19000)
* 🐛 fix(cli): report daemon startup failures * ♻️ refactor(cli): use tinyexec for daemon processes * 🐛 fix(cli): complete daemon startup preflight |
||
|
|
14abb41438 | 🔖 chore(cli): bump version to 0.0.52 (#19009) | ||
|
|
66c0a001be |
✨ feat(goal): parallel task scheduling, trace fixes and CLI 0.0.51 (#18944)
* 🐛 fix(goal): point a running goal's trace key at the object that exists `writeObservationRow` wrote the finalized S3 key from the first advance, but while a goal runs only the partial exists. So `hasTrace` read `yes` for every in-flight goal, `getGoalTrajectoryUrl` signed a URL that 404s, and a goal could not be inspected from the server until it was over — the opposite of the "an unfinished long-horizon goal is the normal thing to inspect" the recorder was written around. Found by running it: the first real production goal listed with `TRACE yes` and then failed to download. Unit tests could not have caught it — they drive an in-memory store where both keys are the same map, and the bug lives in the gap between what the row claims and what object storage holds. The key now follows the object — partial while running, finalized once the goal is terminal, which is exactly when `finalizeGoalTrace` removes the partial. The client cache had the mirror-image bug: caching an in-flight trajectory pinned the reader to whatever the goal looked like the first time it was inspected. Only finished goals are cached now, and an older cached partial is re-fetched rather than served. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ✨ feat(goal): run independent tasks in parallel, under a concurrency cap Four bug fixes that share no code queued behind one another for an hour. Not because of dependencies — the trace recorded the other three as candidates with `blockedBy: []`, so the coordinator knew they were independent and started them anyway one at a time. Three things made the frontier single-threaded, and all three had to go: - `selectFrontier` returned one node. A running node stays eligible, so it was re-picked every tick. - that re-pick reported `waiting_external`, which ends the advance before anything behind it is considered. - dispatching also reported `waiting_external`, so even a fixed frontier would have started exactly one task per advance. The decision now walks the whole ranked frontier, skips what is already in flight or parked on a person, and takes the first candidate it can act on; dispatching reports `advanced` so the advance keeps filling slots. `maxConcurrentTasks` caps the fan-out — defaulting to 3, because a goal that dispatched everything at once would spend its budget before the first result came back. A running task is still examined for staleness rather than skipped outright. Skipping it made abandoned operations unreclaimable: the reclaim only ever runs from the branch that looks at a running task, and an integration test caught that the first version had quietly removed it. The recorded decision input grew with the decision. The scheduler reads every candidate's task state, so a trace that kept only the chosen one would no longer let a replay reproduce the choice — `candidateTasks` and the cap in force are recorded instead, and the replay adapter rebuilds the map. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * 🐛 fix(cli): link `lh goal create` at the goal it just created `goal.create` returns the whole graph snapshot, so the id lives on its `goal`. Reading `result.data.id` printed `app.lobehub.com/goal/undefined` — a link nobody could follow — right under a graph that `printGraph` had just rendered from `graph.goal.id` on the next line. Same blind spot as #18912: the CLI cannot resolve the TRPC router's types, so `result.data` widens to `any` and reading a field that isn't there compiles. `apps/cli` is also outside the root `tsconfig.json` include and outside the CI test matrix, so neither the type checker nor CI would have caught it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * 🔖 chore(cli): bump version to 0.0.51 0.0.50 was published on 08-30, and seven changes have landed on `apps/cli` and `@lobechat/agent-tracing` since — including `lh trace goal`, which is not in the published package at all, and the glyph fix for `lh goal show`, which is only visible to someone running `lh`. The man page is regenerated with the bump: it picks up the version string and Factory Droid joining the heterogeneous agent list. `lh trace goal` adds no entry because the page lists top-level commands and `trace` was already there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * 🐛 fix(goal): close the four gaps Codex found in the parallel scheduler **The budget stopped guarding paid work (P1).** `needsBudget` was asked of the frontier's head, which was safe while the head was the only thing that could be dispatched. The scheduler now walks past a running head, so a head reporting "no budget needed" meant the budget was never read — and `budget_exhausted` can only fire on a budget that was read. A goal could start work past its `maxRounds` / `maxTotalCost`. It is asked of every unblocked candidate now, and counts a retryable failure as spending, because it is. **The cap was advisory, not enforced (P1).** Two overlapping advances could each read the same `inFlight` below the limit and each claim a different ready task; the per-task CAS only prevents dispatching the *same* task twice. The count and the claim are taken together under a per-goal advisory lock, with the in-flight count read from the database rather than from a snapshot — the planner's check is a fast path, this is the enforcement. **Legacy trajectories replayed as `missing_task`.** Ticks recorded before this change carry `frontierTask`, not `candidateTasks`. Dropping the field made every one of those replay with no task state, reporting divergences that never happened. It stays as a deprecated field the replay adapter falls back to. **The cap could not actually be set.** `GoalConfig` advertised `maxConcurrentTasks` while the `goal.create` Zod schema still allowed only `config.recovery`, so Zod stripped it and every API-created goal silently used the default. The schema accepts it with bounds mirroring `resolveMaxConcurrentTasks`, and `lh goal create --max-concurrent-tasks` exposes it — a cap reachable only by hand-written TRPC is not a cap anyone can use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * 🐛 fix(goal): keep parallel scheduling moving --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6beffeb677 |
🔖 chore(cli): bump version to 0.0.50 (#18860)
Co-authored-by: Arvin Xu <arvin.x@lobehub.com> |
||
|
|
77be8bb286 |
✨ feat(cli): lh trace op — inspect and replay a recorded operation, single call or whole trajectory (#18797)
* ✨ feat(cli): add `lh trace replay` to re-issue a frozen LLM call across models An execution snapshot already freezes everything a single LLM call saw — `steps[].contextEngine.output` is the exact message array sent to the model, `context.payload.tools` the toolset it could reach — but nothing consumed it. Diagnosing a bad answer meant re-running the whole agent, which re-assembles context and so mixes the harness and the model into one unattributable result. `lh trace replay` sends that frozen payload back out with only the model swapped, so a difference in output is attributable to the model. If every model fails the same payload, the context assembly is at fault; if some pass, it is model selection. - `packages/agent-tracing`: extract the local → `_remote` cache → download resolution that inspect / resolve / ctx-lint each reimplemented into a shared `loadSnapshot()`, and export it plus the remote-store helpers. Downloads stay opt-in so read-only callers never hit the network. - `apps/cli`: new `trace` command group with `replay`, defaulting to the last `call_llm` step and to the model the snapshot ran on. - `--judge <criteria>` scores each output through `@lobechat/eval-rubric`'s `llm-rubric` matcher, so replay scoring is the same code path the eval pipeline uses — only the transport is local. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit |
||
|
|
9031535652 |
🔖 chore(cli): bump version to 0.0.49 (#18622)
Co-authored-by: Arvin Xu <arvin.x@lobehub.com> |
||
|
|
4eb459c146 | 🔖 chore(cli): bump version to 0.0.48 (#18447) | ||
|
|
f5a941ffeb |
🔖 chore(cli): bump version to 0.0.47 (#17888)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0b5af01cef |
✨ feat(desktop): preview workspace HTML files (#17545)
* ✨ feat(desktop): preview workspace HTML files * ♻️ refactor(mime): unify MIME detection via @lobechat/utils/mimeType Fold the ad-hoc extension→MIME maps in desktop, CLI, and device-control into a single buffer-aware resolver. `resolveMimeType(path, buffer)` trusts file-type's magic-byte detection first, then falls back to mime.getType with a text-sniff downgrade so ambiguous extensions like `.ts` (video/mp2t vs TypeScript source) resolve correctly everywhere. * fix: resolve html preview ci failures |
||
|
|
ebacf7fc6e | 🔖 chore(cli): bump version to 0.0.46 (#17471) | ||
|
|
84f79b95d3 |
🔖 chore(cli): bump version to 0.0.45 (#17358)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0100fb61fc |
♻️ refactor(cli): make lh acceptance the home of the review loop (#17336)
* ♻️ refactor(cli): move run/result/evidence/report/init under `lh acceptance` Reorganize the CLI so the acceptance decision aggregate is the home for the whole review loop, and `verify` keeps only the checker machinery (criteria, rubrics, per-run plans). - New canonical tree: `lh acceptance init` and `lh acceptance run …` (ingest, list, get, delete, result{ingest,list,submit,decision}, evidence, report). `ingest-report` becomes `acceptance run ingest`; a run is one immutable round. - `verify` retains criterion / rubric / plan / execute / install / submit-less machinery. Everything moved keeps a deprecated `lh verify …` alias (same flags, same action, one deprecation warning) for a few releases. - Extract shared helpers into verifyHelpers.ts (re-exported from ./verify for existing importers); action bodies live once in acceptanceRun.ts and wire both the canonical and deprecated spellings, so they can't drift. - `init` now defaults to the `acceptance` skill bundle. Tests: 52 passing — deprecated aliases still dispatch, the canonical run tree dispatches, and the run subtree is absent from the deprecated `verify acceptance` alias. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 📝 docs(acceptance): repoint skills & prompts to the `lh acceptance` surface Follow the CLI reorg through every consumer of the old `lh verify …` command surface. - agent-testing skill (both `.agents/skills/**` and the checked-in CLI mirror `apps/cli/skills/**`): `lh verify ingest-report` → `lh acceptance run ingest`, `lh verify acceptance …` → `lh acceptance …`, atomic-command references → `lh acceptance run {result,evidence,report} …`; "verify run" (the noun) → the acceptance run it now is. - Rename the portable builtin skill `verify` → `acceptance` (`builtin-skills/src/verify` → `.../acceptance`): identifier/name, exports (AcceptanceIdentifier/AcceptanceSkill), and its content's command references (`lh verify submit` → `lh acceptance run result submit`; plan machinery stays `lh verify plan …`). `lh acceptance init` materializes it into `.claude/skills/acceptance`. - Server pull registry serves identifier `acceptance`, with `verify` kept as a back-compat alias so cached `--skill verify` callers still resolve. - Task prompt tells builders to submit via `lh acceptance run result submit`. Tests: prompts task suite green (22), CLI suite green (52). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ♻️ refactor(cli): unify skill install under `lh acceptance install` (server pull) Collapse the two skill-materialization paths into one. `acceptance init` (server pull) and `verify install` (bundled agent-testing copy) become a single `lh acceptance install` that pulls the acceptance skeleton from the server and writes it to `.agents/skills/acceptance` — the harness dir the project's own `.agents/acceptance/` adapter sits beside. - Drop the CLI-bundled skill entirely: remove `verifyInstall.ts`, `apps/cli/skills/`, and `"skills"` from the published `files` — server pull is the single source. - Both legacy spellings (`verify init`, `verify install`) stay as deprecated aliases that route to `installAction`. - Install target moves `.claude/skills` → `.agents/skills`. Tests: CLI suite green (52). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🚚 chore(acceptance): rename project adapter .agents/verify → .agents/acceptance The project-local adapter layer (PROJECT.md env/port/auth contract, LobeHub- specific hook scripts, and the living-log notes) moves to `.agents/acceptance/`, matching the skeleton it feeds (`.agents/skills/acceptance`). All references across the agent-testing skill and the adapter's own files are repointed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🗑️ chore: remove accidentally-committed node_modules symlinks Two symlinks (apps/cli/node_modules, packages/builtin-skills/node_modules) pointing into a local checkout slipped into the previous commit via `git add -A` — .gitignore only ignores node_modules directories, not symlink files. Untracked; they must never ship in the repo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
66a2bb54ff |
feat(cli): generic agent-testing skill + lh verify install (#17280)
* feat(cli): add generic agent-testing skill docs * feat(cli): port generic agent-testing scripts Copy the eight generic agent-testing scripts (plus their test suite and the cdp-capture.cjs dependency) from .codex/skills/agent-testing/scripts/ into apps/cli/skills/agent-testing/scripts/, stripping the fixed repo-relative directory walk-ups so each script self-locates from $0/BASH_SOURCE/import.meta.url and treats the invoking cwd as the consumer repo, per the T1 skill docs already on this branch. * feat(cli): package agent-testing skill and add lh verify init install logic Ship the bundled agent-testing skill inside the published @lobehub/cli package (files += skills), add a package-root locator that walks up from import.meta.url so it resolves from both the built dist/index.js and the monorepo dev entry, and give cdp-capture.cjs a .skill-meta.json/cliRoot fallback for resolving `ws` once the skill is copied into a consumer repo (where the ancestor node_modules walk no longer reaches @lobehub/cli). Also adds the install logic for `lh verify init` (harness-dir detection, copy + exec-bit restoration, .skill-meta.json version marker, semver upgrade/no-op, refuse-without-marker + --force, .gitignore hygiene) as a standalone module with full edge-case test coverage. Not yet wired to a CLI subcommand — `verify init` is already taken by an existing tRPC-based skill-bundle puller (PR #16164); naming/merge is pending a decision. * feat(cli): wire lh verify install, distinct from the existing verify init Register a new `verify install` subcommand that copies the bundled agent-testing skill into the consumer repo's harness skills dirs (.claude/.codex/.agents), per the human's naming decision — `verify init` (PR #16164, pulls a named skill bundle live from the server) is left completely untouched. Renames verifyInit.ts/.test.ts to verifyInstall.ts/.test.ts for naming consistency with the command, and adds command-level tests (registration, happy path, --json, --target, no-harness-dir failure) alongside the existing core install-logic tests. The command-level test for the no-harness-dir case caught a real bug: the NoHarnessDirError catch handler called process.exit(1) without returning, so it fell through to `throw e` under a mocked (non-terminating) process.exit — fixed with an explicit `return`. Also fixes two stale `lh verify init` references left over from before the naming decision, in project-adapter.md and cdp-capture.cjs's comments. * refactor(agent-testing): migrate lobe-chat to the generic skill + adapter Make lobe-chat the first consumer of the generic agent-testing skill: - Add .agents/verify/ adapter: PROJECT.md (six-section contract capturing the env/auth/probe/constraint knowledge from the old SKILL.md), the project-layer living logs (common-mistakes.md, probe-mock-patterns.md — the LobeHub-specific entries not promoted to the generic layer), the moved project scripts (init-dev-env / setup-auth / app-probe / test-env / electron-dev / start-s3 / check-s3 / record-electron-demo / agent-gateway), and the LobeHub reference docs (auth / dev-server / agent-gateway / multi-instance). Script self-locate depths and internal path references updated for the new location. - Replace the old .agents/skills/agent-testing with the generic skill installed via `lh verify install` (bundled generic SKILL.md, surfaces/, references/, scripts/, .skill-meta.json). - Extract the bot-channel surfaces into a standalone project skill .agents/skills/agent-testing-bot, extending the generic Plan/Execute/Finish process; cross-links repointed to the sibling generic skill. - Fix cross-skill reference in ux-audit (agent-testing ui/ -> surfaces/). * docs(agent-testing): refine project-layer probe-mock log Drop two entries duplicated by the generic layer (daemon-serializes, curl-502 proxy env) and restore three project entries missing from the first cut: Electron pool LOBE_IPC_ID length, main-process-restart HMR boundary, and Next dev not hot-reloading apps/server/**. * fix(cli): make skill-meta cliRoot portable, drop lobehub example from skill script The .skill-meta.json marker written by `lh verify install` baked in an absolute filesystem path for cliRoot when the @lobehub/cli package root was vendored inside the consumer repo itself, which is not portable across machines/CI and shows up as noise in commits. Write a path relative to the marker file's own directory in that case, and resolve it accordingly in cdp-capture.cjs's ws-fallback lookup. Global/npx installs (cliRoot outside the consumer repo) keep the absolute path since there's no repo-relative path to express. Also swap the lobehub-specific `--session lobehub-dev` usage example in the generic agent-browser-klm.mjs script for a neutral one. * fix(cli): clarify unversioned --force message, harden meta version parsing - verifyInstall: --force replacing a marker-less install now reports "replaced unversioned install with X" instead of "updated undefined → X" - readSkillMeta: treat a .skill-meta.json with a missing/invalid semver version as no meta (via semver.valid), routing tampered dirs into the existing refuse/--force flow instead of throwing in semver.compare - SKILL.md: drop stale "(ported by a later task)" note from the scripts/ directory-map comment; refresh installed .agents/skills copy to match * 📝 docs(agent-testing): add CLI version gate and subject-creation recipe to publish step * 🐛 fix(cli): ship ws as a runtime dependency and resolve it pnpm-layout-safely in cdp-capture ws lived in devDependencies, so a real npm/pnpm install of @lobehub/cli shipped no ws at all and cdp-capture.cjs failed on both resolution paths. The cliRoot fallback also hand-built <cliRoot>/node_modules/ws, which never exists under pnpm (deps are linked beside the package in .pnpm); use createRequire anchored at cliRoot so Node's own walk covers both layouts. Verified against a packed tarball installed into a scratch prefix: the copied skill's cdp-capture now reaches the CDP probe (ECONNREFUSED) instead of dying with MODULE_NOT_FOUND. * 🐛 fix(cli): address codex review on agent-testing skill - verify install: require the marker's `name` to match the skill before trusting its version, so a foreign or tampered `.skill-meta.json` is refused instead of reported as a no-op / silently downgraded. - report-init.sh: JSON-escape interpolated fields, so a title containing quotes, backslashes or newlines no longer emits invalid result.json. - docs: point the JWKS workaround at the relocated init-dev-env.sh and the bot preflight at the installed skill path (both resolve from repo root). * 🔀 chore(agent-testing): reconcile canary updates |
||
|
|
78d22e955f |
🔖 chore(cli): bump @lobehub/cli to v0.0.44 (#17288)
* 🔖 chore(cli): bump @lobehub/cli to v0.0.44 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 🔖 chore(cli): regenerate man page for v0.0.44 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 📝 docs(agent-testing): group reports by acceptance subject & add per-check fixture tooling - report-init.sh: --subject groups runs under .records/reports/<subject-key>/ with an acceptance.json marker, pre-fills result.json.subject for auto-attach ingest - fixture.mjs: init-check / list / compose reusable per-check fixture assets under .records/fixtures/, composing ingest-ready rounds from check.json templates - SKILL.md & references/report.md: document the grouped layout and inputs-vs-outputs fixture rule - common-mistakes: Case 28 — always publish reports to production, create a prod anchor if the subject only exists locally - probe-mock-patterns: C12 (stale global lh silently orphans ingest), D22 (driving the manual-approval intervention chain in web chat) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
12d9b9b197 | 🔖 chore(cli): bump @lobehub/cli to 0.0.43 (#17237) | ||
|
|
f8ff7ff8ba |
✨ feat(verify): subject-level acceptance aggregate with cross-round check union (#17229)
* ✨ feat(verify): subject-level acceptance aggregate with cross-round check union Wire up the dormant acceptances schema (#16899) end to end: - db: AcceptanceModel (ensureForSubject / status lifecycle) + verify run chain methods (attachToAcceptance, listByAcceptance, setDecision) and batch listByRuns/findByRuns readers for whole-chain reads - server: AcceptanceService — deterministic cross-round check union (final verdict + history trail + fixed/carried/introduced provenance, per-item surface), status rollup (delivered = settled, awaiting the user's decision), accept/reject stamping the round decision; new `acceptance` lambda router (ensure / attachRun / getBundle / list / accept / reject); evidence file-URL resolver extracted and shared with getReportBundle; upsertReport re-derives the aggregate status - cli: `lh verify ingest-report --subject type:id` chains each session onto the subject's acceptance as its next round; new `lh verify acceptance list|view|accept|reject` commands; plan items accept a per-item `surface` - web: /acceptance/:acceptanceId decision page (union checks, round ledger, accept/reject with comment) registered across web + desktop + mobile routers; i18n en-US/zh-CN - docs: agent-testing skill documents the --subject publish contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 💄 feat(verify): rebuild the acceptance page to the final prototype spec Align the /acceptance/:id decision workspace with the final iterated prototype from the design session (business-category groups + iteration timelines), which the first cut missed: - types/server: VerifyCheckItem gains `category` (union grouping key) and `supersedes` (generation declaration); the union folds superseded ids into the successor's iteration timeline, tracks per-round wording (`timeline`), `revisions` and `titleChanged`; bundle enriches evidence for every timeline step, not just the final round - cli: plan items pass category/supersedes through; ingest keeps working for legacy reports - web: check list grouped by category (surface fallback) in one joined card with collapsible groups + per-group expand/collapse and a global group toggle; rows show evidence-type badges, Repeat iteration chips, fixed/introduced/final-round provenance; expanded rows render full-width captioned evidence and a newest-first iteration timeline (gray history dots); the round ledger is newest-first, icon+text status, time top-right, collapsible panel; per-check manual override removed per the design decision (the acceptance-level accept/reject is the human judgment); accept/reject moved to base-ui createModal - proxy: register /acceptance in the middleware matcher (protected) — direct URLs previously 307'd to the home page - cli deps: pin antd/@types/react overrides to the root versions so the standalone install can't drift the root type-check Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 💄 style(verify): promote the acceptance bar to a body block, hide the decision bar for launch - The acceptance requirement renders as a first-class body block (primary accent border + label + body-size text) instead of header fine print - The accept/reject decision bar and its modals are gated behind ENABLE_DECISION_BAR=false — the user-closure loop is not shipping in this release - Round ledger: uncertain rounds get their own HelpCircle icon + roundStatus.uncertain label instead of falling back to the spinner Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 💄 style(verify): render the acceptance bar as a typographic lede Drop the bordered block (the accent border read as clutter): the requirement now sits under the header as a lede — a small tertiary label over 15px body text, prominence through typography instead of chrome. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * 🐛 fix(verify): harden acceptance attach ownership, decision gating, and rejected re-ingest Review follow-ups on the acceptance loop: - attachRun creator-scopes the RUN too (assertWorkspaceRowManageable on run.userId): the attach rewrites verify_runs.acceptance_id/round_index, so a workspace member must not be able to chain another member's report onto their aggregate - accept/reject require a SETTLED aggregate (delivered/errored): deciding mid-flight stamped a terminal state (and could complete a task) before any verdict existed, and recomputeStatus treats accepted as sticky so a premature accept was uncorrectable; rejected now also refuses a second decision until the next round re-opens it - ingest-report: a remembered session that already carries a userDecision is immutable history — the sidecar path now starts a fresh round (which re-opens a rejected acceptance as documented) instead of silently updating the decided round in place; an explicit --run against a decided session errors with guidance Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
df94e19ef2 |
🐛 fix(cli): move verify vocabulary to @lobechat/const/verify (#17195)
* 🐛 fix(cli): move verify vocabulary to @lobechat/const/verify `apps/cli` is a member of the isolated desktop workspace, where `@lobechat/types` resolves to the hand-written stub in `apps/desktop/stubs/types`. Value imports from it are therefore unreachable, and #17166 added three to the CLI's verify command, breaking `@lobehub/cli`'s build on canary. Move the verify closed sets (verifier types, statuses, verdicts, surfaces, evidence media), `normalizeVerifySurface`, `DEFAULT_MAX_REPAIR_ROUNDS` and `VerifyRunOrigin` into a new zero-import `@lobechat/const/verify` narrow subpath, which every workspace can resolve. `@lobechat/types` re-exports the derived types, so type-only consumers are untouched and it stays the one-stop domain vocabulary. The whole vocabulary moves, not just the two symbols the CLI happened to need — splitting on what one consumer imports is an accident, not a boundary. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ♻️ refactor(types): declare verify unions independently of @lobechat/const `@lobechat/types` must not depend on `@lobechat/const` — `const` already type-imports from `types` in ~10 files, so the edge added in the previous commit closed a package-level cycle. Declare the verify unions in `packages/types/src/verify.ts` instead of re-exporting them, and keep the runtime `as const` arrays in `@lobechat/const/verify` (which stays import-free, so the isolated desktop workspace can still reach them). `packages/const/src/verify.test.ts` pins the two sides together: adding a member to one alone is a type error, not a silent divergence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
da451d441f | 🔖 chore(cli): bump @lobehub/cli to 0.0.40 (#16988) | ||
|
|
e8af5258a7 |
♻️ refactor(Agent): Configurable graph agent (#16944)
* ♻️ refactor: rebuild graph agent runtime * ✅ test graph runtime integration paths * 🐛 fix(agent-runtime): stabilize graph agent tests * ✅ test: fix agent settings content mock * 🐛 fix graph prompt value typing * 🐛 fix(agent-runtime): preserve dynamically activated tools * ✅ test: use offered web browsing tool name * 🔧 remove graph runtime feature flag gate * 📝 restore internal flag comment |
||
|
|
0a6f6473ad | 🔖 chore(cli): bump @lobehub/cli to 0.0.39 (#16955) | ||
|
|
7a3540cab2 | 🐛 fix(cli): scope connect service env to unit (#16825) | ||
|
|
ea50d58f94 |
📝 docs(skills): restructure ux-audit skill with layered audit references (#16595)
* 📝 docs(skills): add layered audit references to ux-audit skill Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * 🔖 chore(cli): bump @lobehub/cli to 0.0.38 and regenerate man page Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * 📝 docs(skills): group ux-audit example results under references/example/ Move the worked audit results (home/eval/fleet/onboarding/page/resource/ settings) into a dedicated references/example/ directory, dropping the redundant example- prefix, and update SKILL.md + cross-links accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * 📝 docs(skills): add onboarding audit example, variant-comparison rule & ux 回灌 - ux-audit: new "comparing two variants" ground rule (winner is an outcome verdict, not a craft verdict) + an A/B-winner row in the coverage matrix; mandatory 回灌 and "report the good cases" close-out steps - ux-audit: onboarding worked example (references/example/onboarding.md) with a Strengths/亮点 section; findings landed as LOBE-11138 sub-issues - ux: Grow §5.2 (multi-step flows show progress + stay skippable) and Feedback §4.2 strengthened (awaited gating write needs finally + retry), mirrored into the Quick review - refresh the other worked examples + Read/Act/Edit checklist refinements Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 📝 docs(skills): surface good cases in ux-audit + land ✅ examples in ux Make good cases first-class in the ux-audit worked examples (dedicated "Strengths / good cases (don't regress)" section in create & tasks), and close the ✅ half of the 回灌 loop by landing their good-case exemplars as ✅ examples in the ux checklists: - Feedback §4.1 — elapsed/progress readout derives from a persisted, job-id-keyed start timestamp (survives remount), not a local counter. - Feedback §4.2 — a compound gate on a secondary/dependent fetch gates on its in-flight flag and releases on settled (data / resolved-null / error). - Feedback §4.3 — platform/deployment capability gaps get a full-surface gate with a remedy, vs a soft inline warning for model/config gaps. - Read §1.7 — conditional polling starts from reactive state, not a function-form refreshInterval that never schedules a first timer at 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
be0255b746 |
🔨 chore: update dependency baseline (#16465)
* 🔨 chore: update dependency baseline * 🔨 chore(cli): update dependency baseline * 🔨 chore(desktop): update dependency baseline * 🔨 chore: fix dependency baseline blockers * 🔨 chore: update TypeScript and Next baseline |
||
|
|
2997ba6a3e |
✨ feat(verify): submit check result + evidence in one call, inject acceptance into task prompt (#16214)
* ✨ feat(verify): submit check result + evidence in one call, inject acceptance into task prompt Builder self-evidence contract for Task-level Verify: - server `verify.submitCheckEvidence`: lazily upserts a check_result by `(verifyRunId, checkItemId)` and attaches evidence in one call, so the builder no longer needs a pre-existing checkResultId (closes the run-start handle gap). Verdict and evidence are both optional. - CLI `lh verify submit`: wraps the combined contract. - `lh verify evidence delete`: delete an evidence artifact. - buildTaskPrompt: resolve the task's verify config (with inheritance) + referenced criteria, inject a "delivery acceptance" section listing each criterion and its requiredEvidence so the builder self-evidences while it works. Run-time handles are resolved by the verify skill at runtime. - tests: router (submitCheckEvidence) + prompts (verify section rendering). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(verify): preserve result state on evidence-only submit + resolve run by operation Address two P1 review findings on submitCheckEvidence: - Preserve state on evidence-only submits: hydrate `required`/`verifierType`/ index/title from the run plan item instead of hardcoding `required: true`, and omit `status` when there's no verdict. Previously an evidence-only submit on an existing row reset it to required:true / running on the conflict-update — which could flip a soft or completed criterion into a blocking/verifying one (the executor skips existing rows and VerifyStatusService gates on required+running). - Let submit resolve the run from the operation: accept `operationId` as an alternative to `verifyRunId` (resolved via findByOperation), and expose `verifyRunId` in VerifyRunState/getVerifyState. In the run-start gap there are no result handles yet, so a builder holding only $LOBE_OPERATION_ID can now call `lh verify submit --operation`. - CLI `lh verify submit`: add `--operation`, relax `--run`, require one. - Migrate the portable verify skill (SKILL.md + plan-format/evidence/recording/ cli/native + index) off the broken `result list → checkResultId → evidence upload --check` path onto `lh verify submit --operation --item <checkItemId>`, which lazily creates the result row — the path that actually works in the gap. - tests: operation resolution, require-one-handle, no-clobber, plan hydration. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
04aa01b2f7 |
🔨 chore: bump cli 0.0.35 (#16143)
* chore: update version * chore: update man |
||
|
|
434c16c49d | 🔖 chore(cli): bump @lobehub/cli to 0.0.34 (#16139) | ||
|
|
2487407192 |
✨ feat(cli): add lh update command to self-upgrade the CLI (#16052)
* ✨ feat(cli): add `lh update` command to self-upgrade the CLI Add a `lh update` command that checks the npm registry for the latest published version of `@lobehub/cli`, compares it against the installed version, and runs the matching global-install command. The package manager is auto-detected (npm/pnpm/yarn/bun) from the running binary path, with `--package-manager` to override. Supports `--check` for a dry run and `--tag` to target a non-latest dist-tag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(cli): resolve update command package metadata from the bundled entry `update.ts` loaded `require('../../package.json')`, a path relative to its source location. tsdown bundles everything into `dist/index.js`, where that runtime require resolves to `node_modules/@lobehub/package.json` instead of `@lobehub/cli/package.json` — so a published build threw at startup the moment the update module was registered, breaking every `lh` invocation. Reuse the single package-metadata load already done by the bundled entry (`program.ts`, at the depth where `../package.json` is correct both in source and in dist). Export the package name alongside the version and consume both in the update command. Verified against a real `tsdown` build: `lh --version` starts cleanly and `lh update --check` resolves `@lobehub/cli` correctly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ♻️ refactor(cli): extract package metadata into a shared src/pkg.ts const Importing `cliVersion`/`cliPackageName` from `program.ts` into `update.ts` created a `program ↔ update` import cycle (program registers the update command). Move the single `require('../package.json')` load into a dedicated `src/pkg.ts` const module that both `program.ts` and `update.ts` import, breaking the cycle. The module sits directly under `src/` — the same depth as the bundled `dist/index.js` — so `../package.json` resolves to `@lobehub/cli`'s own package.json in both source and bundled runs. Verified against a real tsdown build: one `createRequire("../package.json")` remains, `lh --version` starts cleanly, `lh update --check` resolves the package, and `man:generate` still reads the version via the re-export. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(cli): use the semver package for update version comparison The hand-rolled comparator ordered prerelease identifiers lexicographically, so `1.0.0-beta.10` sorted below `1.0.0-beta.9` and `lh update --tag beta` reported "already current" instead of offering the newer prerelease. Replace `compareSemver` with `isNewerVersion`, delegating to the `semver` package (already a workspace dependency) so numeric prerelease parts compare numerically. Add `semver` / `@types/semver` to the CLI package and a regression test for the beta.9 → beta.10 case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
80cce10dd5 |
🐛 fix(cli): stop connect daemon on logout (#16038)
* 🐛 fix(cli): stop connect daemon on logout `lh logout` only cleared the credentials file, leaving the `lh connect` daemon running. It kept the device online on the gateway using the cached token until expiry, so the machine stayed remotely driveable after the user thought they had logged out, and left stale daemon.pid / daemon.status.json behind. Tear down the daemon via stopDaemon() before clearing credentials, which SIGTERMs the daemon (disconnecting the gateway) and removes the pid/status files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🔒 fix(cli): verify daemon identity before SIGTERM A bare isProcessAlive() check let logout/connect-stop SIGTERM an unrelated process when the OS reused a dead daemon's PID after a crash or reboot. getRunningDaemonPid() now also confirms the live PID is actually a LobeHub daemon (matches the `connect … --daemon-child` command-line signature) and treats reused PIDs as stale, cleaning up the metadata instead of killing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🔖 chore(cli): bump @lobehub/cli to 0.0.32 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✨ feat(cli): add top-level `disconnect` command `lh connect` was only stoppable via the nested `lh connect stop`, which is hard to discover. Add `lh disconnect` as a top-level alias that runs the same daemon-stop handler, so users have an obvious inverse of `lh connect`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
0ef1309b68 |
🔖 chore(cli): bump @lobehub/cli to 0.0.31 (#15964)
* 🔖 chore(cli): bump @lobehub/cli to 0.0.32 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * update --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
99411041b9 |
✨ feat(device): share remote-device gateway RPC between desktop and CLI (#15780)
* ✨ feat(device): share remote-device gateway RPC between desktop and CLI Extract the desktop's remote-device gateway RPC surface into a shared `@lobechat/device-control` package and wire it into the CLI so `lh connect` serves the same git / workspace / file device RPCs as the desktop app. - local-file-shell: relocate all git operations (branches, working-tree patches, branch diff, checkout/rename/delete/pull/push/revert) from the desktop GitCtr into the shared package as pure functions - device-control (new): the `executeDeviceRpc` dispatch + workspace scan + portable file-preview / file-index defaults, with platform hooks injected - desktop: GitCtr / WorkspaceCtr / GatewayConnectionCtr become thin wrappers delegating to the shared package (local IPC path unchanged) - cli: handle `rpc_request` over the gateway via the shared dispatcher Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ✅ test(device): cover git branch ops and device-control portable defaults - local-file-shell: real-git integration tests for branch checkout / rename / delete (+ validation), working-tree files & patches, revert, branch-diff with no remote, and push / pull / ahead-behind against a bare origin - device-control: defaultGetLocalFilePreview (text / image / accept filter / workspace containment / missing file) and defaultGetProjectFileIndex (git ls-files path + glob fallback) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(device): preserve directory entries in the glob project-file index The CLI `getProjectFileIndex` glob fallback used `globLocalFiles`, which returns only non-hidden file paths and no directory entries — so the Files tree builder flattened nested files to the root and dropped dot-directories. Walk with fast-glob (`dot: true`) and synthesize directory entries via the same `collectProjectDirectories` path the git branch uses, so nesting and dot-dirs (e.g. `.agents`) render correctly. Extracted a shared `buildEntries` helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
06bf82f3e0 | Update dependency node to v24.16.0 (#14621) | ||
|
|
723c4d6daa |
🐛 fix(cli): handle agent_run_request in lh connect so device dispatch doesn't time out (#15634)
* 🐛 fix(cli): handle agent_run_request in `lh connect` so device dispatch doesn't time out `lh connect` auto-registers the CLI as a device, so the gateway can pick it as the dispatch target for a heterogeneous agent run (`agent_run_request`). But the connect daemon only listened for `system_info_request` and `tool_call_request` — it never handled `agent_run_request`, so it never sent `agent_run_ack`. The gateway waited out its ack window and returned `{error:'TIMEOUT',success:false}`, surfaced server-side as "Hetero agent device dispatch failed". Add an `agent_run_request` handler mirroring the desktop app: spawn `lh hetero exec` fire-and-forget and ack `accepted` immediately. The spawned process owns the full execution + server-ingest pipeline. It re-invokes the current CLI entry (process.execPath + argv[1]) rather than relying on `lh` being on PATH, so it works inside the detached daemon. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: bump the cli version * chore: bump the cli manifest * 🐛 fix(cli): ack agent run only after spawn succeeds, reject on spawn error `child_process.spawn` reports a missing/inaccessible cwd asynchronously via the child's `error` event, after the handler had already sent an `accepted` ack. The gateway/server then recorded dispatch success while no `lh hetero exec` process existed to emit `heteroFinish`, leaving the assistant message stuck instead of surfacing a failure. `spawnHeteroAgentRun` now resolves on the child's outcome: `accepted` on the `spawn` event (stdin is written only then), `rejected` on an early `error`. A rejected ack returns the gateway 422 → execAgent writes a ServerAgentRuntimeError onto the assistant message, so a failed dispatch is visible. Still resolves in milliseconds, well within the gateway's 10s ack window. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
e01cadb779 |
✨ feat(hetero): add --raw-dump to persist agent raw stream-json for debugging (#15602)
* ✨ feat(hetero): add --raw-dump to persist agent raw stream-json for debugging The remote-device path (`spawnLhHeteroExec`) leaves no local execution record: `lh hetero exec` consumes the agent's stdout internally and only POSTs adapted events to the server, so a misbehaving remote run can't be inspected. The adapted/ingested view also can't distinguish a CC-side empty `tool_result` from an adapter extraction bug. Add `lh hetero exec --raw-dump <dir>`: spawnAgent gains an `onRawStdout` tee that captures the child's untouched stdout BEFORE the adapter; the CLI writes it (plus stderr + a meta.json) to `<dir>/<timestamp>-<operationId>/`, one file pair per spawn attempt. Fully best-effort — a dump failure never affects the run or exit code. Wire the desktop device path to pass `--raw-dump` (gated by the existing `shouldTraceCliOutput` toggle, into `resolveTraceRootDir`), so remote-device CC runs now leave a raw stream on the device — the same toggle/location the local trace path already uses. Reusable later for the server sandbox path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🔖 chore(cli): bump version to 0.0.27 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
ce5833cb67 |
✨ feat(file): persist image dimensions into file metadata (#15594)
* ✨ feat(file): persist image dimensions into file metadata Record intrinsic width/height for uploaded images so consumers can reserve layout space (avoid CLS) without loading the file first. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ✅ test(file): assert persisted dimensions in upload createFile payload Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * 🔖 chore(cli): bump version to 0.0.26 and regenerate man page Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ✨ feat(file): record image aspect ratio alongside width/height Compute intrinsic aspect ratio (width / height, rounded) at extraction time and persist it into file metadata so consumers can group/reserve layout by orientation without recomputing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
72ea0f94f7 |
🐛 fix(cli): preserve content/state for connect local file/shell tools (#15442)
* 🐛 fix(cli): preserve content/state for connect local file/shell tools Route file/shell tool calls in connect mode through LocalSystemExecutionRuntime so the result carries formatted prompt `content` plus structured `state`, and forward `state` over the gateway tool-call response — aligning the CLI with the desktop gateway path (PR #15114). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * 🐛 fix(cli): preserve getCommandOutput timeout when polling running commands Routing getCommandOutput through the runtime dropped the per-call/gateway timeout: the CLI mapping didn't forward it and LocalSystemExecutionRuntime's denormalizeParams stripped it before ShellProcessManager.getOutput, so polling fell back to the 30s default and could block past the gateway budget. Carry timeout through the runtime param type, denormalize, and the CLI mapping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
73dd0ef136 |
🔖 chore(cli): bump @lobehub/cli to 0.0.24 (#15393)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3caa3efb18 |
✨ feat(device): auto-register desktop & CLI devices with stable machine ID (#15300)
✨ feat(device): auto-register desktop & CLI devices; send connectionId + channel App layer — wires desktop and `lh connect` to the device registry and the connection-routing scheme. Depends on @lobechat/device-identity and the gateway-client connectionId/channel options (earlier PRs in this stack), plus the device.register / listDevices endpoints (already on canary). - desktop derives the stable deviceId on gateway connect (old per-install random UUID demoted to the routing `connectionId`), registers via device.register, and tags channel `desktop` / `desktop-dev` - `lh connect` derives + registers before opening the WS (explicit --device-id still pins a VM); channel `cli` (env-overridable); connectionId persisted in `~/.lobehub/connection-id` - CLI api client preserves explicit --token connects during registration Part of LOBE-9572. Closes LOBE-9576 / LOBE-9577. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
1d2b32bafc |
🔖 chore(cli): bump @lobehub/cli to 0.0.22 (#15254)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
49d191d2a7 | 🐛 fix: unify TypeScript peer resolution on 6.x (#15263) | ||
|
|
924ae8bf1f |
🐛 fix: pass assistantMessageId through sandbox env to eliminate heteroIngest race (#15197)
* 🐛 fix: pass assistantMessageId through sandbox env to eliminate heteroIngest race Before this change, `HeterogeneousPersistenceHandler.loadOrCreateState` always read `topic.metadata.runningOperation` from the DB to obtain `assistantMessageId`. On Vercel serverless, the first `heteroIngest` batch could arrive on a cold Lambda that read from a replica before the orchestrator's `updateMetadata` write was visible, causing a hard throw and BatchIngester exhausting all 5 retries — leaving the assistant message stuck as LOADING_FLAT with no user feedback. Fix: orchestrator passes `assistantMessageId` via `LOBEHUB_ASSISTANT_MESSAGE_ID` env var → CLI → `TrpcIngestSink` → `heteroIngest` payload → `loadOrCreateState`. When present, the DB lookup is skipped entirely for state initialisation, matching the frontend `createGatewayEventHandler` pattern which always receives `assistantMessageId` in-memory before any events are processed. The `topic.metadata` DB read is kept as a fallback for desktop/old-CLI callers that do not send the field, and is still needed to restore `heteroCurrentMsgId` for mid-conversation cold-start reconstruction on step boundaries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(hetero-agent): snapshot text ingests and ignore stale batches * chore: publish the cli to 0.0.21 * 🐛 fix(hetero-agent): validate seeded assistant binding * fix: fixed the little types error --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
a35877f676 |
✨ feat(platform-agent): improve device selection UX with actionable guidance (#15111)
* ✨ feat(platform-agent): improve device UX — copyable lh connect cmd + version-too-low hint - No-device state now shows a copyable `lh connect` command with clearer guidance to run it on the target machine then click Refresh - Capability check failure caused by outdated lh desktop now shows a user-friendly "lh version is too low" alert with a copyable `npm install -g @lobehub/cli` upgrade command instead of the raw internal error string - Changed no-device alert type from warning → info (absence of device is expected, not an error) - Add en-US / zh-CN locale keys: noDevicesCmd, versionTooLow, versionTooLowHint, upgradeCmd Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 📝 fix(platform-agent): correct platform card descriptions — connect not run Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(platform-agent): desktop capability check + improved no-device onboarding - Add checkPlatformCapability / getAgentProfile handlers in GatewayConnectionCtr so desktop devices no longer return "tool not available" error - Redesign no-device alert: primary CTA is Desktop App download (https://lobehub.com/downloads), secondary is copyable lh connect CLI command - Add 5 tests for new capability probing handlers (43 total, all pass) - Add missing execa/fast-glob/fflate mocks to unblock test suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(platform-agent): route openclaw/hermes to correct binary in executeAgentRun Previously all non-codex agent types defaulted to the `claude` command. Now maps claude-code → `claude`, all other types (openclaw, hermes, …) → their own binary name, which matches the pattern used by checkPlatformCapability. Also adds 6 agent-run-routing tests covering openclaw/hermes/codex/claude-code command mapping, accepted ack + sendPrompt wiring, and rejected ack on startSession failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(platform-agent): wire runHeteroTask/cancelHeteroTask on desktop gateway The server dispatches openclaw/hermes via executeToolCall('runHeteroTask'), not agent_run_request. The CLI (lh connect) handles this in its methodMap; now the desktop gateway does too. - Port runHeteroTask + cancelHeteroTask from CLI to GatewayConnectionCtr - openclaw: spawn detached process, save PID, inject notify protocol on first turn, send done signal via sendNotify on close - hermes: ensure gateway daemon is running, POST to /message endpoint - Add in-memory platformTasks registry for cancel support - Add sendNotify helper — calls agentNotify.notify tRPC endpoint directly using desktop token (desktop counterpart to `lh notify`) - Port buildNotifyProtocol inline so desktop and CLI stay in sync - Add resolveLhPath, openclawSessionExists, getHermesPort helpers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(heteroTask): always inject notify protocol and kill concurrent openclaw processes - Remove openclawSessionExists check: always inject buildNotifyProtocol into every turn so openclaw can report back even after a failed session - Before spawning openclaw, kill any existing process for the same topicId to prevent session file lock conflicts (exit code 1) - Apply same fixes to both CLI (heteroTask.ts) and desktop (GatewayConnectionCtr.ts) to keep behaviour in sync - Add CLI unit tests (heteroTask.test.ts, 7 cases) - Extend desktop tests to cover always-inject and kill-concurrent behaviours (52 total, up from 49) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🔀 chore(cli): resolve version conflict — keep 0.0.19 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🔖 chore(cli): bump version to 0.0.20 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(desktop): implement getAgentProfile via openclaw agents list --json Port getAgentProfile from CLI (getAgentProfile.ts) to desktop gateway: - calls `openclaw agents list --json` to get name + emoji - reads workspace IDENTITY.md / SOUL.md for description fallback - falls back to 🦞 emoji when no identityEmoji set Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(desktop): make getAgentProfile async to satisfy methodMap Promise return type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
d15651bbec |
🐛 fix(hetero): fix cloud CC agent execution failures and improve error messages (#15107)
* 🐛 fix(hetero): auto-retry on stale --resume session when cloud sandbox is recycled Cloud sandboxes are ephemeral (~1h idle TTL). When a new container is spawned for the next conversation turn, the previous CC session files under ~/.claude/projects/<cwd>/ are gone, so --resume <staleId> fails with "No conversation found with session ID". Two-layer fix: CLI (lh hetero exec) - Detect resume-not-found errors from stream error events and stderr - Intercept the error event (withheld from the ingester so the server never sees a terminal error) and transparently retry without --resume - The retry emits a fresh CC session id via heteroFinish, replacing the stale heteroSessionId in topic metadata and breaking the failure loop Server (HeterogeneousPersistenceHandler) - When result=error and no sessionId was produced (CC never emitted system.init, typical for resume failures), clear the persisted heteroSessionId from topic metadata as a safety net - When CC ran successfully but produced an error result, sessionId IS set so the valid session is preserved for resume on the next turn Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(hetero): handle context-overflow resume failure + inject conversation history Extends the resume auto-retry to also cover the "long conversation → immediate next turn → Agent execution failed" scenario: CLI (hetero exec) - Renames RESUME_NOT_FOUND_PATTERNS → RESUME_RETRY_PATTERNS and adds context-overflow patterns (`/prompt.*too long/i`, `/context.*too long/i`, etc.) so CC's API-level "prompt too long" error triggers the same retry-without-resume path as the sandbox-recycled case. - Adds a test case that verifies the context-overflow error retries cleanly. Server (cloudHeteroContext + aiAgent) - Exports ConversationHistoryEntry from cloudHeteroContext.ts and adds a conversationHistory? param that renders a <previous_conversation> block (user turns ≤ 1 KB, assistant turns ≤ 2 KB) in the system context. - In execAgent, when resumeSessionId is set, fetches the last 200 messages for the topic, filters to the last 30 user/assistant turns, and passes them as conversationHistory to buildCloudHeteroContext. This gives CC context about prior turns even when the native session file was reset. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(hetero): fix SIGTERM handler leak + remove unused ingestError binding - Store the SIGTERM callback in a variable and process.off() it in the finally block alongside SIGINT, so the first run's handler is removed before the retry run registers its own (fixes duplicate sink.finish calls on SIGTERM mid-retry). - Remove unused `ingestError` from the result destructuring (downstream code already uses result.ingestError directly). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(hetero): surface CC stderr in error message instead of generic fallback Always collect stderr from the agent process (cap 8 KB) and pass its tail (last 1 KB) as the `error` param to `heteroFinish` when the run fails. The persistence handler's `flushFinalState` overwrites the generic "Agent execution failed" fallback with the actual CC stderr, giving users and operators a meaningful error message. Previously: {"message":"Agent execution failed","type":"AgentRuntimeError"} After this fix, e.g.: {"message":"Error: API error: context window exceeded (200 000 tokens)", "type":"AgentRuntimeError"} Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🔨 chore(cli): bump version to 0.0.18 * 🐛 fix(lint): replace inline import() type with static import type * 🐛 fix(lint): fix import sort order for ConversationHistoryEntry --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
6953f188c1 |
✨ feat(platform-agent): openclaw/hermes agent creation UI, device guard, and remote dispatch backend (#15065)
* ♻️ refactor(agent-invocation): add AgentInvocationIntent + unified non-hetero dispatcher (LOBE-8927/8928) Introduce a shared invocation contract and unified dispatcher for the non-hetero, non-group agent call paths (callAgent speak mode and @agent direct mentions). Removes the implicit client-only fallback that existed in both entry points. Changes: - agentDispatcher.ts: add AgentInvocationIntent interface as the unified intent type for callSubAgent / callAgent / @agent invocations - nonHeteroSubAgentDispatcher.ts (new): dispatchNonHeteroSubAgent() resolves child runtime via selectRuntimeType and routes to executeClientAgent (client) or executeGatewayAgent (gateway); throws for hetero (out of scope per LOBE-8926) - conversationLifecycle.ts #executeDirectMentionRoute: replace hardcoded executeClientAgent + TODO fallback with dispatchNonHeteroSubAgent call - builtin-tool-agent-management executor.ts callAgent speak mode: replace hardcoded executeClientAgent + TODO fallback with dispatchNonHeteroSubAgent call Fixes LOBE-8927 Fixes LOBE-8928 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(platform-agent): openclaw/hermes agent creation UI, device guard, and remote dispatch backend - Add CreatePlatformAgent 3-step creation modal (type select → config → bind device) - Add RemoteAgentConfigCard to agent profile editor for openclaw/hermes config - Add device guard banner in HeterogeneousChatInput for offline/unavailable devices - Add useRemoteAgentDeviceGuard hook for real-time device status polling - Fix backend dispatch: openclaw/hermes now use executeToolCall(runHeteroTask) instead of dispatchAgentRun (lh connect only handles tool_call_request) - Add agentNotify router for lh notify → DB write + gateway stream event - Add device.checkCapability endpoint for platform availability probe - Add notify_update event type to gateway stream and event handler - Add sendDoneSignal in heteroTask.ts for clean openclaw exit signaling - Unify non-hetero sub-agent dispatch via dispatchNonHeteroSubAgent (LOBE-8927) - Route openclaw/hermes to gateway runtime; keep claude-code/codex on hetero/client paths - Add i18n keys for platform agent UI and device guard banners Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(agentNotify): reuse execAgent placeholder message on first lh notify call Instead of creating a second empty bubble, the first assistant notify without a messageId now updates the placeholder assistantMessageId that execAgent already seeded in runningOperation.assistantMessageId. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(agentNotify): cancel openclaw/hermes process on interruptTask - Store deviceId + heteroType in topic.metadata.runningOperation at dispatch time - interruptTask now dispatches cancelHeteroTask tool call to the bound device when topicId reveals a remote hetero operation, sending SIGINT to the process - Pass topicId from gateway cancel callback to interruptTask - Add topicId to InterruptTaskSchema and InterruptTaskParams Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ♻️ refactor(hetero-agent): consolidate remote/local type classification into heterogeneous-agents package - Add RemoteHeterogeneousAgentConfig, REMOTE_HETEROGENEOUS_AGENT_CONFIGS, isRemoteHeterogeneousType, and derived type aliases (HeterogeneousAgentType, LocalHeterogeneousAgentType, RemoteHeterogeneousAgentType) to packages/heterogeneous-agents/src/config.ts - Extend HETEROGENEOUS_TYPE_LABELS to cover remote platform types (openclaw, hermes) via REMOTE_HETEROGENEOUS_AGENT_CONFIGS - Replace all inline `=== 'openclaw' || === 'hermes'` checks and local Sets/type aliases across aiAgent service, ProfileEditor, HeterogeneousChatInput, useRemoteAgentDeviceGuard, CreatePlatformAgent, RemoteAgentConfigCard, and deviceProxy with the shared utility - Show OpenClaw/Hermes display name in assistant message model tag (Usage component) by setting provider=heteroType on placeholder message and using HETEROGENEOUS_TYPE_LABELS for rendering - Fix ReferenceError: move remoteDeviceId declaration before updateMetadata call Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add the platform agents get profiles * 🐛 fix(platform-agent): routing, security, and i18n issues from review - Route openclaw/hermes to gateway on desktop (P1): add isRemoteHeterogeneousType check in selectRuntimeType before desktop hetero branch — remote agents never use local desktop IPC, no special-casing needed - Fix race in heteroTask: sendAutoNotify → sendDoneSignal now sequential via .finally() so error message is written before agent_runtime_end is published - Security: validate messageId belongs to topicId in agentNotify before MessageModel.update to prevent cross-conversation data corruption - Clear capability/device/profile state on platform change in creation modal (P2) - Derive PLATFORM_DEFS from REMOTE_HETEROGENEOUS_AGENT_CONFIGS — new platforms automatically appear in the modal without code changes - Use HETEROGENEOUS_TYPE_LABELS for platform names in HeterogeneousChatInput and RemoteAgentConfigCard (remove hardcoded PLATFORM_NAMES map) - i18n: platform card descs, 'online'/'offline' tags, 'Select a device' placeholder, checkFailed error — all now use i18n keys Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ♻️ refactor(platform-agent): derive remote platform enum from config + fix test - device.ts: replace hardcoded z.enum(['hermes','openclaw']) with a zod enum derived from REMOTE_HETEROGENEOUS_AGENT_CONFIGS so new platforms are automatically covered without touching this file - heteroTask.ts / getAgentProfile.ts: use RemoteHeterogeneousAgentType instead of literal 'hermes' | 'openclaw' union for the same reason - gateway.test.ts: update cancel-handler assertion to include topicId which was added to the interruptTask call in the previous commit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✨ feat(platform-agent): gate creation entry behind labs flag + expand dispatcher tests - Add enablePlatformAgent lab preference (default false) — the "Add Platform Agent" menu item is hidden until the user opts in via Settings → Advanced → Labs - Wire toggle in settings/advanced with labs i18n key (en/zh) - createPlatformAgentMenuItem returns null when flag is off - agentDispatcher.test: add remote hetero cases (openclaw/hermes → gateway on both web and desktop) to cover the routing fix added earlier Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(lint): merge duplicate import + sort interface props in nonHeteroSubAgentDispatcher Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 💄 feat(platform-agent): disable Hermes option in creation modal (coming soon) Hermes is not yet ready for production. Mark it as coming-soon in the platform selection step: grayed-out card, not clickable, "Coming Soon" tag next to the name. To enable Hermes when ready: remove 'hermes' from COMING_SOON_PLATFORMS in CreatePlatformAgent/index.tsx. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✅ fix(test): mock CreatePlatformAgentModal in ModalProvider.test The modal always mounts (open=false) and calls lambdaQuery.useQuery which requires a tRPC context not present in the test environment. Mock it out the same way as ChatGroupWizard and EditingPopover. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ✅ fix(test): mock useUserStore + labPreferSelectors in useCreateMenuItems.test Adding useUserStore to useCreateMenuItems triggered user store initialization in tests, which pulled in @lobechat/const and failed because the existing mock only exports isDesktop. Mock the store and selectors directly instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 🐛 fix(platform-agent): hide divider when platform agent entry is disabled The divider before 'Add Platform Agent' was unconditional — it showed even when the labs flag was off. Conditionally include both the divider and the menu item together so no orphaned separator appears. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |