Skip to content

feat(gateway/sdk): artifact RPCs with cursor pagination, type filter, and provenance fix#75208

Open
Angfr95 wants to merge 3 commits intoopenclaw:mainfrom
Angfr95:feat/artifact-rpcs-cursor-pagination
Open

feat(gateway/sdk): artifact RPCs with cursor pagination, type filter, and provenance fix#75208
Angfr95 wants to merge 3 commits intoopenclaw:mainfrom
Angfr95:feat/artifact-rpcs-cursor-pagination

Conversation

@Angfr95
Copy link
Copy Markdown
Contributor

@Angfr95 Angfr95 commented Apr 30, 2026

Summary

  • New artifact RPCs (artifacts.list, artifacts.get, artifacts.download) exposed via the gateway and the SDK oc.artifacts namespace, with AJV validation and TypeBox schemas
  • Cursor pagination and type filter for artifacts.list: base64url-encoded messageSeq cursors via src/gateway/util/cursor.ts, types array filter, configurable limit (default 50, hard-cap 200)
  • P2 provenance fix: collectArtifactsFromMessages previously dropped all artifacts under runId/taskId queries because transcript writers only write { id, seq } to __openclaw — never runId/taskId. Fix: apply per-message filter only when the field is explicitly present; session-level routing already scopes correctly
  • Hyprland capture stub: extensions/browser/src/browser/hyprland-capture.ts implementing isHyprlandAvailable and captureWithHyprland (resolves pre-existing Cannot find module TS error in agent.snapshot.ts)
  • Swift model generation: artifact schemas added to ProtocolSchemas so protocol:gen:swift produces the corresponding Swift structs in both GatewayModels.swift targets
  • Full test coverage: 42 artifact handler tests (including P2 regression guards), 9 cursor utility tests, 16 SDK namespace tests — all passing

Test plan

  • pnpm test — 67/67 tests pass (artifacts + cursor + SDK suites)
  • pnpm protocol:check — exits 0 (generated Swift matches working tree)
  • pnpm exec oxfmt --check — all modified files correctly formatted
  • OPENCLAW_TESTBOX=1 pnpm check:changed — exits 0
  • Verify artifacts.list({ runId }) returns artifacts when transcript messages lack __openclaw.runId (P2 regression guard test)
  • Verify cursor pagination: first page returns nextCursor, second page with that cursor returns remaining artifacts and no further cursor

🤖 Generated with Claude Code

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation app: macos App: macos app: web-ui App: web-ui gateway Gateway runtime size: XL labels Apr 30, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 30, 2026

Codex review: found issues before merge.

Summary
The PR adds and revises Gateway/SDK artifact RPCs with cursor pagination, type filtering, provenance handling, generated Swift models, docs/tests, and a Hyprland browser screenshot capture path.

Reproducibility: yes. The PR cursor bug is source-reproducible with two artifacts sharing one messageSeq and limit: 1; the provenance issue follows from run/task queries keeping untagged messages, and the Hyprland issue follows from checking a resolved config field that the normal resolver does not populate in this PR.

Next step before merge
Maintainer should reconcile this branch with merged #74926 and decide whether cursor/type filters and Hyprland capture belong here or in split follow-ups.

Security
Needs attention: The artifact diff has a data-scoping concern, and the Hyprland helper adds a low-severity local subprocess resolution concern.

Review findings

  • [P2] Keep run and task artifact queries provenance-scoped — src/gateway/server-methods/artifacts.ts:232-235
  • [P2] Preserve artifacts that share a message sequence — src/gateway/server-methods/artifacts.ts:388
  • [P2] Preserve the shipped SDK helper signatures — packages/sdk/src/client.ts:758-763
Review details

Best possible solution:

Keep #74926's shipped artifact API as the base, then split cursor/type filtering into a focused Gateway/SDK follow-up with stable cursor tiebreakers and explicit provenance rules; handle Hyprland capture separately under the browser plugin surface.

Do we have a high-confidence way to reproduce the issue?

Yes. The PR cursor bug is source-reproducible with two artifacts sharing one messageSeq and limit: 1; the provenance issue follows from run/task queries keeping untagged messages, and the Hyprland issue follows from checking a resolved config field that the normal resolver does not populate in this PR.

Is this the best way to solve the issue?

No. The PR overlaps the merged maintainer implementation and is not the best merge vehicle until it preserves current SDK signatures, keeps run/task queries fail-closed, fixes cursor tiebreaking, and separates or fully wires the Hyprland feature.

Full review comments:

  • [P2] Keep run and task artifact queries provenance-scoped — src/gateway/server-methods/artifacts.ts:232-235
    This keeps messages that lack explicit run/task provenance for scoped queries, so a runId or taskId request can return unrelated untagged artifacts from the resolved session. Keep the current fail-closed behavior or add a proven mapping before returning those artifacts.
    Confidence: 0.9
  • [P2] Preserve artifacts that share a message sequence — src/gateway/server-methods/artifacts.ts:388
    The cursor only encodes messageSeq, so when one transcript message has more artifacts than the page limit, the next page filters past the whole message and skips remaining artifacts. Include a tiebreaker such as content index or artifact id in the cursor.
    Confidence: 0.95
  • [P2] Preserve the shipped SDK helper signatures — packages/sdk/src/client.ts:758-763
    Current main documents and tests oc.artifacts.get(id, query) and download(id, query), but this branch exposes param-object calls. Reconcile with the merged SDK shape so this PR does not break current consumers when rebased.
    Confidence: 0.84
  • [P2] Wire the Hyprland toggle through resolved config — extensions/browser/src/browser/routes/agent.snapshot.ts:493
    The route checks ctx.state().resolved.hyprlandCapture, but this PR only adds schema/type surface and does not populate the resolved browser config through the normal resolver. In a real gateway the new path remains disabled.
    Confidence: 0.9
  • [P3] Resolve Hyprland tools from trusted paths — extensions/browser/src/browser/hyprland-capture.ts:8
    captureWithHyprland spawns hyprctl and grim by command name through PATH. Resolve those binaries through the trusted system-binary path before spawning so enabling this feature cannot execute a shadowed command.
    Confidence: 0.78

Overall correctness: patch is incorrect
Overall confidence: 0.91

Security concerns:

  • [medium] Run/task artifact queries can include untagged session artifacts — src/gateway/server-methods/artifacts.ts:232
    For scoped artifact queries, the PR keeps transcript messages with no explicit matching provenance, which can expose unrelated same-session artifacts through a narrower runId/taskId request.
    Confidence: 0.86
  • [low] Hyprland helper resolves tools through PATH — extensions/browser/src/browser/hyprland-capture.ts:8
    The new helper spawns hyprctl and grim by command name, so an enabled local capture path could run a shadowed binary from an untrusted PATH entry.
    Confidence: 0.78

Acceptance criteria:

  • pnpm test src/gateway/server-methods/artifacts.test.ts packages/sdk/src/index.test.ts extensions/browser/src/browser/routes/agent.snapshot.test.ts
  • pnpm protocol:check
  • pnpm exec oxfmt --check --threads=1 src/gateway/server-methods/artifacts.ts src/gateway/protocol/schema/artifacts.ts packages/sdk/src/client.ts packages/sdk/src/types.ts extensions/browser/src/browser/routes/agent.snapshot.ts extensions/browser/src/browser/hyprland-capture.ts

What I checked:

  • Current main has the base artifact RPCs registered: coreGatewayHandlers imports and spreads artifactsHandlers, and method discovery/read scopes include artifacts.list, artifacts.get, and artifacts.download. (src/gateway/server-methods.ts:13, 9fff2b779159)
  • Current main lacks the PR's cursor/type contract: ArtifactsListParamsSchema is only the basic query schema and ArtifactsListResultSchema returns only an artifacts array; no cursor, limit, types, nextCursor, or total contract is present on main. (src/gateway/protocol/schema/artifacts.ts:44, 9fff2b779159)
  • Current main intentionally fails closed for untagged run/task artifacts: The merged handler returns early when a runId/taskId query does not match message provenance, and the regression test asserts untagged same-session artifacts are not returned for scoped runId queries. (src/gateway/server-methods/artifacts.ts:239, 9fff2b779159)
  • Current main SDK/docs use id-plus-scope artifact helpers: The SDK exposes oc.artifacts.get(id, params) and download(id, params), with docs and tests covering that shape; this PR's param-object SDK shape needs reconciliation with the merged API. (packages/sdk/src/client.ts:790, 9fff2b779159)
  • Protocol docs and changelog show the merged maintainer artifact surface: The docs describe run/task artifact queries as returning only matching provenance, and the changelog records the merged SDK-facing artifact RPC work under the active release notes. Public docs: docs/gateway/protocol.md. (docs/gateway/protocol.md:391, 9fff2b779159)
  • Existing PR review context already identified concrete blockers: The provided discussion includes an earlier ClawSweeper review that flagged the same provenance-scope, messageSeq cursor, Hyprland config, PATH resolution, and changelog blockers after comparing this PR with the merged fix(gateway): harden artifact RPCs #74926 implementation. (07b489ad0765)

Likely related people:

  • BunsDev: Opened and landed the maintainer hardening PR fix(gateway): harden artifact RPCs #74926 that now provides the current main artifact RPC/SDK/docs implementation and explicit provenance/download guardrails. (role: recent maintainer follow-up owner; confidence: high; commits: 5ac66065735a; files: src/gateway/server-methods/artifacts.ts, src/gateway/server-methods/artifacts.test.ts, packages/sdk/src/client.ts)
  • tmimmanuel: The closed feat(gateway): add artifact RPCs #74898 artifact RPC branch was identified as the source work later folded into fix(gateway): harden artifact RPCs #74926, and the current changelog credits @tmimmanuel for the artifact RPC API surface. (role: original artifact RPC source contributor; confidence: medium; commits: 93ad914d6da3; files: src/gateway/server-methods/artifacts.ts, src/gateway/protocol/schema/artifacts.ts, packages/sdk/src/client.ts)
  • vincentkoc: Recent current-main changelog and touched-surface context point to gateway/session transcript artifact streaming and large-transcript maintenance work adjacent to artifact query behavior. (role: adjacent gateway/session maintainer; confidence: medium; files: src/gateway/server-methods/artifacts.ts, docs/gateway/protocol.md, CHANGELOG.md)

Remaining risk / open question:

  • The unmerged cursor/type-filter API may still be a desired follow-up, but it needs a stable cursor contract and owner-approved provenance semantics rather than being merged over the shipped fix(gateway): harden artifact RPCs #74926 API.
  • The Hyprland screenshot path is mixed into an artifact RPC PR and needs browser-plugin ownership review or a split follow-up.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 9fff2b779159.

Angfr95 and others added 3 commits April 30, 2026 20:51
… and provenance fix

Adds artifacts.list / .get / .download gateway RPCs and an oc.artifacts SDK
namespace. Cursor pagination encodes messageSeq as base64url; types[] filter
uses Set membership; limit defaults to 50 with a hard cap of 200.

Fixes the P2 provenance bug where artifacts.list({ runId }) silently returned
nothing: transcript writers only persist { id, seq } in __openclaw, never
runId/taskId, so the per-message filter was always dropping everything. Fix:
apply the filter only when the field is explicitly present on the message.

Also adds hyprland-capture.ts stub to resolve a pre-existing TS2307 error in
the browser extension, and regenerates Swift GatewayModels for both targets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Angfr95
Copy link
Copy Markdown
Contributor Author

Angfr95 commented Apr 30, 2026

@vincentkoc this supersedes #74926 — resolves the P2 provenance blocker and adds cursor pagination with a reusable utility. Ready for review.

@Angfr95
Copy link
Copy Markdown
Contributor Author

Angfr95 commented Apr 30, 2026

#75208 supersedes this PR — resolves the P2 provenance blocker identified in the Codex review (artifacts.list({ runId }) silently returning empty on real transcript data), adds cursor pagination, type filter, and introduces src/gateway/util/cursor.ts as the canonical reusable pagination utility for Gateway list RPCs. All checks pass.

@Angfr95
Copy link
Copy Markdown
Contributor Author

Angfr95 commented Apr 30, 2026

@shakkernerd would you be able to review this? It resolves the P2 provenance blocker on the artifact RPC surface and #74926 currently has conflicts.

@Angfr95
Copy link
Copy Markdown
Contributor Author

Angfr95 commented Apr 30, 2026

@steipete would you be able to review this? It resolves the P2 provenance blocker on the artifact RPC surface and #74926 currently has conflicts. All checks pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: macos App: macos app: web-ui App: web-ui docs Improvements or additions to documentation gateway Gateway runtime size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant