본문으로 건너뛰기
cccskills
무료GitHub에 공개한국어 소개

agent-work

에이전트가 만들거나 수정한 리소스를 버전 기록이 있는 Work 기록과 카드로 등록하는 방법을 설명합니다. 중복 방지, 도구 결과 정규화, 셸 명령 스캔, 파일·작업 등록, 공유 대화의 접근 범위를 다룹니다.

원문원문 설명 보기

Use for the Agent Work output registry: registration, deduplication, cards, skill providers, CLI scanners and Work types.

lobehub/lobehub스타 83,500(GitHub 83,500 + cccskills 0)

실행 점수 미검증

설치 방법 보기

이럴 때 유용합니다

  • 에이전트 결과를 대화 카드로 기록할 때
  • CLI가 만든 리소스를 감지할 때
  • Work 중복 등록을 방지할 때
  • 공유 대화의 접근 범위를 정할 때

한국어 소개

할 수 있는 일

이 가이드는 에이전트가 만들거나 수정한 리소스를 Work로 기록하는 등록 체계를 다룹니다. Work는 GitHub 풀 리퀘스트나 이슈, Linear 이슈, 문서, 파일, 작업을 나타낼 수 있습니다. 결과를 만든 어시스턴트 메시지 아래 카드로 표시되며, 같은 리소스를 이후에 다시 처리하면 하나의 기록에 버전이 추가됩니다.

리소스는 (resourceType, resourceId)로 식별하고, 등록 이벤트는 고유한 (workId, toolCallId)로 중복을 막습니다. GitHub와 Linear 같은 제공자의 구조화된 결과를 정규화하고, 저장된 셸 명령과 출력에서 CLI가 만들거나 수정한 리소스를 스캔하며, 파일·작업·문서를 등록하는 방식도 설명합니다. 공유 대화의 접근 범위와 새 Work 유형을 지원하지 않는 클라이언트에 해당 유형을 보내지 않는 호환성 제한도 다룹니다.

이럴 때 유용합니다

도구 제공자, CLI 스캐너, Work 카드 유형을 추가하거나 에이전트 작업 결과를 나중에 확인할 수 있게 할 때 참고할 수 있습니다. 중복 등록, 공유 대화의 접근 권한, 삭제된 리소스가 각 화면에서 표시되는 방식을 검토하는 개발자에게 유용합니다.

사용 예시

  • “GitHub에서 만든 결과를 Work로 등록하는 경로를 추가해 주세요.”
  • “CLI 출력에서 풀 리퀘스트 생성을 감지하는 스캐너를 검토해 주세요.”
  • “공유 대화에서 Work 읽기와 쓰기 범위를 나누는 방법을 설명해 주세요.”

주의 사항

특별한 주의 사항은 없습니다.

이 소개문은 공개된 SKILL.md를 바탕으로 AI(OpenAI Codex)가 작성했습니다. 정확한 사양은 아래 원문을 확인하세요.

문서 점검

AI가 SKILL.md의 내용에서 주의가 필요한 작업과 도입 전제를 확인했습니다. 점수는 실제로 실행한 결과(실행 검증)만으로 매깁니다.

안전성전송·삭제·명령 실행 등을 명시하고 있는가
B일부 부족공유 접근 격리와 실패 시 차단을 명시하지만 CLI 결과도 등록합니다.
도입의 용이성필요한 도구나 API 키가 적고 적혀 있는가
B일부 부족제공자 연동과 데이터베이스 모델을 전제로 하지만 설치 절차는 없습니다.

점수에는 넣지 않았습니다. 2026년 10월 26일 기준으로 AI(OpenAI Codex)가 판정했습니다. 참고용이므로 설치 전에 원문도 확인해 주세요.

평가가 다르다고 생각되면 재검토를 요청하기

어느 항목이 왜 다르다고 생각하는지 적어 주세요. 로그인은 필요 없습니다. SKILL.md와 대조해 평가를 다시 만들고 이곳에 반영합니다.

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

포함된 파일(1)

  • SKILL.md15.8 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Agent Works

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.

Type registry

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:

  • Pulled reads (message.getMessages, work.* lists, shareChat.getMessages): the client service passes includeFileWorks: true on the request.
  • Pushed payloads (the 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.

Access scope (Agent Share visitors)

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.
  • Any registration path reachable from a visitor run (skill results, completion scans, anchor stamping) must build its 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.
  • The resource unique indexes include the visitor topic id, so the creator and each visitor topic keep separate rows for the same external resource.
  • Visitor read payloads drop the creator's userId / workspaceId and gate spend on showModelInfo (toVisitorMessage).

Orphaned Works (backing resource deleted)

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:

  • Resource-page galleries hide orphans. These are 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.
  • Conversation surfaces keep the card. These are 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.

Registration write paths

Four write paths, two timing classes:

PathWhenWhere
Skill structured tools + sandbox runCommand (github / linear)at tool executionWorkModel.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 completionapps/server/src/services/workRegistration/shellWorkRegistration.ts via registerWorksForOperation
File Work scan (sandbox entity files)at operation completionapps/server/src/services/workRegistration/registerWorksForOperation.ts
Task / document worksat creation by their owning featureWorkModel.registerTask / registerDocument

Execution-time: skill providers

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.

Completion-time: the shell Work scan framework

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.

Cost granularity: execution-time vs completion-time

cumulativeCost / cumulativeUsage are per-version SNAPSHOTS ("spend up to this version"), never deltas — summing them across versions double-counts on every path.

  • Execution-time registrations (skill providers) snapshot right after 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.
  • Completion-time registrations (shell scan, file scan) attach one OPERATION-LEVEL figure — the completing run's terminal total plus terminal child-op totals — to EVERY version registered that round: the scan only has persisted command text + stdout, no per-call intermediate snapshots (hetero CLI usage is aggregated per step by the trace recorder, not per tool call). The same create + edit + edit therefore yields three versions all carrying $1.20, indistinguishable per step.
  • Cross-operation is independent: a later run editing the same resource appends a version with THAT run's total only.

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.

Display anchor & completion marker

A Works card renders only under an assistant message stamped with metadata.work.rootOperationId (the "anchor"). Who stamps it:

  • In-process runs: callLlmFinalizer stamps the round's final assistant message.
  • Hetero runs never pass that executor, so 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.

Key decisions (from the Work PRs)

  • Only successful create/edit results become Works (LOBE-10967). Read-only queries, comments, merges/closes, and branch/repo operations are excluded — 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.
  • stdout is the source of truth for identity on the CLI path (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.
  • Persisted URLs are http(s)-allowlisted (sanitizeExternalUrl): gh stdout / tool results are member-controlled and the URL reaches shell.openExternal on desktop.
  • The tokenizer is deliberately hand-rolled (no shell-quote dep): a real parser would expand what must stay literal, and the worst failure mode is skipping a bookkeeping registration.
  • Error-terminated runs skip the completion scan (same success gate as file Works): a gh create that succeeds before a later terminal failure registers no Work that round. Accepted — bounded cost, missing card only.
  • Hetero Works register after the terminal snapshot (agent_runtime_end publishes before completeOperation), so a hetero-created card appears on the next refetch, not in the terminal snapshot. Accepted for now.
  • PRs pushed via codex's own GitHub integration are not covered — no shell record exists; text-mention parsing was rejected as too false-positive-prone.

Extension recipes

New skill provider (structured tools), e.g. Notion:

  1. packages/types/src/work.ts — extend WORK_SKILL_PROVIDERS + WORK_PROVIDER_RESOURCE_TYPES (+ resource-type unions).
  2. packages/database/src/models/work/<provider>ToolResult.ts — write the normalizer; mirror linearToolResult.ts.
  3. packages/database/src/models/work/index.ts — add it to SKILL_TOOL_RESULT_NORMALIZERS (the satisfies forces this).
  4. Pick a stable cross-surface resourceId (the github lesson: choose the identity every surface can produce).

New shell CLI scanner, e.g. linear CLI or vercel deploy:

  1. Add a normalizer in packages/database/src/models/work/ reusing parseShellCommandSegments, plus a WorkModel.registerShell<X>Result facade method.
  2. Add apps/server/src/services/workRegistration/shellWorkScanners/<name>.ts with { matches, name, register } — matches stays a cheap substring check.
  3. Append it to SHELL_WORK_SCANNERS in shellWorkScanners/index.ts. The engine needs zero changes.
  4. Pin the parsing edge cases in packages/database/src/models/work/__tests__/ (wrapper unwrap, chained segments, failed/read-only commands must not register).

New Work type (new card kind):

  1. @lobechat/types — extend WorkType and related unions.
  2. packages/database/src/models/work/ — new adapter module + WORK_TYPE_ADAPTERS entry (compile-enforced).
  3. Client — add the type descriptor for the card UI.
  4. Add the type to the read-path opt-in gate (OPT_IN_WORK_TYPES pattern) — released clients throw on unknown types; never ship a new type into the legacy result set.

File map

ConcernPath
Schemapackages/database/src/schemas/work.ts
Type registry + read gatepackages/database/src/models/work/registry.ts
WorkModel facade + provider normalizer mappackages/database/src/models/work/index.ts
gh CLI + structured github normalizerpackages/database/src/models/work/githubToolResult.ts
Command-agnostic shell parsingpackages/database/src/models/work/shellCommandParsing.ts
Completion scan orchestrator (file + shell + anchor)apps/server/src/services/workRegistration/registerWorksForOperation.ts
Shell scan engineapps/server/src/services/workRegistration/shellWorkRegistration.ts
Desktop-local run scan (client-reported)apps/server/src/services/workRegistration/localRunWorkRegistration.ts
Per-CLI scannersapps/server/src/services/workRegistration/shellWorkScanners/
Client (non-gateway) registrationsrc/store/chat/agents/registerClientWorkFromIntent.ts

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

acceptance

무료요약 있음

실제 제품을 적절한 조작 환경에서 실행해 변경 사항을 검증하고, 사용자가 확인할 수 있는 증거를 모아 인수 보고서로 게시합니다. 기존 검증 계획을 확인하거나 점검 항목을 만들고, `lh` CLI로 결과를 게시할 수 있습니다.

  • 변경된 제품 동작을 실제로 확인하기
  • 스크린샷 등 검증 증거 수집하기
  • 검증 계획과 보고서 작성하기
lobehub/lobehub8.4만2026년 10월 26일 업데이트

add-model-provider

무료요약 있음

LobeHub에 AI 모델 제공자를 추가하는 과정을 안내합니다. 런타임 연동, 모델 정보와 설정, 브랜드 표시, 문서화 및 인수 확인을 다루며, 기존 제공자에 모델만 추가하는 경우와 새 연동이 필요한 경우를 구분합니다.

  • LobeHub에 새 AI 모델 제공자 추가하기
  • 제공자의 설정 안내나 설명 갱신하기
  • 모델 추가에 새 제공자 연동이 필요한지 판단하기
lobehub/lobehub8.4만2026년 10월 26일 업데이트

add-setting-env

무료요약 있음

서버 환경 변수로 사용자 설정의 기본값을 지정하도록 구현하는 절차를 안내합니다. 변수 검증, 서버 설정 구성, 사용자 저장소 반영부터 예시 환경 파일과 셀프 호스팅 문서 갱신까지 다룹니다.

  • 환경 변수로 사용자 설정 기본값 지정
  • 설정을 서버 구성과 사용자 저장소에 연결
  • 환경 변수 예시와 문서 갱신
lobehub/lobehub8.4만2026년 10월 26일 업데이트

agent-runtime-hooks

무료요약 있음

에이전트 실행 단계, 도구 호출, 사람의 승인, 컨텍스트 압축, 하위 에이전트 호출에 훅을 등록하는 방법을 안내합니다. 이벤트를 관찰하고 로컬 도구 결과를 모의하거나 설정에 따라 웹훅으로 도구 호출을 허용 또는 거부할 수 있습니다.

  • 에이전트 단계와 오류 기록
  • 테스트 중 도구 결과 모의
  • 웹훅으로 도구 호출 허용 또는 거부
lobehub/lobehub8.4만2026년 10월 26일 업데이트

agent-signal

무료요약 있음

에이전트의 이벤트 기반 백그라운드 작업을 설계하고 구현하도록 돕습니다. 소스, 시그널, 액션, 정책의 역할을 나누고 비동기 처리, 중복 실행 방지, 완료 기록과 관측 기능을 다룹니다.

  • 이벤트로 시작되는 백그라운드 작업 추가
  • 대화 요청과 작업을 분리
  • 액션의 중복 실행 방지
lobehub/lobehub8.4만2026년 10월 26일 업데이트

agent-testing-bot

무료요약 있음

실제 데스크톱 채팅 앱에서 봇 채널의 인수 테스트를 진행합니다. Discord, Slack, Telegram, WeChat, Lark, QQ에서 메시지를 보내고 응답을 기다린 뒤 결과 화면을 캡처합니다. iMessage는 BlueBubbles 브리지를 사용합니다.

  • 실제 Discord 채널에서 봇 응답 확인하기
  • Slack이나 Telegram 대화 경로 테스트하기
  • macOS 앱의 작업 결과 화면으로 기록하기
lobehub/lobehub8.4만2026년 10월 26일 업데이트

lobehub의 스킬 모두 보기

이 스킬의 문제 신고