通过实际操作产品验证交付内容,记录用户可查看的证据,并发布验收报告。可检查已有验证计划或创建检查项,选择合适的操作界面,并使用 `lh` CLI 发布结果。
- 实际检查更改后的产品行为
- 收集截图或其他验证证据
- 编写验证计划和报告
本指南介绍如何将代理创建或编辑的资源登记为可追溯的 Work 记录和卡片,涵盖版本历史、去重、技能提供方结果、命令行扫描、文件与任务登记,以及共享会话的访问规则。
Use for the Agent Work output registry: registration, deduplication, cards, skill providers, CLI scanners and Work types.
实测评分 未实测
查看安装方法本指南介绍 Work 登记机制,用于记录代理创建或修改的资源。Work 可以代表 GitHub 拉取请求或议题、Linear 议题、文档、文件或任务。它会显示在产生该结果的助手消息下方;后续操作会为同一资源记录增加版本。
资源身份和去重规则包括 (resourceType, resourceId) 组合,以及用于登记事件去重的唯一 (workId, toolCallId)。指南还说明如何规范化 GitHub、Linear 等提供方返回的结构化结果,如何扫描已保存的命令行命令及其输出以识别创建或编辑的资源,以及如何登记文件、任务和文档。材料也介绍共享会话的访问范围,以及如何通过兼容性门控避免向不支持的客户端发送较新的 Work 类型。
添加工具提供方、命令行扫描器或 Work 卡片类型时,可以参考本指南。它也适用于希望之后查看代理操作结果的开发者,可帮助考虑重复登记、共享会话访问权限,以及已删除资源在不同视图中的显示方式。
没有特别事项。
本介绍由 AI(OpenAI Codex)根据公开的 SKILL.md 撰写。准确的规格请查看下方原文。
AI 根据 SKILL.md 的内容检查了需要注意的操作和使用前提。评分只依据实际运行的结果(实测对比)。
不计入评分。 由 AI(OpenAI Codex)于 2026年10月26日 评估。仅供参考,安装前请同时查看原文。
安装前可以查看交给 AI 代理的指令内容。
A Work is a durable record of something an agent produced or touched — a GitHub PR/issue, a Linear issue, an entity file (pptx/xlsx/docx/pdf), a document, a task. Works render as cards under the assistant message that produced them and accumulate versions across operations, so the same PR edited twice shows one card with history.
Two tables (packages/database/src/schemas/work.ts):
works — one row per resource. Identity/dedup key: (resourceType, resourceId) within the user/workspace scope and the share scope (see below). currentVersionId soft-references the latest version.work_versions — one row per registration event. Dedup: unique (workId, toolCallId), so a retried registration with the same real tool call id is a no-op. Versions carry provenance (source message, producing tool) and operation-level cumulativeCost / cumulativeUsage.packages/database/src/models/work/registry.ts is the single registry of Work types (document / external / file / task): WORK_TYPE_ADAPTERS is satisfies Record<WorkType, WorkTypeAdapter>, so a type added to @lobechat/types without an adapter is a compile error.
Read-path compatibility gate: OPT_IN_WORK_TYPES (currently {'file'}) hides newer types from clients that did not opt in (includeFileWorks). Released Electron clients lag by weeks and crash on unknown type descriptors (descriptor.getIcon on undefined), so a request without the opt-in receives exactly the pre-file set. Any NEW Work type must ship behind the same kind of opt-in.
The gate has two kinds of emission points, and both must honor the client's declaration:
message.getMessages, work.* lists, shareChat.getMessages): the client service passes includeFileWorks: true on the request.uiMessages snapshots and message_patch revisions built by AgentRuntimeService.queryUiMessages): no request carries the opt-in, so the starting client declares the file_works stream feature and the run stores it as state.host.acceptsFileWorks; approval continuations and group members inherit it. A pushed path that skips this check drops the card until the user reloads the page.A share visitor's run executes as the creator, so owner scope alone cannot separate its Works. Every Work read and write takes a WorkAccessScope (packages/types/src/work.ts): ordinary sees only rows without share provenance; agentShare sees only rows stamped in works.metadata.agentShare with exactly that share/topic/visitor.
workOwnership applies the scope on every path, so a new query built on it inherits the fence. Do not query works without it.WorkModel with the run's scope via resolveRunWorkAccessScope. A visitor run without a topic yields null: skip registration (fail closed) rather than registering unscoped into the creator's lists.userId / workspaceId and gate spend on showModelInfo (toVisitorMessage).works.resourceId has no FK, and deleting a document, task, or file never cascades into its Work (LOBE-13917). Deleting the Work would also drop its version history and its project and goal references. Each read surface handles orphans itself:
listByWorkspace surfaces: /resource, /resource/works, and the project dashboard. They filter orphans out with resourceLiveFilter (packages/database/src/models/work/internal.ts). A task or document must still exist and not be trashed. A file Work's current-version fileId must point to a files row that exists and is not trashed. Restoring the resource brings the card back. Any new gallery-style Work list must apply the same filter.listByConversation and the cards under messages; the conversation did produce the Work. resourceDeleted drives a click-time prompt (useResourceDeletedPrompt) that lets the user remove it.Four write paths, two timing classes:
| Path | When | Where |
|---|---|---|
Skill structured tools + sandbox runCommand (github / linear) | at tool execution | WorkModel.handleSkillToolResult, called from server toolExecution and the client executor (registerClientWorkFromIntent.ts on the legacy non-gateway runtime) |
Shell Work scan (hetero codex/claude-code + device lobe-local-system) | at operation completion | apps/server/src/services/workRegistration/shellWorkRegistration.ts via registerWorksForOperation |
| File Work scan (sandbox entity files) | at operation completion | apps/server/src/services/workRegistration/registerWorksForOperation.ts |
| Task / document works | at creation by their owning feature | WorkModel.registerTask / registerDocument |
SKILL_TOOL_RESULT_NORMALIZERS in packages/database/src/models/work/index.ts maps WorkSkillProvider (github, linear; vocabulary in packages/types/src/work.ts) to a normalizer. Both satisfies a Record<WorkSkillProvider, …>, so provider list and normalizer map cannot drift.
A normalizer turns one tool result into ExternalToolWorkOperation | null — null means "not Work-worthy", which is the common case and never an error.
Heterogeneous CLI agents (codex, claude-code) and the device lobe-local-system tool run CLIs like gh through their own shell surfaces, which never pass the skill-tool hook. registerWorksForOperation recovers their Works at completion from the persisted command text + stdout. Layering:
registerWorksForOperation (registerWorksForOperation.ts)
├─ collectOperationRecords one pass over the operation tree, shared by both scans
├─ registerShellWorks (shellWorkRegistration.ts — the ENGINE, command-agnostic)
│ ├─ SHELL_COMMAND_SOURCES identifier→apiName scoping of shell surfaces
│ ├─ success gate plugin error / state.success===false / state.error → skip
│ ├─ extract {command, exitCode, output} from arguments / state / message content
│ └─ shellWorkScanners/ one file per CLI family
│ ├─ types.ts ShellWorkScanner = { matches, name, register }
│ ├─ github.ts matches: includes('gh ') → workModel.registerShellGithubResult
│ └─ index.ts SHELL_WORK_SCANNERS registry
└─ file Work scan aggregate per-path fold + sandbox export pipeline (NOT a scanner)
Command parsing is split the same way: packages/database/src/models/work/shellCommandParsing.ts holds the command-agnostic layer (POSIX-ish tokenizer, control-operator segmenting, codex /bin/zsh -lc wrapper expansion — parseShellCommandSegments), while githubToolResult.ts holds only the gh-specific parseGhSegment + field mapping.
cumulativeCost / cumulativeUsage are per-version SNAPSHOTS ("spend up to this version"), never deltas — summing them across versions double-counts on every path.
UsageCounter.accumulateTool for that call, so create + edit + edit within one run carries an INCREASING series (e.g. $0.30 → $0.70 → $1.20) and step deltas are recoverable.Why file Works are not a scanner: their unit is a file path folded across ALL records (multi-edit merge, renames, last-edit provenance), the registration is a heavy IO pipeline (idempotency probe → sandbox export → upload → version → redeploy, bounded concurrency), hetero records are deliberately EXCLUDED (the file lives on the executing device, not the exportable sandbox), and the version key is the synthetic op:${operationId} instead of a real toolCallId. Shoehorning that into ShellWorkScanner would break the interface. If a second aggregate-style scan ever appears, extract an aggregate layer then.
A Works card renders only under an assistant message stamped with metadata.work.rootOperationId (the "anchor"). Who stamps it:
callLlmFinalizer stamps the round's final assistant message.registerWorksForOperation stamps the anchor itself when the shell scan registered anything — using the completion's assistantMessageId, falling back to the parentId of the last registered tool message (hetero single-step runs persist no final-assistant pointer at all; see heteroFinish).The whole scan returns {attempted, failed} and the completion backstop (CompletionLifecycle) writes its idempotency marker only when failed === 0. Anything that must be retried — a thrown registration, a {success: false} anchor stamp, an unresolvable anchor — counts into failed. Registration is retry-safe end to end (probe short-circuits, (workId, toolCallId) guard backs it up), so counting a failure is always the right move.
gh pr view printing an entity URL must NOT register.owner/repo#number is the canonical github identity, not node_id: the gh CLI surface never returns node_id, and the same entity touched via REST tools and CLI must land on one Work row.gh … create/edit prints the entity URL); the command's edit target is the fallback. The LAST gh create/edit segment of a chained command owns the trailing URL.sanitizeExternalUrl): gh stdout / tool results are member-controlled and the URL reaches shell.openExternal on desktop.shell-quote dep): a real parser would expand what must stay literal, and the worst failure mode is skipping a bookkeeping registration.gh create that succeeds before a later terminal failure registers no Work that round. Accepted — bounded cost, missing card only.agent_runtime_end publishes before completeOperation), so a hetero-created card appears on the next refetch, not in the terminal snapshot. Accepted for now.New skill provider (structured tools), e.g. Notion:
packages/types/src/work.ts — extend WORK_SKILL_PROVIDERS + WORK_PROVIDER_RESOURCE_TYPES (+ resource-type unions).packages/database/src/models/work/<provider>ToolResult.ts — write the normalizer; mirror linearToolResult.ts.packages/database/src/models/work/index.ts — add it to SKILL_TOOL_RESULT_NORMALIZERS (the satisfies forces this).resourceId (the github lesson: choose the identity every surface can produce).New shell CLI scanner, e.g. linear CLI or vercel deploy:
packages/database/src/models/work/ reusing parseShellCommandSegments, plus a WorkModel.registerShell<X>Result facade method.apps/server/src/services/workRegistration/shellWorkScanners/<name>.ts with { matches, name, register } — matches stays a cheap substring check.SHELL_WORK_SCANNERS in shellWorkScanners/index.ts. The engine needs zero changes.packages/database/src/models/work/__tests__/ (wrapper unwrap, chained segments, failed/read-only commands must not register).New Work type (new card kind):
@lobechat/types — extend WorkType and related unions.packages/database/src/models/work/ — new adapter module + WORK_TYPE_ADAPTERS entry (compile-enforced).OPT_IN_WORK_TYPES pattern) — released clients throw on unknown types; never ship a new type into the legacy result set.| Concern | Path |
|---|---|
| Schema | packages/database/src/schemas/work.ts |
| Type registry + read gate | packages/database/src/models/work/registry.ts |
| WorkModel facade + provider normalizer map | packages/database/src/models/work/index.ts |
| gh CLI + structured github normalizer | packages/database/src/models/work/githubToolResult.ts |
| Command-agnostic shell parsing | packages/database/src/models/work/shellCommandParsing.ts |
| Completion scan orchestrator (file + shell + anchor) | apps/server/src/services/workRegistration/registerWorksForOperation.ts |
| Shell scan engine | apps/server/src/services/workRegistration/shellWorkRegistration.ts |
| Desktop-local run scan (client-reported) | apps/server/src/services/workRegistration/localRunWorkRegistration.ts |
| Per-CLI scanners | apps/server/src/services/workRegistration/shellWorkScanners/ |
| Client (non-gateway) registration | src/store/chat/agents/registerClientWorkFromIntent.ts |
还没有评价。欢迎分享你的使用感受。
无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。
还没有评论。来写第一条吧。
通过实际操作产品验证交付内容,记录用户可查看的证据,并发布验收报告。可检查已有验证计划或创建检查项,选择合适的操作界面,并使用 `lh` CLI 发布结果。
本技能指导在 LobeHub 中接入 AI 模型提供商,涵盖运行时、模型信息、设置、品牌展示、文档和验收,并帮助判断新增模型是否需要新的提供商集成。
本技能指导如何用服务器端环境变量设置用户偏好默认值,涵盖变量校验、服务器配置、写入用户状态,以及更新 `.env.example` 和自托管文档。
说明如何在智能体执行步骤、工具调用、人工审批、上下文压缩和子智能体调用时注册钩子。钩子可观察事件、模拟本地工具结果,也可通过 Webhook 按配置策略允许或拒绝工具调用。
此技能用于为智能体设计事件驱动的后台工作流,划分来源、信号、动作和策略的职责,并处理异步交接、去重、执行范围、完成记录与运行观测。
本技能在 macOS 桌面聊天应用中测试机器人频道:打开应用、进入频道或联系人、发送消息、等待回复并截图。支持 Discord、Slack、Telegram、微信、飞书和 QQ;iMessage 使用 BlueBubbles 桥接。