Files
multica/packages/core/package.json
Naiyuan Qing 949dffdf7e feat: permission-aware UI across agent/comment/runtime/skill surfaces (#1915)
* feat(permissions): add core permission module and shared UI primitives

Foundation for permission-aware UI: pure rules that mirror the Go backend
permission gates, lightweight per-resource hooks, and two reusable display
components used across agent/skill/runtime detail pages.

- packages/core/permissions: types, rules, hooks (Decision-shaped — carries
  reason + message so UI can render disabled state, tooltip, and banner
  copy from one source)
- packages/core/agents/visibility-label: VISIBILITY_LABEL/DESCRIPTION/TOOLTIP
  constants ("Personal" / "Workspace") to replace scattered hard-coded copy
- packages/views/agents/visibility-badge: read-only visibility chip used on
  hover cards, list rows, and inspector when not editable
- packages/ui/components/common/capability-banner: "View only — only X and
  admins can edit Y" banner shown on agent / skill detail when current user
  lacks edit permission

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(views): permission-aware UI across agent/comment/runtime/skill surfaces

Apply the new permission rules to every surface where the UI was either
lying about who can do what or letting users hit 403s by clicking buttons
the backend would reject.

Agent detail
- Hide archive/restore actions for non-owner non-admin
- Replace inline editors (avatar, name, description, runtime/model/visibility/
  concurrency picker, skill-attach) with read-only display when canEdit is
  false — value is information, the editor is the action
- Show CapabilityBanner under the header explaining who can edit

Visibility surfaces
- visibility-picker / create-agent-dialog: replace "only you can assign"
  (false) with "Only you and workspace admins can assign" via shared
  VISIBILITY_DESCRIPTION constants
- agent-columns: truthful tooltip + "You" badge on agents the current user
  owns

Comments
- Restore admin override on comment edit/delete (backend already permits
  it via comment.go:507-512; the frontend was incorrectly hiding the menu).
  canModerate is computed once in issue-detail and threaded down.

Other
- Members tab: disable "demote" options for the last owner with tooltip
- Assignee picker: tooltip on disabled personal agents the user can't assign
- Runtime delete: tooltip and dialog explain the gate; owner column gains
  a name label next to the avatar in All scope
- Skill detail: page-level CapabilityBanner alongside the existing lock chip
- Issue delete (single + batch): note that any workspace member can delete
  issues — by-design semantics, made transparent

Backend is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(agents): hide personal agents from list and @mention for non-owners

Until now an agent's "Personal" visibility only narrowed the assign-to-issue
gate — every workspace member still saw every personal agent in the list
and the @mention dropdown. Members would see, click, and fail.

This filters those surfaces with the canonical canAssignAgentToIssue rule:
regular members only see workspace-visibility agents and the personal
agents they own; workspace owners and admins continue to see everything
(admin override path is intact).

- agents-page: visibleInView layer between active/archived and Mine/All
  scope so segment counts also reflect the filter
- mention-suggestion: filter agentItems before they enter the recency-
  ranked list; expand the test mock to cover the auth + visibility paths
  and add two assertions (member hides others' personal agents; admin
  still sees them)

Backend keeps returning every agent — admin tools and direct API access
are unaffected. This is a UI-only filter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 09:31:19 +08:00

100 lines
3.8 KiB
JSON

{
"name": "@multica/core",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit",
"lint": "eslint .",
"test": "vitest run"
},
"exports": {
".": "./index.ts",
"./types": "./types/index.ts",
"./types/*": "./types/*.ts",
"./api": "./api/index.ts",
"./api/client": "./api/client.ts",
"./api/ws-client": "./api/ws-client.ts",
"./config": "./config/index.ts",
"./auth": "./auth/index.ts",
"./workspace": "./workspace/index.ts",
"./workspace/queries": "./workspace/queries.ts",
"./workspace/mutations": "./workspace/mutations.ts",
"./workspace/hooks": "./workspace/hooks.ts",
"./issues": "./issues/index.ts",
"./issues/queries": "./issues/queries.ts",
"./issues/mutations": "./issues/mutations.ts",
"./issues/ws-updaters": "./issues/ws-updaters.ts",
"./issues/config": "./issues/config/index.ts",
"./issues/config/status": "./issues/config/status.ts",
"./issues/config/priority": "./issues/config/priority.ts",
"./issues/stores": "./issues/stores/index.ts",
"./issues/stores/view-store-context": "./issues/stores/view-store-context.tsx",
"./issues/stores/*": "./issues/stores/*.ts",
"./inbox": "./inbox/index.ts",
"./inbox/queries": "./inbox/queries.ts",
"./inbox/mutations": "./inbox/mutations.ts",
"./inbox/ws-updaters": "./inbox/ws-updaters.ts",
"./notification-preferences": "./notification-preferences/index.ts",
"./notification-preferences/queries": "./notification-preferences/queries.ts",
"./notification-preferences/mutations": "./notification-preferences/mutations.ts",
"./chat": "./chat/index.ts",
"./chat/queries": "./chat/queries.ts",
"./chat/mutations": "./chat/mutations.ts",
"./runtimes": "./runtimes/index.ts",
"./runtimes/queries": "./runtimes/queries.ts",
"./runtimes/mutations": "./runtimes/mutations.ts",
"./runtimes/hooks": "./runtimes/hooks.ts",
"./agents": "./agents/index.ts",
"./agents/queries": "./agents/queries.ts",
"./agents/derive-presence": "./agents/derive-presence.ts",
"./agents/use-agent-presence": "./agents/use-agent-presence.ts",
"./agents/visibility-label": "./agents/visibility-label.ts",
"./permissions": "./permissions/index.ts",
"./projects": "./projects/index.ts",
"./projects/queries": "./projects/queries.ts",
"./projects/mutations": "./projects/mutations.ts",
"./projects/config": "./projects/config.ts",
"./labels": "./labels/index.ts",
"./labels/queries": "./labels/queries.ts",
"./labels/mutations": "./labels/mutations.ts",
"./autopilots": "./autopilots/index.ts",
"./autopilots/queries": "./autopilots/queries.ts",
"./autopilots/mutations": "./autopilots/mutations.ts",
"./pins": "./pins/index.ts",
"./pins/queries": "./pins/queries.ts",
"./pins/mutations": "./pins/mutations.ts",
"./feedback": "./feedback/index.ts",
"./feedback/mutations": "./feedback/mutations.ts",
"./realtime": "./realtime/index.ts",
"./navigation": "./navigation/index.ts",
"./modals": "./modals/index.ts",
"./onboarding": "./onboarding/index.ts",
"./paths": "./paths/index.ts",
"./hooks": "./hooks.tsx",
"./hooks/*": "./hooks/*.ts",
"./query-client": "./query-client.ts",
"./provider": "./provider.tsx",
"./logger": "./logger.ts",
"./utils": "./utils.ts",
"./constants/*": "./constants/*.ts",
"./platform": "./platform/index.ts",
"./analytics": "./analytics/index.ts"
},
"dependencies": {
"@tanstack/react-query": "catalog:",
"@tanstack/react-query-devtools": "^5.96.2",
"posthog-js": "catalog:",
"zustand": "catalog:"
},
"peerDependencies": {
"react": "catalog:"
},
"devDependencies": {
"@multica/tsconfig": "workspace:*",
"@types/react": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
}
}