@vess-id/ai-identity 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -4006,7 +4006,7 @@ function validateRegistryObject(registry) {
4006
4006
  for (const a of typed.actions) {
4007
4007
  if (a.input_schema) {
4008
4008
  try {
4009
- const local = new Ajv({ strict: true, allErrors: true });
4009
+ const local = new Ajv({ strict: true, allErrors: true, allowUnionTypes: true });
4010
4010
  addFormats(local);
4011
4011
  const compiled = local.compile(a.input_schema);
4012
4012
  if (compiled.errors?.length) {
@@ -7572,472 +7572,1513 @@ var ACTION_REGISTRY = {
7572
7572
  target_bindings: { resource_id: { source: "param", param: "portalId" } },
7573
7573
  version: "1.0.0"
7574
7574
  },
7575
- // ─── Agent Inbox Actions (internal) ───
7576
- // Inbox actions back the Agent Inbox MCP tools (task delegation between
7577
- // agents/people). They are internal (no external OAuth provider), so they
7578
- // carry a single nominal base scope `inbox` to satisfy the registry
7579
- // meta-schema's `minItems: 1` (same pattern as hubspot.account.link.get).
7575
+ // ─── Linear Actions ───
7580
7576
  {
7581
- action: "inbox.send",
7582
- resource_type: "inbox:item",
7583
- required_relations: ["editor", "act_as"],
7584
- required_scopes: ["inbox"],
7585
- capability: "inbox.write",
7577
+ action: "linear.issue.list",
7578
+ resource_type: "linear:issue",
7579
+ required_relations: ["viewer", "editor", "admin", "owner"],
7580
+ required_scopes: ["read"],
7581
+ capability: "linear.read.basic",
7586
7582
  input_schema: {
7587
7583
  type: "object",
7588
7584
  properties: {
7589
- to: { type: "string", minLength: 1, description: "Recipient (user id / handle) the task request is sent to" },
7590
- taskType: { type: "string", minLength: 1, description: "Type of task being requested" },
7591
- payload: { type: "object", description: "Task-specific payload data" },
7592
- message: { type: "string", description: "Optional human-readable message" }
7585
+ teamId: { type: "string", description: "Filter by team ID" },
7586
+ assigneeId: { type: "string", description: "Filter by assignee user ID" },
7587
+ assigneeMe: { type: "boolean", description: "If true, filter issues assigned to the authenticated user" },
7588
+ stateId: { type: "string", description: "Filter by workflow state ID" },
7589
+ priority: { type: "number", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
7590
+ limit: { type: "number", minimum: 1, maximum: 250, description: "Max number of issues to return (default 50)" }
7593
7591
  },
7594
- required: ["to", "taskType", "payload"],
7595
7592
  additionalProperties: false
7596
7593
  },
7597
- constraints: { rate_bucket: "inbox.write" },
7598
- effects: ["Create:InboxItem"],
7599
- risk: "high",
7594
+ constraints: { rate_bucket: "linear.read" },
7595
+ effects: ["Read:IssueList"],
7596
+ risk: "low",
7600
7597
  target_bindings: {
7601
- resource_id: { source: "param", param: "to" }
7598
+ resource_id: { source: "param", param: "teamId", required: false }
7602
7599
  },
7603
7600
  version: "1.0.0"
7604
7601
  },
7605
7602
  {
7606
- action: "inbox.respond",
7607
- resource_type: "inbox:item",
7608
- required_relations: ["editor", "act_as"],
7609
- required_scopes: ["inbox"],
7610
- capability: "inbox.write",
7603
+ action: "linear.issue.read",
7604
+ resource_type: "linear:issue",
7605
+ required_relations: ["viewer", "editor", "admin", "owner"],
7606
+ required_scopes: ["read"],
7607
+ capability: "linear.read.basic",
7611
7608
  input_schema: {
7612
7609
  type: "object",
7613
7610
  properties: {
7614
- id: { type: "string", minLength: 1, description: "Inbox item id being responded to" },
7615
- action: { type: "string", enum: ["accept", "decline", "counter"], description: "Response action" },
7616
- data: { type: "object", description: "Optional response data (e.g. counter-proposal)" }
7611
+ issueId: { type: "string", minLength: 1, description: "Issue ID or identifier (e.g. ENG-123)" }
7617
7612
  },
7618
- required: ["id", "action"],
7613
+ required: ["issueId"],
7619
7614
  additionalProperties: false
7620
7615
  },
7621
- constraints: { rate_bucket: "inbox.write" },
7622
- effects: ["Update:InboxItem"],
7623
- // high (not medium): an agent-mediated response (MCP invoke) must always
7624
- // go through human approval — the Phase 1 "always confirm" guarantee made
7625
- // enforceable. target_bindings below binds the concrete inbox item id so
7626
- // the high-risk approval has a specific resource constraint (the public
7627
- // /scheduling/:token link flow does NOT go through this gate — it calls
7628
- // InboxService.respond directly, system-key-signed).
7629
- risk: "high",
7616
+ constraints: { rate_bucket: "linear.read" },
7617
+ effects: ["Read:Issue"],
7618
+ risk: "low",
7630
7619
  target_bindings: {
7631
- resource_id: { source: "param", param: "id" }
7620
+ resource_id: { source: "param", param: "issueId" }
7632
7621
  },
7633
7622
  version: "1.0.0"
7634
7623
  },
7635
7624
  {
7636
- action: "inbox.check",
7637
- resource_type: "inbox:item",
7625
+ action: "linear.issue.search",
7626
+ resource_type: "linear:issue",
7638
7627
  required_relations: ["viewer", "editor", "admin", "owner"],
7639
- required_scopes: ["inbox"],
7640
- capability: "inbox.read",
7628
+ required_scopes: ["read"],
7629
+ capability: "linear.read.basic",
7641
7630
  input_schema: {
7642
7631
  type: "object",
7643
7632
  properties: {
7644
- types: { type: "array", items: { type: "string" }, description: "Filter by task types" },
7645
- status: { type: "string", description: "Filter by item status" }
7633
+ query: { type: "string", minLength: 1, description: "Full-text search query" },
7634
+ teamId: { type: "string", description: "Restrict search to a specific team" },
7635
+ limit: { type: "number", minimum: 1, maximum: 250, description: "Max number of results (default 50)" }
7646
7636
  },
7637
+ required: ["query"],
7647
7638
  additionalProperties: false
7648
7639
  },
7649
- constraints: { rate_bucket: "inbox.read" },
7650
- effects: ["Read:InboxItem"],
7640
+ constraints: { rate_bucket: "linear.read" },
7641
+ effects: ["Read:IssueList"],
7651
7642
  risk: "low",
7652
7643
  target_bindings: {
7653
- resource_id: { source: "param", param: "id", required: false }
7644
+ resource_id: { source: "param", param: "teamId", required: false }
7654
7645
  },
7655
7646
  version: "1.0.0"
7656
7647
  },
7657
7648
  {
7658
- action: "inbox.checkByToken",
7659
- resource_type: "inbox:item",
7660
- required_relations: ["viewer", "editor", "admin", "owner"],
7661
- required_scopes: ["inbox"],
7662
- capability: "inbox.read",
7649
+ action: "linear.issue.create",
7650
+ resource_type: "linear:issue",
7651
+ required_relations: ["editor", "act_as"],
7652
+ required_scopes: ["issues:create"],
7653
+ capability: "linear.write.basic",
7663
7654
  input_schema: {
7664
7655
  type: "object",
7665
7656
  properties: {
7666
- token: { type: "string", minLength: 1, description: "Opaque access token for a single inbox item" }
7657
+ teamId: { type: "string", minLength: 1, description: "Team ID to create the issue in" },
7658
+ title: { type: "string", minLength: 1, description: "Issue title" },
7659
+ description: { type: "string", description: "Issue description (Markdown)" },
7660
+ assigneeId: { type: "string", description: "Assignee user ID" },
7661
+ stateId: { type: "string", description: "Workflow state ID" },
7662
+ priority: { type: "number", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
7663
+ labelIds: { type: "array", items: { type: "string" }, description: "Label IDs to attach" }
7667
7664
  },
7668
- required: ["token"],
7665
+ required: ["teamId", "title"],
7669
7666
  additionalProperties: false
7670
7667
  },
7671
- constraints: { rate_bucket: "inbox.read" },
7672
- effects: ["Read:InboxItem"],
7673
- risk: "low",
7668
+ constraints: { rate_bucket: "linear.write" },
7669
+ effects: ["Create:Issue"],
7670
+ risk: "high",
7674
7671
  target_bindings: {
7675
- resource_id: { source: "param", param: "token" }
7672
+ resource_id: { source: "param", param: "teamId" }
7676
7673
  },
7677
7674
  version: "1.0.0"
7678
7675
  },
7679
7676
  {
7680
- action: "inbox.complete",
7681
- resource_type: "inbox:item",
7677
+ action: "linear.issue.update",
7678
+ resource_type: "linear:issue",
7682
7679
  required_relations: ["editor", "act_as"],
7683
- required_scopes: ["inbox"],
7684
- capability: "inbox.write",
7680
+ required_scopes: ["write"],
7681
+ capability: "linear.write.basic",
7685
7682
  input_schema: {
7686
7683
  type: "object",
7687
7684
  properties: {
7688
- id: { type: "string", minLength: 1, description: "Inbox item id being completed" },
7689
- result: { type: "object", description: "Result payload for the completed task" }
7685
+ issueId: { type: "string", minLength: 1, description: "Issue ID or identifier (e.g. ENG-123)" },
7686
+ title: { type: "string", description: "New title" },
7687
+ description: { type: "string", description: "New description (Markdown)" },
7688
+ assigneeId: { type: "string", description: "Assignee user ID (null to unassign)" },
7689
+ stateId: { type: "string", description: "Workflow state ID" },
7690
+ priority: { type: "number", minimum: 0, maximum: 4, description: "0=No priority, 1=Urgent, 2=High, 3=Medium, 4=Low" },
7691
+ labelIds: { type: "array", items: { type: "string" }, description: "New set of label IDs" }
7690
7692
  },
7691
- required: ["id", "result"],
7693
+ required: ["issueId"],
7692
7694
  additionalProperties: false
7693
7695
  },
7694
- constraints: { rate_bucket: "inbox.write" },
7695
- effects: ["Update:InboxItem"],
7696
- risk: "low",
7696
+ constraints: { rate_bucket: "linear.write" },
7697
+ effects: ["Update:Issue"],
7698
+ risk: "high",
7697
7699
  target_bindings: {
7698
- resource_id: { source: "param", param: "id" }
7700
+ resource_id: { source: "param", param: "issueId" }
7699
7701
  },
7700
7702
  version: "1.0.0"
7701
7703
  },
7702
- // ─── Scheduling Actions (internal) ───
7703
- // scheduling.request is a meeting-time negotiation façade over inbox.send.
7704
- // Candidate slots may be supplied two ways (server-side EXCLUSIVE — exactly
7705
- // one): (a) `candidates` — the agent computes them LLM-side from the user
7706
- // calendar and the server validates + freebusy-filters them, or (b)
7707
- // `candidateWindow` — the agent hands the server a { start, end } window and
7708
- // the server generates busy-free candidates from the sender's Google
7709
- // Calendar. Like the inbox actions it is internal (no external OAuth
7710
- // provider), so it carries the single nominal base scope `inbox`.
7711
- //
7712
- // NOTE: the candidates/candidateWindow exclusivity is expressed here as
7713
- // `oneOf` for documentation + the deep JSON-Schema authorities, but the SDK's
7714
- // hand-written `validateActionInput` is a SHALLOW shape gate that does NOT
7715
- // interpret `oneOf` (neither key is in top-level `required`). The exclusivity
7716
- // (exactly one of candidates/candidateWindow) is ENFORCED server-side in
7717
- // SchedulingRequestService — see that service for the 400s on both-given /
7718
- // both-missing.
7719
- // Spec: docs/specs/2026-06-10-inbox-intent-routing-design.md §2.1
7720
7704
  {
7721
- action: "scheduling.request",
7722
- resource_type: "inbox:item",
7705
+ action: "linear.comment.create",
7706
+ resource_type: "linear:comment",
7723
7707
  required_relations: ["editor", "act_as"],
7724
- required_scopes: ["inbox"],
7725
- capability: "inbox.write",
7708
+ required_scopes: ["comments:create"],
7709
+ capability: "linear.write.basic",
7726
7710
  input_schema: {
7727
7711
  type: "object",
7728
7712
  properties: {
7729
- to: { type: "string", minLength: 1, description: "Counterpart (user id / email) to negotiate a meeting time with" },
7730
- topic: { type: "string", minLength: 1, description: "Meeting topic shown to the counterpart" },
7731
- durationMinutes: { type: "integer", minimum: 5, maximum: 480, description: "Meeting length in minutes" },
7732
- candidates: {
7733
- type: "array",
7734
- minItems: 1,
7735
- maxItems: 10,
7736
- description: "Explicit candidate slots computed by the agent from the user calendar. The server validates them AND (when Google Calendar is connected) filters out any that conflict with the sender's calendar. Mutually exclusive with candidateWindow \u2014 supply exactly one.",
7737
- items: {
7738
- type: "object",
7739
- properties: {
7740
- start: { type: "string", description: "ISO 8601 start" },
7741
- end: { type: "string", description: "ISO 8601 end" }
7742
- },
7743
- required: ["start", "end"],
7744
- additionalProperties: false
7745
- }
7746
- },
7747
- candidateWindow: {
7748
- type: "object",
7749
- description: "A { start, end } search window (ISO 8601, span \u2264 62 days). The server generates busy-free candidate slots from the sender's Google Calendar within it. Mutually exclusive with candidates \u2014 supply exactly one. Requires a connected Google Calendar.",
7750
- properties: {
7751
- start: { type: "string", description: "ISO 8601 window start" },
7752
- end: { type: "string", description: "ISO 8601 window end" }
7753
- },
7754
- required: ["start", "end"],
7755
- additionalProperties: false
7756
- },
7757
- hold: {
7758
- type: "boolean",
7759
- description: "When true (or omitted, when the sender's delegation policy has holds enabled), tentatively hold the offered slots on the sender's calendar as [\u4EEE] events until the counterpart responds or the request expires. Pass false to skip holds for this request. Only takes effect when the sender has an enabled scheduling delegation policy."
7760
- },
7761
- message: { type: "string", description: "Optional human-readable message" }
7713
+ issueId: { type: "string", minLength: 1, description: "Issue ID to comment on" },
7714
+ body: { type: "string", minLength: 1, description: "Comment body (Markdown)" }
7762
7715
  },
7763
- // candidates / candidateWindow are intentionally NOT in `required`: the
7764
- // exclusive-choice is documented via `oneOf` and enforced server-side.
7765
- // Each oneOf branch re-declares its property locally (empty `properties`
7766
- // entry) so AJV strict mode (`strictRequired`) sees the required key as
7767
- // defined within the branch scope — the value shape itself is validated
7768
- // by the top-level `properties` above.
7769
- required: ["to", "topic", "durationMinutes"],
7770
- oneOf: [
7771
- { required: ["candidates"], properties: { candidates: {} } },
7772
- { required: ["candidateWindow"], properties: { candidateWindow: {} } }
7773
- ],
7716
+ required: ["issueId", "body"],
7774
7717
  additionalProperties: false
7775
7718
  },
7776
- constraints: { rate_bucket: "inbox.write" },
7777
- effects: ["Create:InboxItem"],
7719
+ constraints: { rate_bucket: "linear.write" },
7720
+ effects: ["Create:Comment"],
7778
7721
  risk: "high",
7779
7722
  target_bindings: {
7780
- resource_id: { source: "param", param: "to" }
7723
+ resource_id: { source: "param", param: "issueId" }
7781
7724
  },
7782
7725
  version: "1.0.0"
7783
- }
7784
- ],
7785
- capabilities: [
7786
- {
7787
- capability: "slack.messaging.basic",
7788
- description: "Post, update, and delete messages in channels",
7789
- includes: ["slack.message.post", "slack.message.update", "slack.message.delete"],
7790
- version: "1.0.0"
7791
- },
7792
- {
7793
- capability: "slack.read.basic",
7794
- description: "Read channels, messages, and user info",
7795
- includes: ["slack.channel.read", "slack.user.read", "slack.message.read", "slack.message.read_paginated", "slack.batch.read"],
7796
- version: "1.0.0"
7797
7726
  },
7798
7727
  {
7799
- capability: "github.read.basic",
7800
- description: "Read issues, pull requests and repository contents from repositories",
7801
- includes: ["github.issue.list", "github.issue.read", "github.pr.list", "github.pr.read", "github.pr.files.list", "github.pr.review.list", "github.pr.search", "github.repo.search", "github.repo.collaborator.list", "github.content.get", "github.tree.get", "github.branch.list", "github.commit.get", "github.commit.list", "github.commit.search", "github.tag.list", "github.release.list", "github.release.getLatest", "github.release.getByTag", "github.code.search", "github.issue.search", "github.issue.types.list", "github.label.get", "github.label.list", "github.workflow.get", "github.workflow.list", "github.workflow.jobLogs.get", "github.repo.starred.list", "github.user.me.get", "github.org.teams.list", "github.team.members.list", "github.user.search", "github.notification.list", "github.notification.get", "github.gist.get", "github.gist.list"],
7728
+ action: "linear.comment.list",
7729
+ resource_type: "linear:comment",
7730
+ required_relations: ["viewer", "editor", "admin", "owner"],
7731
+ required_scopes: ["read"],
7732
+ capability: "linear.read.basic",
7733
+ input_schema: {
7734
+ type: "object",
7735
+ properties: {
7736
+ issueId: { type: "string", minLength: 1, description: "Issue ID to list comments for" },
7737
+ limit: { type: "number", minimum: 1, maximum: 250, description: "Max number of comments (default 50)" }
7738
+ },
7739
+ required: ["issueId"],
7740
+ additionalProperties: false
7741
+ },
7742
+ constraints: { rate_bucket: "linear.read" },
7743
+ effects: ["Read:CommentList"],
7744
+ risk: "low",
7745
+ target_bindings: {
7746
+ resource_id: { source: "param", param: "issueId" }
7747
+ },
7802
7748
  version: "1.0.0"
7803
7749
  },
7804
7750
  {
7805
- capability: "github.issues.triage",
7806
- description: "Create and update issues, post issue comments, manage sub-issues and labels",
7807
- includes: ["github.issue.create", "github.issue.update", "github.issue.comment.create", "github.issue.subissue.write", "github.label.write"],
7751
+ action: "linear.project.list",
7752
+ resource_type: "linear:project",
7753
+ required_relations: ["viewer", "editor", "admin", "owner"],
7754
+ required_scopes: ["read"],
7755
+ capability: "linear.read.basic",
7756
+ input_schema: {
7757
+ type: "object",
7758
+ properties: {
7759
+ teamId: { type: "string", description: "Filter by team ID" },
7760
+ limit: { type: "number", minimum: 1, maximum: 250, description: "Max number of projects (default 50)" }
7761
+ },
7762
+ additionalProperties: false
7763
+ },
7764
+ constraints: { rate_bucket: "linear.read" },
7765
+ effects: ["Read:ProjectList"],
7766
+ risk: "low",
7767
+ target_bindings: {
7768
+ resource_id: { source: "param", param: "teamId", required: false }
7769
+ },
7808
7770
  version: "1.0.0"
7809
7771
  },
7810
7772
  {
7811
- capability: "github.pulls.write",
7812
- description: "Create, update, merge, and review pull requests",
7813
- includes: ["github.pr.create", "github.pr.update", "github.pr.merge", "github.pr.review.create", "github.pr.comment.reply", "github.pr.branch.update"],
7773
+ action: "linear.project.read",
7774
+ resource_type: "linear:project",
7775
+ required_relations: ["viewer", "editor", "admin", "owner"],
7776
+ required_scopes: ["read"],
7777
+ capability: "linear.read.basic",
7778
+ input_schema: {
7779
+ type: "object",
7780
+ properties: {
7781
+ projectId: { type: "string", minLength: 1, description: "Project ID" }
7782
+ },
7783
+ required: ["projectId"],
7784
+ additionalProperties: false
7785
+ },
7786
+ constraints: { rate_bucket: "linear.read" },
7787
+ effects: ["Read:Project"],
7788
+ risk: "low",
7789
+ target_bindings: {
7790
+ resource_id: { source: "param", param: "projectId" }
7791
+ },
7814
7792
  version: "1.0.0"
7815
7793
  },
7816
7794
  {
7817
- capability: "github.repos.write",
7818
- description: "\u30EA\u30DD\u30B8\u30C8\u30EA\u306E\u4F5C\u6210\u30FB\u30D5\u30A9\u30FC\u30AF\u30FB\u30D5\u30A1\u30A4\u30EB\u66F4\u65B0\u30FB\u30D6\u30E9\u30F3\u30C1\u4F5C\u6210\u30FB\u30EF\u30FC\u30AF\u30D5\u30ED\u30FC\u5B9F\u884C\u30FB\u30B9\u30BF\u30FC\u64CD\u4F5C",
7819
- includes: ["github.repo.create", "github.repo.fork", "github.content.createOrUpdate", "github.content.delete", "github.branch.create", "github.workflow.run", "github.repo.star", "github.repo.unstar"],
7795
+ action: "linear.team.list",
7796
+ resource_type: "linear:team",
7797
+ required_relations: ["viewer", "editor", "admin", "owner"],
7798
+ required_scopes: ["read"],
7799
+ capability: "linear.read.basic",
7800
+ input_schema: {
7801
+ type: "object",
7802
+ properties: {
7803
+ limit: { type: "number", minimum: 1, maximum: 250, description: "Max number of teams (default 50)" }
7804
+ },
7805
+ additionalProperties: false
7806
+ },
7807
+ constraints: { rate_bucket: "linear.read" },
7808
+ effects: ["Read:TeamList"],
7809
+ risk: "low",
7810
+ // No teamId parameter: this action lists ALL accessible teams — intentionally
7811
+ // not scoped to a single team. required:false signals that missing resource
7812
+ // binding is expected (not an omission), consistent with slack.channel.read.
7813
+ target_bindings: {
7814
+ resource_id: { source: "param", param: "teamId", required: false }
7815
+ },
7820
7816
  version: "1.0.0"
7821
7817
  },
7822
7818
  {
7823
- capability: "github.notifications.write",
7824
- description: "\u901A\u77E5\u306E\u65E2\u8AAD\u5316\u30FB\u30B9\u30EC\u30C3\u30C9/\u30EA\u30DD\u30B8\u30C8\u30EA\u306E\u8CFC\u8AAD\u8A2D\u5B9A",
7825
- includes: ["github.notification.markRead", "github.notification.markAllRead", "github.notification.subscription.set", "github.notification.repoSubscription.set"],
7819
+ action: "linear.workflowState.list",
7820
+ resource_type: "linear:workflowState",
7821
+ required_relations: ["viewer", "editor", "admin", "owner"],
7822
+ required_scopes: ["read"],
7823
+ capability: "linear.read.basic",
7824
+ input_schema: {
7825
+ type: "object",
7826
+ properties: {
7827
+ teamId: { type: "string", description: "Filter by team ID" },
7828
+ limit: { type: "number", minimum: 1, maximum: 250, description: "Max number of workflow states to return (default 50)" }
7829
+ },
7830
+ additionalProperties: false
7831
+ },
7832
+ constraints: { rate_bucket: "linear.read" },
7833
+ effects: ["Read:WorkflowStateList"],
7834
+ risk: "low",
7835
+ target_bindings: {
7836
+ resource_id: { source: "param", param: "teamId", required: false }
7837
+ },
7826
7838
  version: "1.0.0"
7827
7839
  },
7828
7840
  {
7829
- capability: "github.gists.write",
7830
- description: "Gist \u306E\u4F5C\u6210\u30FB\u66F4\u65B0",
7831
- includes: ["github.gist.create", "github.gist.update"],
7841
+ action: "linear.viewer.read",
7842
+ resource_type: "linear:viewer",
7843
+ required_relations: ["viewer", "editor", "admin", "owner"],
7844
+ required_scopes: ["read"],
7845
+ capability: "linear.read.basic",
7846
+ input_schema: {
7847
+ type: "object",
7848
+ properties: {},
7849
+ additionalProperties: false
7850
+ },
7851
+ constraints: { rate_bucket: "linear.read" },
7852
+ effects: ["Read:Viewer"],
7853
+ risk: "low",
7854
+ // No parameters: this action reads the currently authenticated user — there is
7855
+ // no resource to scope to. required:false signals intentional exemption, not
7856
+ // omission, consistent with the slack.channel.read precedent.
7857
+ target_bindings: {
7858
+ resource_id: { source: "param", param: "viewerId", required: false }
7859
+ },
7832
7860
  version: "1.0.0"
7833
7861
  },
7834
7862
  {
7835
- capability: "gmail.read.basic",
7836
- description: "Read and search Gmail messages and labels",
7837
- includes: ["gmail.message.search", "gmail.message.read", "gmail.label.read", "gmail.batch.read"],
7863
+ action: "linear.label.list",
7864
+ resource_type: "linear:label",
7865
+ required_relations: ["viewer", "editor", "admin", "owner"],
7866
+ required_scopes: ["read"],
7867
+ capability: "linear.read.basic",
7868
+ input_schema: {
7869
+ type: "object",
7870
+ properties: {
7871
+ teamId: { type: "string", description: "Filter labels by team ID" },
7872
+ limit: { type: "number", minimum: 1, maximum: 250, description: "Max number of labels to return (default 50)" }
7873
+ },
7874
+ additionalProperties: false
7875
+ },
7876
+ constraints: { rate_bucket: "linear.read" },
7877
+ effects: ["Read:LabelList"],
7878
+ risk: "low",
7879
+ target_bindings: {
7880
+ resource_id: { source: "param", param: "teamId", required: false }
7881
+ },
7838
7882
  version: "1.0.0"
7839
7883
  },
7840
7884
  {
7841
- capability: "gmail.compose",
7842
- description: "Send emails and create drafts via Gmail",
7843
- includes: ["gmail.message.send", "gmail.draft.create"],
7885
+ action: "linear.user.list",
7886
+ resource_type: "linear:team",
7887
+ required_relations: ["viewer", "editor", "admin", "owner"],
7888
+ required_scopes: ["read"],
7889
+ capability: "linear.read.basic",
7890
+ input_schema: {
7891
+ type: "object",
7892
+ properties: {
7893
+ teamId: { type: "string", description: "Filter by team ID (lists team members only)" },
7894
+ limit: { type: "number", minimum: 1, maximum: 250, description: "Max number of users to return (default 50)" }
7895
+ },
7896
+ additionalProperties: false
7897
+ },
7898
+ constraints: { rate_bucket: "linear.read" },
7899
+ effects: ["Read:UserList"],
7900
+ risk: "low",
7901
+ target_bindings: {
7902
+ resource_id: { source: "param", param: "teamId", required: false }
7903
+ },
7844
7904
  version: "1.0.0"
7845
7905
  },
7846
7906
  {
7847
- capability: "gmail.manage",
7848
- description: "Trash and permanently delete Gmail messages",
7849
- includes: ["gmail.message.trash", "gmail.message.delete"],
7907
+ action: "linear.issue.archive",
7908
+ resource_type: "linear:issue",
7909
+ required_relations: ["editor", "act_as"],
7910
+ required_scopes: ["write"],
7911
+ capability: "linear.write.basic",
7912
+ input_schema: {
7913
+ type: "object",
7914
+ properties: {
7915
+ issueId: { type: "string", minLength: 1, description: "Issue ID or identifier to archive (e.g. ENG-123)" }
7916
+ },
7917
+ required: ["issueId"],
7918
+ additionalProperties: false
7919
+ },
7920
+ constraints: { rate_bucket: "linear.write" },
7921
+ effects: ["Archive:Issue"],
7922
+ risk: "high",
7923
+ target_bindings: {
7924
+ resource_id: { source: "param", param: "issueId" }
7925
+ },
7850
7926
  version: "1.0.0"
7851
7927
  },
7852
7928
  {
7853
- capability: "calendar.read.basic",
7854
- description: "Read and list Google Calendar events, and check free/busy availability",
7855
- includes: ["calendar.event.list", "calendar.event.read", "calendar.freebusy"],
7929
+ action: "linear.project.create",
7930
+ resource_type: "linear:project",
7931
+ required_relations: ["editor", "act_as"],
7932
+ required_scopes: ["write"],
7933
+ capability: "linear.write.basic",
7934
+ input_schema: {
7935
+ type: "object",
7936
+ properties: {
7937
+ name: { type: "string", minLength: 1, description: "Project name" },
7938
+ teamIds: { type: "array", items: { type: "string" }, minItems: 1, description: "Team IDs to associate with the project (at least one required)" },
7939
+ teamId: { type: "string", description: "Primary team ID (first element of teamIds; used for authorization scope)" },
7940
+ description: { type: "string", description: "Project description (Markdown)" },
7941
+ statusId: { type: "string", description: "Project status ID. Note: this is a ProjectStatus ID, not a WorkflowState ID. ProjectStatus can be retrieved via the Linear API projectStatuses query." },
7942
+ startDate: { type: "string", description: "Start date (ISO 8601, e.g. 2024-01-01)" },
7943
+ targetDate: { type: "string", description: "Target completion date (ISO 8601)" }
7944
+ },
7945
+ required: ["name", "teamIds"],
7946
+ additionalProperties: false
7947
+ },
7948
+ constraints: { rate_bucket: "linear.write" },
7949
+ effects: ["Create:Project"],
7950
+ risk: "high",
7951
+ target_bindings: {
7952
+ // teamId はスキーマの scalar フィールド (teamIds[0] の alias)。
7953
+ // teamId が明示指定されている場合はそれを使い、未指定の場合は
7954
+ // fallback_param: 'teamIds' で teamIds[0] にフォールバック(resolver は
7955
+ // 配列の first element を自動的にスカラーに変換する)。
7956
+ // extractResources も同じ優先順位 (teamId → teamIds[0]) で解決するため
7957
+ // 両者のバインドが常に一致する。required:true で高リスク CREATE の
7958
+ // resource binding 必須契約を明示 (teamIds は minItems:1 なので常に解決する)。
7959
+ resource_id: { source: "param", param: "teamId", fallback_param: "teamIds", required: true }
7960
+ },
7856
7961
  version: "1.0.0"
7857
7962
  },
7858
7963
  {
7859
- capability: "calendar.write",
7860
- description: "Create, update, and delete Google Calendar events",
7861
- includes: ["calendar.event.create", "calendar.event.update", "calendar.event.delete"],
7964
+ action: "linear.project.update",
7965
+ resource_type: "linear:project",
7966
+ required_relations: ["editor", "act_as"],
7967
+ required_scopes: ["write"],
7968
+ capability: "linear.write.basic",
7969
+ input_schema: {
7970
+ type: "object",
7971
+ properties: {
7972
+ projectId: { type: "string", minLength: 1, description: "Project ID to update" },
7973
+ name: { type: "string", description: "New project name" },
7974
+ description: { type: "string", description: "New project description (Markdown)" },
7975
+ statusId: { type: "string", description: "New project status ID. Note: this is a ProjectStatus ID, not a WorkflowState ID." },
7976
+ startDate: { type: "string", description: "New start date (ISO 8601)" },
7977
+ targetDate: { type: "string", description: "New target date (ISO 8601)" }
7978
+ },
7979
+ required: ["projectId"],
7980
+ additionalProperties: false
7981
+ },
7982
+ constraints: { rate_bucket: "linear.write" },
7983
+ effects: ["Update:Project"],
7984
+ risk: "high",
7985
+ target_bindings: {
7986
+ resource_id: { source: "param", param: "projectId" }
7987
+ },
7862
7988
  version: "1.0.0"
7863
7989
  },
7990
+ // ─── Linear Comment ops ───
7864
7991
  {
7865
- capability: "jira.read.basic",
7866
- description: "Read Jira issues, projects, boards, sprints, issue link types, and users",
7867
- includes: ["jira.project.read", "jira.board.read", "jira.sprint.read", "jira.issue.list", "jira.issue.search", "jira.worklog.read", "jira.issue.read", "jira.batch.read", "jira.comment.read", "jira.transition.list", "jira.issuelinktype.list", "jira.user.search"],
7992
+ action: "linear.comment.update",
7993
+ resource_type: "linear:comment",
7994
+ required_relations: ["editor", "act_as"],
7995
+ required_scopes: ["comments:create"],
7996
+ capability: "linear.write.basic",
7997
+ input_schema: {
7998
+ type: "object",
7999
+ properties: {
8000
+ commentId: { type: "string", minLength: 1, description: "Comment ID to update" },
8001
+ body: { type: "string", minLength: 1, description: "New comment body (Markdown)" }
8002
+ },
8003
+ required: ["commentId", "body"],
8004
+ additionalProperties: false
8005
+ },
8006
+ constraints: { rate_bucket: "linear.write" },
8007
+ effects: ["Update:Comment"],
8008
+ risk: "high",
8009
+ target_bindings: {
8010
+ resource_id: { source: "param", param: "commentId" }
8011
+ },
7868
8012
  version: "1.0.0"
7869
8013
  },
7870
8014
  {
7871
- capability: "jira.write.basic",
7872
- description: "Create, update, delete Jira issues, manage issue links, and add/delete comments",
7873
- includes: ["jira.issue.create", "jira.issue.update", "jira.issue.delete", "jira.comment.create", "jira.comment.delete", "jira.issue.transition", "jira.issuelink.create", "jira.issuelink.delete"],
8015
+ action: "linear.comment.delete",
8016
+ resource_type: "linear:comment",
8017
+ required_relations: ["editor", "act_as"],
8018
+ required_scopes: ["write"],
8019
+ capability: "linear.write.basic",
8020
+ input_schema: {
8021
+ type: "object",
8022
+ properties: {
8023
+ commentId: { type: "string", minLength: 1, description: "Comment ID to delete" }
8024
+ },
8025
+ required: ["commentId"],
8026
+ additionalProperties: false
8027
+ },
8028
+ constraints: { rate_bucket: "linear.write" },
8029
+ effects: ["Delete:Comment"],
8030
+ risk: "high",
8031
+ target_bindings: {
8032
+ resource_id: { source: "param", param: "commentId" }
8033
+ },
7874
8034
  version: "1.0.0"
7875
8035
  },
7876
8036
  {
7877
- capability: "inbox.write",
7878
- description: "Send and respond to agent inbox tasks",
7879
- includes: ["inbox.send", "inbox.respond", "inbox.complete", "scheduling.request"],
8037
+ action: "linear.comment.resolve",
8038
+ resource_type: "linear:comment",
8039
+ required_relations: ["editor", "act_as"],
8040
+ required_scopes: ["write"],
8041
+ capability: "linear.write.basic",
8042
+ input_schema: {
8043
+ type: "object",
8044
+ properties: {
8045
+ commentId: { type: "string", minLength: 1, description: "Comment ID to resolve" }
8046
+ },
8047
+ required: ["commentId"],
8048
+ additionalProperties: false
8049
+ },
8050
+ constraints: { rate_bucket: "linear.write" },
8051
+ effects: ["Update:Comment"],
8052
+ risk: "high",
8053
+ target_bindings: {
8054
+ resource_id: { source: "param", param: "commentId" }
8055
+ },
7880
8056
  version: "1.0.0"
7881
8057
  },
7882
8058
  {
7883
- capability: "inbox.read",
7884
- description: "Read agent inbox tasks",
7885
- includes: ["inbox.check", "inbox.checkByToken"],
8059
+ action: "linear.comment.unresolve",
8060
+ resource_type: "linear:comment",
8061
+ required_relations: ["editor", "act_as"],
8062
+ required_scopes: ["write"],
8063
+ capability: "linear.write.basic",
8064
+ input_schema: {
8065
+ type: "object",
8066
+ properties: {
8067
+ commentId: { type: "string", minLength: 1, description: "Comment ID to unresolve" }
8068
+ },
8069
+ required: ["commentId"],
8070
+ additionalProperties: false
8071
+ },
8072
+ constraints: { rate_bucket: "linear.write" },
8073
+ effects: ["Update:Comment"],
8074
+ risk: "high",
8075
+ target_bindings: {
8076
+ resource_id: { source: "param", param: "commentId" }
8077
+ },
7886
8078
  version: "1.0.0"
7887
- }
7888
- ]
7889
- };
7890
-
7891
- // src/registry/resource-type-labels.ts
7892
- var RESOURCE_TYPE_LABELS = {
7893
- "github:repo": "\u5BFE\u8C61\u30EA\u30DD\u30B8\u30C8\u30EA",
7894
- "github:org": "\u5BFE\u8C61\u7D44\u7E54"
7895
- };
7896
-
7897
- // src/registry/action-input-validator.ts
7898
- var SCHEMA_INDEX = (() => {
7899
- const map = /* @__PURE__ */ new Map();
7900
- for (const a of ACTION_REGISTRY.actions) {
7901
- if (a && typeof a.action === "string" && a.input_schema) {
7902
- map.set(a.action, a.input_schema);
7903
- }
7904
- }
7905
- return map;
7906
- })();
7907
- function describeExpectedShape(schema) {
7908
- const props = schema.properties ?? {};
7909
- const required = Array.isArray(schema.required) ? schema.required : [];
7910
- const requiredSet = new Set(required);
7911
- const parts = [];
7912
- const orderedKeys = [
7913
- ...required.filter((k) => k in props),
7914
- ...Object.keys(props).filter((k) => !requiredSet.has(k))
7915
- ];
7916
- for (const key of orderedKeys) {
7917
- const prop = props[key] || {};
7918
- const optionalMark = requiredSet.has(key) ? "" : "?";
7919
- const enumVals = Array.isArray(prop.enum) ? prop.enum : void 0;
7920
- if (enumVals && enumVals.length > 0) {
7921
- const rendered = enumVals.map((v) => typeof v === "string" ? `'${v}'` : String(v)).join(" | ");
7922
- parts.push(`${key}${optionalMark}: ${rendered}`);
7923
- } else {
7924
- parts.push(`${key}${optionalMark}`);
7925
- }
7926
- }
7927
- return `{ ${parts.join(", ")} }`;
7928
- }
7929
- function matchesType(value, type) {
7930
- if (!type) return true;
7931
- switch (type) {
7932
- case "string":
7933
- return typeof value === "string";
7934
- case "integer":
7935
- return typeof value === "number" && Number.isInteger(value);
7936
- case "number":
7937
- return typeof value === "number";
7938
- case "boolean":
7939
- return typeof value === "boolean";
7940
- case "array":
7941
- return Array.isArray(value);
7942
- case "object":
7943
- return typeof value === "object" && value !== null && !Array.isArray(value);
7944
- default:
7945
- return true;
7946
- }
7947
- }
7948
- function validateActionInput(action, params) {
7949
- const schema = SCHEMA_INDEX.get(action);
7950
- if (!schema) {
7951
- return { valid: true };
7952
- }
7953
- const props = schema.properties ?? {};
7954
- const required = Array.isArray(schema.required) ? schema.required : [];
7955
- const additionalProps = schema.additionalProperties;
7956
- const safeParams = params && typeof params === "object" && !Array.isArray(params) ? params : {};
7957
- const missing = [];
7958
- const unknown = [];
7959
- const enumErrors = [];
7960
- const typeErrors = [];
7961
- for (const key of required) {
7962
- if (!(key in safeParams) || safeParams[key] === void 0 || safeParams[key] === null) {
7963
- missing.push(key);
7964
- }
7965
- }
7966
- if (additionalProps === false) {
7967
- for (const key of Object.keys(safeParams)) {
7968
- if (!(key in props)) {
7969
- unknown.push(key);
7970
- }
7971
- }
7972
- }
7973
- for (const key of Object.keys(props)) {
7974
- if (!(key in safeParams)) continue;
7975
- const value = safeParams[key];
7976
- if (value === void 0 || value === null) continue;
7977
- const prop = props[key] || {};
7978
- const enumVals = Array.isArray(prop.enum) ? prop.enum : void 0;
7979
- if (enumVals && enumVals.length > 0) {
7980
- if (!enumVals.includes(value)) {
7981
- enumErrors.push(key);
7982
- continue;
7983
- }
7984
- }
7985
- if (!matchesType(value, prop.type)) {
7986
- typeErrors.push(key);
7987
- }
7988
- }
7989
- if (missing.length === 0 && unknown.length === 0 && enumErrors.length === 0 && typeErrors.length === 0) {
7990
- return { valid: true };
7991
- }
7992
- const segments = [];
7993
- if (missing.length > 0) {
7994
- segments.push(`missing required parameter(s) [${missing.join(", ")}]`);
7995
- }
7996
- if (unknown.length > 0) {
7997
- segments.push(`unknown parameter(s) [${unknown.join(", ")}]`);
7998
- }
7999
- if (enumErrors.length > 0) {
8000
- const details = enumErrors.map((key) => {
8001
- const enumVals = props[key]?.enum || [];
8002
- const rendered = enumVals.map((v) => typeof v === "string" ? `'${v}'` : String(v)).join(" | ");
8003
- return `${key} (allowed: ${rendered})`;
8004
- }).join(", ");
8005
- segments.push(`invalid value for enum parameter(s) [${details}]`);
8006
- }
8007
- if (typeErrors.length > 0) {
8008
- const details = typeErrors.map((key) => `${key} (expected ${props[key]?.type})`).join(", ");
8009
- segments.push(`wrong type for parameter(s) [${details}]`);
8010
- }
8011
- const message = `${action}: ${segments.join("; ")}. Expected shape: ${describeExpectedShape(schema)}`;
8012
- return { valid: false, errors: [message] };
8013
- }
8014
-
8015
- // src/registry/resource-types.ts
8016
- var RESOURCE_TYPES = {
8017
- // Slack
8018
- SLACK_CHANNEL: "slack:channel",
8019
- SLACK_USER: "slack:user",
8020
- SLACK_WORKSPACE: "slack:workspace",
8021
- // GitHub
8022
- GITHUB_REPO: "github:repo",
8023
- GITHUB_ORG: "github:org",
8024
- GITHUB_ISSUE: "github:issue",
8025
- // Google Drive
8026
- GOOGLE_DRIVE_FILE: "google:drive:file",
8027
- GOOGLE_DRIVE_FOLDER: "google:drive:folder",
8028
- // Gmail
8029
- GMAIL_THREAD: "gmail:thread",
8030
- GMAIL_LABEL: "gmail:label",
8031
- // Jira
8032
- JIRA_PROJECT: "jira:project",
8033
- JIRA_BOARD: "jira:board",
8034
- JIRA_ISSUE: "jira:issue",
8035
- JIRA_SPRINT: "jira:sprint",
8036
- // HubSpot
8037
- HUBSPOT_OBJECT: "hubspot:object",
8038
- HUBSPOT_PROPERTY: "hubspot:property",
8039
- HUBSPOT_SCHEMA: "hubspot:schema",
8040
- HUBSPOT_ASSOCIATION: "hubspot:association",
8079
+ },
8080
+ // ─── Linear Issue ops ───
8081
+ {
8082
+ action: "linear.issue.unarchive",
8083
+ resource_type: "linear:issue",
8084
+ required_relations: ["editor", "act_as"],
8085
+ required_scopes: ["write"],
8086
+ capability: "linear.write.basic",
8087
+ input_schema: {
8088
+ type: "object",
8089
+ properties: {
8090
+ issueId: { type: "string", minLength: 1, description: "Issue ID or identifier to unarchive (e.g. ENG-123)" }
8091
+ },
8092
+ required: ["issueId"],
8093
+ additionalProperties: false
8094
+ },
8095
+ constraints: { rate_bucket: "linear.write" },
8096
+ effects: ["Update:Issue"],
8097
+ risk: "high",
8098
+ target_bindings: {
8099
+ resource_id: { source: "param", param: "issueId" }
8100
+ },
8101
+ version: "1.0.0"
8102
+ },
8103
+ {
8104
+ action: "linear.issue.addLabel",
8105
+ resource_type: "linear:issue",
8106
+ required_relations: ["editor", "act_as"],
8107
+ required_scopes: ["write"],
8108
+ capability: "linear.write.basic",
8109
+ input_schema: {
8110
+ type: "object",
8111
+ properties: {
8112
+ issueId: { type: "string", minLength: 1, description: "Issue ID or identifier" },
8113
+ labelId: { type: "string", minLength: 1, description: "Label ID to add" }
8114
+ },
8115
+ required: ["issueId", "labelId"],
8116
+ additionalProperties: false
8117
+ },
8118
+ constraints: { rate_bucket: "linear.write" },
8119
+ effects: ["Update:Issue"],
8120
+ risk: "high",
8121
+ target_bindings: {
8122
+ resource_id: { source: "param", param: "issueId" }
8123
+ },
8124
+ version: "1.0.0"
8125
+ },
8126
+ {
8127
+ action: "linear.issue.removeLabel",
8128
+ resource_type: "linear:issue",
8129
+ required_relations: ["editor", "act_as"],
8130
+ required_scopes: ["write"],
8131
+ capability: "linear.write.basic",
8132
+ input_schema: {
8133
+ type: "object",
8134
+ properties: {
8135
+ issueId: { type: "string", minLength: 1, description: "Issue ID or identifier" },
8136
+ labelId: { type: "string", minLength: 1, description: "Label ID to remove" }
8137
+ },
8138
+ required: ["issueId", "labelId"],
8139
+ additionalProperties: false
8140
+ },
8141
+ constraints: { rate_bucket: "linear.write" },
8142
+ effects: ["Update:Issue"],
8143
+ risk: "high",
8144
+ target_bindings: {
8145
+ resource_id: { source: "param", param: "issueId" }
8146
+ },
8147
+ version: "1.0.0"
8148
+ },
8149
+ {
8150
+ action: "linear.issue.subscribe",
8151
+ resource_type: "linear:issue",
8152
+ required_relations: ["editor", "act_as"],
8153
+ required_scopes: ["write"],
8154
+ capability: "linear.write.basic",
8155
+ input_schema: {
8156
+ type: "object",
8157
+ properties: {
8158
+ issueId: { type: "string", minLength: 1, description: "Issue ID or identifier to subscribe to" }
8159
+ },
8160
+ required: ["issueId"],
8161
+ additionalProperties: false
8162
+ },
8163
+ constraints: { rate_bucket: "linear.write" },
8164
+ effects: ["Update:Issue"],
8165
+ risk: "high",
8166
+ target_bindings: {
8167
+ resource_id: { source: "param", param: "issueId" }
8168
+ },
8169
+ version: "1.0.0"
8170
+ },
8171
+ {
8172
+ action: "linear.issue.delete",
8173
+ resource_type: "linear:issue",
8174
+ required_relations: ["admin", "owner", "act_as"],
8175
+ required_scopes: ["write"],
8176
+ capability: "linear.write.basic",
8177
+ input_schema: {
8178
+ type: "object",
8179
+ properties: {
8180
+ issueId: { type: "string", minLength: 1, description: "Issue ID or identifier to permanently delete" }
8181
+ },
8182
+ required: ["issueId"],
8183
+ additionalProperties: false
8184
+ },
8185
+ constraints: { rate_bucket: "linear.write" },
8186
+ effects: ["Delete:Issue"],
8187
+ risk: "critical",
8188
+ target_bindings: {
8189
+ resource_id: { source: "param", param: "issueId" }
8190
+ },
8191
+ version: "1.0.0"
8192
+ },
8193
+ // ─── Linear Issue Relations ───
8194
+ {
8195
+ action: "linear.issueRelation.create",
8196
+ resource_type: "linear:issueRelation",
8197
+ required_relations: ["editor", "act_as"],
8198
+ required_scopes: ["write"],
8199
+ capability: "linear.write.basic",
8200
+ input_schema: {
8201
+ type: "object",
8202
+ properties: {
8203
+ issueId: { type: "string", minLength: 1, description: "Issue ID to create a relation from" },
8204
+ relatedIssueId: { type: "string", minLength: 1, description: "Related issue ID" },
8205
+ type: { type: "string", enum: ["blocks", "duplicate", "related", "similar"], description: "Relation type" }
8206
+ },
8207
+ required: ["issueId", "relatedIssueId", "type"],
8208
+ additionalProperties: false
8209
+ },
8210
+ constraints: { rate_bucket: "linear.write" },
8211
+ effects: ["Create:IssueRelation"],
8212
+ risk: "high",
8213
+ target_bindings: {
8214
+ resource_id: { source: "param", param: "issueId" }
8215
+ },
8216
+ version: "1.0.0"
8217
+ },
8218
+ {
8219
+ action: "linear.issueRelation.list",
8220
+ resource_type: "linear:issueRelation",
8221
+ required_relations: ["viewer", "editor", "admin", "owner"],
8222
+ required_scopes: ["read"],
8223
+ capability: "linear.read.basic",
8224
+ input_schema: {
8225
+ type: "object",
8226
+ properties: {
8227
+ issueId: { type: "string", minLength: 1, description: "Issue ID to list relations for" }
8228
+ },
8229
+ required: ["issueId"],
8230
+ additionalProperties: false
8231
+ },
8232
+ constraints: { rate_bucket: "linear.read" },
8233
+ effects: ["Read:IssueRelationList"],
8234
+ risk: "low",
8235
+ target_bindings: {
8236
+ resource_id: { source: "param", param: "issueId" }
8237
+ },
8238
+ version: "1.0.0"
8239
+ },
8240
+ {
8241
+ action: "linear.issueRelation.delete",
8242
+ resource_type: "linear:issueRelation",
8243
+ required_relations: ["editor", "act_as"],
8244
+ required_scopes: ["write"],
8245
+ capability: "linear.write.basic",
8246
+ input_schema: {
8247
+ type: "object",
8248
+ properties: {
8249
+ relationId: { type: "string", minLength: 1, description: "Issue relation ID to delete" }
8250
+ },
8251
+ required: ["relationId"],
8252
+ additionalProperties: false
8253
+ },
8254
+ constraints: { rate_bucket: "linear.write" },
8255
+ effects: ["Delete:IssueRelation"],
8256
+ risk: "high",
8257
+ target_bindings: {
8258
+ resource_id: { source: "param", param: "relationId" }
8259
+ },
8260
+ version: "1.0.0"
8261
+ },
8262
+ // ─── Linear Cycles ───
8263
+ {
8264
+ action: "linear.cycle.list",
8265
+ resource_type: "linear:cycle",
8266
+ required_relations: ["viewer", "editor", "admin", "owner"],
8267
+ required_scopes: ["read"],
8268
+ capability: "linear.read.basic",
8269
+ input_schema: {
8270
+ type: "object",
8271
+ properties: {
8272
+ teamId: { type: "string", description: "Filter by team ID" },
8273
+ limit: { type: "number", minimum: 1, maximum: 250, description: "Max number of cycles to return (default 50)" }
8274
+ },
8275
+ additionalProperties: false
8276
+ },
8277
+ constraints: { rate_bucket: "linear.read" },
8278
+ effects: ["Read:CycleList"],
8279
+ risk: "low",
8280
+ target_bindings: {
8281
+ resource_id: { source: "param", param: "teamId", required: false }
8282
+ },
8283
+ version: "1.0.0"
8284
+ },
8285
+ {
8286
+ action: "linear.cycle.read",
8287
+ resource_type: "linear:cycle",
8288
+ required_relations: ["viewer", "editor", "admin", "owner"],
8289
+ required_scopes: ["read"],
8290
+ capability: "linear.read.basic",
8291
+ input_schema: {
8292
+ type: "object",
8293
+ properties: {
8294
+ cycleId: { type: "string", minLength: 1, description: "Cycle ID" }
8295
+ },
8296
+ required: ["cycleId"],
8297
+ additionalProperties: false
8298
+ },
8299
+ constraints: { rate_bucket: "linear.read" },
8300
+ effects: ["Read:Cycle"],
8301
+ risk: "low",
8302
+ target_bindings: {
8303
+ resource_id: { source: "param", param: "cycleId" }
8304
+ },
8305
+ version: "1.0.0"
8306
+ },
8307
+ {
8308
+ action: "linear.cycle.create",
8309
+ resource_type: "linear:cycle",
8310
+ required_relations: ["editor", "act_as"],
8311
+ required_scopes: ["write"],
8312
+ capability: "linear.write.basic",
8313
+ input_schema: {
8314
+ type: "object",
8315
+ properties: {
8316
+ teamId: { type: "string", minLength: 1, description: "Team ID to create the cycle in" },
8317
+ name: { type: "string", description: "Cycle name" },
8318
+ startsAt: { type: "string", minLength: 1, description: "Cycle start date (ISO 8601)" },
8319
+ endsAt: { type: "string", minLength: 1, description: "Cycle end date (ISO 8601)" },
8320
+ description: { type: "string", description: "Cycle description" }
8321
+ },
8322
+ required: ["teamId", "startsAt", "endsAt"],
8323
+ additionalProperties: false
8324
+ },
8325
+ constraints: { rate_bucket: "linear.write" },
8326
+ effects: ["Create:Cycle"],
8327
+ risk: "high",
8328
+ target_bindings: {
8329
+ resource_id: { source: "param", param: "teamId" }
8330
+ },
8331
+ version: "1.0.0"
8332
+ },
8333
+ {
8334
+ action: "linear.cycle.update",
8335
+ resource_type: "linear:cycle",
8336
+ required_relations: ["editor", "act_as"],
8337
+ required_scopes: ["write"],
8338
+ capability: "linear.write.basic",
8339
+ input_schema: {
8340
+ type: "object",
8341
+ properties: {
8342
+ cycleId: { type: "string", minLength: 1, description: "Cycle ID to update" },
8343
+ name: { type: "string", description: "New cycle name" },
8344
+ startsAt: { type: "string", description: "New start date (ISO 8601)" },
8345
+ endsAt: { type: "string", description: "New end date (ISO 8601)" },
8346
+ description: { type: "string", description: "New description" },
8347
+ completedAt: { type: "string", description: "Completion date (ISO 8601) or null to unset" }
8348
+ },
8349
+ required: ["cycleId"],
8350
+ additionalProperties: false
8351
+ },
8352
+ constraints: { rate_bucket: "linear.write" },
8353
+ effects: ["Update:Cycle"],
8354
+ risk: "high",
8355
+ target_bindings: {
8356
+ resource_id: { source: "param", param: "cycleId" }
8357
+ },
8358
+ version: "1.0.0"
8359
+ },
8360
+ // ─── Linear Labels ───
8361
+ {
8362
+ action: "linear.label.create",
8363
+ resource_type: "linear:label",
8364
+ required_relations: ["editor", "act_as"],
8365
+ required_scopes: ["write"],
8366
+ capability: "linear.write.basic",
8367
+ input_schema: {
8368
+ type: "object",
8369
+ properties: {
8370
+ name: { type: "string", minLength: 1, description: "Label name" },
8371
+ color: { type: "string", description: "Label color (hex, e.g. #FF0000)" },
8372
+ description: { type: "string", description: "Label description" },
8373
+ teamId: { type: "string", description: "Team ID to scope the label to (omit for workspace-level label)" }
8374
+ },
8375
+ required: ["name"],
8376
+ additionalProperties: false
8377
+ },
8378
+ constraints: { rate_bucket: "linear.write" },
8379
+ effects: ["Create:Label"],
8380
+ risk: "high",
8381
+ target_bindings: {
8382
+ resource_id: { source: "param", param: "teamId", required: false }
8383
+ },
8384
+ version: "1.0.0"
8385
+ },
8386
+ {
8387
+ action: "linear.label.update",
8388
+ resource_type: "linear:label",
8389
+ required_relations: ["editor", "act_as"],
8390
+ required_scopes: ["write"],
8391
+ capability: "linear.write.basic",
8392
+ input_schema: {
8393
+ type: "object",
8394
+ properties: {
8395
+ labelId: { type: "string", minLength: 1, description: "Label ID to update" },
8396
+ name: { type: "string", description: "New label name" },
8397
+ color: { type: "string", description: "New label color (hex)" },
8398
+ description: { type: "string", description: "New description" }
8399
+ },
8400
+ required: ["labelId"],
8401
+ additionalProperties: false
8402
+ },
8403
+ constraints: { rate_bucket: "linear.write" },
8404
+ effects: ["Update:Label"],
8405
+ risk: "high",
8406
+ target_bindings: {
8407
+ resource_id: { source: "param", param: "labelId" }
8408
+ },
8409
+ version: "1.0.0"
8410
+ },
8411
+ // ─── Linear Attachments ───
8412
+ {
8413
+ action: "linear.attachment.create",
8414
+ resource_type: "linear:attachment",
8415
+ required_relations: ["editor", "act_as"],
8416
+ required_scopes: ["write"],
8417
+ capability: "linear.write.basic",
8418
+ input_schema: {
8419
+ type: "object",
8420
+ properties: {
8421
+ issueId: { type: "string", minLength: 1, description: "Issue ID to attach to" },
8422
+ title: { type: "string", minLength: 1, description: "Attachment title" },
8423
+ url: { type: "string", minLength: 1, description: "Attachment URL" },
8424
+ subtitle: { type: "string", description: "Attachment subtitle" }
8425
+ },
8426
+ required: ["issueId", "title", "url"],
8427
+ additionalProperties: false
8428
+ },
8429
+ constraints: { rate_bucket: "linear.write" },
8430
+ effects: ["Create:Attachment"],
8431
+ risk: "high",
8432
+ target_bindings: {
8433
+ resource_id: { source: "param", param: "issueId" }
8434
+ },
8435
+ version: "1.0.0"
8436
+ },
8437
+ {
8438
+ action: "linear.attachment.list",
8439
+ resource_type: "linear:attachment",
8440
+ required_relations: ["viewer", "editor", "admin", "owner"],
8441
+ required_scopes: ["read"],
8442
+ capability: "linear.read.basic",
8443
+ input_schema: {
8444
+ type: "object",
8445
+ properties: {
8446
+ issueId: { type: "string", minLength: 1, description: "Issue ID to list attachments for" }
8447
+ },
8448
+ required: ["issueId"],
8449
+ additionalProperties: false
8450
+ },
8451
+ constraints: { rate_bucket: "linear.read" },
8452
+ effects: ["Read:AttachmentList"],
8453
+ risk: "low",
8454
+ target_bindings: {
8455
+ resource_id: { source: "param", param: "issueId" }
8456
+ },
8457
+ version: "1.0.0"
8458
+ },
8459
+ {
8460
+ action: "linear.attachment.delete",
8461
+ resource_type: "linear:attachment",
8462
+ required_relations: ["editor", "act_as"],
8463
+ required_scopes: ["write"],
8464
+ capability: "linear.write.basic",
8465
+ input_schema: {
8466
+ type: "object",
8467
+ properties: {
8468
+ attachmentId: { type: "string", minLength: 1, description: "Attachment ID to delete" }
8469
+ },
8470
+ required: ["attachmentId"],
8471
+ additionalProperties: false
8472
+ },
8473
+ constraints: { rate_bucket: "linear.write" },
8474
+ effects: ["Delete:Attachment"],
8475
+ risk: "high",
8476
+ target_bindings: {
8477
+ resource_id: { source: "param", param: "attachmentId" }
8478
+ },
8479
+ version: "1.0.0"
8480
+ },
8481
+ // ─── Agent Inbox Actions (internal) ───
8482
+ // Inbox actions back the Agent Inbox MCP tools (task delegation between
8483
+ // agents/people). They are internal (no external OAuth provider), so they
8484
+ // carry a single nominal base scope `inbox` to satisfy the registry
8485
+ // meta-schema's `minItems: 1` (same pattern as hubspot.account.link.get).
8486
+ {
8487
+ action: "inbox.send",
8488
+ resource_type: "inbox:item",
8489
+ required_relations: ["editor", "act_as"],
8490
+ required_scopes: ["inbox"],
8491
+ capability: "inbox.write",
8492
+ input_schema: {
8493
+ type: "object",
8494
+ properties: {
8495
+ to: { type: "string", minLength: 1, description: "Recipient (user id / handle) the task request is sent to" },
8496
+ taskType: { type: "string", minLength: 1, description: "Type of task being requested" },
8497
+ payload: { type: "object", description: "Task-specific payload data" },
8498
+ message: { type: "string", description: "Optional human-readable message" }
8499
+ },
8500
+ required: ["to", "taskType", "payload"],
8501
+ additionalProperties: false
8502
+ },
8503
+ constraints: { rate_bucket: "inbox.write" },
8504
+ effects: ["Create:InboxItem"],
8505
+ risk: "high",
8506
+ target_bindings: {
8507
+ resource_id: { source: "param", param: "to" }
8508
+ },
8509
+ version: "1.0.0"
8510
+ },
8511
+ {
8512
+ action: "inbox.respond",
8513
+ resource_type: "inbox:item",
8514
+ required_relations: ["editor", "act_as"],
8515
+ required_scopes: ["inbox"],
8516
+ capability: "inbox.write",
8517
+ input_schema: {
8518
+ type: "object",
8519
+ properties: {
8520
+ id: { type: "string", minLength: 1, description: "Inbox item id being responded to" },
8521
+ action: { type: "string", enum: ["accept", "decline", "counter"], description: "Response action" },
8522
+ data: { type: "object", description: "Optional response data (e.g. counter-proposal)" }
8523
+ },
8524
+ required: ["id", "action"],
8525
+ additionalProperties: false
8526
+ },
8527
+ constraints: { rate_bucket: "inbox.write" },
8528
+ effects: ["Update:InboxItem"],
8529
+ // high (not medium): an agent-mediated response (MCP invoke) must always
8530
+ // go through human approval — the Phase 1 "always confirm" guarantee made
8531
+ // enforceable. target_bindings below binds the concrete inbox item id so
8532
+ // the high-risk approval has a specific resource constraint (the public
8533
+ // /scheduling/:token link flow does NOT go through this gate — it calls
8534
+ // InboxService.respond directly, system-key-signed).
8535
+ risk: "high",
8536
+ target_bindings: {
8537
+ resource_id: { source: "param", param: "id" }
8538
+ },
8539
+ version: "1.0.0"
8540
+ },
8541
+ {
8542
+ action: "inbox.check",
8543
+ resource_type: "inbox:item",
8544
+ required_relations: ["viewer", "editor", "admin", "owner"],
8545
+ required_scopes: ["inbox"],
8546
+ capability: "inbox.read",
8547
+ input_schema: {
8548
+ type: "object",
8549
+ properties: {
8550
+ types: { type: "array", items: { type: "string" }, description: "Filter by task types" },
8551
+ status: { type: "string", description: "Filter by item status" }
8552
+ },
8553
+ additionalProperties: false
8554
+ },
8555
+ constraints: { rate_bucket: "inbox.read" },
8556
+ effects: ["Read:InboxItem"],
8557
+ risk: "low",
8558
+ target_bindings: {
8559
+ resource_id: { source: "param", param: "id", required: false }
8560
+ },
8561
+ version: "1.0.0"
8562
+ },
8563
+ {
8564
+ action: "inbox.checkByToken",
8565
+ resource_type: "inbox:item",
8566
+ required_relations: ["viewer", "editor", "admin", "owner"],
8567
+ required_scopes: ["inbox"],
8568
+ capability: "inbox.read",
8569
+ input_schema: {
8570
+ type: "object",
8571
+ properties: {
8572
+ token: { type: "string", minLength: 1, description: "Opaque access token for a single inbox item" }
8573
+ },
8574
+ required: ["token"],
8575
+ additionalProperties: false
8576
+ },
8577
+ constraints: { rate_bucket: "inbox.read" },
8578
+ effects: ["Read:InboxItem"],
8579
+ risk: "low",
8580
+ target_bindings: {
8581
+ // inbox.checkByToken authenticates via the opaque HMAC token itself —
8582
+ // the token IS the authorization credential and must NOT appear in logs
8583
+ // or API error responses (resolvedResourceId surfaces on resource mismatch).
8584
+ // This action is dispatched before requireActor() so it bypasses the
8585
+ // normal grant-mediated authz path entirely. We deliberately bind to a
8586
+ // context key that the runtime never populates so that resource_id always
8587
+ // resolves to null and the secret token never reaches resolvedResourceId.
8588
+ resource_id: { source: "context", key: "inbox_item_id" }
8589
+ },
8590
+ version: "1.0.0"
8591
+ },
8592
+ {
8593
+ action: "inbox.complete",
8594
+ resource_type: "inbox:item",
8595
+ required_relations: ["editor", "act_as"],
8596
+ required_scopes: ["inbox"],
8597
+ capability: "inbox.write",
8598
+ input_schema: {
8599
+ type: "object",
8600
+ properties: {
8601
+ id: { type: "string", minLength: 1, description: "Inbox item id being completed" },
8602
+ result: { type: "object", description: "Result payload for the completed task" }
8603
+ },
8604
+ required: ["id", "result"],
8605
+ additionalProperties: false
8606
+ },
8607
+ constraints: { rate_bucket: "inbox.write" },
8608
+ effects: ["Update:InboxItem"],
8609
+ // low: inbox.complete marks an inbox item as done by the owning agent —
8610
+ // this is an internal state update on the agent's own task list, not an
8611
+ // external-facing write that needs human approval. (inbox.send / inbox.respond
8612
+ // are high because they create messages visible to other people.)
8613
+ risk: "low",
8614
+ target_bindings: {
8615
+ resource_id: { source: "param", param: "id" }
8616
+ },
8617
+ version: "1.0.0"
8618
+ },
8619
+ // ─── Scheduling Actions (internal) ───
8620
+ // scheduling.request is a meeting-time negotiation façade over inbox.send.
8621
+ // Candidate slots may be supplied two ways (server-side EXCLUSIVE — exactly
8622
+ // one): (a) `candidates` — the agent computes them LLM-side from the user
8623
+ // calendar and the server validates + freebusy-filters them, or (b)
8624
+ // `candidateWindow` — the agent hands the server a { start, end } window and
8625
+ // the server generates busy-free candidates from the sender's Google
8626
+ // Calendar. Like the inbox actions it is internal (no external OAuth
8627
+ // provider), so it carries the single nominal base scope `inbox`.
8628
+ //
8629
+ // NOTE: the candidates/candidateWindow exclusivity is expressed here as
8630
+ // `oneOf` for documentation + the deep JSON-Schema authorities, but the SDK's
8631
+ // hand-written `validateActionInput` is a SHALLOW shape gate that does NOT
8632
+ // interpret `oneOf` (neither key is in top-level `required`). The exclusivity
8633
+ // (exactly one of candidates/candidateWindow) is ENFORCED server-side in
8634
+ // SchedulingRequestService — see that service for the 400s on both-given /
8635
+ // both-missing.
8636
+ // Spec: docs/specs/2026-06-10-inbox-intent-routing-design.md §2.1
8637
+ {
8638
+ action: "scheduling.request",
8639
+ resource_type: "inbox:item",
8640
+ required_relations: ["editor", "act_as"],
8641
+ required_scopes: ["inbox"],
8642
+ capability: "inbox.write",
8643
+ input_schema: {
8644
+ type: "object",
8645
+ properties: {
8646
+ to: { type: "string", minLength: 1, description: "Counterpart (user id / email) to negotiate a meeting time with" },
8647
+ topic: { type: "string", minLength: 1, description: "Meeting topic shown to the counterpart" },
8648
+ durationMinutes: { type: "integer", minimum: 5, maximum: 480, description: "Meeting length in minutes" },
8649
+ candidates: {
8650
+ type: "array",
8651
+ minItems: 1,
8652
+ maxItems: 10,
8653
+ description: "Explicit candidate slots computed by the agent from the user calendar. The server validates them AND (when Google Calendar is connected) filters out any that conflict with the sender's calendar. Mutually exclusive with candidateWindow \u2014 supply exactly one.",
8654
+ items: {
8655
+ type: "object",
8656
+ properties: {
8657
+ start: { type: "string", description: "ISO 8601 start" },
8658
+ end: { type: "string", description: "ISO 8601 end" }
8659
+ },
8660
+ required: ["start", "end"],
8661
+ additionalProperties: false
8662
+ }
8663
+ },
8664
+ // candidateWindow is `{start,end}` OR an ARRAY of `{start,end}` ranges
8665
+ // (multi-range). We declare `type: ['object', 'array']` — the SDK
8666
+ // shallow validator (matchesType) does not handle an ARRAY of types
8667
+ // (its switch is on a single string), so it hits `default: return true`
8668
+ // and passes BOTH the single object and the array form (B-1 — an array
8669
+ // must NOT be rejected before reaching the server). The union type also
8670
+ // keeps the AJV strict-mode meta-schema happy (a `required`/`properties`
8671
+ // subschema needs SOME type). The nested `properties`/`required` below
8672
+ // describe the object form for the deep JSON-Schema authorities; the
8673
+ // array element shape and the deep constraints (ISO, start<end, ≤5
8674
+ // ranges, ≤62-day total span) are validated server-side in
8675
+ // SchedulingRequestService.validateWindowRanges.
8676
+ candidateWindow: {
8677
+ type: ["object", "array"],
8678
+ description: 'A search window OR an ARRAY of search windows the server fills with busy-free candidate slots from the sender\'s Google Calendar. Each range is `{ start, end }` (ISO 8601) \u2014 an independent contiguous interval the server searches within, spreading candidates across ranges. "\u5E73\u65E5\u5348\u5F8C" = one range per business day (Mon 13-18 / Tue 13-18 / \u2026); "\u6708\u66DC\u5348\u524D or \u6728\u66DC\u5348\u5F8C" = two ranges. A single contiguous span = one range. Max 5 ranges, total span \u2264 62 days. Mutually exclusive with candidates \u2014 supply exactly one. Requires a connected Google Calendar.',
8679
+ properties: {
8680
+ start: { type: "string", description: "ISO 8601 range start" },
8681
+ end: { type: "string", description: "ISO 8601 range end" }
8682
+ },
8683
+ required: ["start", "end"],
8684
+ additionalProperties: false
8685
+ },
8686
+ hold: {
8687
+ type: "boolean",
8688
+ description: "When true (or omitted, when the sender's delegation policy has holds enabled), tentatively hold the offered slots on the sender's calendar as [\u4EEE] events until the counterpart responds or the request expires. Pass false to skip holds for this request. Only takes effect when the sender has an enabled scheduling delegation policy."
8689
+ },
8690
+ message: { type: "string", description: "Optional human-readable message" }
8691
+ },
8692
+ // candidates / candidateWindow are intentionally NOT in `required`: the
8693
+ // exclusive-choice is documented via `oneOf` and enforced server-side.
8694
+ // Each oneOf branch re-declares its property locally (empty `properties`
8695
+ // entry) so AJV strict mode (`strictRequired`) sees the required key as
8696
+ // defined within the branch scope — the value shape itself is validated
8697
+ // by the top-level `properties` above.
8698
+ required: ["to", "topic", "durationMinutes"],
8699
+ oneOf: [
8700
+ { required: ["candidates"], properties: { candidates: {} } },
8701
+ { required: ["candidateWindow"], properties: { candidateWindow: {} } }
8702
+ ],
8703
+ additionalProperties: false
8704
+ },
8705
+ constraints: { rate_bucket: "inbox.write" },
8706
+ effects: ["Create:InboxItem"],
8707
+ risk: "high",
8708
+ target_bindings: {
8709
+ resource_id: { source: "param", param: "to" }
8710
+ },
8711
+ version: "1.0.0"
8712
+ },
8713
+ // ─── PR-R2 (design §3.2): the raw booking executor's ONE new action ───
8714
+ // The booking agent submits its scheduling proposal through a REAL PEP
8715
+ // crossing instead of a managed custom_tool. Registered here in R2; the
8716
+ // adapter dispatch case lands in R3.
8717
+ //
8718
+ // 🔴 risk: 'high' is deliberate and must NOT be downgraded. Every writing
8719
+ // sibling (inbox.send / inbox.respond / scheduling.request) is high, and
8720
+ // inbox.respond's own comment states that agent-mediated responses must
8721
+ // always go through human approval. The approval basis here is the
8722
+ // visitor's explicit "AIで日程調整する" click (or the flag-gated OIDC
8723
+ // auto-consent), captured at mint time as a first-class ApprovalToken
8724
+ // ledger row — NOT an absence of approval.
8725
+ //
8726
+ // 🪤 target_bindings.resource_id binds the BARE `itemId`. It must equal
8727
+ // canonicalResourceId('scheduling.submit_response', { itemId }) exactly.
8728
+ // A prefixed value (e.g. 'inbox:item/<id>') does not match the approval
8729
+ // assertion at invoke time and the call SILENTLY falls through to
8730
+ // auth_required (202) — no error surfaces, which is why the api side
8731
+ // carries a preflight-parity spec asserting three-way equality
8732
+ // (migration grant resource == assertion resource_id == invoke-time
8733
+ // canonicalResourceId).
8734
+ {
8735
+ action: "scheduling.submit_response",
8736
+ resource_type: "inbox:item",
8737
+ required_relations: ["editor", "act_as"],
8738
+ required_scopes: ["inbox"],
8739
+ capability: "inbox.write",
8740
+ input_schema: {
8741
+ type: "object",
8742
+ properties: {
8743
+ itemId: { type: "string", minLength: 1, description: "The inbox item the proposal answers" },
8744
+ decision: { type: "string", enum: ["propose", "decline"], description: "Whether the agent proposes slots or declines" },
8745
+ slots: {
8746
+ type: "array",
8747
+ maxItems: 10,
8748
+ description: "Proposed candidate slots (empty for a decline)",
8749
+ items: {
8750
+ type: "object",
8751
+ properties: {
8752
+ start: { type: "string", description: "ISO 8601 start" },
8753
+ end: { type: "string", description: "ISO 8601 end" }
8754
+ },
8755
+ required: ["start", "end"],
8756
+ additionalProperties: false
8757
+ }
8758
+ },
8759
+ recommendedSlotIndex: { type: ["integer", "null"], description: "Index into slots of the recommended option, or null" },
8760
+ recommendationReason: { type: "string", description: "Short rationale shown to the visitor" }
8761
+ },
8762
+ required: ["itemId", "decision"],
8763
+ additionalProperties: false
8764
+ },
8765
+ constraints: { rate_bucket: "inbox.write" },
8766
+ effects: ["Update:InboxItem"],
8767
+ risk: "high",
8768
+ target_bindings: {
8769
+ resource_id: { source: "param", param: "itemId" }
8770
+ },
8771
+ version: "1.0.0"
8772
+ }
8773
+ ],
8774
+ capabilities: [
8775
+ {
8776
+ capability: "slack.messaging.basic",
8777
+ description: "Post, update, and delete messages in channels",
8778
+ includes: ["slack.message.post", "slack.message.update", "slack.message.delete"],
8779
+ version: "1.0.0"
8780
+ },
8781
+ {
8782
+ capability: "slack.read.basic",
8783
+ description: "Read channels, messages, and user info",
8784
+ includes: ["slack.channel.read", "slack.user.read", "slack.message.read", "slack.message.read_paginated", "slack.batch.read"],
8785
+ version: "1.0.0"
8786
+ },
8787
+ {
8788
+ capability: "github.read.basic",
8789
+ description: "Read issues, pull requests and repository contents from repositories",
8790
+ includes: ["github.issue.list", "github.issue.read", "github.pr.list", "github.pr.read", "github.pr.files.list", "github.pr.review.list", "github.pr.search", "github.repo.search", "github.repo.collaborator.list", "github.content.get", "github.tree.get", "github.branch.list", "github.commit.get", "github.commit.list", "github.commit.search", "github.tag.list", "github.release.list", "github.release.getLatest", "github.release.getByTag", "github.code.search", "github.issue.search", "github.issue.types.list", "github.label.get", "github.label.list", "github.workflow.get", "github.workflow.list", "github.workflow.jobLogs.get", "github.repo.starred.list", "github.user.me.get", "github.org.teams.list", "github.team.members.list", "github.user.search", "github.notification.list", "github.notification.get", "github.gist.get", "github.gist.list"],
8791
+ version: "1.0.0"
8792
+ },
8793
+ {
8794
+ capability: "github.issues.triage",
8795
+ description: "Create and update issues, post issue comments, manage sub-issues and labels",
8796
+ includes: ["github.issue.create", "github.issue.update", "github.issue.comment.create", "github.issue.subissue.write", "github.label.write"],
8797
+ version: "1.0.0"
8798
+ },
8799
+ {
8800
+ capability: "github.pulls.write",
8801
+ description: "Create, update, merge, and review pull requests",
8802
+ includes: ["github.pr.create", "github.pr.update", "github.pr.merge", "github.pr.review.create", "github.pr.comment.reply", "github.pr.branch.update"],
8803
+ version: "1.0.0"
8804
+ },
8805
+ {
8806
+ capability: "github.repos.write",
8807
+ description: "\u30EA\u30DD\u30B8\u30C8\u30EA\u306E\u4F5C\u6210\u30FB\u30D5\u30A9\u30FC\u30AF\u30FB\u30D5\u30A1\u30A4\u30EB\u66F4\u65B0\u30FB\u30D6\u30E9\u30F3\u30C1\u4F5C\u6210\u30FB\u30EF\u30FC\u30AF\u30D5\u30ED\u30FC\u5B9F\u884C\u30FB\u30B9\u30BF\u30FC\u64CD\u4F5C",
8808
+ includes: ["github.repo.create", "github.repo.fork", "github.content.createOrUpdate", "github.content.delete", "github.branch.create", "github.workflow.run", "github.repo.star", "github.repo.unstar"],
8809
+ version: "1.0.0"
8810
+ },
8811
+ {
8812
+ capability: "github.notifications.write",
8813
+ description: "\u901A\u77E5\u306E\u65E2\u8AAD\u5316\u30FB\u30B9\u30EC\u30C3\u30C9/\u30EA\u30DD\u30B8\u30C8\u30EA\u306E\u8CFC\u8AAD\u8A2D\u5B9A",
8814
+ includes: ["github.notification.markRead", "github.notification.markAllRead", "github.notification.subscription.set", "github.notification.repoSubscription.set"],
8815
+ version: "1.0.0"
8816
+ },
8817
+ {
8818
+ capability: "github.gists.write",
8819
+ description: "Gist \u306E\u4F5C\u6210\u30FB\u66F4\u65B0",
8820
+ includes: ["github.gist.create", "github.gist.update"],
8821
+ version: "1.0.0"
8822
+ },
8823
+ {
8824
+ capability: "gmail.read.basic",
8825
+ description: "Read and search Gmail messages and labels",
8826
+ includes: ["gmail.message.search", "gmail.message.read", "gmail.label.read", "gmail.batch.read"],
8827
+ version: "1.0.0"
8828
+ },
8829
+ {
8830
+ capability: "gmail.compose",
8831
+ description: "Send emails and create drafts via Gmail",
8832
+ includes: ["gmail.message.send", "gmail.draft.create"],
8833
+ version: "1.0.0"
8834
+ },
8835
+ {
8836
+ capability: "gmail.manage",
8837
+ description: "Trash and permanently delete Gmail messages",
8838
+ includes: ["gmail.message.trash", "gmail.message.delete"],
8839
+ version: "1.0.0"
8840
+ },
8841
+ {
8842
+ capability: "calendar.read.basic",
8843
+ description: "Read and list Google Calendar events, and check free/busy availability",
8844
+ includes: ["calendar.event.list", "calendar.event.read", "calendar.freebusy"],
8845
+ version: "1.0.0"
8846
+ },
8847
+ {
8848
+ capability: "calendar.write",
8849
+ description: "Create, update, and delete Google Calendar events",
8850
+ includes: ["calendar.event.create", "calendar.event.update", "calendar.event.delete"],
8851
+ version: "1.0.0"
8852
+ },
8853
+ {
8854
+ capability: "jira.read.basic",
8855
+ description: "Read Jira issues, projects, boards, sprints, issue link types, and users",
8856
+ includes: ["jira.project.read", "jira.board.read", "jira.sprint.read", "jira.issue.list", "jira.issue.search", "jira.worklog.read", "jira.issue.read", "jira.batch.read", "jira.comment.read", "jira.transition.list", "jira.issuelinktype.list", "jira.user.search"],
8857
+ version: "1.0.0"
8858
+ },
8859
+ {
8860
+ capability: "jira.write.basic",
8861
+ description: "Create, update, delete Jira issues, manage issue links, and add/delete comments",
8862
+ includes: ["jira.issue.create", "jira.issue.update", "jira.issue.delete", "jira.comment.create", "jira.comment.delete", "jira.issue.transition", "jira.issuelink.create", "jira.issuelink.delete"],
8863
+ version: "1.0.0"
8864
+ },
8865
+ {
8866
+ capability: "inbox.write",
8867
+ description: "Send and respond to agent inbox tasks",
8868
+ includes: ["inbox.send", "inbox.respond", "inbox.complete", "scheduling.request", "scheduling.submit_response"],
8869
+ version: "1.0.0"
8870
+ },
8871
+ {
8872
+ capability: "inbox.read",
8873
+ description: "Read agent inbox tasks",
8874
+ includes: ["inbox.check", "inbox.checkByToken"],
8875
+ version: "1.0.0"
8876
+ },
8877
+ {
8878
+ capability: "linear.read.basic",
8879
+ description: "Read Linear issues, comments, projects, teams, workflow states, labels, users, viewer info, issue relations, cycles, and attachments",
8880
+ includes: [
8881
+ "linear.issue.list",
8882
+ "linear.issue.read",
8883
+ "linear.issue.search",
8884
+ "linear.comment.list",
8885
+ "linear.project.list",
8886
+ "linear.project.read",
8887
+ "linear.team.list",
8888
+ "linear.workflowState.list",
8889
+ "linear.viewer.read",
8890
+ "linear.label.list",
8891
+ "linear.user.list",
8892
+ "linear.issueRelation.list",
8893
+ "linear.cycle.list",
8894
+ "linear.cycle.read",
8895
+ "linear.attachment.list"
8896
+ ],
8897
+ version: "1.0.0"
8898
+ },
8899
+ {
8900
+ capability: "linear.write.basic",
8901
+ description: "Create, update, archive, delete Linear issues, comments, projects, labels, cycles, attachments, and issue relations",
8902
+ includes: [
8903
+ "linear.issue.create",
8904
+ "linear.issue.update",
8905
+ "linear.comment.create",
8906
+ "linear.issue.archive",
8907
+ "linear.project.create",
8908
+ "linear.project.update",
8909
+ "linear.comment.update",
8910
+ "linear.comment.delete",
8911
+ "linear.comment.resolve",
8912
+ "linear.comment.unresolve",
8913
+ "linear.issue.unarchive",
8914
+ "linear.issue.addLabel",
8915
+ "linear.issue.removeLabel",
8916
+ "linear.issue.subscribe",
8917
+ "linear.issue.delete",
8918
+ "linear.issueRelation.create",
8919
+ "linear.issueRelation.delete",
8920
+ "linear.cycle.create",
8921
+ "linear.cycle.update",
8922
+ "linear.label.create",
8923
+ "linear.label.update",
8924
+ "linear.attachment.create",
8925
+ "linear.attachment.delete"
8926
+ ],
8927
+ version: "1.0.0"
8928
+ }
8929
+ ]
8930
+ };
8931
+
8932
+ // src/registry/resource-type-labels.ts
8933
+ var RESOURCE_TYPE_LABELS = {
8934
+ "github:repo": "\u5BFE\u8C61\u30EA\u30DD\u30B8\u30C8\u30EA",
8935
+ "github:org": "\u5BFE\u8C61\u7D44\u7E54"
8936
+ };
8937
+
8938
+ // src/registry/action-input-validator.ts
8939
+ var SCHEMA_INDEX = (() => {
8940
+ const map = /* @__PURE__ */ new Map();
8941
+ for (const a of ACTION_REGISTRY.actions) {
8942
+ if (a && typeof a.action === "string" && a.input_schema) {
8943
+ map.set(a.action, a.input_schema);
8944
+ }
8945
+ }
8946
+ return map;
8947
+ })();
8948
+ function describeExpectedShape(schema) {
8949
+ const props = schema.properties ?? {};
8950
+ const required = Array.isArray(schema.required) ? schema.required : [];
8951
+ const requiredSet = new Set(required);
8952
+ const parts = [];
8953
+ const orderedKeys = [
8954
+ ...required.filter((k) => k in props),
8955
+ ...Object.keys(props).filter((k) => !requiredSet.has(k))
8956
+ ];
8957
+ for (const key of orderedKeys) {
8958
+ const prop = props[key] || {};
8959
+ const optionalMark = requiredSet.has(key) ? "" : "?";
8960
+ const enumVals = Array.isArray(prop.enum) ? prop.enum : void 0;
8961
+ if (enumVals && enumVals.length > 0) {
8962
+ const rendered = enumVals.map((v) => typeof v === "string" ? `'${v}'` : String(v)).join(" | ");
8963
+ parts.push(`${key}${optionalMark}: ${rendered}`);
8964
+ } else {
8965
+ parts.push(`${key}${optionalMark}`);
8966
+ }
8967
+ }
8968
+ return `{ ${parts.join(", ")} }`;
8969
+ }
8970
+ function matchesType(value, type) {
8971
+ if (!type) return true;
8972
+ switch (type) {
8973
+ case "string":
8974
+ return typeof value === "string";
8975
+ case "integer":
8976
+ return typeof value === "number" && Number.isInteger(value);
8977
+ case "number":
8978
+ return typeof value === "number";
8979
+ case "boolean":
8980
+ return typeof value === "boolean";
8981
+ case "array":
8982
+ return Array.isArray(value);
8983
+ case "object":
8984
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8985
+ default:
8986
+ return true;
8987
+ }
8988
+ }
8989
+ function validateActionInput(action, params) {
8990
+ const schema = SCHEMA_INDEX.get(action);
8991
+ if (!schema) {
8992
+ return { valid: true };
8993
+ }
8994
+ const props = schema.properties ?? {};
8995
+ const required = Array.isArray(schema.required) ? schema.required : [];
8996
+ const additionalProps = schema.additionalProperties;
8997
+ const safeParams = params && typeof params === "object" && !Array.isArray(params) ? params : {};
8998
+ const missing = [];
8999
+ const unknown = [];
9000
+ const enumErrors = [];
9001
+ const typeErrors = [];
9002
+ for (const key of required) {
9003
+ if (!(key in safeParams) || safeParams[key] === void 0 || safeParams[key] === null) {
9004
+ missing.push(key);
9005
+ }
9006
+ }
9007
+ if (additionalProps === false) {
9008
+ for (const key of Object.keys(safeParams)) {
9009
+ if (!(key in props)) {
9010
+ unknown.push(key);
9011
+ }
9012
+ }
9013
+ }
9014
+ for (const key of Object.keys(props)) {
9015
+ if (!(key in safeParams)) continue;
9016
+ const value = safeParams[key];
9017
+ if (value === void 0 || value === null) continue;
9018
+ const prop = props[key] || {};
9019
+ const enumVals = Array.isArray(prop.enum) ? prop.enum : void 0;
9020
+ if (enumVals && enumVals.length > 0) {
9021
+ if (!enumVals.includes(value)) {
9022
+ enumErrors.push(key);
9023
+ continue;
9024
+ }
9025
+ }
9026
+ if (!matchesType(value, prop.type)) {
9027
+ typeErrors.push(key);
9028
+ }
9029
+ }
9030
+ if (missing.length === 0 && unknown.length === 0 && enumErrors.length === 0 && typeErrors.length === 0) {
9031
+ return { valid: true };
9032
+ }
9033
+ const segments = [];
9034
+ if (missing.length > 0) {
9035
+ segments.push(`missing required parameter(s) [${missing.join(", ")}]`);
9036
+ }
9037
+ if (unknown.length > 0) {
9038
+ segments.push(`unknown parameter(s) [${unknown.join(", ")}]`);
9039
+ }
9040
+ if (enumErrors.length > 0) {
9041
+ const details = enumErrors.map((key) => {
9042
+ const enumVals = props[key]?.enum || [];
9043
+ const rendered = enumVals.map((v) => typeof v === "string" ? `'${v}'` : String(v)).join(" | ");
9044
+ return `${key} (allowed: ${rendered})`;
9045
+ }).join(", ");
9046
+ segments.push(`invalid value for enum parameter(s) [${details}]`);
9047
+ }
9048
+ if (typeErrors.length > 0) {
9049
+ const details = typeErrors.map((key) => `${key} (expected ${props[key]?.type})`).join(", ");
9050
+ segments.push(`wrong type for parameter(s) [${details}]`);
9051
+ }
9052
+ const message = `${action}: ${segments.join("; ")}. Expected shape: ${describeExpectedShape(schema)}`;
9053
+ return { valid: false, errors: [message] };
9054
+ }
9055
+
9056
+ // src/registry/resource-types.ts
9057
+ var RESOURCE_TYPES = {
9058
+ // Slack
9059
+ SLACK_CHANNEL: "slack:channel",
9060
+ SLACK_USER: "slack:user",
9061
+ SLACK_WORKSPACE: "slack:workspace",
9062
+ // GitHub
9063
+ GITHUB_REPO: "github:repo",
9064
+ GITHUB_ORG: "github:org",
9065
+ GITHUB_ISSUE: "github:issue",
9066
+ // Google Drive
9067
+ GOOGLE_DRIVE_FILE: "google:drive:file",
9068
+ GOOGLE_DRIVE_FOLDER: "google:drive:folder",
9069
+ // Gmail
9070
+ GMAIL_THREAD: "gmail:thread",
9071
+ GMAIL_LABEL: "gmail:label",
9072
+ // Jira
9073
+ JIRA_PROJECT: "jira:project",
9074
+ JIRA_BOARD: "jira:board",
9075
+ JIRA_ISSUE: "jira:issue",
9076
+ JIRA_SPRINT: "jira:sprint",
9077
+ // HubSpot
9078
+ HUBSPOT_OBJECT: "hubspot:object",
9079
+ HUBSPOT_PROPERTY: "hubspot:property",
9080
+ HUBSPOT_SCHEMA: "hubspot:schema",
9081
+ HUBSPOT_ASSOCIATION: "hubspot:association",
8041
9082
  HUBSPOT_ENGAGEMENT: "hubspot:engagement",
8042
9083
  HUBSPOT_WORKFLOW: "hubspot:workflow",
8043
9084
  HUBSPOT_ACCOUNT: "hubspot:account",
@@ -8258,188 +9299,773 @@ var ACTION_DISPLAY_CONFIGS = {
8258
9299
  "slack.message.post": {
8259
9300
  summaryTemplate: (p) => {
8260
9301
  const parts = [];
8261
- if (p.channel) parts.push(p.channel + ":");
8262
- if (p.text) parts.push(String(p.text));
9302
+ if (p.channel) parts.push(p.channel + ":");
9303
+ if (p.text) parts.push(String(p.text));
9304
+ return parts.join(" ");
9305
+ },
9306
+ displayFields: [
9307
+ { key: "channel", label: "Channel" },
9308
+ { key: "text", label: "Message" }
9309
+ ]
9310
+ },
9311
+ "gmail.message.send": {
9312
+ summaryTemplate: (p) => {
9313
+ const parts = [];
9314
+ if (p.to) parts.push(`To: ${p.to}`);
9315
+ if (p.subject) parts.push(`Subject: ${p.subject}`);
9316
+ return parts.join(", ");
9317
+ },
9318
+ displayFields: [
9319
+ { key: "to", label: "To" },
9320
+ { key: "subject", label: "Subject" },
9321
+ { key: "body", label: "Body" }
9322
+ ]
9323
+ },
9324
+ "gmail.draft.create": {
9325
+ summaryTemplate: (p) => {
9326
+ const parts = [];
9327
+ if (p.to) parts.push(`To: ${p.to}`);
9328
+ if (p.subject) parts.push(`Subject: ${p.subject}`);
9329
+ return parts.join(", ");
9330
+ },
9331
+ displayFields: [
9332
+ { key: "to", label: "To" },
9333
+ { key: "subject", label: "Subject" },
9334
+ { key: "body", label: "Body" }
9335
+ ]
9336
+ },
9337
+ "calendar.event.create": {
9338
+ summaryTemplate: (p) => {
9339
+ const parts = [];
9340
+ if (p.summary) parts.push(p.summary);
9341
+ if (p.start) parts.push(`(${formatCalendarTime(p.start)})`);
9342
+ return parts.join(" ");
9343
+ },
9344
+ displayFields: [
9345
+ { key: "summary", label: "Event" },
9346
+ { key: "start", label: "Start" },
9347
+ { key: "end", label: "End" },
9348
+ { key: "attendees", label: "Attendees" }
9349
+ ]
9350
+ },
9351
+ "calendar.event.update": {
9352
+ summaryTemplate: (p) => {
9353
+ const parts = [];
9354
+ if (p.summary) parts.push(p.summary);
9355
+ if (p.start) parts.push(`(${formatCalendarTime(p.start)})`);
9356
+ return parts.join(" ");
9357
+ },
9358
+ displayFields: [
9359
+ { key: "summary", label: "Event" },
9360
+ { key: "start", label: "Start" },
9361
+ { key: "end", label: "End" },
9362
+ { key: "attendees", label: "Attendees" }
9363
+ ]
9364
+ },
9365
+ "jira.issue.create": {
9366
+ summaryTemplate: (p) => {
9367
+ const parts = [];
9368
+ const projectKey = jiraField(
9369
+ p,
9370
+ ["projectKey", "project"],
9371
+ "project",
9372
+ (v) => typeof v === "string" ? v : v?.key
9373
+ );
9374
+ const summary = jiraField(p, ["summary"], "summary");
9375
+ if (projectKey) parts.push(`${projectKey}:`);
9376
+ if (summary) parts.push(summary);
9377
+ return parts.join(" ");
9378
+ },
9379
+ // AIDENTITY-54: nested `fields.X` 形式と top-level canonical の両方を
9380
+ // 拾えるよう extract を使う。assignee は null (unassign) も表示。
9381
+ // legacy alias (project / issuetype 等) も top-level 候補に含めて後方互換を保つ。
9382
+ displayFields: [
9383
+ {
9384
+ label: "Project",
9385
+ extract: (p) => jiraField(
9386
+ p,
9387
+ ["projectKey", "project"],
9388
+ "project",
9389
+ (v) => typeof v === "string" ? v : v?.key
9390
+ )
9391
+ },
9392
+ {
9393
+ label: "Type",
9394
+ extract: (p) => jiraField(
9395
+ p,
9396
+ ["issueTypeName", "issueType", "issuetype"],
9397
+ "issuetype",
9398
+ (v) => typeof v === "string" ? v : v?.name
9399
+ )
9400
+ },
9401
+ { label: "Summary", extract: (p) => jiraField(p, ["summary"], "summary") },
9402
+ { label: "Description", extract: (p) => jiraField(p, ["description"], "description") },
9403
+ {
9404
+ label: "Priority",
9405
+ extract: (p) => jiraField(
9406
+ p,
9407
+ ["priority"],
9408
+ "priority",
9409
+ (v) => typeof v === "string" ? v : v?.name
9410
+ )
9411
+ },
9412
+ { label: "Assignee", extract: extractJiraAssigneeDisplay },
9413
+ { label: "Labels", extract: (p) => jiraField(p, ["labels"], "labels") }
9414
+ ]
9415
+ },
9416
+ "jira.issue.update": {
9417
+ summaryTemplate: (p) => {
9418
+ const parts = [];
9419
+ const key = p.issueIdOrKey ?? p.issueKey;
9420
+ const summary = jiraField(p, ["summary"], "summary");
9421
+ if (key) parts.push(`${key}:`);
9422
+ if (summary) parts.push(summary);
9423
+ return parts.join(" ");
9424
+ },
9425
+ displayFields: [
9426
+ // canonical は issueIdOrKey、agent が issueKey で送ってくる旧仕様も拾う。
9427
+ { label: "Issue", extract: (p) => p.issueIdOrKey ?? p.issueKey },
9428
+ { label: "Summary", extract: (p) => jiraField(p, ["summary"], "summary") },
9429
+ { label: "Description", extract: (p) => jiraField(p, ["description"], "description") },
9430
+ {
9431
+ label: "Priority",
9432
+ extract: (p) => jiraField(
9433
+ p,
9434
+ ["priority"],
9435
+ "priority",
9436
+ (v) => typeof v === "string" ? v : v?.name
9437
+ )
9438
+ },
9439
+ // AIDENTITY-54: top-level alias (string / object) と null (unassign) も解決。
9440
+ { label: "Assignee", extract: extractJiraAssigneeDisplay },
9441
+ { label: "Labels", extract: (p) => jiraField(p, ["labels"], "labels") }
9442
+ ]
9443
+ },
9444
+ // AIDENTITY-65: comment 本体は string と ADF document の両方を受けるため、
9445
+ // detail 表示の ADF 描画は formatValueFull 側に任せて key 指定で OK。
9446
+ "jira.comment.create": {
9447
+ summaryTemplate: (p) => {
9448
+ const parts = [];
9449
+ if (p.issueIdOrKey) parts.push(`${p.issueIdOrKey}:`);
9450
+ if (p.body) parts.push(typeof p.body === "string" ? p.body : "[ADF]");
9451
+ return parts.join(" ");
9452
+ },
9453
+ displayFields: [
9454
+ { key: "issueIdOrKey", label: "Issue" },
9455
+ { key: "body", label: "Comment" }
9456
+ ]
9457
+ },
9458
+ // AIDENTITY-66: 破壊的操作なので「どの issue のどの comment を消すか」を明示する
9459
+ // (jira.issue.delete / jira.issuelink.delete と同じ destructive 表現の方針)。
9460
+ "jira.comment.delete": {
9461
+ summaryTemplate: (p) => {
9462
+ const issue = p.issueIdOrKey ?? p.issueKey;
9463
+ const commentId = p.commentId ?? p.id;
9464
+ if (issue && commentId) return `Delete comment ${commentId} on ${issue}`;
9465
+ if (commentId) return `Delete comment ${commentId}`;
9466
+ return "";
9467
+ },
9468
+ displayFields: [
9469
+ { label: "Issue", extract: (p) => p.issueIdOrKey ?? p.issueKey },
9470
+ { label: "Comment", extract: (p) => p.commentId ?? p.id }
9471
+ ]
9472
+ },
9473
+ // AIDENTITY-65: API enricher が 'Link' label を upsert で書き換える。
9474
+ "jira.issuelink.delete": {
9475
+ summaryTemplate: (p) => {
9476
+ const parts = [];
9477
+ if (p.linkId) parts.push(`Delete link ${p.linkId}`);
9478
+ return parts.join(" ");
9479
+ },
9480
+ displayFields: [
9481
+ { key: "linkId", label: "Link" }
9482
+ ]
9483
+ },
9484
+ // ─── HubSpot ───
9485
+ "hubspot.crm.objects.list": {
9486
+ summaryTemplate: (p) => {
9487
+ const parts = [];
9488
+ if (p.objectType) parts.push(String(p.objectType));
9489
+ parts.push("list");
9490
+ return parts.join(" ");
9491
+ },
9492
+ displayFields: [
9493
+ { key: "objectType", label: "Object Type" },
9494
+ { key: "limit", label: "Limit" },
9495
+ { key: "properties", label: "Properties" }
9496
+ ]
9497
+ },
9498
+ "hubspot.crm.objects.search": {
9499
+ summaryTemplate: (p) => {
9500
+ const parts = [];
9501
+ if (p.objectType) parts.push(String(p.objectType));
9502
+ if (p.query) parts.push(`"${String(p.query)}"`);
9503
+ return parts.join(" ");
9504
+ },
9505
+ displayFields: [
9506
+ { key: "objectType", label: "Object Type" },
9507
+ { key: "query", label: "Query" },
9508
+ { key: "limit", label: "Limit" }
9509
+ ]
9510
+ },
9511
+ "hubspot.crm.objects.batchRead": {
9512
+ summaryTemplate: (p) => {
9513
+ const parts = [];
9514
+ if (p.objectType) parts.push(String(p.objectType));
9515
+ if (Array.isArray(p.inputs)) parts.push(`(${p.inputs.length} items)`);
9516
+ return parts.join(" ");
9517
+ },
9518
+ displayFields: [
9519
+ { key: "objectType", label: "Object Type" },
9520
+ { key: "inputs", label: "Records" }
9521
+ ]
9522
+ },
9523
+ "hubspot.crm.objects.batchCreate": {
9524
+ summaryTemplate: (p) => {
9525
+ const parts = [];
9526
+ if (p.objectType) parts.push(String(p.objectType));
9527
+ if (Array.isArray(p.inputs)) parts.push(`create ${p.inputs.length}`);
9528
+ return parts.join(" ");
9529
+ },
9530
+ displayFields: [
9531
+ { key: "objectType", label: "Object Type" },
9532
+ { key: "inputs", label: "Records" }
9533
+ ]
9534
+ },
9535
+ "hubspot.crm.objects.batchUpdate": {
9536
+ summaryTemplate: (p) => {
9537
+ const parts = [];
9538
+ if (p.objectType) parts.push(String(p.objectType));
9539
+ if (Array.isArray(p.inputs)) parts.push(`update ${p.inputs.length}`);
9540
+ return parts.join(" ");
9541
+ },
9542
+ displayFields: [
9543
+ { key: "objectType", label: "Object Type" },
9544
+ { key: "inputs", label: "Records" }
9545
+ ]
9546
+ },
9547
+ "hubspot.crm.properties.list": {
9548
+ summaryTemplate: (p) => {
9549
+ const parts = [];
9550
+ if (p.objectType) parts.push(String(p.objectType));
9551
+ parts.push("properties list");
9552
+ return parts.join(" ");
9553
+ },
9554
+ displayFields: [
9555
+ { key: "objectType", label: "Object Type" }
9556
+ ]
9557
+ },
9558
+ "hubspot.crm.properties.create": {
9559
+ summaryTemplate: (p) => {
9560
+ const parts = [];
9561
+ if (p.objectType) parts.push(String(p.objectType));
9562
+ if (p.name) parts.push(String(p.name));
9563
+ return parts.join(" ");
9564
+ },
9565
+ displayFields: [
9566
+ { key: "objectType", label: "Object Type" },
9567
+ { key: "name", label: "Property Name" },
9568
+ { key: "label", label: "Label" },
9569
+ { key: "type", label: "Type" }
9570
+ ]
9571
+ },
9572
+ "hubspot.crm.engagements.create": {
9573
+ summaryTemplate: (p) => {
9574
+ const parts = [];
9575
+ if (p.type) parts.push(String(p.type));
9576
+ parts.push("engagement");
9577
+ return parts.join(" ");
9578
+ },
9579
+ displayFields: [
9580
+ { key: "type", label: "Type" },
9581
+ { key: "metadata", label: "Metadata" }
9582
+ ]
9583
+ },
9584
+ "hubspot.automation.workflows.list": {
9585
+ summaryTemplate: (_p) => "List workflows",
9586
+ displayFields: [
9587
+ { key: "limit", label: "Limit" }
9588
+ ]
9589
+ },
9590
+ "hubspot.account.userDetails.get": {
9591
+ summaryTemplate: (_p) => "Get HubSpot account details",
9592
+ displayFields: []
9593
+ },
9594
+ "hubspot.account.link.get": {
9595
+ summaryTemplate: (p) => {
9596
+ const parts = [];
9597
+ if (p.portalId) parts.push(`portal:${p.portalId}`);
9598
+ if (Array.isArray(p.pageRequests)) parts.push(`(${p.pageRequests.length} links)`);
9599
+ return parts.join(" ");
9600
+ },
9601
+ displayFields: [
9602
+ { key: "portalId", label: "Portal ID" },
9603
+ { key: "pageRequests", label: "Page Requests" }
9604
+ ]
9605
+ },
9606
+ // ─── Linear ───
9607
+ "linear.issue.create": {
9608
+ summaryTemplate: (p) => {
9609
+ const parts = [];
9610
+ if (p.teamId) parts.push(`${p.teamId}:`);
9611
+ if (p.title) parts.push(String(p.title));
9612
+ return parts.join(" ");
9613
+ },
9614
+ displayFields: [
9615
+ { key: "teamId", label: "Team" },
9616
+ { key: "title", label: "Title" },
9617
+ { key: "description", label: "Description" },
9618
+ { key: "stateId", label: "State" },
9619
+ { key: "assigneeId", label: "Assignee" },
9620
+ { key: "priority", label: "Priority" },
9621
+ { key: "labelIds", label: "Labels" }
9622
+ ]
9623
+ },
9624
+ "linear.issue.update": {
9625
+ summaryTemplate: (p) => {
9626
+ const parts = [];
9627
+ if (p.issueId) parts.push(`${p.issueId}:`);
9628
+ if (p.title) parts.push(String(p.title));
9629
+ return parts.join(" ");
9630
+ },
9631
+ displayFields: [
9632
+ { key: "issueId", label: "Issue" },
9633
+ { key: "title", label: "Title" },
9634
+ { key: "description", label: "Description" },
9635
+ { key: "stateId", label: "State" },
9636
+ { key: "assigneeId", label: "Assignee" },
9637
+ { key: "priority", label: "Priority" },
9638
+ { key: "labelIds", label: "Labels" }
9639
+ ]
9640
+ },
9641
+ "linear.comment.create": {
9642
+ summaryTemplate: (p) => {
9643
+ const parts = [];
9644
+ if (p.issueId) parts.push(`${p.issueId}:`);
9645
+ if (p.body) parts.push(String(p.body));
9646
+ return parts.join(" ");
9647
+ },
9648
+ displayFields: [
9649
+ { key: "issueId", label: "Issue" },
9650
+ { key: "body", label: "Comment" }
9651
+ ]
9652
+ },
9653
+ "linear.issue.archive": {
9654
+ summaryTemplate: (p) => {
9655
+ const parts = [];
9656
+ if (p.issueId) parts.push(`${p.issueId}:`);
9657
+ parts.push("Archive");
9658
+ return parts.join(" ");
9659
+ },
9660
+ displayFields: [
9661
+ { key: "issueId", label: "Issue" }
9662
+ ]
9663
+ },
9664
+ "linear.label.list": {
9665
+ summaryTemplate: (p) => {
9666
+ const parts = [];
9667
+ if (p.teamId) parts.push(`${p.teamId}:`);
9668
+ parts.push("List labels");
9669
+ return parts.join(" ");
9670
+ },
9671
+ displayFields: [
9672
+ { key: "teamId", label: "Team" },
9673
+ { key: "limit", label: "Limit" }
9674
+ ]
9675
+ },
9676
+ "linear.user.list": {
9677
+ summaryTemplate: (p) => {
9678
+ const parts = [];
9679
+ if (p.teamId) parts.push(`${p.teamId}:`);
9680
+ parts.push("List users");
9681
+ return parts.join(" ");
9682
+ },
9683
+ displayFields: [
9684
+ { key: "teamId", label: "Team" },
9685
+ { key: "limit", label: "Limit" }
9686
+ ]
9687
+ },
9688
+ "linear.project.create": {
9689
+ summaryTemplate: (p) => {
9690
+ const parts = [];
9691
+ if (p.teamIds) parts.push(`${Array.isArray(p.teamIds) ? p.teamIds.join(",") : String(p.teamIds)}:`);
9692
+ if (p.name) parts.push(String(p.name));
9693
+ return parts.join(" ");
9694
+ },
9695
+ displayFields: [
9696
+ { key: "teamIds", label: "Teams" },
9697
+ { key: "name", label: "Name" },
9698
+ { key: "description", label: "Description" },
9699
+ { key: "statusId", label: "Status ID" },
9700
+ { key: "startDate", label: "Start Date" },
9701
+ { key: "targetDate", label: "Target Date" }
9702
+ ]
9703
+ },
9704
+ "linear.project.update": {
9705
+ summaryTemplate: (p) => {
9706
+ const parts = [];
9707
+ if (p.projectId) parts.push(`${p.projectId}:`);
9708
+ if (p.name) parts.push(String(p.name));
9709
+ return parts.join(" ");
9710
+ },
9711
+ displayFields: [
9712
+ { key: "projectId", label: "Project" },
9713
+ { key: "name", label: "Name" },
9714
+ { key: "description", label: "Description" },
9715
+ { key: "statusId", label: "Status ID" },
9716
+ { key: "startDate", label: "Start Date" },
9717
+ { key: "targetDate", label: "Target Date" }
9718
+ ]
9719
+ },
9720
+ // ─── Linear Comment ops ───
9721
+ "linear.comment.update": {
9722
+ summaryTemplate: (p) => {
9723
+ const parts = [];
9724
+ if (p.commentId) parts.push(`${p.commentId}:`);
9725
+ parts.push("Update comment");
9726
+ return parts.join(" ");
9727
+ },
9728
+ displayFields: [
9729
+ { key: "commentId", label: "Comment" },
9730
+ { key: "body", label: "Body" }
9731
+ ]
9732
+ },
9733
+ "linear.comment.delete": {
9734
+ summaryTemplate: (p) => {
9735
+ const parts = [];
9736
+ if (p.commentId) parts.push(`Delete comment ${p.commentId}`);
9737
+ return parts.join(" ");
9738
+ },
9739
+ displayFields: [
9740
+ { key: "commentId", label: "Comment" }
9741
+ ]
9742
+ },
9743
+ "linear.comment.resolve": {
9744
+ summaryTemplate: (p) => {
9745
+ const parts = [];
9746
+ if (p.commentId) parts.push(`${p.commentId}:`);
9747
+ parts.push("Resolve comment");
8263
9748
  return parts.join(" ");
8264
9749
  },
8265
9750
  displayFields: [
8266
- { key: "channel", label: "Channel" },
8267
- { key: "text", label: "Message" }
9751
+ { key: "commentId", label: "Comment" }
8268
9752
  ]
8269
9753
  },
8270
- "gmail.message.send": {
9754
+ "linear.comment.unresolve": {
8271
9755
  summaryTemplate: (p) => {
8272
9756
  const parts = [];
8273
- if (p.to) parts.push(`To: ${p.to}`);
8274
- if (p.subject) parts.push(`Subject: ${p.subject}`);
8275
- return parts.join(", ");
9757
+ if (p.commentId) parts.push(`${p.commentId}:`);
9758
+ parts.push("Unresolve comment");
9759
+ return parts.join(" ");
8276
9760
  },
8277
9761
  displayFields: [
8278
- { key: "to", label: "To" },
8279
- { key: "subject", label: "Subject" },
8280
- { key: "body", label: "Body" }
9762
+ { key: "commentId", label: "Comment" }
8281
9763
  ]
8282
9764
  },
8283
- "gmail.draft.create": {
9765
+ // ─── Linear Issue ops ───
9766
+ "linear.issue.unarchive": {
8284
9767
  summaryTemplate: (p) => {
8285
9768
  const parts = [];
8286
- if (p.to) parts.push(`To: ${p.to}`);
8287
- if (p.subject) parts.push(`Subject: ${p.subject}`);
8288
- return parts.join(", ");
9769
+ if (p.issueId) parts.push(`${p.issueId}:`);
9770
+ parts.push("Unarchive");
9771
+ return parts.join(" ");
8289
9772
  },
8290
9773
  displayFields: [
8291
- { key: "to", label: "To" },
8292
- { key: "subject", label: "Subject" },
8293
- { key: "body", label: "Body" }
9774
+ { key: "issueId", label: "Issue" }
8294
9775
  ]
8295
9776
  },
8296
- "calendar.event.create": {
9777
+ "linear.issue.addLabel": {
8297
9778
  summaryTemplate: (p) => {
8298
9779
  const parts = [];
8299
- if (p.summary) parts.push(p.summary);
8300
- if (p.start) parts.push(`(${formatCalendarTime(p.start)})`);
9780
+ if (p.issueId) parts.push(`${p.issueId}:`);
9781
+ if (p.labelId) parts.push(`Add label ${p.labelId}`);
8301
9782
  return parts.join(" ");
8302
9783
  },
8303
9784
  displayFields: [
8304
- { key: "summary", label: "Event" },
8305
- { key: "start", label: "Start" },
8306
- { key: "end", label: "End" },
8307
- { key: "attendees", label: "Attendees" }
9785
+ { key: "issueId", label: "Issue" },
9786
+ { key: "labelId", label: "Label" }
8308
9787
  ]
8309
9788
  },
8310
- "calendar.event.update": {
9789
+ "linear.issue.removeLabel": {
8311
9790
  summaryTemplate: (p) => {
8312
9791
  const parts = [];
8313
- if (p.summary) parts.push(p.summary);
8314
- if (p.start) parts.push(`(${formatCalendarTime(p.start)})`);
9792
+ if (p.issueId) parts.push(`${p.issueId}:`);
9793
+ if (p.labelId) parts.push(`Remove label ${p.labelId}`);
8315
9794
  return parts.join(" ");
8316
9795
  },
8317
9796
  displayFields: [
8318
- { key: "summary", label: "Event" },
8319
- { key: "start", label: "Start" },
8320
- { key: "end", label: "End" },
8321
- { key: "attendees", label: "Attendees" }
9797
+ { key: "issueId", label: "Issue" },
9798
+ { key: "labelId", label: "Label" }
8322
9799
  ]
8323
9800
  },
8324
- "jira.issue.create": {
9801
+ "linear.issue.subscribe": {
8325
9802
  summaryTemplate: (p) => {
8326
9803
  const parts = [];
8327
- const projectKey = jiraField(
8328
- p,
8329
- ["projectKey", "project"],
8330
- "project",
8331
- (v) => typeof v === "string" ? v : v?.key
8332
- );
8333
- const summary = jiraField(p, ["summary"], "summary");
8334
- if (projectKey) parts.push(`${projectKey}:`);
8335
- if (summary) parts.push(summary);
9804
+ if (p.issueId) parts.push(`${p.issueId}:`);
9805
+ parts.push("Subscribe");
8336
9806
  return parts.join(" ");
8337
9807
  },
8338
- // AIDENTITY-54: nested `fields.X` 形式と top-level canonical の両方を
8339
- // 拾えるよう extract を使う。assignee は null (unassign) も表示。
8340
- // legacy alias (project / issuetype 等) も top-level 候補に含めて後方互換を保つ。
8341
9808
  displayFields: [
8342
- {
8343
- label: "Project",
8344
- extract: (p) => jiraField(
8345
- p,
8346
- ["projectKey", "project"],
8347
- "project",
8348
- (v) => typeof v === "string" ? v : v?.key
8349
- )
8350
- },
8351
- {
8352
- label: "Type",
8353
- extract: (p) => jiraField(
8354
- p,
8355
- ["issueTypeName", "issueType", "issuetype"],
8356
- "issuetype",
8357
- (v) => typeof v === "string" ? v : v?.name
8358
- )
8359
- },
8360
- { label: "Summary", extract: (p) => jiraField(p, ["summary"], "summary") },
8361
- { label: "Description", extract: (p) => jiraField(p, ["description"], "description") },
8362
- {
8363
- label: "Priority",
8364
- extract: (p) => jiraField(
8365
- p,
8366
- ["priority"],
8367
- "priority",
8368
- (v) => typeof v === "string" ? v : v?.name
8369
- )
8370
- },
8371
- { label: "Assignee", extract: extractJiraAssigneeDisplay },
8372
- { label: "Labels", extract: (p) => jiraField(p, ["labels"], "labels") }
9809
+ { key: "issueId", label: "Issue" }
8373
9810
  ]
8374
9811
  },
8375
- "jira.issue.update": {
9812
+ "linear.issue.delete": {
8376
9813
  summaryTemplate: (p) => {
8377
9814
  const parts = [];
8378
- const key = p.issueIdOrKey ?? p.issueKey;
8379
- const summary = jiraField(p, ["summary"], "summary");
8380
- if (key) parts.push(`${key}:`);
8381
- if (summary) parts.push(summary);
9815
+ if (p.issueId) parts.push(`Delete issue ${p.issueId}`);
8382
9816
  return parts.join(" ");
8383
9817
  },
8384
9818
  displayFields: [
8385
- // canonical issueIdOrKey、agent issueKey で送ってくる旧仕様も拾う。
8386
- { label: "Issue", extract: (p) => p.issueIdOrKey ?? p.issueKey },
8387
- { label: "Summary", extract: (p) => jiraField(p, ["summary"], "summary") },
8388
- { label: "Description", extract: (p) => jiraField(p, ["description"], "description") },
8389
- {
8390
- label: "Priority",
8391
- extract: (p) => jiraField(
8392
- p,
8393
- ["priority"],
8394
- "priority",
8395
- (v) => typeof v === "string" ? v : v?.name
8396
- )
8397
- },
8398
- // AIDENTITY-54: top-level alias (string / object) と null (unassign) も解決。
8399
- { label: "Assignee", extract: extractJiraAssigneeDisplay },
8400
- { label: "Labels", extract: (p) => jiraField(p, ["labels"], "labels") }
9819
+ { key: "issueId", label: "Issue" }
8401
9820
  ]
8402
9821
  },
8403
- // AIDENTITY-65: comment 本体は string と ADF document の両方を受けるため、
8404
- // detail 表示の ADF 描画は formatValueFull 側に任せて key 指定で OK。
8405
- "jira.comment.create": {
9822
+ // ─── Linear Issue Relations ───
9823
+ "linear.issueRelation.create": {
8406
9824
  summaryTemplate: (p) => {
8407
9825
  const parts = [];
8408
- if (p.issueIdOrKey) parts.push(`${p.issueIdOrKey}:`);
8409
- if (p.body) parts.push(typeof p.body === "string" ? p.body : "[ADF]");
9826
+ if (p.issueId) parts.push(`${p.issueId}`);
9827
+ if (p.type) parts.push(String(p.type));
9828
+ if (p.relatedIssueId) parts.push(String(p.relatedIssueId));
8410
9829
  return parts.join(" ");
8411
9830
  },
8412
9831
  displayFields: [
8413
- { key: "issueIdOrKey", label: "Issue" },
8414
- { key: "body", label: "Comment" }
9832
+ { key: "issueId", label: "Issue" },
9833
+ { key: "type", label: "Relation Type" },
9834
+ { key: "relatedIssueId", label: "Related Issue" }
8415
9835
  ]
8416
9836
  },
8417
- // AIDENTITY-66: 破壊的操作なので「どの issue のどの comment を消すか」を明示する
8418
- // (jira.issue.delete / jira.issuelink.delete と同じ destructive 表現の方針)。
8419
- "jira.comment.delete": {
9837
+ "linear.issueRelation.list": {
8420
9838
  summaryTemplate: (p) => {
8421
- const issue = p.issueIdOrKey ?? p.issueKey;
8422
- const commentId = p.commentId ?? p.id;
8423
- if (issue && commentId) return `Delete comment ${commentId} on ${issue}`;
8424
- if (commentId) return `Delete comment ${commentId}`;
8425
- return "";
9839
+ const parts = [];
9840
+ if (p.issueId) parts.push(`${p.issueId}:`);
9841
+ parts.push("List relations");
9842
+ return parts.join(" ");
8426
9843
  },
8427
9844
  displayFields: [
8428
- { label: "Issue", extract: (p) => p.issueIdOrKey ?? p.issueKey },
8429
- { label: "Comment", extract: (p) => p.commentId ?? p.id }
9845
+ { key: "issueId", label: "Issue" }
8430
9846
  ]
8431
9847
  },
8432
- // AIDENTITY-65: API enricher が 'Link' label を upsert で書き換える。
8433
- "jira.issuelink.delete": {
9848
+ "linear.issueRelation.delete": {
8434
9849
  summaryTemplate: (p) => {
8435
9850
  const parts = [];
8436
- if (p.linkId) parts.push(`Delete link ${p.linkId}`);
9851
+ if (p.relationId) parts.push(`Delete relation ${p.relationId}`);
8437
9852
  return parts.join(" ");
8438
9853
  },
8439
9854
  displayFields: [
8440
- { key: "linkId", label: "Link" }
9855
+ { key: "relationId", label: "Relation" }
9856
+ ]
9857
+ },
9858
+ // ─── Linear Cycles ───
9859
+ "linear.cycle.list": {
9860
+ summaryTemplate: (p) => {
9861
+ const parts = [];
9862
+ if (p.teamId) parts.push(`${p.teamId}:`);
9863
+ parts.push("List cycles");
9864
+ return parts.join(" ");
9865
+ },
9866
+ displayFields: [
9867
+ { key: "teamId", label: "Team" },
9868
+ { key: "limit", label: "Limit" }
9869
+ ]
9870
+ },
9871
+ "linear.cycle.read": {
9872
+ summaryTemplate: (p) => {
9873
+ const parts = [];
9874
+ if (p.cycleId) parts.push(`${p.cycleId}:`);
9875
+ parts.push("Read cycle");
9876
+ return parts.join(" ");
9877
+ },
9878
+ displayFields: [
9879
+ { key: "cycleId", label: "Cycle" }
9880
+ ]
9881
+ },
9882
+ "linear.cycle.create": {
9883
+ summaryTemplate: (p) => {
9884
+ const parts = [];
9885
+ if (p.teamId) parts.push(`${p.teamId}:`);
9886
+ if (p.name) parts.push(String(p.name));
9887
+ else if (p.startsAt) parts.push(`Cycle ${p.startsAt}`);
9888
+ return parts.join(" ");
9889
+ },
9890
+ displayFields: [
9891
+ { key: "teamId", label: "Team" },
9892
+ { key: "name", label: "Name" },
9893
+ { key: "startsAt", label: "Starts At" },
9894
+ { key: "endsAt", label: "Ends At" }
9895
+ ]
9896
+ },
9897
+ "linear.cycle.update": {
9898
+ summaryTemplate: (p) => {
9899
+ const parts = [];
9900
+ if (p.cycleId) parts.push(`${p.cycleId}:`);
9901
+ if (p.name) parts.push(String(p.name));
9902
+ else parts.push("Update cycle");
9903
+ return parts.join(" ");
9904
+ },
9905
+ displayFields: [
9906
+ { key: "cycleId", label: "Cycle" },
9907
+ { key: "name", label: "Name" },
9908
+ { key: "startsAt", label: "Starts At" },
9909
+ { key: "endsAt", label: "Ends At" }
9910
+ ]
9911
+ },
9912
+ // ─── Linear Labels ───
9913
+ "linear.label.create": {
9914
+ summaryTemplate: (p) => {
9915
+ const parts = [];
9916
+ if (p.name) parts.push(String(p.name));
9917
+ return parts.join(" ");
9918
+ },
9919
+ displayFields: [
9920
+ { key: "name", label: "Name" },
9921
+ { key: "color", label: "Color" },
9922
+ { key: "teamId", label: "Team" }
9923
+ ]
9924
+ },
9925
+ "linear.label.update": {
9926
+ summaryTemplate: (p) => {
9927
+ const parts = [];
9928
+ if (p.labelId) parts.push(`${p.labelId}:`);
9929
+ if (p.name) parts.push(String(p.name));
9930
+ else parts.push("Update label");
9931
+ return parts.join(" ");
9932
+ },
9933
+ displayFields: [
9934
+ { key: "labelId", label: "Label" },
9935
+ { key: "name", label: "Name" },
9936
+ { key: "color", label: "Color" }
9937
+ ]
9938
+ },
9939
+ // ─── Linear Attachments ───
9940
+ "linear.attachment.create": {
9941
+ summaryTemplate: (p) => {
9942
+ const parts = [];
9943
+ if (p.issueId) parts.push(`${p.issueId}:`);
9944
+ if (p.title) parts.push(String(p.title));
9945
+ return parts.join(" ");
9946
+ },
9947
+ displayFields: [
9948
+ { key: "issueId", label: "Issue" },
9949
+ { key: "title", label: "Title" },
9950
+ { key: "url", label: "URL" }
9951
+ ]
9952
+ },
9953
+ "linear.attachment.list": {
9954
+ summaryTemplate: (p) => {
9955
+ const parts = [];
9956
+ if (p.issueId) parts.push(`${p.issueId}:`);
9957
+ parts.push("List attachments");
9958
+ return parts.join(" ");
9959
+ },
9960
+ displayFields: [
9961
+ { key: "issueId", label: "Issue" }
9962
+ ]
9963
+ },
9964
+ "linear.attachment.delete": {
9965
+ summaryTemplate: (p) => {
9966
+ const parts = [];
9967
+ if (p.attachmentId) parts.push(`Delete attachment ${p.attachmentId}`);
9968
+ return parts.join(" ");
9969
+ },
9970
+ displayFields: [
9971
+ { key: "attachmentId", label: "Attachment" }
9972
+ ]
9973
+ },
9974
+ // ─── Linear read actions (登録漏れ補完) ───
9975
+ "linear.issue.list": {
9976
+ summaryTemplate: (p) => {
9977
+ const parts = [];
9978
+ if (p.teamId) parts.push(`${p.teamId}:`);
9979
+ parts.push("List issues");
9980
+ return parts.join(" ");
9981
+ },
9982
+ displayFields: [
9983
+ { key: "teamId", label: "Team" },
9984
+ { key: "assigneeId", label: "Assignee" },
9985
+ { key: "stateId", label: "State" },
9986
+ { key: "limit", label: "Limit" }
9987
+ ]
9988
+ },
9989
+ "linear.issue.read": {
9990
+ summaryTemplate: (p) => {
9991
+ const parts = [];
9992
+ if (p.issueId) parts.push(`${p.issueId}:`);
9993
+ parts.push("Read issue");
9994
+ return parts.join(" ");
9995
+ },
9996
+ displayFields: [
9997
+ { key: "issueId", label: "Issue" }
9998
+ ]
9999
+ },
10000
+ "linear.issue.search": {
10001
+ summaryTemplate: (p) => {
10002
+ const parts = [];
10003
+ if (p.teamId) parts.push(`${p.teamId}:`);
10004
+ if (p.query) parts.push(String(p.query));
10005
+ return parts.join(" ");
10006
+ },
10007
+ displayFields: [
10008
+ { key: "query", label: "Query" },
10009
+ { key: "teamId", label: "Team" },
10010
+ { key: "limit", label: "Limit" }
10011
+ ]
10012
+ },
10013
+ "linear.comment.list": {
10014
+ summaryTemplate: (p) => {
10015
+ const parts = [];
10016
+ if (p.issueId) parts.push(`${p.issueId}:`);
10017
+ parts.push("List comments");
10018
+ return parts.join(" ");
10019
+ },
10020
+ displayFields: [
10021
+ { key: "issueId", label: "Issue" }
10022
+ ]
10023
+ },
10024
+ "linear.project.list": {
10025
+ summaryTemplate: (p) => {
10026
+ const parts = [];
10027
+ if (p.teamId) parts.push(`${p.teamId}:`);
10028
+ parts.push("List projects");
10029
+ return parts.join(" ");
10030
+ },
10031
+ displayFields: [
10032
+ { key: "teamId", label: "Team" },
10033
+ { key: "limit", label: "Limit" }
10034
+ ]
10035
+ },
10036
+ "linear.project.read": {
10037
+ summaryTemplate: (p) => {
10038
+ const parts = [];
10039
+ if (p.projectId) parts.push(`${p.projectId}:`);
10040
+ parts.push("Read project");
10041
+ return parts.join(" ");
10042
+ },
10043
+ displayFields: [
10044
+ { key: "projectId", label: "Project" }
10045
+ ]
10046
+ },
10047
+ "linear.team.list": {
10048
+ summaryTemplate: (_p) => "List teams",
10049
+ displayFields: [
10050
+ { key: "limit", label: "Limit" }
10051
+ ]
10052
+ },
10053
+ "linear.workflowState.list": {
10054
+ summaryTemplate: (p) => {
10055
+ const parts = [];
10056
+ if (p.teamId) parts.push(`${p.teamId}:`);
10057
+ parts.push("List workflow states");
10058
+ return parts.join(" ");
10059
+ },
10060
+ displayFields: [
10061
+ { key: "teamId", label: "Team" },
10062
+ { key: "limit", label: "Limit" }
8441
10063
  ]
8442
10064
  },
10065
+ "linear.viewer.read": {
10066
+ summaryTemplate: (_p) => "Read viewer (current user)",
10067
+ displayFields: []
10068
+ },
8443
10069
  // AIDENTITY-83: email / displayName から accountId を解決する read action。
8444
10070
  "jira.user.search": {
8445
10071
  summaryTemplate: (p) => {
@@ -8462,7 +10088,8 @@ var ACTION_DISPLAY_CONFIGS = {
8462
10088
  displayFields: [
8463
10089
  { key: "to", label: "To" },
8464
10090
  { key: "taskType", label: "Task" },
8465
- { key: "message", label: "Message" }
10091
+ { key: "message", label: "Message" },
10092
+ { key: "payload", label: "Payload" }
8466
10093
  ]
8467
10094
  },
8468
10095
  "inbox.respond": {
@@ -8490,15 +10117,40 @@ var ACTION_DISPLAY_CONFIGS = {
8490
10117
  ]
8491
10118
  },
8492
10119
  "inbox.checkByToken": {
8493
- summaryTemplate: (p) => p.token ? `Token: ${p.token}` : "",
8494
- displayFields: [
8495
- { key: "token", label: "Token" }
8496
- ]
10120
+ // token is an opaque access credential never render it in summaries or display fields.
10121
+ summaryTemplate: (_p) => "Check inbox by token",
10122
+ displayFields: []
8497
10123
  },
8498
10124
  "inbox.complete": {
8499
10125
  summaryTemplate: (p) => p.id ? `Complete #${p.id}` : "",
8500
10126
  displayFields: [
8501
- { key: "id", label: "Item" }
10127
+ { key: "id", label: "Item" },
10128
+ { key: "result", label: "Result" }
10129
+ ]
10130
+ },
10131
+ "scheduling.request": {
10132
+ summaryTemplate: (p) => p.topic ? `Request scheduling: ${p.topic}` : "Request scheduling",
10133
+ displayFields: [
10134
+ { key: "to", label: "To" },
10135
+ { key: "topic", label: "Topic" },
10136
+ { key: "durationMinutes", label: "Duration (min)" },
10137
+ { key: "candidates", label: "Candidates" },
10138
+ { key: "message", label: "Message" }
10139
+ ]
10140
+ },
10141
+ // PR-R2 (design §3.2), registry checklist B. This action is risk:'high', so
10142
+ // its summary/displayFields are what a human actually reads in the approval
10143
+ // and audit surfaces — the string is user-facing, not decoration. Names the
10144
+ // item being answered (the counterpart anchor available on this action's
10145
+ // params) the way scheduling.request names its topic.
10146
+ "scheduling.submit_response": {
10147
+ summaryTemplate: (p) => p.decision === "decline" ? `Decline the scheduling request for item ${p.itemId ?? "(unknown)"}` : `Submit a scheduling proposal for item ${p.itemId ?? "(unknown)"}`,
10148
+ displayFields: [
10149
+ { key: "itemId", label: "Inbox item" },
10150
+ { key: "decision", label: "Decision" },
10151
+ { key: "slots", label: "Proposed slots" },
10152
+ { key: "recommendedSlotIndex", label: "Recommended index" },
10153
+ { key: "recommendationReason", label: "Reason" }
8502
10154
  ]
8503
10155
  }
8504
10156
  };
@@ -8806,7 +10458,7 @@ function isWriteAction(action) {
8806
10458
  if (!meta) {
8807
10459
  return true;
8808
10460
  }
8809
- const writeEffectPrefixes = ["Create:", "Update:", "Delete:", "Write:", "Execute:", "Merge:", "Run:"];
10461
+ const writeEffectPrefixes = ["Create:", "Update:", "Delete:", "Write:", "Execute:", "Merge:", "Run:", "Archive:"];
8810
10462
  if (meta.effects?.some((e) => writeEffectPrefixes.some((p) => e.startsWith(p)))) {
8811
10463
  return true;
8812
10464
  }