Compare commits

..

42 Commits

Author SHA1 Message Date
Waleed
ab4e9dc72f v0.5.73: ci, helm updates, kb, ui fixes, note block enhancements 2026-01-26 22:04:35 -08:00
Waleed
46ba315701 feat(tools): added more intercom tools (#3022) 2026-01-26 21:41:44 -08:00
Waleed
077e702dd8 improvement(tools): updated kalshi and polymarket tools and blocks (#3021) 2026-01-26 21:01:33 -08:00
Vikhyath Mondreti
1c58c35bd8 v0.5.72: azure connection string, supabase improvement, multitrigger resolution, docs quick reference 2026-01-25 23:42:27 -08:00
Waleed
d63a5cb504 v0.5.71: ux, ci improvements, docs updates 2026-01-25 03:08:08 -08:00
Waleed
8bd5d41723 v0.5.70: router fix, anthropic agent response format adherence 2026-01-24 20:57:02 -08:00
Waleed
c12931bc50 v0.5.69: kb upgrades, blog, copilot improvements, auth consolidation (#2973)
* fix(subflows): tag dropdown + resolution logic (#2949)

* fix(subflows): tag dropdown + resolution logic

* fixes;

* revert parallel change

* chore(deps): bump posthog-js to 1.334.1 (#2948)

* fix(idempotency): add conflict target to atomicallyClaimDb query + remove redundant db namespace tracking (#2950)

* fix(idempotency): add conflict target to atomicallyClaimDb query

* delete needs to account for namespace

* simplify namespace filtering logic

* fix cleanup

* consistent target

* improvement(kb): add document filtering, select all, and React Query migration (#2951)

* improvement(kb): add document filtering, select all, and React Query migration

* test(kb): update tests for enabledFilter and removed userId params

* fix(kb): remove non-null assertion, add explicit guard

* improvement(logs): trace span, details (#2952)

* improvement(action-bar): ordering

* improvement(logs): details, trace span

* feat(blog): v0.5 release post (#2953)

* feat(blog): v0.5 post

* improvement(blog): simplify title and remove code block header

- Simplified blog title from Introducing Sim Studio v0.5 to Introducing Sim v0.5
- Removed language label header and copy button from code blocks for cleaner appearance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* ack PR comments

* small styling improvements

* created system to create post-specific components

* updated componnet

* cache invalidation

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(admin): add credits endpoint to issue credits to users (#2954)

* feat(admin): add credits endpoint to issue credits to users

* fix(admin): use existing credit functions and handle enterprise seats

* fix(admin): reject NaN and Infinity in amount validation

* styling

* fix(admin): validate userId and email are strings

* improvement(copilot): fast mode, subagent tool responses and allow preferences (#2955)

* Improvements

* Fix actions mapping

* Remove console logs

* fix(billing): handle missing userStats and prevent crashes (#2956)

* fix(billing): handle missing userStats and prevent crashes

* fix(billing): correct import path for getFilledPillColor

* fix(billing): add Number.isFinite check to lastPeriodCost

* fix(logs): refresh logic to refresh logs details (#2958)

* fix(security): add authentication and input validation to API routes (#2959)

* fix(security): add authentication and input validation to API routes

* moved utils

* remove extraneous commetns

* removed unused dep

* improvement(helm): add internal ingress support and same-host path consolidation (#2960)

* improvement(helm): add internal ingress support and same-host path consolidation

* improvement(helm): clean up ingress template comments

Simplify verbose inline Helm comments and section dividers to match the
minimal style used in services.yaml.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(helm): add missing copilot path consolidation for realtime host

When copilot.host equals realtime.host but differs from app.host,
copilot paths were not being routed. Added logic to consolidate
copilot paths into the realtime rule for this scenario.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* improvement(helm): follow ingress best practices

- Remove orphan comments that appeared when services were disabled
- Add documentation about path ordering requirements
- Paths rendered in order: realtime, copilot, app (specific before catch-all)
- Clean template output matching industry Helm chart standards

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat(blog): enterprise post (#2961)

* feat(blog): enterprise post

* added more images, styling

* more content

* updated v0-5 post

* remove unused transition

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>

* fix(envvars): resolution standardized (#2957)

* fix(envvars): resolution standardized

* remove comments

* address bugbot

* fix highlighting for env vars

* remove comments

* address greptile

* address bugbot

* fix(copilot): mask credentials fix (#2963)

* Fix copilot masking

* Clean up

* Lint

* improvement(webhooks): remove dead code (#2965)

* fix(webhooks): subscription recreation path

* improvement(webhooks): remove dead code

* fix tests

* address bugbot comments

* fix restoration edge case

* fix more edge cases

* address bugbot comments

* fix gmail polling

* add warnings for UI indication for credential sets

* fix(preview): subblock values (#2969)

* fix(child-workflow): nested spans handoff (#2966)

* fix(child-workflow): nested spans handoff

* remove overly defensive programming

* update type check

* type more code

* remove more dead code

* address bugbot comments

* fix(security): restrict API key access on internal-only routes (#2964)

* fix(security): restrict API key access on internal-only routes

* test(security): update function execute tests for checkInternalAuth

* updated agent handler

* move session check higher in checkSessionOrInternalAuth

* extracted duplicate code into helper for resolving user from jwt

* fix(copilot): update copilot chat title (#2968)

* fix(hitl): fix condition blocks after hitl (#2967)

* fix(notes): ghost edges (#2970)

* fix(notes): ghost edges

* fix deployed state fallback

* fallback

* remove UI level checks

* annotation missing from autoconnect source check

* improvement(docs): loop and parallel var reference syntax (#2975)

* fix(blog): slash actions description (#2976)

* improvement(docs): loop and parallel var reference syntax

* fix(blog): slash actions description

* fix(auth): copilot routes (#2977)

* Fix copilot auth

* Fix

* Fix

* Fix

* fix(copilot): fix edit summary for loops/parallels (#2978)

* fix(integrations): hide from tool bar (#2544)

* fix(landing): ui (#2979)

* fix(edge-validation): race condition on collaborative add (#2980)

* fix(variables): boolean type support and input improvements (#2981)

* fix(variables): boolean type support and input improvements

* fix formatting

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
2026-01-24 14:29:53 -08:00
Waleed
e9c4251c1c v0.5.68: router block reasoning, executor improvements, variable resolution consolidation, helm updates (#2946)
* improvement(workflow-item): stabilize avatar layout and fix name truncation (#2939)

* improvement(workflow-item): stabilize avatar layout and fix name truncation

* fix(avatars): revert overflow bg to hardcoded color for contrast

* fix(executor): stop parallel execution when block errors (#2940)

* improvement(helm): add per-deployment extraVolumes support (#2942)

* fix(gmail): expose messageId field in read email block (#2943)

* fix(resolver): consolidate reference resolution  (#2941)

* fix(resolver): consolidate code to resolve references

* fix edge cases

* use already formatted error

* fix multi index

* fix backwards compat reachability

* handle backwards compatibility accurately

* use shared constant correctly

* feat(router): expose reasoning output in router v2 block (#2945)

* fix(copilot): always allow, credential masking (#2947)

* Fix always allow, credential validation

* Credential masking

* Autoload

* fix(executor): handle condition dead-end branches in loops (#2944)

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Siddharth Ganesan <33737564+Sg312@users.noreply.github.com>
2026-01-22 13:48:15 -08:00
Waleed
cc2be33d6b v0.5.67: loading, password reset, ui improvements, helm updates (#2928)
* fix(zustand): updated to useShallow from deprecated createWithEqualityFn (#2919)

* fix(logger): use direct env access for webpack inlining (#2920)

* fix(notifications): text overflow with line-clamp (#2921)

* chore(helm): add env vars for Vertex AI, orgs, and telemetry (#2922)

* fix(auth): improve reset password flow and consolidate brand detection (#2924)

* fix(auth): improve reset password flow and consolidate brand detection

* fix(auth): set errorHandled for EMAIL_NOT_VERIFIED to prevent duplicate error

* fix(auth): clear success message on login errors

* chore(auth): fix import order per lint

* fix(action-bar): duplicate subflows with children (#2923)

* fix(action-bar): duplicate subflows with children

* fix(action-bar): add validateTriggerPaste for subflow duplicate

* fix(resolver): agent response format, input formats, root level (#2925)

* fix(resolvers): agent response format, input formats, root level

* fix response block initial seeding

* fix tests

* fix(messages-input): fix cursor alignment and auto-resize with overlay (#2926)

* fix(messages-input): fix cursor alignment and auto-resize with overlay

* fixed remaining zustand warnings

* fix(stores): remove dead code causing log spam on startup (#2927)

* fix(stores): remove dead code causing log spam on startup

* fix(stores): replace custom tools zustand store with react query cache

* improvement(ui): use BrandedButton and BrandedLink components (#2930)

- Refactor auth forms to use BrandedButton component
- Add BrandedLink component for changelog page
- Reduce code duplication in login, signup, reset-password forms
- Update star count default value

* fix(custom-tools): remove unsafe title fallback in getCustomTool (#2929)

* fix(custom-tools): remove unsafe title fallback in getCustomTool

* fix(custom-tools): restore title fallback in getCustomTool lookup

Custom tools are referenced by title (custom_${title}), not database ID.
The title fallback is required for client-side tool resolution to work.

* fix(null-bodies): empty bodies handling (#2931)

* fix(null-statuses): empty bodies handling

* address bugbot comment

* fix(token-refresh): microsoft, notion, x, linear (#2933)

* fix(microsoft): proactive refresh needed

* fix(x): missing token refresh flag

* notion and linear missing flag too

* address bugbot comment

* fix(auth): handle EMAIL_NOT_VERIFIED in onError callback (#2932)

* fix(auth): handle EMAIL_NOT_VERIFIED in onError callback

* refactor(auth): extract redirectToVerify helper to reduce duplication

* fix(workflow-selector): use dedicated selector for workflow dropdown (#2934)

* feat(workflow-block): preview (#2935)

* improvement(copilot): tool configs to show nested props (#2936)

* fix(auth): add genericOAuth providers to trustedProviders (#2937)

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com>
2026-01-21 22:53:25 -08:00
Vikhyath Mondreti
45371e521e v0.5.66: external http requests fix, ring highlighting 2026-01-21 02:55:39 -08:00
Waleed
0ce0f98aa5 v0.5.65: gemini updates, textract integration, ui updates (#2909)
* fix(google): wrap primitive tool responses for Gemini API compatibility (#2900)

* fix(canonical): copilot path + update parent (#2901)

* fix(rss): add top-level title, link, pubDate fields to RSS trigger output (#2902)

* fix(rss): add top-level title, link, pubDate fields to RSS trigger output

* fix(imap): add top-level fields to IMAP trigger output

* improvement(browseruse): add profile id param (#2903)

* improvement(browseruse): add profile id param

* make request a stub since we have directExec

* improvement(executor): upgraded abort controller to handle aborts for loops and parallels (#2880)

* improvement(executor): upgraded abort controller to handle aborts for loops and parallels

* comments

* improvement(files): update execution for passing base64 strings (#2906)

* progress

* improvement(execution): update execution for passing base64 strings

* fix types

* cleanup comments

* path security vuln

* reject promise correctly

* fix redirect case

* remove proxy routes

* fix tests

* use ipaddr

* feat(tools): added textract, added v2 for mistral, updated tag dropdown (#2904)

* feat(tools): added textract

* cleanup

* ack pr comments

* reorder

* removed upload for textract async version

* fix additional fields dropdown in editor, update parser to leave validation to be done on the server

* added mistral v2, files v2, and finalized textract

* updated the rest of the old file patterns, updated mistral outputs for v2

* updated tag dropdown to parse non-operation fields as well

* updated extension finder

* cleanup

* added description for inputs to workflow

* use helper for internal route check

* fix tag dropdown merge conflict change

* remove duplicate code

---------

Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>

* fix(ui): change add inputs button to match output selector (#2907)

* fix(canvas): removed invite to workspace from canvas popover (#2908)

* fix(canvas): removed invite to workspace

* removed unused props

* fix(copilot): legacy tool display names (#2911)

* fix(a2a): canonical merge  (#2912)

* fix canonical merge

* fix empty array case

* fix(change-detection): copilot diffs have extra field (#2913)

* improvement(logs): improved logs ui bugs, added subflow disable UI (#2910)

* improvement(logs): improved logs ui bugs, added subflow disable UI

* added duplicate to action bar for subflows

* feat(broadcast): email v0.5 (#2905)

---------

Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
Co-authored-by: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com>
2026-01-20 23:54:55 -08:00
Waleed
dff1c9d083 v0.5.64: unsubscribe, search improvements, metrics, additional SSO configuration 2026-01-20 00:34:11 -08:00
Vikhyath Mondreti
b09f683072 v0.5.63: ui and performance improvements, more google tools 2026-01-18 15:22:42 -08:00
Vikhyath Mondreti
a8bb0db660 v0.5.62: webhook bug fixes, seeding default subblock values, block selection fixes 2026-01-16 20:27:06 -08:00
Waleed
af82820a28 v0.5.61: webhook improvements, workflow controls, react query for deployment status, chat fixes, reducto and pulse OCR, linear fixes 2026-01-16 18:06:23 -08:00
Waleed
4372841797 v0.5.60: invitation flow improvements, chat fixes, a2a improvements, additional copilot actions 2026-01-15 00:02:18 -08:00
Waleed
5e8c843241 v0.5.59: a2a support, documentation 2026-01-13 13:21:21 -08:00
Waleed
7bf3d73ee6 v0.5.58: export folders, new tools, permissions groups enhancements 2026-01-13 00:56:59 -08:00
Vikhyath Mondreti
7ffc11a738 v0.5.57: subagents, context menu improvements, bug fixes 2026-01-11 11:38:40 -08:00
Waleed
be578e2ed7 v0.5.56: batch operations, access control and permission groups, billing fixes 2026-01-10 00:31:34 -08:00
Waleed
f415e5edc4 v0.5.55: polling groups, bedrock provider, devcontainer fixes, workflow preview enhancements 2026-01-08 23:36:56 -08:00
Waleed
13a6e6c3fa v0.5.54: seo, model blacklist, helm chart updates, fireflies integration, autoconnect improvements, billing fixes 2026-01-07 16:09:45 -08:00
Waleed
f5ab7f21ae v0.5.53: hotkey improvements, added redis fallback, fixes for workflow tool 2026-01-06 23:34:52 -08:00
Waleed
bfb6fffe38 v0.5.52: new port-based router block, combobox expression and variable support 2026-01-06 16:14:10 -08:00
Waleed
4fbec0a43f v0.5.51: triggers, kb, condition block improvements, supabase and grain integration updates 2026-01-06 14:26:46 -08:00
Waleed
585f5e365b v0.5.50: import improvements, ui upgrades, kb styling and performance improvements 2026-01-05 00:35:55 -08:00
Waleed
3792bdd252 v0.5.49: hitl improvements, new email styles, imap trigger, logs context menu (#2672)
* feat(logs-context-menu): consolidated logs utils and types, added logs record context menu (#2659)

* feat(email): welcome email; improvement(emails): ui/ux (#2658)

* feat(email): welcome email; improvement(emails): ui/ux

* improvement(emails): links, accounts, preview

* refactor(emails): file structure and wrapper components

* added envvar for personal emails sent, added isHosted gate

* fixed failing tests, added env mock

* fix: removed comment

---------

Co-authored-by: waleed <walif6@gmail.com>

* fix(logging): hitl + trigger dev crash protection (#2664)

* hitl gaps

* deal with trigger worker crashes

* cleanup import strcuture

* feat(imap): added support for imap trigger (#2663)

* feat(tools): added support for imap trigger

* feat(imap): added parity, tested

* ack PR comments

* final cleanup

* feat(i18n): update translations (#2665)

Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>

* fix(grain): updated grain trigger to auto-establish trigger (#2666)

Co-authored-by: aadamgough <adam@sim.ai>

* feat(admin): routes to manage deployments (#2667)

* feat(admin): routes to manage deployments

* fix naming fo deployed by

* feat(time-picker): added timepicker emcn component, added to playground, added searchable prop for dropdown, added more timezones for schedule, updated license and notice date (#2668)

* feat(time-picker): added timepicker emcn component, added to playground, added searchable prop for dropdown, added more timezones for schedule, updated license and notice date

* removed unused params, cleaned up redundant utils

* improvement(invite): aligned styling (#2669)

* improvement(invite): aligned with rest of app

* fix(invite): error handling

* fix: addressed comments

---------

Co-authored-by: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com>
Co-authored-by: Vikhyath Mondreti <vikhyathvikku@gmail.com>
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
Co-authored-by: Adam Gough <77861281+aadamgough@users.noreply.github.com>
Co-authored-by: aadamgough <adam@sim.ai>
2026-01-03 13:19:18 -08:00
Waleed
eb5d1f3e5b v0.5.48: copy-paste workflow blocks, docs updates, mcp tool fixes 2025-12-31 18:00:04 -08:00
Waleed
54ab82c8dd v0.5.47: deploy workflow as mcp, kb chunks tokenizer, UI improvements, jira service management tools 2025-12-30 23:18:58 -08:00
Waleed
f895bf469b v0.5.46: build improvements, greptile, light mode improvements 2025-12-29 02:17:52 -08:00
Waleed
dd3209af06 v0.5.45: light mode fixes, realtime usage indicator, docker build improvements 2025-12-27 19:57:42 -08:00
Waleed
b6ba3b50a7 v0.5.44: keyboard shortcuts, autolayout, light mode, byok, testing improvements 2025-12-26 21:25:19 -08:00
Waleed
b304233062 v0.5.43: export logs, circleback, grain, vertex, code hygiene, schedule improvements 2025-12-23 19:19:18 -08:00
Vikhyath Mondreti
57e4b49bd6 v0.5.42: fix memory migration 2025-12-23 01:24:54 -08:00
Vikhyath Mondreti
e12dd204ed v0.5.41: memory fixes, copilot improvements, knowledgebase improvements, LLM providers standardization 2025-12-23 00:15:18 -08:00
Vikhyath Mondreti
3d9d9cbc54 v0.5.40: supabase ops to allow non-public schemas, jira uuid 2025-12-21 22:28:05 -08:00
Waleed
0f4ec962ad v0.5.39: notion, workflow variables fixes 2025-12-20 20:44:00 -08:00
Waleed
4827866f9a v0.5.38: snap to grid, copilot ux improvements, billing line items 2025-12-20 17:24:38 -08:00
Waleed
3e697d9ed9 v0.5.37: redaction utils consolidation, logs updates, autoconnect improvements, additional kb tag types 2025-12-19 22:31:55 -08:00
Martin Yankov
4431a1a484 fix(helm): add custom egress rules to realtime network policy (#2481)
The realtime service network policy was missing the custom egress rules section
that allows configuration of additional egress rules via values.yaml. This caused
the realtime pods to be unable to connect to external databases (e.g., PostgreSQL
on port 5432) when using external database configurations.

The app network policy already had this section, but the realtime network policy
was missing it, creating an inconsistency and preventing the realtime service
from accessing external databases configured via networkPolicy.egress values.

This fix adds the same custom egress rules template section to the realtime
network policy, matching the app network policy behavior and allowing users to
configure database connectivity via values.yaml.
2025-12-19 18:59:08 -08:00
Waleed
4d1a9a3f22 v0.5.36: hitl improvements, opengraph, slack fixes, one-click unsubscribe, auth checks, new db indexes 2025-12-19 01:27:49 -08:00
Vikhyath Mondreti
eb07a080fb v0.5.35: helm updates, copilot improvements, 404 for docs, salesforce fixes, subflow resize clamping 2025-12-18 16:23:19 -08:00
89 changed files with 8886 additions and 2347 deletions

View File

@@ -101,7 +101,6 @@ import {
ShopifyIcon,
SlackIcon,
SmtpIcon,
SpotifyIcon,
SQSIcon,
SshIcon,
STTIcon,
@@ -182,7 +181,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
jina: JinaAIIcon,
jira: JiraIcon,
jira_service_management: JiraServiceManagementIcon,
kalshi: KalshiIcon,
kalshi_v2: KalshiIcon,
knowledge: PackageSearchIcon,
langsmith: LangsmithIcon,
lemlist: LemlistIcon,
@@ -229,7 +228,6 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
shopify: ShopifyIcon,
slack: SlackIcon,
smtp: SmtpIcon,
spotify: SpotifyIcon,
sqs: SQSIcon,
ssh: SshIcon,
stagehand: StagehandIcon,

View File

@@ -47,6 +47,7 @@ Runs a browser automation task using BrowserUse
| `save_browser_data` | boolean | No | Whether to save browser data |
| `model` | string | No | LLM model to use \(default: gpt-4o\) |
| `apiKey` | string | Yes | API key for BrowserUse API |
| `profile_id` | string | No | Browser profile ID for persistent sessions \(cookies, login state\) |
#### Output

View File

@@ -647,6 +647,42 @@ Retrieve a single ticket by ID from Intercom. Returns API-aligned fields only.
| `ticketId` | string | ID of the retrieved ticket |
| `success` | boolean | Operation success status |
### `intercom_update_ticket`
Update a ticket in Intercom (change state, assignment, attributes)
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `ticketId` | string | Yes | The ID of the ticket to update |
| `ticket_attributes` | string | No | JSON object with ticket attributes \(e.g., \{"_default_title_":"New Title","_default_description_":"Updated description"\}\) |
| `open` | boolean | No | Set to false to close the ticket, true to keep it open |
| `is_shared` | boolean | No | Whether the ticket is visible to users |
| `snoozed_until` | number | No | Unix timestamp for when the ticket should reopen |
| `admin_id` | string | No | The ID of the admin performing the update \(needed for workflows and attribution\) |
| `assignee_id` | string | No | The ID of the admin or team to assign the ticket to. Set to "0" to unassign. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `ticket` | object | The updated ticket object |
| ↳ `id` | string | Unique identifier for the ticket |
| ↳ `type` | string | Object type \(ticket\) |
| ↳ `ticket_id` | string | Ticket ID shown in Intercom UI |
| ↳ `ticket_state` | string | State of the ticket |
| ↳ `ticket_attributes` | object | Attributes of the ticket |
| ↳ `open` | boolean | Whether the ticket is open |
| ↳ `is_shared` | boolean | Whether the ticket is visible to users |
| ↳ `snoozed_until` | number | Unix timestamp when ticket will reopen |
| ↳ `admin_assignee_id` | string | ID of assigned admin |
| ↳ `team_assignee_id` | string | ID of assigned team |
| ↳ `created_at` | number | Unix timestamp when ticket was created |
| ↳ `updated_at` | number | Unix timestamp when ticket was last updated |
| `ticketId` | string | ID of the updated ticket |
| `ticket_state` | string | Current state of the ticket |
### `intercom_create_message`
Create and send a new admin-initiated message in Intercom. Returns API-aligned fields only.
@@ -680,4 +716,340 @@ Create and send a new admin-initiated message in Intercom. Returns API-aligned f
| `messageId` | string | ID of the created message |
| `success` | boolean | Operation success status |
### `intercom_list_admins`
Fetch a list of all admins for the workspace
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `admins` | array | Array of admin objects |
| ↳ `id` | string | Unique identifier for the admin |
| ↳ `type` | string | Object type \(admin\) |
| ↳ `name` | string | Name of the admin |
| ↳ `email` | string | Email of the admin |
| ↳ `job_title` | string | Job title of the admin |
| ↳ `away_mode_enabled` | boolean | Whether admin is in away mode |
| ↳ `away_mode_reassign` | boolean | Whether to reassign conversations when away |
| ↳ `has_inbox_seat` | boolean | Whether admin has a paid inbox seat |
| ↳ `team_ids` | array | List of team IDs the admin belongs to |
| ↳ `avatar` | object | Avatar information |
| ↳ `email_verified` | boolean | Whether email is verified |
| `type` | string | Object type \(admin.list\) |
### `intercom_close_conversation`
Close a conversation in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to close |
| `admin_id` | string | Yes | The ID of the admin performing the action |
| `body` | string | No | Optional closing message to add to the conversation |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `conversation` | object | The closed conversation object |
| ↳ `id` | string | Unique identifier for the conversation |
| ↳ `type` | string | Object type \(conversation\) |
| ↳ `state` | string | State of the conversation \(closed\) |
| ↳ `open` | boolean | Whether the conversation is open \(false\) |
| ↳ `read` | boolean | Whether the conversation has been read |
| ↳ `created_at` | number | Unix timestamp when conversation was created |
| ↳ `updated_at` | number | Unix timestamp when conversation was last updated |
| `conversationId` | string | ID of the closed conversation |
| `state` | string | State of the conversation \(closed\) |
### `intercom_open_conversation`
Open a closed or snoozed conversation in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to open |
| `admin_id` | string | Yes | The ID of the admin performing the action |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `conversation` | object | The opened conversation object |
| ↳ `id` | string | Unique identifier for the conversation |
| ↳ `type` | string | Object type \(conversation\) |
| ↳ `state` | string | State of the conversation \(open\) |
| ↳ `open` | boolean | Whether the conversation is open \(true\) |
| ↳ `read` | boolean | Whether the conversation has been read |
| ↳ `created_at` | number | Unix timestamp when conversation was created |
| ↳ `updated_at` | number | Unix timestamp when conversation was last updated |
| `conversationId` | string | ID of the opened conversation |
| `state` | string | State of the conversation \(open\) |
### `intercom_snooze_conversation`
Snooze a conversation to reopen at a future time
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to snooze |
| `admin_id` | string | Yes | The ID of the admin performing the action |
| `snoozed_until` | number | Yes | Unix timestamp for when the conversation should reopen |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `conversation` | object | The snoozed conversation object |
| ↳ `id` | string | Unique identifier for the conversation |
| ↳ `type` | string | Object type \(conversation\) |
| ↳ `state` | string | State of the conversation \(snoozed\) |
| ↳ `open` | boolean | Whether the conversation is open |
| ↳ `snoozed_until` | number | Unix timestamp when conversation will reopen |
| ↳ `created_at` | number | Unix timestamp when conversation was created |
| ↳ `updated_at` | number | Unix timestamp when conversation was last updated |
| `conversationId` | string | ID of the snoozed conversation |
| `state` | string | State of the conversation \(snoozed\) |
| `snoozed_until` | number | Unix timestamp when conversation will reopen |
### `intercom_assign_conversation`
Assign a conversation to an admin or team in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to assign |
| `admin_id` | string | Yes | The ID of the admin performing the assignment |
| `assignee_id` | string | Yes | The ID of the admin or team to assign the conversation to. Set to "0" to unassign. |
| `body` | string | No | Optional message to add when assigning \(e.g., "Passing to the support team"\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `conversation` | object | The assigned conversation object |
| ↳ `id` | string | Unique identifier for the conversation |
| ↳ `type` | string | Object type \(conversation\) |
| ↳ `state` | string | State of the conversation |
| ↳ `open` | boolean | Whether the conversation is open |
| ↳ `admin_assignee_id` | number | ID of the assigned admin |
| ↳ `team_assignee_id` | string | ID of the assigned team |
| ↳ `created_at` | number | Unix timestamp when conversation was created |
| ↳ `updated_at` | number | Unix timestamp when conversation was last updated |
| `conversationId` | string | ID of the assigned conversation |
| `admin_assignee_id` | number | ID of the assigned admin |
| `team_assignee_id` | string | ID of the assigned team |
### `intercom_list_tags`
Fetch a list of all tags in the workspace
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `tags` | array | Array of tag objects |
| ↳ `id` | string | Unique identifier for the tag |
| ↳ `type` | string | Object type \(tag\) |
| ↳ `name` | string | Name of the tag |
| `type` | string | Object type \(list\) |
### `intercom_create_tag`
Create a new tag or update an existing tag name
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `name` | string | Yes | The name of the tag. Will create a new tag if not found, or update the name if id is provided. |
| `id` | string | No | The ID of an existing tag to update. Omit to create a new tag. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the tag |
| `name` | string | Name of the tag |
| `type` | string | Object type \(tag\) |
### `intercom_tag_contact`
Add a tag to a specific contact
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to tag |
| `tagId` | string | Yes | The ID of the tag to apply |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the tag |
| `name` | string | Name of the tag |
| `type` | string | Object type \(tag\) |
### `intercom_untag_contact`
Remove a tag from a specific contact
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to untag |
| `tagId` | string | Yes | The ID of the tag to remove |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the tag that was removed |
| `name` | string | Name of the tag that was removed |
| `type` | string | Object type \(tag\) |
### `intercom_tag_conversation`
Add a tag to a specific conversation
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `conversationId` | string | Yes | The ID of the conversation to tag |
| `tagId` | string | Yes | The ID of the tag to apply |
| `admin_id` | string | Yes | The ID of the admin applying the tag |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the tag |
| `name` | string | Name of the tag |
| `type` | string | Object type \(tag\) |
### `intercom_create_note`
Add a note to a specific contact
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to add the note to |
| `body` | string | Yes | The text content of the note |
| `admin_id` | string | No | The ID of the admin creating the note |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | string | Unique identifier for the note |
| `body` | string | The text content of the note |
| `created_at` | number | Unix timestamp when the note was created |
| `type` | string | Object type \(note\) |
| `author` | object | The admin who created the note |
| ↳ `type` | string | Author type \(admin\) |
| ↳ `id` | string | Author ID |
| ↳ `name` | string | Author name |
| ↳ `email` | string | Author email |
| `contact` | object | The contact the note was created for |
| ↳ `type` | string | Contact type |
| ↳ `id` | string | Contact ID |
### `intercom_create_event`
Track a custom event for a contact in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `event_name` | string | Yes | The name of the event \(e.g., "order-completed"\). Use past-tense verb-noun format for readability. |
| `created_at` | number | No | Unix timestamp for when the event occurred. Strongly recommended for uniqueness. |
| `user_id` | string | No | Your identifier for the user \(external_id\) |
| `email` | string | No | Email address of the user. Use only if your app uses email to uniquely identify users. |
| `id` | string | No | The Intercom contact ID |
| `metadata` | string | No | JSON object with up to 10 metadata key-value pairs about the event \(e.g., \{"order_value": 99.99\}\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `accepted` | boolean | Whether the event was accepted \(202 Accepted\) |
### `intercom_attach_contact_to_company`
Attach a contact to a company in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to attach to the company |
| `companyId` | string | Yes | The ID of the company to attach the contact to |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `company` | object | The company object the contact was attached to |
| ↳ `id` | string | Unique identifier for the company |
| ↳ `type` | string | Object type \(company\) |
| ↳ `company_id` | string | The company_id you defined |
| ↳ `name` | string | Name of the company |
| ↳ `created_at` | number | Unix timestamp when company was created |
| ↳ `updated_at` | number | Unix timestamp when company was updated |
| ↳ `user_count` | number | Number of users in the company |
| ↳ `session_count` | number | Number of sessions |
| ↳ `monthly_spend` | number | Monthly spend amount |
| ↳ `plan` | object | Company plan details |
| `companyId` | string | ID of the company |
| `name` | string | Name of the company |
### `intercom_detach_contact_from_company`
Remove a contact from a company in Intercom
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `contactId` | string | Yes | The ID of the contact to detach from the company |
| `companyId` | string | Yes | The ID of the company to detach the contact from |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `company` | object | The company object the contact was detached from |
| ↳ `id` | string | Unique identifier for the company |
| ↳ `type` | string | Object type \(company\) |
| ↳ `company_id` | string | The company_id you defined |
| ↳ `name` | string | Name of the company |
| `companyId` | string | ID of the company |
| `name` | string | Name of the company |

View File

@@ -6,7 +6,7 @@ description: Access prediction markets and trade on Kalshi
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="kalshi"
type="kalshi_v2"
color="#09C285"
/>
@@ -36,7 +36,7 @@ Integrate Kalshi prediction markets into the workflow. Can get markets, market,
### `kalshi_get_markets`
Retrieve a list of prediction markets from Kalshi with optional filtering
Retrieve a list of prediction markets from Kalshi with all filtering options (V2 - full API response)
#### Input
@@ -52,12 +52,12 @@ Retrieve a list of prediction markets from Kalshi with optional filtering
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `markets` | array | Array of market objects |
| `paging` | object | Pagination cursor for fetching more results |
| `markets` | array | Array of market objects with all API fields |
| `cursor` | string | Pagination cursor for fetching more results |
### `kalshi_get_market`
Retrieve details of a specific prediction market by ticker
Retrieve details of a specific prediction market by ticker (V2 - full API response)
#### Input
@@ -69,11 +69,62 @@ Retrieve details of a specific prediction market by ticker
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `market` | object | Market object with details |
| `market` | object | Market object with all API fields |
| ↳ `ticker` | string | Market ticker |
| ↳ `event_ticker` | string | Event ticker |
| ↳ `market_type` | string | Market type |
| ↳ `title` | string | Market title |
| ↳ `subtitle` | string | Market subtitle |
| ↳ `yes_sub_title` | string | Yes outcome subtitle |
| ↳ `no_sub_title` | string | No outcome subtitle |
| ↳ `open_time` | string | Market open time |
| ↳ `close_time` | string | Market close time |
| ↳ `expected_expiration_time` | string | Expected expiration time |
| ↳ `expiration_time` | string | Expiration time |
| ↳ `latest_expiration_time` | string | Latest expiration time |
| ↳ `settlement_timer_seconds` | number | Settlement timer in seconds |
| ↳ `status` | string | Market status |
| ↳ `response_price_units` | string | Response price units |
| ↳ `notional_value` | number | Notional value |
| ↳ `tick_size` | number | Tick size |
| ↳ `yes_bid` | number | Current yes bid price |
| ↳ `yes_ask` | number | Current yes ask price |
| ↳ `no_bid` | number | Current no bid price |
| ↳ `no_ask` | number | Current no ask price |
| ↳ `last_price` | number | Last trade price |
| ↳ `previous_yes_bid` | number | Previous yes bid |
| ↳ `previous_yes_ask` | number | Previous yes ask |
| ↳ `previous_price` | number | Previous price |
| ↳ `volume` | number | Total volume |
| ↳ `volume_24h` | number | 24-hour volume |
| ↳ `liquidity` | number | Market liquidity |
| ↳ `open_interest` | number | Open interest |
| ↳ `result` | string | Market result |
| ↳ `cap_strike` | number | Cap strike |
| ↳ `floor_strike` | number | Floor strike |
| ↳ `can_close_early` | boolean | Can close early |
| ↳ `expiration_value` | string | Expiration value |
| ↳ `category` | string | Market category |
| ↳ `risk_limit_cents` | number | Risk limit in cents |
| ↳ `strike_type` | string | Strike type |
| ↳ `rules_primary` | string | Primary rules |
| ↳ `rules_secondary` | string | Secondary rules |
| ↳ `settlement_source_url` | string | Settlement source URL |
| ↳ `custom_strike` | object | Custom strike object |
| ↳ `underlying` | string | Underlying asset |
| ↳ `settlement_value` | number | Settlement value |
| ↳ `cfd_contract_size` | number | CFD contract size |
| ↳ `yes_fee_fp` | number | Yes fee \(fixed-point\) |
| ↳ `no_fee_fp` | number | No fee \(fixed-point\) |
| ↳ `last_price_fp` | number | Last price \(fixed-point\) |
| ↳ `yes_bid_fp` | number | Yes bid \(fixed-point\) |
| ↳ `yes_ask_fp` | number | Yes ask \(fixed-point\) |
| ↳ `no_bid_fp` | number | No bid \(fixed-point\) |
| ↳ `no_ask_fp` | number | No ask \(fixed-point\) |
### `kalshi_get_events`
Retrieve a list of events from Kalshi with optional filtering
Retrieve a list of events from Kalshi with optional filtering (V2 - exact API response)
#### Input
@@ -90,11 +141,12 @@ Retrieve a list of events from Kalshi with optional filtering
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `events` | array | Array of event objects |
| `paging` | object | Pagination cursor for fetching more results |
| `milestones` | array | Array of milestone objects \(if requested\) |
| `cursor` | string | Pagination cursor for fetching more results |
### `kalshi_get_event`
Retrieve details of a specific event by ticker
Retrieve details of a specific event by ticker (V2 - exact API response)
#### Input
@@ -107,11 +159,23 @@ Retrieve details of a specific event by ticker
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `event` | object | Event object with details |
| `event` | object | Event object with full details matching Kalshi API response |
| ↳ `event_ticker` | string | Event ticker |
| ↳ `series_ticker` | string | Series ticker |
| ↳ `title` | string | Event title |
| ↳ `sub_title` | string | Event subtitle |
| ↳ `mutually_exclusive` | boolean | Mutually exclusive markets |
| ↳ `category` | string | Event category |
| ↳ `collateral_return_type` | string | Collateral return type |
| ↳ `strike_date` | string | Strike date |
| ↳ `strike_period` | string | Strike period |
| ↳ `available_on_brokers` | boolean | Available on brokers |
| ↳ `product_metadata` | object | Product metadata |
| ↳ `markets` | array | Nested markets \(if requested\) |
### `kalshi_get_balance`
Retrieve your account balance and portfolio value from Kalshi
Retrieve your account balance and portfolio value from Kalshi (V2 - exact API response)
#### Input
@@ -125,11 +189,12 @@ Retrieve your account balance and portfolio value from Kalshi
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `balance` | number | Account balance in cents |
| `portfolioValue` | number | Portfolio value in cents |
| `portfolio_value` | number | Portfolio value in cents |
| `updated_ts` | number | Unix timestamp of last update \(milliseconds\) |
### `kalshi_get_positions`
Retrieve your open positions from Kalshi
Retrieve your open positions from Kalshi (V2 - exact API response)
#### Input
@@ -147,12 +212,13 @@ Retrieve your open positions from Kalshi
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `positions` | array | Array of position objects |
| `paging` | object | Pagination cursor for fetching more results |
| `market_positions` | array | Array of market position objects |
| `event_positions` | array | Array of event position objects |
| `cursor` | string | Pagination cursor for fetching more results |
### `kalshi_get_orders`
Retrieve your orders from Kalshi with optional filtering
Retrieve your orders from Kalshi with optional filtering (V2 with full API response)
#### Input
@@ -170,12 +236,12 @@ Retrieve your orders from Kalshi with optional filtering
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `orders` | array | Array of order objects |
| `paging` | object | Pagination cursor for fetching more results |
| `orders` | array | Array of order objects with full API response fields |
| `cursor` | string | Pagination cursor for fetching more results |
### `kalshi_get_order`
Retrieve details of a specific order by ID from Kalshi
Retrieve details of a specific order by ID from Kalshi (V2 with full API response)
#### Input
@@ -189,11 +255,44 @@ Retrieve details of a specific order by ID from Kalshi
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `order` | object | Order object with details |
| `order` | object | Order object with full API response fields |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `status` | string | Order status \(resting/canceled/executed\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `yes_price_dollars` | string | Yes price in dollars |
| ↳ `no_price_dollars` | string | No price in dollars |
| ↳ `fill_count` | number | Filled contract count |
| ↳ `fill_count_fp` | string | Filled count \(fixed-point\) |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `remaining_count_fp` | string | Remaining count \(fixed-point\) |
| ↳ `initial_count` | number | Initial contract count |
| ↳ `initial_count_fp` | string | Initial count \(fixed-point\) |
| ↳ `taker_fees` | number | Taker fees in cents |
| ↳ `maker_fees` | number | Maker fees in cents |
| ↳ `taker_fees_dollars` | string | Taker fees in dollars |
| ↳ `maker_fees_dollars` | string | Maker fees in dollars |
| ↳ `taker_fill_cost` | number | Taker fill cost in cents |
| ↳ `maker_fill_cost` | number | Maker fill cost in cents |
| ↳ `taker_fill_cost_dollars` | string | Taker fill cost in dollars |
| ↳ `maker_fill_cost_dollars` | string | Maker fill cost in dollars |
| ↳ `queue_position` | number | Queue position \(deprecated\) |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `created_time` | string | Order creation time |
| ↳ `last_update_time` | string | Last update time |
| ↳ `self_trade_prevention_type` | string | Self-trade prevention type |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `cancel_order_on_pause` | boolean | Cancel on market pause |
### `kalshi_get_orderbook`
Retrieve the orderbook (yes and no bids) for a specific market
Retrieve the orderbook (yes and no bids) for a specific market (V2 - includes depth and fp fields)
#### Input
@@ -205,11 +304,18 @@ Retrieve the orderbook (yes and no bids) for a specific market
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `orderbook` | object | Orderbook with yes/no bids and asks |
| `orderbook` | object | Orderbook with yes/no bids \(legacy integer counts\) |
| ↳ `yes` | array | Yes side bids as tuples \[price_cents, count\] |
| ↳ `no` | array | No side bids as tuples \[price_cents, count\] |
| ↳ `yes_dollars` | array | Yes side bids as tuples \[dollars_string, count\] |
| ↳ `no_dollars` | array | No side bids as tuples \[dollars_string, count\] |
| `orderbook_fp` | object | Orderbook with fixed-point counts \(preferred\) |
| ↳ `yes_dollars` | array | Yes side bids as tuples \[dollars_string, fp_count_string\] |
| ↳ `no_dollars` | array | No side bids as tuples \[dollars_string, fp_count_string\] |
### `kalshi_get_trades`
Retrieve recent trades across all markets
Retrieve recent trades with additional filtering options (V2 - includes trade_id and count_fp)
#### Input
@@ -222,12 +328,12 @@ Retrieve recent trades across all markets
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `trades` | array | Array of trade objects |
| `paging` | object | Pagination cursor for fetching more results |
| `trades` | array | Array of trade objects with trade_id and count_fp |
| `cursor` | string | Pagination cursor for fetching more results |
### `kalshi_get_candlesticks`
Retrieve OHLC candlestick data for a specific market
Retrieve OHLC candlestick data for a specific market (V2 - full API response)
#### Input
@@ -243,7 +349,8 @@ Retrieve OHLC candlestick data for a specific market
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `candlesticks` | array | Array of OHLC candlestick data |
| `ticker` | string | Market ticker |
| `candlesticks` | array | Array of OHLC candlestick data with nested bid/ask/price objects |
### `kalshi_get_fills`
@@ -266,12 +373,12 @@ Retrieve your portfolio
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `fills` | array | Array of fill/trade objects |
| `paging` | object | Pagination cursor for fetching more results |
| `fills` | array | Array of fill/trade objects with all API fields |
| `cursor` | string | Pagination cursor for fetching more results |
### `kalshi_get_series_by_ticker`
Retrieve details of a specific market series by ticker
Retrieve details of a specific market series by ticker (V2 - exact API response)
#### Input
@@ -283,11 +390,25 @@ Retrieve details of a specific market series by ticker
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | object | Series object with details |
| `series` | object | Series object with full details matching Kalshi API response |
| ↳ `ticker` | string | Series ticker |
| ↳ `title` | string | Series title |
| ↳ `frequency` | string | Event frequency |
| ↳ `category` | string | Series category |
| ↳ `tags` | array | Series tags |
| ↳ `settlement_sources` | array | Settlement sources |
| ↳ `contract_url` | string | Contract URL |
| ↳ `contract_terms_url` | string | Contract terms URL |
| ↳ `fee_type` | string | Fee type |
| ↳ `fee_multiplier` | number | Fee multiplier |
| ↳ `additional_prohibitions` | array | Additional prohibitions |
| ↳ `product_metadata` | object | Product metadata |
| ↳ `volume` | number | Series volume |
| ↳ `volume_fp` | number | Volume \(fixed-point\) |
### `kalshi_get_exchange_status`
Retrieve the current status of the Kalshi exchange (trading and exchange activity)
Retrieve the current status of the Kalshi exchange (V2 - exact API response)
#### Input
@@ -298,11 +419,13 @@ Retrieve the current status of the Kalshi exchange (trading and exchange activit
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `status` | object | Exchange status with trading_active and exchange_active flags |
| `exchange_active` | boolean | Whether the exchange is active |
| `trading_active` | boolean | Whether trading is active |
| `exchange_estimated_resume_time` | string | Estimated time when exchange will resume \(if inactive\) |
### `kalshi_create_order`
Create a new order on a Kalshi prediction market
Create a new order on a Kalshi prediction market (V2 with full API response)
#### Input
@@ -332,11 +455,44 @@ Create a new order on a Kalshi prediction market
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `order` | object | The created order object |
| `order` | object | The created order object with full API response fields |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `status` | string | Order status \(resting/canceled/executed\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `yes_price_dollars` | string | Yes price in dollars |
| ↳ `no_price_dollars` | string | No price in dollars |
| ↳ `fill_count` | number | Filled contract count |
| ↳ `fill_count_fp` | string | Filled count \(fixed-point\) |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `remaining_count_fp` | string | Remaining count \(fixed-point\) |
| ↳ `initial_count` | number | Initial contract count |
| ↳ `initial_count_fp` | string | Initial count \(fixed-point\) |
| ↳ `taker_fees` | number | Taker fees in cents |
| ↳ `maker_fees` | number | Maker fees in cents |
| ↳ `taker_fees_dollars` | string | Taker fees in dollars |
| ↳ `maker_fees_dollars` | string | Maker fees in dollars |
| ↳ `taker_fill_cost` | number | Taker fill cost in cents |
| ↳ `maker_fill_cost` | number | Maker fill cost in cents |
| ↳ `taker_fill_cost_dollars` | string | Taker fill cost in dollars |
| ↳ `maker_fill_cost_dollars` | string | Maker fill cost in dollars |
| ↳ `queue_position` | number | Queue position \(deprecated\) |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `created_time` | string | Order creation time |
| ↳ `last_update_time` | string | Last update time |
| ↳ `self_trade_prevention_type` | string | Self-trade prevention type |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `cancel_order_on_pause` | boolean | Cancel on market pause |
### `kalshi_cancel_order`
Cancel an existing order on Kalshi
Cancel an existing order on Kalshi (V2 with full API response)
#### Input
@@ -350,12 +506,46 @@ Cancel an existing order on Kalshi
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `order` | object | The canceled order object |
| `reducedBy` | number | Number of contracts canceled |
| `order` | object | The canceled order object with full API response fields |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `status` | string | Order status \(resting/canceled/executed\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `yes_price_dollars` | string | Yes price in dollars |
| ↳ `no_price_dollars` | string | No price in dollars |
| ↳ `fill_count` | number | Filled contract count |
| ↳ `fill_count_fp` | string | Filled count \(fixed-point\) |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `remaining_count_fp` | string | Remaining count \(fixed-point\) |
| ↳ `initial_count` | number | Initial contract count |
| ↳ `initial_count_fp` | string | Initial count \(fixed-point\) |
| ↳ `taker_fees` | number | Taker fees in cents |
| ↳ `maker_fees` | number | Maker fees in cents |
| ↳ `taker_fees_dollars` | string | Taker fees in dollars |
| ↳ `maker_fees_dollars` | string | Maker fees in dollars |
| ↳ `taker_fill_cost` | number | Taker fill cost in cents |
| ↳ `maker_fill_cost` | number | Maker fill cost in cents |
| ↳ `taker_fill_cost_dollars` | string | Taker fill cost in dollars |
| ↳ `maker_fill_cost_dollars` | string | Maker fill cost in dollars |
| ↳ `queue_position` | number | Queue position \(deprecated\) |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `created_time` | string | Order creation time |
| ↳ `last_update_time` | string | Last update time |
| ↳ `self_trade_prevention_type` | string | Self-trade prevention type |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `cancel_order_on_pause` | boolean | Cancel on market pause |
| `reduced_by` | number | Number of contracts canceled |
| `reduced_by_fp` | string | Number of contracts canceled in fixed-point format |
### `kalshi_amend_order`
Modify the price or quantity of an existing order on Kalshi
Modify the price or quantity of an existing order on Kalshi (V2 with full API response)
#### Input
@@ -379,6 +569,63 @@ Modify the price or quantity of an existing order on Kalshi
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `order` | object | The amended order object |
| `old_order` | object | The original order object before amendment |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `event_ticker` | string | Event ticker |
| ↳ `status` | string | Order status |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `count` | number | Number of contracts |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `created_time` | string | Order creation time |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `place_count` | number | Place count |
| ↳ `decrease_count` | number | Decrease count |
| ↳ `queue_position` | number | Queue position |
| ↳ `maker_fill_count` | number | Maker fill count |
| ↳ `taker_fill_count` | number | Taker fill count |
| ↳ `maker_fees` | number | Maker fees |
| ↳ `taker_fees` | number | Taker fees |
| ↳ `last_update_time` | string | Last update time |
| ↳ `take_profit_order_id` | string | Take profit order ID |
| ↳ `stop_loss_order_id` | string | Stop loss order ID |
| ↳ `amend_count` | number | Amend count |
| ↳ `amend_taker_fill_count` | number | Amend taker fill count |
| `order` | object | The amended order object with full API response fields |
| ↳ `order_id` | string | Order ID |
| ↳ `user_id` | string | User ID |
| ↳ `ticker` | string | Market ticker |
| ↳ `event_ticker` | string | Event ticker |
| ↳ `status` | string | Order status |
| ↳ `side` | string | Order side \(yes/no\) |
| ↳ `type` | string | Order type \(limit/market\) |
| ↳ `yes_price` | number | Yes price in cents |
| ↳ `no_price` | number | No price in cents |
| ↳ `action` | string | Action \(buy/sell\) |
| ↳ `count` | number | Number of contracts |
| ↳ `remaining_count` | number | Remaining contracts |
| ↳ `created_time` | string | Order creation time |
| ↳ `expiration_time` | string | Order expiration time |
| ↳ `order_group_id` | string | Order group ID |
| ↳ `client_order_id` | string | Client order ID |
| ↳ `place_count` | number | Place count |
| ↳ `decrease_count` | number | Decrease count |
| ↳ `queue_position` | number | Queue position |
| ↳ `maker_fill_count` | number | Maker fill count |
| ↳ `taker_fill_count` | number | Taker fill count |
| ↳ `maker_fees` | number | Maker fees |
| ↳ `taker_fees` | number | Taker fees |
| ↳ `last_update_time` | string | Last update time |
| ↳ `take_profit_order_id` | string | Take profit order ID |
| ↳ `stop_loss_order_id` | string | Stop loss order ID |
| ↳ `amend_count` | number | Amend count |
| ↳ `amend_taker_fill_count` | number | Amend taker fill count |

View File

@@ -97,7 +97,6 @@
"shopify",
"slack",
"smtp",
"spotify",
"sqs",
"ssh",
"stagehand",

View File

@@ -29,7 +29,7 @@ By using these documented API endpoints, you can seamlessly integrate Polymarket
## Usage Instructions
Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, and search.
Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, activity, leaderboard, holders, and search.
@@ -43,7 +43,7 @@ Retrieve a list of prediction markets from Polymarket with optional filtering
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `closed` | string | No | Filter by closed status \(true/false\). Use false for active markets only. |
| `closed` | string | No | Filter by closed status \(true/false\). Use false for open markets only. |
| `order` | string | No | Sort field \(e.g., volumeNum, liquidityNum, startDate, endDate, createdAt\) |
| `ascending` | string | No | Sort direction \(true for ascending, false for descending\) |
| `tagId` | string | No | Filter by tag ID |
@@ -55,6 +55,21 @@ Retrieve a list of prediction markets from Polymarket with optional filtering
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `markets` | array | Array of market objects |
| ↳ `id` | string | Market ID |
| ↳ `question` | string | Market question |
| ↳ `conditionId` | string | Condition ID |
| ↳ `slug` | string | Market slug |
| ↳ `endDate` | string | End date |
| ↳ `image` | string | Market image URL |
| ↳ `outcomes` | string | Outcomes JSON string |
| ↳ `outcomePrices` | string | Outcome prices JSON string |
| ↳ `volume` | string | Total volume |
| ↳ `liquidity` | string | Total liquidity |
| ↳ `active` | boolean | Whether market is active |
| ↳ `closed` | boolean | Whether market is closed |
| ↳ `volumeNum` | number | Volume as number |
| ↳ `liquidityNum` | number | Liquidity as number |
| ↳ `clobTokenIds` | array | CLOB token IDs |
### `polymarket_get_market`
@@ -72,6 +87,28 @@ Retrieve details of a specific prediction market by ID or slug
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `market` | object | Market object with details |
| ↳ `id` | string | Market ID |
| ↳ `question` | string | Market question |
| ↳ `conditionId` | string | Condition ID |
| ↳ `slug` | string | Market slug |
| ↳ `resolutionSource` | string | Resolution source |
| ↳ `endDate` | string | End date |
| ↳ `startDate` | string | Start date |
| ↳ `image` | string | Market image URL |
| ↳ `icon` | string | Market icon URL |
| ↳ `description` | string | Market description |
| ↳ `outcomes` | string | Outcomes JSON string |
| ↳ `outcomePrices` | string | Outcome prices JSON string |
| ↳ `volume` | string | Total volume |
| ↳ `liquidity` | string | Total liquidity |
| ↳ `active` | boolean | Whether market is active |
| ↳ `closed` | boolean | Whether market is closed |
| ↳ `archived` | boolean | Whether market is archived |
| ↳ `volumeNum` | number | Volume as number |
| ↳ `liquidityNum` | number | Liquidity as number |
| ↳ `clobTokenIds` | array | CLOB token IDs |
| ↳ `acceptingOrders` | boolean | Whether accepting orders |
| ↳ `negRisk` | boolean | Whether negative risk |
### `polymarket_get_events`
@@ -81,7 +118,7 @@ Retrieve a list of events from Polymarket with optional filtering
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `closed` | string | No | Filter by closed status \(true/false\). Use false for active events only. |
| `closed` | string | No | Filter by closed status \(true/false\). Use false for open events only. |
| `order` | string | No | Sort field \(e.g., volume, liquidity, startDate, endDate, createdAt\) |
| `ascending` | string | No | Sort direction \(true for ascending, false for descending\) |
| `tagId` | string | No | Filter by tag ID |
@@ -93,6 +130,21 @@ Retrieve a list of events from Polymarket with optional filtering
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `events` | array | Array of event objects |
| ↳ `id` | string | Event ID |
| ↳ `ticker` | string | Event ticker |
| ↳ `slug` | string | Event slug |
| ↳ `title` | string | Event title |
| ↳ `description` | string | Event description |
| ↳ `startDate` | string | Start date |
| ↳ `endDate` | string | End date |
| ↳ `image` | string | Event image URL |
| ↳ `icon` | string | Event icon URL |
| ↳ `active` | boolean | Whether event is active |
| ↳ `closed` | boolean | Whether event is closed |
| ↳ `archived` | boolean | Whether event is archived |
| ↳ `liquidity` | number | Total liquidity |
| ↳ `volume` | number | Total volume |
| ↳ `markets` | array | Array of markets in this event |
### `polymarket_get_event`
@@ -110,6 +162,24 @@ Retrieve details of a specific event by ID or slug
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `event` | object | Event object with details |
| ↳ `id` | string | Event ID |
| ↳ `ticker` | string | Event ticker |
| ↳ `slug` | string | Event slug |
| ↳ `title` | string | Event title |
| ↳ `description` | string | Event description |
| ↳ `startDate` | string | Start date |
| ↳ `creationDate` | string | Creation date |
| ↳ `endDate` | string | End date |
| ↳ `image` | string | Event image URL |
| ↳ `icon` | string | Event icon URL |
| ↳ `active` | boolean | Whether event is active |
| ↳ `closed` | boolean | Whether event is closed |
| ↳ `archived` | boolean | Whether event is archived |
| ↳ `liquidity` | number | Total liquidity |
| ↳ `volume` | number | Total volume |
| ↳ `openInterest` | number | Open interest |
| ↳ `commentCount` | number | Comment count |
| ↳ `markets` | array | Array of markets in this event |
### `polymarket_get_tags`
@@ -126,7 +196,12 @@ Retrieve available tags for filtering markets from Polymarket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `tags` | array | Array of tag objects with id, label, and slug |
| `tags` | array | Array of tag objects |
| ↳ `id` | string | Tag ID |
| ↳ `label` | string | Tag label |
| ↳ `slug` | string | Tag slug |
| ↳ `createdAt` | string | Creation timestamp |
| ↳ `updatedAt` | string | Last update timestamp |
### `polymarket_search`
@@ -138,13 +213,28 @@ Search for markets, events, and profiles on Polymarket
| --------- | ---- | -------- | ----------- |
| `query` | string | Yes | Search query term |
| `limit` | string | No | Number of results per page \(max 50\) |
| `offset` | string | No | Pagination offset |
| `page` | string | No | Page number for pagination \(1-indexed\) |
| `cache` | string | No | Enable caching \(true/false\) |
| `eventsStatus` | string | No | Filter events by status |
| `limitPerType` | string | No | Limit results per type \(markets, events, profiles\) |
| `eventsTag` | string | No | Filter by event tags \(comma-separated\) |
| `sort` | string | No | Sort field |
| `ascending` | string | No | Sort direction \(true for ascending, false for descending\) |
| `searchTags` | string | No | Include tags in search results \(true/false\) |
| `searchProfiles` | string | No | Include profiles in search results \(true/false\) |
| `recurrence` | string | No | Filter by recurrence type |
| `excludeTagId` | string | No | Exclude events with these tag IDs \(comma-separated\) |
| `keepClosedMarkets` | string | No | Include closed markets in results \(0 or 1\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `results` | object | Search results containing markets, events, and profiles arrays |
| `results` | object | Search results containing markets, events, tags, and profiles arrays |
| ↳ `markets` | array | Array of matching market objects |
| ↳ `events` | array | Array of matching event objects |
| ↳ `tags` | array | Array of matching tag objects |
| ↳ `profiles` | array | Array of matching profile objects |
### `polymarket_get_series`
@@ -162,6 +252,21 @@ Retrieve series (related market groups) from Polymarket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | array | Array of series objects |
| ↳ `id` | string | Series ID |
| ↳ `ticker` | string | Series ticker |
| ↳ `slug` | string | Series slug |
| ↳ `title` | string | Series title |
| ↳ `seriesType` | string | Series type |
| ↳ `recurrence` | string | Recurrence pattern |
| ↳ `image` | string | Series image URL |
| ↳ `icon` | string | Series icon URL |
| ↳ `active` | boolean | Whether series is active |
| ↳ `closed` | boolean | Whether series is closed |
| ↳ `archived` | boolean | Whether series is archived |
| ↳ `featured` | boolean | Whether series is featured |
| ↳ `volume` | number | Total volume |
| ↳ `liquidity` | number | Total liquidity |
| ↳ `eventCount` | number | Number of events in series |
### `polymarket_get_series_by_id`
@@ -178,6 +283,23 @@ Retrieve a specific series (related market group) by ID from Polymarket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `series` | object | Series object with details |
| ↳ `id` | string | Series ID |
| ↳ `ticker` | string | Series ticker |
| ↳ `slug` | string | Series slug |
| ↳ `title` | string | Series title |
| ↳ `seriesType` | string | Series type |
| ↳ `recurrence` | string | Recurrence pattern |
| ↳ `image` | string | Series image URL |
| ↳ `icon` | string | Series icon URL |
| ↳ `active` | boolean | Whether series is active |
| ↳ `closed` | boolean | Whether series is closed |
| ↳ `archived` | boolean | Whether series is archived |
| ↳ `featured` | boolean | Whether series is featured |
| ↳ `volume` | number | Total volume |
| ↳ `liquidity` | number | Total liquidity |
| ↳ `commentCount` | number | Comment count |
| ↳ `eventCount` | number | Number of events in series |
| ↳ `events` | array | Array of events in this series |
### `polymarket_get_orderbook`
@@ -194,6 +316,21 @@ Retrieve the order book summary for a specific token
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `orderbook` | object | Order book with bids and asks arrays |
| ↳ `market` | string | Market identifier |
| ↳ `asset_id` | string | Asset token ID |
| ↳ `hash` | string | Order book hash |
| ↳ `timestamp` | string | Timestamp |
| ↳ `bids` | array | Bid orders |
| ↳ `price` | string | Bid price |
| ↳ `size` | string | Bid size |
| ↳ `price` | string | Ask price |
| ↳ `size` | string | Ask size |
| ↳ `asks` | array | Ask orders |
| ↳ `price` | string | Ask price |
| ↳ `size` | string | Ask size |
| ↳ `min_order_size` | string | Minimum order size |
| ↳ `tick_size` | string | Tick size |
| ↳ `neg_risk` | boolean | Whether negative risk |
### `polymarket_get_price`
@@ -246,7 +383,9 @@ Retrieve historical price data for a specific market token
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `history` | array | Array of price history entries with timestamp \(t\) and price \(p\) |
| `history` | array | Array of price history entries |
| ↳ `t` | number | Unix timestamp |
| ↳ `p` | number | Price at timestamp |
### `polymarket_get_last_trade_price`
@@ -263,6 +402,7 @@ Retrieve the last trade price for a specific token
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `price` | string | Last trade price |
| `side` | string | Side of the last trade \(BUY or SELL\) |
### `polymarket_get_spread`
@@ -278,7 +418,8 @@ Retrieve the bid-ask spread for a specific token
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `spread` | object | Bid-ask spread with bid and ask prices |
| `spread` | object | Spread value between bid and ask |
| ↳ `spread` | string | The spread value |
### `polymarket_get_tick_size`
@@ -305,13 +446,47 @@ Retrieve user positions from Polymarket
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user` | string | Yes | User wallet address |
| `market` | string | No | Optional market ID to filter positions |
| `market` | string | No | Condition IDs to filter positions \(comma-separated, mutually exclusive with eventId\) |
| `eventId` | string | No | Event ID to filter positions \(mutually exclusive with market\) |
| `sizeThreshold` | string | No | Minimum position size threshold \(default: 1\) |
| `redeemable` | string | No | Filter for redeemable positions only \(true/false\) |
| `mergeable` | string | No | Filter for mergeable positions only \(true/false\) |
| `sortBy` | string | No | Sort field \(TOKENS, CURRENT, INITIAL, CASHPNL, PERCENTPNL, TITLE, RESOLVING, PRICE, AVGPRICE\) |
| `sortDirection` | string | No | Sort direction \(ASC or DESC\) |
| `title` | string | No | Search filter by title |
| `limit` | string | No | Number of results per page |
| `offset` | string | No | Pagination offset |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `positions` | array | Array of position objects |
| ↳ `proxyWallet` | string | Proxy wallet address |
| ↳ `asset` | string | Asset token ID |
| ↳ `conditionId` | string | Condition ID |
| ↳ `size` | number | Position size |
| ↳ `avgPrice` | number | Average price |
| ↳ `initialValue` | number | Initial value |
| ↳ `currentValue` | number | Current value |
| ↳ `cashPnl` | number | Cash profit/loss |
| ↳ `percentPnl` | number | Percent profit/loss |
| ↳ `totalBought` | number | Total bought |
| ↳ `realizedPnl` | number | Realized profit/loss |
| ↳ `percentRealizedPnl` | number | Percent realized profit/loss |
| ↳ `curPrice` | number | Current price |
| ↳ `redeemable` | boolean | Whether position is redeemable |
| ↳ `mergeable` | boolean | Whether position is mergeable |
| ↳ `title` | string | Market title |
| ↳ `slug` | string | Market slug |
| ↳ `icon` | string | Market icon URL |
| ↳ `eventSlug` | string | Event slug |
| ↳ `outcome` | string | Outcome name |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `oppositeOutcome` | string | Opposite outcome name |
| ↳ `oppositeAsset` | string | Opposite asset token ID |
| ↳ `endDate` | string | End date |
| ↳ `negativeRisk` | boolean | Whether negative risk |
### `polymarket_get_trades`
@@ -322,8 +497,13 @@ Retrieve trade history from Polymarket
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user` | string | No | User wallet address to filter trades |
| `market` | string | No | Market ID to filter trades |
| `limit` | string | No | Number of results per page \(max 50\) |
| `market` | string | No | Market/condition ID to filter trades \(mutually exclusive with eventId\) |
| `eventId` | string | No | Event ID to filter trades \(mutually exclusive with market\) |
| `side` | string | No | Trade direction filter \(BUY or SELL\) |
| `takerOnly` | string | No | Filter for taker trades only \(true/false, default: true\) |
| `filterType` | string | No | Filter type \(CASH or TOKENS\) - requires filterAmount |
| `filterAmount` | string | No | Filter amount threshold - requires filterType |
| `limit` | string | No | Number of results per page \(default: 100, max: 10000\) |
| `offset` | string | No | Pagination offset \(skip this many results\) |
#### Output
@@ -331,5 +511,141 @@ Retrieve trade history from Polymarket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `trades` | array | Array of trade objects |
| ↳ `proxyWallet` | string | Proxy wallet address |
| ↳ `side` | string | Trade side \(BUY or SELL\) |
| ↳ `asset` | string | Asset token ID |
| ↳ `conditionId` | string | Condition ID |
| ↳ `size` | number | Trade size |
| ↳ `price` | number | Trade price |
| ↳ `timestamp` | number | Unix timestamp |
| ↳ `title` | string | Market title |
| ↳ `slug` | string | Market slug |
| ↳ `icon` | string | Market icon URL |
| ↳ `eventSlug` | string | Event slug |
| ↳ `outcome` | string | Outcome name |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `name` | string | Trader name |
| ↳ `pseudonym` | string | Trader pseudonym |
| ↳ `bio` | string | Trader bio |
| ↳ `profileImage` | string | Profile image URL |
| ↳ `profileImageOptimized` | string | Optimized profile image URL |
| ↳ `transactionHash` | string | Transaction hash |
### `polymarket_get_activity`
Retrieve on-chain activity for a user including trades, splits, merges, redemptions, rewards, and conversions
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user` | string | Yes | User wallet address \(0x-prefixed\) |
| `limit` | string | No | Maximum results \(default: 100, max: 500\) |
| `offset` | string | No | Pagination offset \(default: 0, max: 10000\) |
| `market` | string | No | Comma-separated condition IDs \(mutually exclusive with eventId\) |
| `eventId` | string | No | Comma-separated event IDs \(mutually exclusive with market\) |
| `type` | string | No | Activity type filter: TRADE, SPLIT, MERGE, REDEEM, REWARD, CONVERSION, MAKER_REBATE |
| `start` | number | No | Start timestamp \(Unix seconds\) |
| `end` | number | No | End timestamp \(Unix seconds\) |
| `sortBy` | string | No | Sort field: TIMESTAMP, TOKENS, or CASH \(default: TIMESTAMP\) |
| `sortDirection` | string | No | Sort direction: ASC or DESC \(default: DESC\) |
| `side` | string | No | Trade side filter: BUY or SELL \(only applies to trades\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `activity` | array | Array of activity entries |
| ↳ `proxyWallet` | string | User proxy wallet address |
| ↳ `timestamp` | number | Unix timestamp of activity |
| ↳ `conditionId` | string | Market condition ID |
| ↳ `type` | string | Activity type \(TRADE, SPLIT, MERGE, REDEEM, REWARD, CONVERSION\) |
| ↳ `size` | number | Size in tokens |
| ↳ `usdcSize` | number | Size in USDC |
| ↳ `transactionHash` | string | Blockchain transaction hash |
| ↳ `price` | number | Price \(for trades\) |
| ↳ `asset` | string | Asset/token ID |
| ↳ `side` | string | Trade side \(BUY/SELL\) |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `title` | string | Market title |
| ↳ `slug` | string | Market slug |
| ↳ `icon` | string | Market icon URL |
| ↳ `eventSlug` | string | Event slug |
| ↳ `outcome` | string | Outcome name |
| ↳ `name` | string | User display name |
| ↳ `pseudonym` | string | User pseudonym |
| ↳ `bio` | string | User bio |
| ↳ `profileImage` | string | User profile image URL |
| ↳ `profileImageOptimized` | string | Optimized profile image URL |
### `polymarket_get_leaderboard`
Retrieve trader leaderboard rankings by profit/loss or volume
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `category` | string | No | Category filter: OVERALL, POLITICS, SPORTS, CRYPTO, CULTURE, MENTIONS, WEATHER, ECONOMICS, TECH, FINANCE \(default: OVERALL\) |
| `timePeriod` | string | No | Time period: DAY, WEEK, MONTH, ALL \(default: DAY\) |
| `orderBy` | string | No | Order by: PNL or VOL \(default: PNL\) |
| `limit` | string | No | Number of results \(1-50, default: 25\) |
| `offset` | string | No | Pagination offset \(0-1000, default: 0\) |
| `user` | string | No | Filter by specific user wallet address |
| `userName` | string | No | Filter by username |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `leaderboard` | array | Array of leaderboard entries |
| ↳ `rank` | string | Leaderboard rank position |
| ↳ `proxyWallet` | string | User proxy wallet address |
| ↳ `userName` | string | User display name |
| ↳ `vol` | number | Trading volume |
| ↳ `pnl` | number | Profit and loss |
| ↳ `profileImage` | string | User profile image URL |
| ↳ `xUsername` | string | Twitter/X username |
| ↳ `verifiedBadge` | boolean | Whether user has verified badge |
### `polymarket_get_holders`
Retrieve top holders of a specific market token
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `market` | string | Yes | Comma-separated list of condition IDs |
| `limit` | string | No | Number of holders to return \(0-20, default: 20\) |
| `minBalance` | string | No | Minimum balance threshold \(default: 1\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `holders` | array | Array of market holder groups by token |
| ↳ `token` | string | Token/asset ID |
| ↳ `holders` | array | Array of holders for this token |
| ↳ `proxyWallet` | string | Holder wallet address |
| ↳ `bio` | string | Holder bio |
| ↳ `asset` | string | Asset ID |
| ↳ `pseudonym` | string | Holder pseudonym |
| ↳ `amount` | number | Amount held |
| ↳ `displayUsernamePublic` | boolean | Whether username is publicly displayed |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `name` | string | Holder display name |
| ↳ `profileImage` | string | Profile image URL |
| ↳ `profileImageOptimized` | string | Optimized profile image URL |
| ↳ `proxyWallet` | string | Holder wallet address |
| ↳ `bio` | string | Holder bio |
| ↳ `asset` | string | Asset ID |
| ↳ `pseudonym` | string | Holder pseudonym |
| ↳ `amount` | number | Amount held |
| ↳ `displayUsernamePublic` | boolean | Whether username is publicly displayed |
| ↳ `outcomeIndex` | number | Outcome index |
| ↳ `name` | string | Holder display name |
| ↳ `profileImage` | string | Profile image URL |
| ↳ `profileImageOptimized` | string | Optimized profile image URL |

File diff suppressed because it is too large Load Diff

View File

@@ -299,7 +299,7 @@ Upload a file to a Supabase storage bucket
| `bucket` | string | Yes | The name of the storage bucket |
| `fileName` | string | Yes | The name of the file \(e.g., "document.pdf", "image.jpg"\) |
| `path` | string | No | Optional folder path \(e.g., "folder/subfolder/"\) |
| `fileContent` | string | Yes | The file content \(base64 encoded for binary files, or plain text\) |
| `fileData` | json | Yes | File to upload - UserFile object \(basic mode\) or string content \(advanced mode: base64 or plain text\). Supports data URLs. |
| `contentType` | string | No | MIME type of the file \(e.g., "image/jpeg", "text/plain"\) |
| `upsert` | boolean | No | If true, overwrites existing file \(default: false\) |
| `apiKey` | string | Yes | Your Supabase service role secret key |
@@ -309,7 +309,7 @@ Upload a file to a Supabase storage bucket
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `message` | string | Operation status message |
| `results` | object | Upload result including file path and metadata |
| `results` | object | Upload result including file path, bucket, and public URL |
### `supabase_storage_download`

View File

@@ -58,7 +58,6 @@ export function ExecutionSnapshot({
onClose = () => {},
}: ExecutionSnapshotProps) {
const { data, isLoading, error } = useExecutionSnapshot(executionId)
const lastExecutionIdRef = useRef<string | null>(null)
const [isMenuOpen, setIsMenuOpen] = useState(false)
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 })
@@ -82,12 +81,6 @@ export function ExecutionSnapshot({
const workflowState = data?.workflowState as WorkflowState | undefined
// Track execution ID changes for key reset
const executionKey = executionId !== lastExecutionIdRef.current ? executionId : undefined
if (executionId !== lastExecutionIdRef.current) {
lastExecutionIdRef.current = executionId
}
const renderContent = () => {
if (isLoading) {
return (
@@ -152,7 +145,7 @@ export function ExecutionSnapshot({
return (
<Preview
key={executionKey}
key={executionId}
workflowState={workflowState}
traceSpans={traceSpans}
className={className}

View File

@@ -268,7 +268,6 @@ const OutputCodeContent = React.memo(function OutputCodeContent({
onMatchCountChange={onMatchCountChange}
contentRef={contentRef}
virtualized
showCollapseColumn={language === 'json'}
/>
)
})

View File

@@ -36,7 +36,22 @@ export const IntercomBlock: BlockConfig = {
{ label: 'Search Conversations', id: 'search_conversations' },
{ label: 'Create Ticket', id: 'create_ticket' },
{ label: 'Get Ticket', id: 'get_ticket' },
{ label: 'Update Ticket', id: 'update_ticket' },
{ label: 'Create Message', id: 'create_message' },
{ label: 'List Admins', id: 'list_admins' },
{ label: 'Close Conversation', id: 'close_conversation' },
{ label: 'Open Conversation', id: 'open_conversation' },
{ label: 'Snooze Conversation', id: 'snooze_conversation' },
{ label: 'Assign Conversation', id: 'assign_conversation' },
{ label: 'List Tags', id: 'list_tags' },
{ label: 'Create Tag', id: 'create_tag' },
{ label: 'Tag Contact', id: 'tag_contact' },
{ label: 'Untag Contact', id: 'untag_contact' },
{ label: 'Tag Conversation', id: 'tag_conversation' },
{ label: 'Create Note', id: 'create_note' },
{ label: 'Create Event', id: 'create_event' },
{ label: 'Attach Contact to Company', id: 'attach_contact_to_company' },
{ label: 'Detach Contact from Company', id: 'detach_contact_from_company' },
],
value: () => 'create_contact',
},
@@ -384,7 +399,15 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
required: true,
condition: {
field: 'operation',
value: ['get_conversation', 'reply_conversation'],
value: [
'get_conversation',
'reply_conversation',
'close_conversation',
'open_conversation',
'snooze_conversation',
'assign_conversation',
'tag_conversation',
],
},
},
{
@@ -477,11 +500,20 @@ Return ONLY the message text - no explanations.`,
id: 'admin_id',
title: 'Admin ID',
type: 'short-input',
placeholder: 'ID of the admin sending the message',
placeholder: 'ID of the admin performing the action',
required: true,
condition: {
field: 'operation',
value: ['reply_conversation'],
value: [
'reply_conversation',
'close_conversation',
'open_conversation',
'snooze_conversation',
'assign_conversation',
'tag_conversation',
'create_note',
'update_ticket',
],
},
},
{
@@ -526,7 +558,7 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
required: true,
condition: {
field: 'operation',
value: ['get_ticket'],
value: ['get_ticket', 'update_ticket'],
},
},
{
@@ -799,6 +831,307 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
value: ['list_companies'],
},
},
// Close/Open conversation body
{
id: 'close_body',
title: 'Closing Message',
type: 'long-input',
placeholder: 'Optional message to add when closing',
condition: {
field: 'operation',
value: ['close_conversation'],
},
},
// Snooze conversation
{
id: 'snoozed_until',
title: 'Snooze Until',
type: 'short-input',
placeholder: 'Unix timestamp when conversation should reopen',
required: true,
condition: {
field: 'operation',
value: ['snooze_conversation'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
The timestamp should be a Unix epoch time in seconds (10 digits).
Examples:
- "tomorrow" -> Tomorrow at 09:00:00 as Unix timestamp
- "in 2 hours" -> Current time plus 7200 seconds
- "next Monday" -> Next Monday at 09:00:00 as Unix timestamp
Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
placeholder: 'Describe when to unsnooze (e.g., "tomorrow", "in 2 hours")...',
generationType: 'timestamp',
},
},
// Assign conversation
{
id: 'assignee_id',
title: 'Assignee ID',
type: 'short-input',
placeholder: 'Admin or team ID to assign to (0 to unassign)',
required: true,
condition: {
field: 'operation',
value: ['assign_conversation'],
},
},
{
id: 'assign_body',
title: 'Assignment Message',
type: 'long-input',
placeholder: 'Optional message when assigning',
condition: {
field: 'operation',
value: ['assign_conversation'],
},
},
// Update ticket fields
{
id: 'update_ticket_attributes',
title: 'Ticket Attributes',
type: 'long-input',
placeholder: 'JSON object with ticket attributes to update',
condition: {
field: 'operation',
value: ['update_ticket'],
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for Intercom ticket attributes based on the user's description.
Example: {"_default_title_": "Updated title", "_default_description_": "Updated description"}
Return ONLY the JSON object - no explanations or markdown formatting.`,
placeholder: 'Describe the ticket updates (e.g., "change title to Bug Fixed")...',
generationType: 'json-object',
},
},
{
id: 'ticket_open',
title: 'Ticket Open',
type: 'dropdown',
options: [
{ label: 'Keep Open', id: 'true' },
{ label: 'Close Ticket', id: 'false' },
],
condition: {
field: 'operation',
value: ['update_ticket'],
},
},
{
id: 'ticket_is_shared',
title: 'Ticket Visible to Users',
type: 'dropdown',
options: [
{ label: 'Yes', id: 'true' },
{ label: 'No', id: 'false' },
],
condition: {
field: 'operation',
value: ['update_ticket'],
},
},
{
id: 'ticket_snoozed_until',
title: 'Snooze Ticket Until',
type: 'short-input',
placeholder: 'Unix timestamp when ticket should reopen',
condition: {
field: 'operation',
value: ['update_ticket'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
Examples:
- "tomorrow" -> Tomorrow at 09:00:00 as Unix timestamp
- "next week" -> 7 days from now
Return ONLY the numeric timestamp.`,
placeholder: 'Describe when to unsnooze (e.g., "tomorrow")...',
generationType: 'timestamp',
},
},
{
id: 'ticket_assignee_id',
title: 'Ticket Assignee ID',
type: 'short-input',
placeholder: 'Admin or team ID to assign to (0 to unassign)',
condition: {
field: 'operation',
value: ['update_ticket'],
},
},
// Tag fields
{
id: 'tagId',
title: 'Tag ID',
type: 'short-input',
placeholder: 'ID of the tag',
required: true,
condition: {
field: 'operation',
value: ['tag_contact', 'untag_contact', 'tag_conversation'],
},
},
{
id: 'tag_name',
title: 'Tag Name',
type: 'short-input',
placeholder: 'Name of the tag to create',
required: true,
condition: {
field: 'operation',
value: ['create_tag'],
},
},
{
id: 'tag_id_update',
title: 'Tag ID (for update)',
type: 'short-input',
placeholder: 'ID of existing tag to update (leave empty to create new)',
condition: {
field: 'operation',
value: ['create_tag'],
},
},
// Contact ID for tag/untag/note operations
{
id: 'tag_contact_id',
title: 'Contact ID',
type: 'short-input',
placeholder: 'ID of the contact',
required: true,
condition: {
field: 'operation',
value: [
'tag_contact',
'untag_contact',
'create_note',
'attach_contact_to_company',
'detach_contact_from_company',
],
},
},
// Note fields
{
id: 'note_body',
title: 'Note Content',
type: 'long-input',
placeholder: 'Text content of the note',
required: true,
condition: {
field: 'operation',
value: ['create_note'],
},
wandConfig: {
enabled: true,
prompt: `Generate a note for Intercom based on the user's description.
The note should be clear, professional, and capture the key information.
Return ONLY the note text - no explanations.`,
placeholder: 'Describe the note content (e.g., "customer requested callback")...',
},
},
// Event fields
{
id: 'event_name',
title: 'Event Name',
type: 'short-input',
placeholder: 'Event name (e.g., order-completed)',
required: true,
condition: {
field: 'operation',
value: ['create_event'],
},
},
{
id: 'event_user_id',
title: 'User ID',
type: 'short-input',
placeholder: 'Your identifier for the user',
condition: {
field: 'operation',
value: ['create_event'],
},
},
{
id: 'event_email',
title: 'User Email',
type: 'short-input',
placeholder: 'Email address of the user',
condition: {
field: 'operation',
value: ['create_event'],
},
},
{
id: 'event_contact_id',
title: 'Contact ID',
type: 'short-input',
placeholder: 'Intercom contact ID',
condition: {
field: 'operation',
value: ['create_event'],
},
},
{
id: 'event_metadata',
title: 'Event Metadata',
type: 'long-input',
placeholder: 'JSON object with event metadata (max 10 keys)',
condition: {
field: 'operation',
value: ['create_event'],
},
wandConfig: {
enabled: true,
prompt: `Generate a JSON object for Intercom event metadata based on the user's description.
The object should contain key-value pairs (max 10 keys).
Example: {"order_value": 99.99, "items": 3, "coupon_used": true}
Return ONLY the JSON object - no explanations or markdown formatting.`,
placeholder: 'Describe the event data (e.g., "order value $50, 2 items")...',
generationType: 'json-object',
},
},
{
id: 'event_created_at',
title: 'Event Time',
type: 'short-input',
placeholder: 'Unix timestamp when event occurred',
condition: {
field: 'operation',
value: ['create_event'],
},
wandConfig: {
enabled: true,
prompt: `Generate a Unix timestamp in seconds based on the user's description.
Examples:
- "now" -> Current Unix timestamp
- "5 minutes ago" -> Current time minus 300 seconds
Return ONLY the numeric timestamp.`,
placeholder: 'Describe when the event occurred (e.g., "now")...',
generationType: 'timestamp',
},
},
// Company attachment fields
{
id: 'attach_company_id',
title: 'Company ID',
type: 'short-input',
placeholder: 'ID of the company to attach/detach',
required: true,
condition: {
field: 'operation',
value: ['attach_contact_to_company', 'detach_contact_from_company'],
},
},
],
tools: {
access: [
@@ -818,6 +1151,21 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
'intercom_create_ticket',
'intercom_get_ticket',
'intercom_create_message',
'intercom_update_ticket_v2',
'intercom_list_admins_v2',
'intercom_close_conversation_v2',
'intercom_open_conversation_v2',
'intercom_snooze_conversation_v2',
'intercom_assign_conversation_v2',
'intercom_list_tags_v2',
'intercom_create_tag_v2',
'intercom_tag_contact_v2',
'intercom_untag_contact_v2',
'intercom_tag_conversation_v2',
'intercom_create_note_v2',
'intercom_create_event_v2',
'intercom_attach_contact_to_company_v2',
'intercom_detach_contact_from_company_v2',
],
config: {
tool: (params) => {
@@ -854,6 +1202,36 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
return 'intercom_get_ticket'
case 'create_message':
return 'intercom_create_message'
case 'update_ticket':
return 'intercom_update_ticket_v2'
case 'list_admins':
return 'intercom_list_admins_v2'
case 'close_conversation':
return 'intercom_close_conversation_v2'
case 'open_conversation':
return 'intercom_open_conversation_v2'
case 'snooze_conversation':
return 'intercom_snooze_conversation_v2'
case 'assign_conversation':
return 'intercom_assign_conversation_v2'
case 'list_tags':
return 'intercom_list_tags_v2'
case 'create_tag':
return 'intercom_create_tag_v2'
case 'tag_contact':
return 'intercom_tag_contact_v2'
case 'untag_contact':
return 'intercom_untag_contact_v2'
case 'tag_conversation':
return 'intercom_tag_conversation_v2'
case 'create_note':
return 'intercom_create_note_v2'
case 'create_event':
return 'intercom_create_event_v2'
case 'attach_contact_to_company':
return 'intercom_attach_contact_to_company_v2'
case 'detach_contact_from_company':
return 'intercom_detach_contact_from_company_v2'
default:
throw new Error(`Unknown operation: ${params.operation}`)
}
@@ -870,6 +1248,23 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
message_created_at,
include_translations,
disable_notifications,
close_body,
assign_body,
tag_contact_id,
attach_company_id,
update_ticket_attributes,
ticket_open,
ticket_is_shared,
ticket_snoozed_until,
ticket_assignee_id,
tag_name,
tag_id_update,
note_body,
event_user_id,
event_email,
event_contact_id,
event_metadata,
event_created_at,
...rest
} = params
const cleanParams: Record<string, any> = {}
@@ -897,7 +1292,7 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
cleanParams.created_at = Number(reply_created_at)
}
// Map ticket fields
// Map ticket fields for create_ticket
if (operation === 'create_ticket') {
if (ticket_company_id) cleanParams.company_id = ticket_company_id
if (ticket_created_at) cleanParams.created_at = Number(ticket_created_at)
@@ -920,6 +1315,71 @@ Return ONLY the numeric timestamp - no explanations, no quotes, no extra text.`,
cleanParams.include_translations = include_translations === 'true'
}
// Map close_body to body for close_conversation
if (operation === 'close_conversation' && close_body) {
cleanParams.body = close_body
}
// Map assign_body to body for assign_conversation
if (operation === 'assign_conversation' && assign_body) {
cleanParams.body = assign_body
}
// Map tag_contact_id to contactId for tag/note/company attachment operations
if (
[
'tag_contact',
'untag_contact',
'create_note',
'attach_contact_to_company',
'detach_contact_from_company',
].includes(operation) &&
tag_contact_id
) {
cleanParams.contactId = tag_contact_id
}
// Map attach_company_id to companyId for company attachment operations
if (
['attach_contact_to_company', 'detach_contact_from_company'].includes(operation) &&
attach_company_id
) {
cleanParams.companyId = attach_company_id
}
// Map update_ticket fields
if (operation === 'update_ticket') {
if (update_ticket_attributes) cleanParams.ticket_attributes = update_ticket_attributes
if (ticket_open !== undefined && ticket_open !== '') {
cleanParams.open = ticket_open === 'true'
}
if (ticket_is_shared !== undefined && ticket_is_shared !== '') {
cleanParams.is_shared = ticket_is_shared === 'true'
}
if (ticket_snoozed_until) cleanParams.snoozed_until = Number(ticket_snoozed_until)
if (ticket_assignee_id) cleanParams.assignee_id = ticket_assignee_id
}
// Map tag fields for create_tag
if (operation === 'create_tag') {
if (tag_name) cleanParams.name = tag_name
if (tag_id_update) cleanParams.id = tag_id_update
}
// Map note_body to body for create_note
if (operation === 'create_note' && note_body) {
cleanParams.body = note_body
}
// Map event fields for create_event
if (operation === 'create_event') {
if (event_user_id) cleanParams.user_id = event_user_id
if (event_email) cleanParams.email = event_email
if (event_contact_id) cleanParams.id = event_contact_id
if (event_metadata) cleanParams.metadata = event_metadata
if (event_created_at) cleanParams.created_at = Number(event_created_at)
}
Object.entries(rest).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
cleanParams[key] = value
@@ -963,7 +1423,22 @@ export const IntercomV2Block: BlockConfig = {
'intercom_search_conversations_v2',
'intercom_create_ticket_v2',
'intercom_get_ticket_v2',
'intercom_update_ticket_v2',
'intercom_create_message_v2',
'intercom_list_admins_v2',
'intercom_close_conversation_v2',
'intercom_open_conversation_v2',
'intercom_snooze_conversation_v2',
'intercom_assign_conversation_v2',
'intercom_list_tags_v2',
'intercom_create_tag_v2',
'intercom_tag_contact_v2',
'intercom_untag_contact_v2',
'intercom_tag_conversation_v2',
'intercom_create_note_v2',
'intercom_create_event_v2',
'intercom_attach_contact_to_company_v2',
'intercom_detach_contact_from_company_v2',
],
config: {
tool: createVersionedToolSelector({
@@ -999,8 +1474,38 @@ export const IntercomV2Block: BlockConfig = {
return 'intercom_create_ticket'
case 'get_ticket':
return 'intercom_get_ticket'
case 'update_ticket':
return 'intercom_update_ticket'
case 'create_message':
return 'intercom_create_message'
case 'list_admins':
return 'intercom_list_admins'
case 'close_conversation':
return 'intercom_close_conversation'
case 'open_conversation':
return 'intercom_open_conversation'
case 'snooze_conversation':
return 'intercom_snooze_conversation'
case 'assign_conversation':
return 'intercom_assign_conversation'
case 'list_tags':
return 'intercom_list_tags'
case 'create_tag':
return 'intercom_create_tag'
case 'tag_contact':
return 'intercom_tag_contact'
case 'untag_contact':
return 'intercom_untag_contact'
case 'tag_conversation':
return 'intercom_tag_conversation'
case 'create_note':
return 'intercom_create_note'
case 'create_event':
return 'intercom_create_event'
case 'attach_contact_to_company':
return 'intercom_attach_contact_to_company'
case 'detach_contact_from_company':
return 'intercom_detach_contact_from_company'
default:
return 'intercom_create_contact'
}
@@ -1008,7 +1513,158 @@ export const IntercomV2Block: BlockConfig = {
suffix: '_v2',
fallbackToolId: 'intercom_create_contact_v2',
}),
params: IntercomBlock.tools!.config!.params,
params: (params) => {
const {
operation,
message_type_msg,
company_name,
contact_company_id,
reply_created_at,
ticket_company_id,
ticket_created_at,
message_created_at,
include_translations,
disable_notifications,
close_body,
assign_body,
tag_contact_id,
attach_company_id,
update_ticket_attributes,
ticket_open,
ticket_is_shared,
ticket_snoozed_until,
ticket_assignee_id,
tag_name,
tag_id_update,
note_body,
event_user_id,
event_email,
event_contact_id,
event_metadata,
event_created_at,
...rest
} = params
const cleanParams: Record<string, any> = {}
// Special mapping for message_type in create_message
if (operation === 'create_message' && message_type_msg) {
cleanParams.message_type = message_type_msg
}
// Special mapping for company name
if (operation === 'create_company' && company_name) {
cleanParams.name = company_name
}
// Map contact_company_id to company_id for contact operations
if (
(operation === 'create_contact' || operation === 'update_contact') &&
contact_company_id
) {
cleanParams.company_id = contact_company_id
}
// Map reply_created_at to created_at for reply_conversation
if (operation === 'reply_conversation' && reply_created_at) {
cleanParams.created_at = Number(reply_created_at)
}
// Map ticket fields for create_ticket
if (operation === 'create_ticket') {
if (ticket_company_id) cleanParams.company_id = ticket_company_id
if (ticket_created_at) cleanParams.created_at = Number(ticket_created_at)
if (disable_notifications !== undefined && disable_notifications !== '') {
cleanParams.disable_notifications = disable_notifications === 'true'
}
}
// Map message_created_at to created_at for create_message
if (operation === 'create_message' && message_created_at) {
cleanParams.created_at = Number(message_created_at)
}
// Convert include_translations string to boolean for get_conversation
if (
operation === 'get_conversation' &&
include_translations !== undefined &&
include_translations !== ''
) {
cleanParams.include_translations = include_translations === 'true'
}
// Map close_body to body for close_conversation
if (operation === 'close_conversation' && close_body) {
cleanParams.body = close_body
}
// Map assign_body to body for assign_conversation
if (operation === 'assign_conversation' && assign_body) {
cleanParams.body = assign_body
}
// Map tag_contact_id to contactId for tag/note/company attachment operations
if (
[
'tag_contact',
'untag_contact',
'create_note',
'attach_contact_to_company',
'detach_contact_from_company',
].includes(operation) &&
tag_contact_id
) {
cleanParams.contactId = tag_contact_id
}
// Map attach_company_id to companyId for company attachment operations
if (
['attach_contact_to_company', 'detach_contact_from_company'].includes(operation) &&
attach_company_id
) {
cleanParams.companyId = attach_company_id
}
// Map update_ticket fields
if (operation === 'update_ticket') {
if (update_ticket_attributes) cleanParams.ticket_attributes = update_ticket_attributes
if (ticket_open !== undefined && ticket_open !== '') {
cleanParams.open = ticket_open === 'true'
}
if (ticket_is_shared !== undefined && ticket_is_shared !== '') {
cleanParams.is_shared = ticket_is_shared === 'true'
}
if (ticket_snoozed_until) cleanParams.snoozed_until = Number(ticket_snoozed_until)
if (ticket_assignee_id) cleanParams.assignee_id = ticket_assignee_id
}
// Map tag fields for create_tag
if (operation === 'create_tag') {
if (tag_name) cleanParams.name = tag_name
if (tag_id_update) cleanParams.id = tag_id_update
}
// Map note_body to body for create_note
if (operation === 'create_note' && note_body) {
cleanParams.body = note_body
}
// Map event fields for create_event
if (operation === 'create_event') {
if (event_user_id) cleanParams.user_id = event_user_id
if (event_email) cleanParams.email = event_email
if (event_contact_id) cleanParams.id = event_contact_id
if (event_metadata) cleanParams.metadata = event_metadata
if (event_created_at) cleanParams.created_at = Number(event_created_at)
}
Object.entries(rest).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
cleanParams[key] = value
}
})
return cleanParams
},
},
},
outputs: {
@@ -1031,10 +1687,23 @@ export const IntercomV2Block: BlockConfig = {
type: 'array',
description: 'Array of conversations (for list/search operations)',
},
state: { type: 'string', description: 'Conversation state (for close/open/snooze operations)' },
ticket: { type: 'json', description: 'Ticket object with id, ticket_id, ticket_state' },
ticketId: { type: 'string', description: 'ID of the ticket (for create operations)' },
ticketId: { type: 'string', description: 'ID of the ticket (for create/update operations)' },
ticket_state: { type: 'string', description: 'Ticket state (for update_ticket operation)' },
message: { type: 'json', description: 'Message object with id, type' },
messageId: { type: 'string', description: 'ID of the message (for create operations)' },
admins: { type: 'array', description: 'Array of admin objects (for list_admins operation)' },
tags: { type: 'array', description: 'Array of tag objects (for list_tags operation)' },
tag: { type: 'json', description: 'Tag object with id and name (for tag operations)' },
tagId: { type: 'string', description: 'ID of the tag (for create_tag operation)' },
note: { type: 'json', description: 'Note object with id and body (for create_note operation)' },
noteId: { type: 'string', description: 'ID of the note (for create_note operation)' },
event_name: {
type: 'string',
description: 'Name of the tracked event (for create_event operation)',
},
name: { type: 'string', description: 'Name of the resource (for various operations)' },
total_count: { type: 'number', description: 'Total count (for list/search operations)' },
pages: { type: 'json', description: 'Pagination info with page, per_page, total_pages' },
id: { type: 'string', description: 'ID of the deleted item (for delete operations)' },

View File

@@ -1,16 +1,18 @@
import { KalshiIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import { createVersionedToolSelector } from '@/blocks/utils'
export const KalshiBlock: BlockConfig = {
type: 'kalshi',
name: 'Kalshi',
name: 'Kalshi (Legacy)',
description: 'Access prediction markets and trade on Kalshi',
longDescription:
'Integrate Kalshi prediction markets into the workflow. Can get markets, market, events, event, balance, positions, orders, orderbook, trades, candlesticks, fills, series, exchange status, and place/cancel/amend trades.',
docsLink: 'https://docs.sim.ai/tools/kalshi',
authMode: AuthMode.ApiKey,
category: 'tools',
hideFromToolbar: true,
bgColor: '#09C285',
icon: KalshiIcon,
subBlocks: [
@@ -349,8 +351,14 @@ Return ONLY the numeric timestamp (seconds since Unix epoch) - no explanations,
id: 'count',
title: 'Contracts',
type: 'short-input',
placeholder: 'Number of contracts',
required: true,
placeholder: 'Number of contracts (or use countFp)',
condition: { field: 'operation', value: ['create_order'] },
},
{
id: 'countFp',
title: 'Contracts (Fixed-Point)',
type: 'short-input',
placeholder: 'Fixed-point count (e.g., "10.50")',
condition: { field: 'operation', value: ['create_order'] },
},
{
@@ -674,3 +682,143 @@ Return ONLY the numeric timestamp (seconds since Unix epoch) - no explanations,
paging: { type: 'json', description: 'Pagination cursor for fetching more results' },
},
}
export const KalshiV2Block: BlockConfig = {
...KalshiBlock,
type: 'kalshi_v2',
name: 'Kalshi',
description: 'Access prediction markets and trade on Kalshi',
longDescription:
'Integrate Kalshi prediction markets into the workflow. Can get markets, market, events, event, balance, positions, orders, orderbook, trades, candlesticks, fills, series, exchange status, and place/cancel/amend trades.',
hideFromToolbar: false,
tools: {
...KalshiBlock.tools,
access: [
'kalshi_get_markets_v2',
'kalshi_get_market_v2',
'kalshi_get_events_v2',
'kalshi_get_event_v2',
'kalshi_get_balance_v2',
'kalshi_get_positions_v2',
'kalshi_get_orders_v2',
'kalshi_get_order_v2',
'kalshi_get_orderbook_v2',
'kalshi_get_trades_v2',
'kalshi_get_candlesticks_v2',
'kalshi_get_fills_v2',
'kalshi_get_series_by_ticker_v2',
'kalshi_get_exchange_status_v2',
'kalshi_create_order_v2',
'kalshi_cancel_order_v2',
'kalshi_amend_order_v2',
],
config: {
...KalshiBlock.tools!.config,
tool: createVersionedToolSelector({
baseToolSelector: (params) => {
switch (params.operation) {
case 'get_markets':
return 'kalshi_get_markets'
case 'get_market':
return 'kalshi_get_market'
case 'get_events':
return 'kalshi_get_events'
case 'get_event':
return 'kalshi_get_event'
case 'get_balance':
return 'kalshi_get_balance'
case 'get_positions':
return 'kalshi_get_positions'
case 'get_orders':
return 'kalshi_get_orders'
case 'get_order':
return 'kalshi_get_order'
case 'get_orderbook':
return 'kalshi_get_orderbook'
case 'get_trades':
return 'kalshi_get_trades'
case 'get_candlesticks':
return 'kalshi_get_candlesticks'
case 'get_fills':
return 'kalshi_get_fills'
case 'get_series_by_ticker':
return 'kalshi_get_series_by_ticker'
case 'get_exchange_status':
return 'kalshi_get_exchange_status'
case 'create_order':
return 'kalshi_create_order'
case 'cancel_order':
return 'kalshi_cancel_order'
case 'amend_order':
return 'kalshi_amend_order'
default:
return 'kalshi_get_markets'
}
},
suffix: '_v2',
fallbackToolId: 'kalshi_get_markets_v2',
}),
},
},
outputs: {
// List operations (V2 uses snake_case and flat cursor)
markets: { type: 'json', description: 'Array of market objects (get_markets)' },
events: { type: 'json', description: 'Array of event objects (get_events)' },
orders: { type: 'json', description: 'Array of order objects (get_orders)' },
market_positions: {
type: 'json',
description: 'Array of market position objects (get_positions)',
},
event_positions: {
type: 'json',
description: 'Array of event position objects (get_positions)',
},
fills: { type: 'json', description: 'Array of fill objects (get_fills)' },
trades: { type: 'json', description: 'Array of trade objects (get_trades)' },
candlesticks: {
type: 'json',
description: 'Array of candlestick data with yes_bid/yes_ask/price nested objects',
},
milestones: {
type: 'json',
description: 'Array of milestone objects (get_events with milestones)',
},
// Single item operations
market: { type: 'json', description: 'Single market object (get_market)' },
event: { type: 'json', description: 'Single event object (get_event)' },
order: {
type: 'json',
description: 'Order object with _dollars and _fp fields (get_order, create_order, etc.)',
},
series: { type: 'json', description: 'Series object (get_series_by_ticker)' },
// Account operations
balance: { type: 'number', description: 'Account balance in cents (get_balance)' },
portfolio_value: { type: 'number', description: 'Portfolio value in cents (get_balance)' },
updated_ts: { type: 'number', description: 'Unix timestamp of last update (get_balance)' },
// Orderbook (V2 uses tuple arrays)
orderbook: {
type: 'json',
description: 'Orderbook with yes/no/yes_dollars/no_dollars tuple arrays',
},
orderbook_fp: {
type: 'json',
description: 'Fixed-point orderbook with yes_dollars/no_dollars tuple arrays',
},
// Exchange status
exchange_status: {
type: 'string',
description: 'Exchange status string (get_exchange_status)',
},
trading_active: { type: 'boolean', description: 'Trading active flag (get_exchange_status)' },
// Cancel order specific
reduced_by: { type: 'number', description: 'Number of contracts reduced (cancel_order)' },
reduced_by_fp: {
type: 'string',
description: 'Contracts reduced in fixed-point (cancel_order)',
},
// Candlesticks ticker
ticker: { type: 'string', description: 'Market ticker (get_candlesticks)' },
// Pagination (flat cursor instead of nested paging object)
cursor: { type: 'string', description: 'Pagination cursor for fetching more results' },
},
}

View File

@@ -6,7 +6,7 @@ export const PolymarketBlock: BlockConfig = {
name: 'Polymarket',
description: 'Access prediction markets data from Polymarket',
longDescription:
'Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, and search.',
'Integrate Polymarket prediction markets into the workflow. Can get markets, market, events, event, tags, series, orderbook, price, midpoint, price history, last trade price, spread, tick size, positions, trades, activity, leaderboard, holders, and search.',
docsLink: 'https://docs.sim.ai/tools/polymarket',
category: 'tools',
bgColor: '#4C82FB',
@@ -34,6 +34,9 @@ export const PolymarketBlock: BlockConfig = {
{ label: 'Get Tick Size', id: 'get_tick_size' },
{ label: 'Get Positions', id: 'get_positions' },
{ label: 'Get Trades', id: 'get_trades' },
{ label: 'Get Activity', id: 'get_activity' },
{ label: 'Get Leaderboard', id: 'get_leaderboard' },
{ label: 'Get Market Holders', id: 'get_holders' },
],
value: () => 'get_markets',
},
@@ -101,14 +104,281 @@ export const PolymarketBlock: BlockConfig = {
placeholder: 'Wallet address (optional filter)',
condition: { field: 'operation', value: ['get_trades'] },
},
// Market filter for positions and trades
// Market/Event filter for positions and trades
{
id: 'market',
title: 'Market ID',
title: 'Condition ID',
type: 'short-input',
placeholder: 'Market ID (optional filter)',
placeholder: 'Condition ID filter (comma-separated)',
condition: { field: 'operation', value: ['get_positions', 'get_trades'] },
},
{
id: 'positionEventId',
title: 'Event ID',
type: 'short-input',
placeholder: 'Event ID filter (alternative to Condition ID)',
condition: { field: 'operation', value: ['get_positions', 'get_trades'] },
},
// Positions-specific filters
{
id: 'sizeThreshold',
title: 'Size Threshold',
type: 'short-input',
placeholder: 'Minimum position size (default: 1)',
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'redeemable',
title: 'Redeemable',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Redeemable Only', id: 'true' },
{ label: 'Non-Redeemable Only', id: 'false' },
],
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'mergeable',
title: 'Mergeable',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Mergeable Only', id: 'true' },
{ label: 'Non-Mergeable Only', id: 'false' },
],
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'positionSortBy',
title: 'Sort By',
type: 'dropdown',
options: [
{ label: 'Default', id: '' },
{ label: 'Tokens', id: 'TOKENS' },
{ label: 'Current Value', id: 'CURRENT' },
{ label: 'Initial Value', id: 'INITIAL' },
{ label: 'Cash P&L', id: 'CASHPNL' },
{ label: 'Percent P&L', id: 'PERCENTPNL' },
{ label: 'Title', id: 'TITLE' },
{ label: 'Price', id: 'PRICE' },
{ label: 'Avg Price', id: 'AVGPRICE' },
],
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'positionSortDirection',
title: 'Sort Direction',
type: 'dropdown',
options: [
{ label: 'Descending', id: 'DESC' },
{ label: 'Ascending', id: 'ASC' },
],
condition: { field: 'operation', value: ['get_positions'] },
},
{
id: 'positionTitle',
title: 'Title Filter',
type: 'short-input',
placeholder: 'Search by title',
condition: { field: 'operation', value: ['get_positions'] },
},
// Trades-specific filters
{
id: 'tradeSide',
title: 'Trade Side',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Buy', id: 'BUY' },
{ label: 'Sell', id: 'SELL' },
],
condition: { field: 'operation', value: ['get_trades'] },
},
{
id: 'takerOnly',
title: 'Taker Only',
type: 'dropdown',
options: [
{ label: 'Yes (default)', id: 'true' },
{ label: 'No', id: 'false' },
],
condition: { field: 'operation', value: ['get_trades'] },
},
{
id: 'filterType',
title: 'Filter Type',
type: 'dropdown',
options: [
{ label: 'None', id: '' },
{ label: 'Cash', id: 'CASH' },
{ label: 'Tokens', id: 'TOKENS' },
],
condition: { field: 'operation', value: ['get_trades'] },
},
{
id: 'filterAmount',
title: 'Filter Amount',
type: 'short-input',
placeholder: 'Minimum amount threshold',
condition: { field: 'operation', value: ['get_trades'] },
},
// Activity-specific fields
{
id: 'activityUser',
title: 'User Wallet Address',
type: 'short-input',
placeholder: 'Wallet address (0x-prefixed)',
required: true,
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityType',
title: 'Activity Type',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Trade', id: 'TRADE' },
{ label: 'Split', id: 'SPLIT' },
{ label: 'Merge', id: 'MERGE' },
{ label: 'Redeem', id: 'REDEEM' },
{ label: 'Reward', id: 'REWARD' },
{ label: 'Conversion', id: 'CONVERSION' },
{ label: 'Maker Rebate', id: 'MAKER_REBATE' },
],
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityMarket',
title: 'Condition ID',
type: 'short-input',
placeholder: 'Condition ID filter (comma-separated)',
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityEventId',
title: 'Event ID',
type: 'short-input',
placeholder: 'Event ID filter (comma-separated)',
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activitySide',
title: 'Trade Side',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Buy', id: 'BUY' },
{ label: 'Sell', id: 'SELL' },
],
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activitySortBy',
title: 'Sort By',
type: 'dropdown',
options: [
{ label: 'Timestamp', id: 'TIMESTAMP' },
{ label: 'Tokens', id: 'TOKENS' },
{ label: 'Cash', id: 'CASH' },
],
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activitySortDirection',
title: 'Sort Direction',
type: 'dropdown',
options: [
{ label: 'Descending', id: 'DESC' },
{ label: 'Ascending', id: 'ASC' },
],
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityStart',
title: 'Start Timestamp',
type: 'short-input',
placeholder: 'Unix timestamp (seconds)',
condition: { field: 'operation', value: ['get_activity'] },
},
{
id: 'activityEnd',
title: 'End Timestamp',
type: 'short-input',
placeholder: 'Unix timestamp (seconds)',
condition: { field: 'operation', value: ['get_activity'] },
},
// Leaderboard-specific fields
{
id: 'leaderboardCategory',
title: 'Category',
type: 'dropdown',
options: [
{ label: 'Overall', id: 'OVERALL' },
{ label: 'Politics', id: 'POLITICS' },
{ label: 'Sports', id: 'SPORTS' },
{ label: 'Crypto', id: 'CRYPTO' },
{ label: 'Culture', id: 'CULTURE' },
{ label: 'Mentions', id: 'MENTIONS' },
{ label: 'Weather', id: 'WEATHER' },
{ label: 'Economics', id: 'ECONOMICS' },
{ label: 'Tech', id: 'TECH' },
{ label: 'Finance', id: 'FINANCE' },
],
condition: { field: 'operation', value: ['get_leaderboard'] },
},
{
id: 'leaderboardTimePeriod',
title: 'Time Period',
type: 'dropdown',
options: [
{ label: 'Day', id: 'DAY' },
{ label: 'Week', id: 'WEEK' },
{ label: 'Month', id: 'MONTH' },
{ label: 'All Time', id: 'ALL' },
],
condition: { field: 'operation', value: ['get_leaderboard'] },
},
{
id: 'leaderboardOrderBy',
title: 'Order By',
type: 'dropdown',
options: [
{ label: 'Profit/Loss', id: 'PNL' },
{ label: 'Volume', id: 'VOL' },
],
condition: { field: 'operation', value: ['get_leaderboard'] },
},
{
id: 'leaderboardUser',
title: 'User Address',
type: 'short-input',
placeholder: 'Filter by specific user wallet',
condition: { field: 'operation', value: ['get_leaderboard'] },
},
{
id: 'leaderboardUserName',
title: 'Username',
type: 'short-input',
placeholder: 'Filter by username',
condition: { field: 'operation', value: ['get_leaderboard'] },
},
// Market Holders-specific fields
{
id: 'holdersMarket',
title: 'Condition ID',
type: 'short-input',
placeholder: 'Condition ID (comma-separated)',
required: true,
condition: { field: 'operation', value: ['get_holders'] },
},
{
id: 'holdersMinBalance',
title: 'Min Balance',
type: 'short-input',
placeholder: 'Minimum balance threshold (default: 1)',
condition: { field: 'operation', value: ['get_holders'] },
},
// Token ID for CLOB operations
{
id: 'tokenId',
@@ -205,11 +475,11 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
// Filters for list operations
{
id: 'closed',
title: 'Status',
title: 'Closed Status',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Active Only', id: 'false' },
{ label: 'Open Only', id: 'false' },
{ label: 'Closed Only', id: 'true' },
],
condition: { field: 'operation', value: ['get_markets', 'get_events'] },
@@ -269,7 +539,18 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
placeholder: 'Number of results (max 50)',
condition: {
field: 'operation',
value: ['get_markets', 'get_events', 'get_tags', 'search', 'get_series', 'get_trades'],
value: [
'get_markets',
'get_events',
'get_tags',
'search',
'get_series',
'get_trades',
'get_positions',
'get_activity',
'get_leaderboard',
'get_holders',
],
},
},
{
@@ -279,9 +560,25 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
placeholder: 'Pagination offset',
condition: {
field: 'operation',
value: ['get_markets', 'get_events', 'get_tags', 'search', 'get_series', 'get_trades'],
value: [
'get_markets',
'get_events',
'get_tags',
'get_series',
'get_trades',
'get_positions',
'get_activity',
'get_leaderboard',
],
},
},
{
id: 'page',
title: 'Page',
type: 'short-input',
placeholder: 'Page number (1-indexed)',
condition: { field: 'operation', value: ['search'] },
},
],
tools: {
access: [
@@ -302,6 +599,9 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
'polymarket_get_tick_size',
'polymarket_get_positions',
'polymarket_get_trades',
'polymarket_get_activity',
'polymarket_get_leaderboard',
'polymarket_get_holders',
],
config: {
tool: (params) => {
@@ -340,12 +640,49 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
return 'polymarket_get_positions'
case 'get_trades':
return 'polymarket_get_trades'
case 'get_activity':
return 'polymarket_get_activity'
case 'get_leaderboard':
return 'polymarket_get_leaderboard'
case 'get_holders':
return 'polymarket_get_holders'
default:
return 'polymarket_get_markets'
}
},
params: (params) => {
const { operation, marketSlug, eventSlug, orderEvents, order, ...rest } = params
const {
operation,
marketSlug,
eventSlug,
orderEvents,
order,
positionEventId,
tradeSide,
positionSortBy,
positionSortDirection,
positionTitle,
// Activity params
activityUser,
activityType,
activityMarket,
activityEventId,
activitySide,
activitySortBy,
activitySortDirection,
activityStart,
activityEnd,
// Leaderboard params
leaderboardCategory,
leaderboardTimePeriod,
leaderboardOrderBy,
leaderboardUser,
leaderboardUserName,
// Holders params
holdersMarket,
holdersMinBalance,
...rest
} = params
const cleanParams: Record<string, any> = {}
// Map marketSlug to slug for get_market
@@ -365,6 +702,51 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
cleanParams.order = orderEvents
}
// Map positionEventId to eventId for positions and trades
if ((operation === 'get_positions' || operation === 'get_trades') && positionEventId) {
cleanParams.eventId = positionEventId
}
// Map tradeSide to side for trades
if (operation === 'get_trades' && tradeSide) {
cleanParams.side = tradeSide
}
// Map position-specific fields
if (operation === 'get_positions') {
if (positionSortBy) cleanParams.sortBy = positionSortBy
if (positionSortDirection) cleanParams.sortDirection = positionSortDirection
if (positionTitle) cleanParams.title = positionTitle
}
// Map activity-specific fields
if (operation === 'get_activity') {
if (activityUser) cleanParams.user = activityUser
if (activityType) cleanParams.type = activityType
if (activityMarket) cleanParams.market = activityMarket
if (activityEventId) cleanParams.eventId = activityEventId
if (activitySide) cleanParams.side = activitySide
if (activitySortBy) cleanParams.sortBy = activitySortBy
if (activitySortDirection) cleanParams.sortDirection = activitySortDirection
if (activityStart) cleanParams.start = Number(activityStart)
if (activityEnd) cleanParams.end = Number(activityEnd)
}
// Map leaderboard-specific fields
if (operation === 'get_leaderboard') {
if (leaderboardCategory) cleanParams.category = leaderboardCategory
if (leaderboardTimePeriod) cleanParams.timePeriod = leaderboardTimePeriod
if (leaderboardOrderBy) cleanParams.orderBy = leaderboardOrderBy
if (leaderboardUser) cleanParams.user = leaderboardUser
if (leaderboardUserName) cleanParams.userName = leaderboardUserName
}
// Map holders-specific fields
if (operation === 'get_holders') {
if (holdersMarket) cleanParams.market = holdersMarket
if (holdersMinBalance) cleanParams.minBalance = holdersMinBalance
}
// Convert numeric fields from string to number for get_price_history
if (operation === 'get_price_history') {
if (rest.fidelity) cleanParams.fidelity = Number(rest.fidelity)
@@ -394,13 +776,55 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
seriesId: { type: 'string', description: 'Series ID' },
query: { type: 'string', description: 'Search query' },
user: { type: 'string', description: 'User wallet address' },
market: { type: 'string', description: 'Market ID filter' },
market: { type: 'string', description: 'Condition ID filter' },
positionEventId: { type: 'string', description: 'Event ID filter for positions/trades' },
tokenId: { type: 'string', description: 'CLOB Token ID' },
side: { type: 'string', description: 'Order side (buy/sell)' },
interval: { type: 'string', description: 'Price history interval' },
fidelity: { type: 'number', description: 'Data resolution in minutes' },
startTs: { type: 'number', description: 'Start timestamp (Unix)' },
endTs: { type: 'number', description: 'End timestamp (Unix)' },
// Positions-specific inputs
sizeThreshold: { type: 'string', description: 'Minimum position size threshold' },
redeemable: { type: 'string', description: 'Filter by redeemable status' },
mergeable: { type: 'string', description: 'Filter by mergeable status' },
positionSortBy: { type: 'string', description: 'Sort positions by field' },
positionSortDirection: { type: 'string', description: 'Sort direction (ASC/DESC)' },
positionTitle: { type: 'string', description: 'Filter positions by title' },
// Trades-specific inputs
tradeSide: { type: 'string', description: 'Filter trades by side (BUY/SELL)' },
takerOnly: { type: 'string', description: 'Filter to taker trades only' },
filterType: { type: 'string', description: 'Trade filter type (CASH/TOKENS)' },
filterAmount: { type: 'string', description: 'Minimum trade amount threshold' },
// List operation filters
closed: { type: 'string', description: 'Filter by closed status' },
order: { type: 'string', description: 'Sort field for markets' },
orderEvents: { type: 'string', description: 'Sort field for events' },
ascending: { type: 'string', description: 'Sort order (true/false)' },
tagId: { type: 'string', description: 'Filter by tag ID' },
// Pagination
limit: { type: 'string', description: 'Number of results per page' },
offset: { type: 'string', description: 'Pagination offset' },
page: { type: 'string', description: 'Page number for search' },
// Activity-specific inputs
activityUser: { type: 'string', description: 'User wallet address for activity' },
activityType: { type: 'string', description: 'Activity type filter' },
activityMarket: { type: 'string', description: 'Condition ID filter for activity' },
activityEventId: { type: 'string', description: 'Event ID filter for activity' },
activitySide: { type: 'string', description: 'Trade side filter for activity' },
activitySortBy: { type: 'string', description: 'Sort field for activity' },
activitySortDirection: { type: 'string', description: 'Sort direction for activity' },
activityStart: { type: 'string', description: 'Start timestamp for activity' },
activityEnd: { type: 'string', description: 'End timestamp for activity' },
// Leaderboard-specific inputs
leaderboardCategory: { type: 'string', description: 'Leaderboard category' },
leaderboardTimePeriod: { type: 'string', description: 'Leaderboard time period' },
leaderboardOrderBy: { type: 'string', description: 'Leaderboard order by field' },
leaderboardUser: { type: 'string', description: 'Filter leaderboard by user' },
leaderboardUserName: { type: 'string', description: 'Filter leaderboard by username' },
// Holders-specific inputs
holdersMarket: { type: 'string', description: 'Condition ID for holders lookup' },
holdersMinBalance: { type: 'string', description: 'Minimum balance threshold' },
},
outputs: {
// List operations
@@ -422,11 +846,19 @@ Return ONLY the Unix timestamp as a number - no explanations, no quotes, no extr
description: 'Search results with markets, events, profiles (search)',
},
// CLOB operations
orderbook: { type: 'json', description: 'Order book with bids and asks (get_orderbook)' },
orderbook: {
type: 'json',
description: 'Order book with bids and asks (get_orderbook)',
},
price: { type: 'string', description: 'Market price (get_price, get_last_trade_price)' },
side: { type: 'string', description: 'Last trade side - BUY or SELL (get_last_trade_price)' },
midpoint: { type: 'string', description: 'Midpoint price (get_midpoint)' },
history: { type: 'json', description: 'Price history entries (get_price_history)' },
spread: { type: 'json', description: 'Bid-ask spread (get_spread)' },
spread: { type: 'json', description: 'Spread value object (get_spread)' },
tickSize: { type: 'string', description: 'Minimum tick size (get_tick_size)' },
// Data API operations
activity: { type: 'json', description: 'Array of user activity entries (get_activity)' },
leaderboard: { type: 'json', description: 'Array of leaderboard entries (get_leaderboard)' },
holders: { type: 'json', description: 'Array of market holder groups (get_holders)' },
},
}

View File

@@ -58,7 +58,7 @@ import { IntercomBlock, IntercomV2Block } from '@/blocks/blocks/intercom'
import { JinaBlock } from '@/blocks/blocks/jina'
import { JiraBlock } from '@/blocks/blocks/jira'
import { JiraServiceManagementBlock } from '@/blocks/blocks/jira_service_management'
import { KalshiBlock } from '@/blocks/blocks/kalshi'
import { KalshiBlock, KalshiV2Block } from '@/blocks/blocks/kalshi'
import { KnowledgeBlock } from '@/blocks/blocks/knowledge'
import { LangsmithBlock } from '@/blocks/blocks/langsmith'
import { LemlistBlock } from '@/blocks/blocks/lemlist'
@@ -222,6 +222,7 @@ export const registry: Record<string, BlockConfig> = {
jira: JiraBlock,
jira_service_management: JiraServiceManagementBlock,
kalshi: KalshiBlock,
kalshi_v2: KalshiV2Block,
knowledge: KnowledgeBlock,
langsmith: LangsmithBlock,
lemlist: LemlistBlock,

View File

@@ -10,7 +10,6 @@ import {
useRef,
useState,
} from 'react'
import { ChevronRight } from 'lucide-react'
import { highlight, languages } from 'prismjs'
import { List, type RowComponentProps, useDynamicRowHeight, useListRef } from 'react-window'
import 'prismjs/components/prism-javascript'
@@ -37,11 +36,6 @@ export const CODE_LINE_HEIGHT_PX = 21
*/
const GUTTER_WIDTHS = [20, 20, 30, 38, 46, 54] as const
/**
* Width of the collapse column in pixels.
*/
const COLLAPSE_COLUMN_WIDTH = 12
/**
* Calculates the dynamic gutter width based on the number of lines.
* @param lineCount - The total number of lines in the code
@@ -52,261 +46,6 @@ export function calculateGutterWidth(lineCount: number): number {
return GUTTER_WIDTHS[Math.min(digits - 1, GUTTER_WIDTHS.length - 1)]
}
/**
* Information about a collapsible region in code.
*/
interface CollapsibleRegion {
/** Line index where the region starts (0-based) */
startLine: number
/** Line index where the region ends (0-based, inclusive) */
endLine: number
/** Type of collapsible region */
type: 'block' | 'string'
}
/**
* Minimum string length to be considered collapsible.
*/
const MIN_COLLAPSIBLE_STRING_LENGTH = 80
/**
* Regex to match a JSON string value (key: "value" pattern).
* Pre-compiled for performance.
*/
const STRING_VALUE_REGEX = /:\s*"([^"\\]|\\.)*"[,]?\s*$/
/**
* Finds collapsible regions in JSON code by matching braces and detecting long strings.
* A region is collapsible if it spans multiple lines OR contains a long string value.
*
* @param lines - Array of code lines
* @returns Map of start line index to CollapsibleRegion
*/
function findCollapsibleRegions(lines: string[]): Map<number, CollapsibleRegion> {
const regions = new Map<number, CollapsibleRegion>()
const stack: { char: '{' | '['; line: number }[] = []
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const stringMatch = line.match(STRING_VALUE_REGEX)
if (stringMatch) {
const colonIdx = line.indexOf('":')
if (colonIdx !== -1) {
const valueStart = line.indexOf('"', colonIdx + 1)
const valueEnd = line.lastIndexOf('"')
if (valueStart !== -1 && valueEnd > valueStart) {
const stringValue = line.slice(valueStart + 1, valueEnd)
// Check if string is long enough or contains escaped newlines
if (stringValue.length >= MIN_COLLAPSIBLE_STRING_LENGTH || stringValue.includes('\\n')) {
regions.set(i, { startLine: i, endLine: i, type: 'string' })
}
}
}
}
// Check for block regions (objects/arrays)
for (const char of line) {
if (char === '{' || char === '[') {
stack.push({ char, line: i })
} else if (char === '}' || char === ']') {
const expected = char === '}' ? '{' : '['
if (stack.length > 0 && stack[stack.length - 1].char === expected) {
const start = stack.pop()!
// Only create a region if it spans multiple lines
if (i > start.line) {
regions.set(start.line, {
startLine: start.line,
endLine: i,
type: 'block',
})
}
}
}
}
}
return regions
}
/**
* Computes visible line indices based on collapsed regions.
* Only block regions hide lines; string regions just truncate content.
*
* @param totalLines - Total number of lines
* @param collapsedLines - Set of line indices that are collapsed (start lines of regions)
* @param regions - Map of collapsible regions
* @returns Sorted array of visible line indices
*/
function computeVisibleLineIndices(
totalLines: number,
collapsedLines: Set<number>,
regions: Map<number, CollapsibleRegion>
): number[] {
if (collapsedLines.size === 0) {
return Array.from({ length: totalLines }, (_, i) => i)
}
// Build sorted list of hidden ranges (only for block regions, not string regions)
const hiddenRanges: Array<{ start: number; end: number }> = []
for (const startLine of collapsedLines) {
const region = regions.get(startLine)
if (region && region.type === 'block' && region.endLine > region.startLine + 1) {
hiddenRanges.push({ start: region.startLine + 1, end: region.endLine - 1 })
}
}
hiddenRanges.sort((a, b) => a.start - b.start)
// Merge overlapping ranges
const merged: Array<{ start: number; end: number }> = []
for (const range of hiddenRanges) {
if (merged.length === 0 || merged[merged.length - 1].end < range.start - 1) {
merged.push(range)
} else {
merged[merged.length - 1].end = Math.max(merged[merged.length - 1].end, range.end)
}
}
// Build visible indices by skipping hidden ranges
const visible: number[] = []
let rangeIdx = 0
for (let i = 0; i < totalLines; i++) {
while (rangeIdx < merged.length && merged[rangeIdx].end < i) {
rangeIdx++
}
if (rangeIdx < merged.length && i >= merged[rangeIdx].start && i <= merged[rangeIdx].end) {
continue
}
visible.push(i)
}
return visible
}
/**
* Truncates a long string value in a JSON line for collapsed display.
*
* @param line - The original line content
* @returns Truncated line with ellipsis
*/
function truncateStringLine(line: string): string {
const colonIdx = line.indexOf('":')
if (colonIdx === -1) return line
const valueStart = line.indexOf('"', colonIdx + 1)
if (valueStart === -1) return line
const prefix = line.slice(0, valueStart + 1)
const suffix = line.charCodeAt(line.length - 1) === 44 /* ',' */ ? '",' : '"'
const truncated = line.slice(valueStart + 1, valueStart + 31)
return `${prefix}${truncated}...${suffix}`
}
/**
* Custom hook for managing JSON collapse state and computations.
*
* @param lines - Array of code lines
* @param showCollapseColumn - Whether collapse functionality is enabled
* @param language - Programming language for syntax detection
* @returns Object containing collapse state and handlers
*/
function useJsonCollapse(
lines: string[],
showCollapseColumn: boolean,
language: string
): {
collapsedLines: Set<number>
collapsibleLines: Set<number>
collapsibleRegions: Map<number, CollapsibleRegion>
collapsedStringLines: Set<number>
visibleLineIndices: number[]
toggleCollapse: (lineIndex: number) => void
} {
const [collapsedLines, setCollapsedLines] = useState<Set<number>>(new Set())
const collapsibleRegions = useMemo(() => {
if (!showCollapseColumn || language !== 'json') return new Map<number, CollapsibleRegion>()
return findCollapsibleRegions(lines)
}, [lines, showCollapseColumn, language])
const collapsibleLines = useMemo(() => new Set(collapsibleRegions.keys()), [collapsibleRegions])
// Track which collapsed lines are string type (need truncation, not hiding)
const collapsedStringLines = useMemo(() => {
const stringLines = new Set<number>()
for (const lineIdx of collapsedLines) {
const region = collapsibleRegions.get(lineIdx)
if (region?.type === 'string') {
stringLines.add(lineIdx)
}
}
return stringLines
}, [collapsedLines, collapsibleRegions])
const visibleLineIndices = useMemo(() => {
if (!showCollapseColumn) {
return Array.from({ length: lines.length }, (_, i) => i)
}
return computeVisibleLineIndices(lines.length, collapsedLines, collapsibleRegions)
}, [lines.length, collapsedLines, collapsibleRegions, showCollapseColumn])
const toggleCollapse = useCallback((lineIndex: number) => {
setCollapsedLines((prev) => {
const next = new Set(prev)
if (next.has(lineIndex)) {
next.delete(lineIndex)
} else {
next.add(lineIndex)
}
return next
})
}, [])
return {
collapsedLines,
collapsibleLines,
collapsibleRegions,
collapsedStringLines,
visibleLineIndices,
toggleCollapse,
}
}
/**
* Props for the CollapseButton component.
*/
interface CollapseButtonProps {
/** Whether the region is currently collapsed */
isCollapsed: boolean
/** Handler for toggle click */
onClick: () => void
}
/**
* Collapse/expand button with chevron icon.
* Rotates chevron based on collapse state.
*
* @param props - Component props
*/
const CollapseButton = memo(function CollapseButton({ isCollapsed, onClick }: CollapseButtonProps) {
return (
<button
type='button'
onClick={onClick}
className='flex h-[21px] w-[12px] cursor-pointer items-center justify-center border-none bg-transparent p-0 text-[var(--text-muted)] hover:text-[var(--text-secondary)]'
aria-label={isCollapsed ? 'Expand' : 'Collapse'}
>
<ChevronRight
className={cn(
'!h-[12px] !w-[12px] transition-transform duration-100',
!isCollapsed && 'rotate-90'
)}
/>
</button>
)
})
/**
* Props for the Code.Container component.
*/
@@ -517,64 +256,28 @@ function Placeholder({ children, gutterWidth, show, className }: CodePlaceholder
}
/**
* Represents a highlighted line of code.
* Props for virtualized row rendering.
*/
interface HighlightedLine {
/** 1-based line number */
lineNumber: number
/** Syntax-highlighted HTML content */
html: string
}
/**
* Props for virtualized row rendering.
*/
interface CodeRowProps {
/** Array of highlighted lines to render */
lines: HighlightedLine[]
/** Width of the gutter in pixels */
gutterWidth: number
/** Whether to show the line number gutter */
showGutter: boolean
/** Custom styles for the gutter */
gutterStyle?: React.CSSProperties
/** Left offset for alignment */
leftOffset: number
/** Whether to wrap long lines */
wrapText: boolean
/** Whether to show the collapse column */
showCollapseColumn: boolean
/** Set of line indices that can be collapsed */
collapsibleLines: Set<number>
/** Set of line indices that are currently collapsed */
collapsedLines: Set<number>
/** Handler for toggling collapse state */
onToggleCollapse: (lineIndex: number) => void
}
/**
* Row component for virtualized code viewer.
* Renders a single line with optional gutter and collapse button.
*
* @param props - Row component props from react-window
*/
function CodeRow({ index, style, ...props }: RowComponentProps<CodeRowProps>) {
const {
lines,
gutterWidth,
showGutter,
gutterStyle,
leftOffset,
wrapText,
showCollapseColumn,
collapsibleLines,
collapsedLines,
onToggleCollapse,
} = props
const { lines, gutterWidth, showGutter, gutterStyle, leftOffset, wrapText } = props
const line = lines[index]
const originalLineIndex = line.lineNumber - 1
const isCollapsible = showCollapseColumn && collapsibleLines.has(originalLineIndex)
const isCollapsed = collapsedLines.has(originalLineIndex)
return (
<div style={style} className={cn('flex', wrapText && 'overflow-hidden')} data-row-index={index}>
@@ -586,19 +289,6 @@ function CodeRow({ index, style, ...props }: RowComponentProps<CodeRowProps>) {
{line.lineNumber}
</div>
)}
{showCollapseColumn && (
<div
className='ml-1 flex flex-shrink-0 items-start justify-end'
style={{ width: COLLAPSE_COLUMN_WIDTH }}
>
{isCollapsible && (
<CollapseButton
isCollapsed={isCollapsed}
onClick={() => onToggleCollapse(originalLineIndex)}
/>
)}
</div>
)}
<pre
className={cn(
'm-0 flex-1 pr-2 pl-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]',
@@ -612,12 +302,6 @@ function CodeRow({ index, style, ...props }: RowComponentProps<CodeRowProps>) {
/**
* Applies search highlighting to a single line for virtualized rendering.
*
* @param html - The syntax-highlighted HTML string
* @param searchQuery - The search query to highlight
* @param currentMatchIndex - Index of the current match (for distinct highlighting)
* @param globalMatchOffset - Cumulative match count before this line
* @returns Object containing highlighted HTML and count of matches in this line
*/
function applySearchHighlightingToLine(
html: string,
@@ -682,8 +366,6 @@ interface CodeViewerProps {
contentRef?: React.RefObject<HTMLDivElement | null>
/** Enable virtualized rendering for large outputs (uses react-window) */
virtualized?: boolean
/** Whether to show a collapse column for JSON folding (only for json language) */
showCollapseColumn?: boolean
}
/**
@@ -740,41 +422,42 @@ function applySearchHighlighting(
.join('')
}
/**
* Counts all matches for a search query in the given code.
*
* @param code - The raw code string
* @param searchQuery - The search query
* @returns Number of matches found
*/
function countSearchMatches(code: string, searchQuery: string): number {
if (!searchQuery.trim()) return 0
const escaped = escapeRegex(searchQuery)
const regex = new RegExp(escaped, 'gi')
const matches = code.match(regex)
return matches?.length ?? 0
}
/**
* Props for inner viewer components (with defaults already applied).
*/
type ViewerInnerProps = {
/** Code content to display */
code: string
/** Whether to show line numbers gutter */
showGutter: boolean
/** Language for syntax highlighting */
language: 'javascript' | 'json' | 'python'
/** Additional CSS classes for the container */
className?: string
/** Left padding offset in pixels */
paddingLeft: number
/** Custom styles for the gutter */
gutterStyle?: React.CSSProperties
/** Whether to wrap long lines */
wrapText: boolean
/** Search query to highlight */
searchQuery?: string
/** Index of the current active match */
currentMatchIndex: number
/** Callback when match count changes */
onMatchCountChange?: (count: number) => void
/** Ref for the content container */
contentRef?: React.RefObject<HTMLDivElement | null>
/** Whether to show collapse column for JSON folding */
showCollapseColumn: boolean
}
/**
* Virtualized code viewer implementation using react-window.
* Optimized for large outputs with efficient scrolling and dynamic row heights.
*
* @param props - Viewer props
*/
const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
code,
@@ -788,7 +471,6 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
currentMatchIndex,
onMatchCountChange,
contentRef,
showCollapseColumn,
}: ViewerInnerProps) {
const containerRef = useRef<HTMLDivElement>(null)
const listRef = useListRef(null)
@@ -799,70 +481,52 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
key: wrapText ? 'wrap' : 'nowrap',
})
const lines = useMemo(() => code.split('\n'), [code])
const gutterWidth = useMemo(() => calculateGutterWidth(lines.length), [lines.length])
const {
collapsedLines,
collapsibleLines,
collapsedStringLines,
visibleLineIndices,
toggleCollapse,
} = useJsonCollapse(lines, showCollapseColumn, language)
// Compute display lines (accounting for truncation of collapsed strings)
const displayLines = useMemo(() => {
return lines.map((line, idx) =>
collapsedStringLines.has(idx) ? truncateStringLine(line) : line
)
}, [lines, collapsedStringLines])
// Pre-compute cumulative match offsets based on DISPLAYED content (handles truncation)
const { matchOffsets, matchCount } = useMemo(() => {
if (!searchQuery?.trim()) return { matchOffsets: [], matchCount: 0 }
const offsets: number[] = []
let cumulative = 0
const escaped = escapeRegex(searchQuery)
const regex = new RegExp(escaped, 'gi')
const visibleSet = new Set(visibleLineIndices)
for (let i = 0; i < lines.length; i++) {
offsets.push(cumulative)
// Only count matches in visible lines, using displayed (possibly truncated) content
if (visibleSet.has(i)) {
const matches = displayLines[i].match(regex)
cumulative += matches?.length ?? 0
}
}
return { matchOffsets: offsets, matchCount: cumulative }
}, [lines.length, displayLines, visibleLineIndices, searchQuery])
const matchCount = useMemo(() => countSearchMatches(code, searchQuery || ''), [code, searchQuery])
useEffect(() => {
onMatchCountChange?.(matchCount)
}, [matchCount, onMatchCountChange])
// Only process visible lines for efficiency (not all lines)
const visibleLines = useMemo(() => {
const lines = useMemo(() => code.split('\n'), [code])
const lineCount = lines.length
const gutterWidth = useMemo(() => calculateGutterWidth(lineCount), [lineCount])
const highlightedLines = useMemo(() => {
const lang = languages[language] || languages.javascript
const hasSearch = searchQuery?.trim()
return lines.map((line, idx) => ({
lineNumber: idx + 1,
html: highlight(line, lang, language),
}))
}, [lines, language])
return visibleLineIndices.map((idx) => {
let html = highlight(displayLines[idx], lang, language)
const matchOffsets = useMemo(() => {
if (!searchQuery?.trim()) return []
const offsets: number[] = []
let cumulative = 0
const escaped = escapeRegex(searchQuery)
const regex = new RegExp(escaped, 'gi')
if (hasSearch && searchQuery) {
const result = applySearchHighlightingToLine(
html,
searchQuery,
currentMatchIndex,
matchOffsets[idx]
)
html = result.html
}
for (const line of lines) {
offsets.push(cumulative)
const matches = line.match(regex)
cumulative += matches?.length ?? 0
}
return offsets
}, [lines, searchQuery])
return { lineNumber: idx + 1, html }
const linesWithSearch = useMemo(() => {
if (!searchQuery?.trim()) return highlightedLines
return highlightedLines.map((line, idx) => {
const { html } = applySearchHighlightingToLine(
line.html,
searchQuery,
currentMatchIndex,
matchOffsets[idx]
)
return { ...line, html }
})
}, [displayLines, language, visibleLineIndices, searchQuery, currentMatchIndex, matchOffsets])
}, [highlightedLines, searchQuery, currentMatchIndex, matchOffsets])
useEffect(() => {
if (!searchQuery?.trim() || matchCount === 0 || !listRef.current) return
@@ -871,15 +535,12 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
for (let i = 0; i < matchOffsets.length; i++) {
const matchesInThisLine = (matchOffsets[i + 1] ?? matchCount) - matchOffsets[i]
if (currentMatchIndex >= accumulated && currentMatchIndex < accumulated + matchesInThisLine) {
const visibleIndex = visibleLineIndices.indexOf(i)
if (visibleIndex !== -1) {
listRef.current.scrollToRow({ index: visibleIndex, align: 'center' })
}
listRef.current.scrollToRow({ index: i, align: 'center' })
break
}
accumulated += matchesInThisLine
}
}, [currentMatchIndex, searchQuery, matchCount, matchOffsets, listRef, visibleLineIndices])
}, [currentMatchIndex, searchQuery, matchCount, matchOffsets, listRef])
useEffect(() => {
const container = containerRef.current
@@ -888,11 +549,15 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
const parent = container.parentElement
if (!parent) return
const updateHeight = () => setContainerHeight(parent.clientHeight)
const updateHeight = () => {
setContainerHeight(parent.clientHeight)
}
updateHeight()
const resizeObserver = new ResizeObserver(updateHeight)
resizeObserver.observe(parent)
return () => resizeObserver.disconnect()
}, [])
@@ -906,7 +571,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
if (rows.length === 0) return
return dynamicRowHeight.observeRowElements(rows)
}, [wrapText, dynamicRowHeight, visibleLines])
}, [wrapText, dynamicRowHeight, linesWithSearch])
const setRefs = useCallback(
(el: HTMLDivElement | null) => {
@@ -920,29 +585,14 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
const rowProps = useMemo(
() => ({
lines: visibleLines,
lines: linesWithSearch,
gutterWidth,
showGutter,
gutterStyle,
leftOffset: paddingLeft,
wrapText,
showCollapseColumn,
collapsibleLines,
collapsedLines,
onToggleCollapse: toggleCollapse,
}),
[
visibleLines,
gutterWidth,
showGutter,
gutterStyle,
paddingLeft,
wrapText,
showCollapseColumn,
collapsibleLines,
collapsedLines,
toggleCollapse,
]
[linesWithSearch, gutterWidth, showGutter, gutterStyle, paddingLeft, wrapText]
)
return (
@@ -959,7 +609,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
<List
listRef={listRef}
defaultHeight={containerHeight}
rowCount={visibleLines.length}
rowCount={lineCount}
rowHeight={wrapText ? dynamicRowHeight : CODE_LINE_HEIGHT_PX}
rowComponent={CodeRow}
rowProps={rowProps}
@@ -997,9 +647,6 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
*/
/**
* Non-virtualized code viewer implementation.
* Renders all lines directly without windowing.
*
* @param props - Viewer props
*/
function ViewerInner({
code,
@@ -1013,96 +660,23 @@ function ViewerInner({
currentMatchIndex,
onMatchCountChange,
contentRef,
showCollapseColumn,
}: ViewerInnerProps) {
const lines = useMemo(() => code.split('\n'), [code])
const gutterWidth = useMemo(() => calculateGutterWidth(lines.length), [lines.length])
const {
collapsedLines,
collapsibleLines,
collapsedStringLines,
visibleLineIndices,
toggleCollapse,
} = useJsonCollapse(lines, showCollapseColumn, language)
// Compute display lines (accounting for truncation of collapsed strings)
const displayLines = useMemo(() => {
return lines.map((line, idx) =>
collapsedStringLines.has(idx) ? truncateStringLine(line) : line
)
}, [lines, collapsedStringLines])
// Pre-compute cumulative match offsets based on DISPLAYED content (handles truncation)
const { cumulativeMatches, matchCount } = useMemo(() => {
if (!searchQuery?.trim()) return { cumulativeMatches: [0], matchCount: 0 }
const cumulative: number[] = [0]
const escaped = escapeRegex(searchQuery)
const regex = new RegExp(escaped, 'gi')
const visibleSet = new Set(visibleLineIndices)
for (let i = 0; i < lines.length; i++) {
const prev = cumulative[cumulative.length - 1]
// Only count matches in visible lines, using displayed content
if (visibleSet.has(i)) {
const matches = displayLines[i].match(regex)
cumulative.push(prev + (matches?.length ?? 0))
} else {
cumulative.push(prev)
}
}
return { cumulativeMatches: cumulative, matchCount: cumulative[cumulative.length - 1] }
}, [lines.length, displayLines, visibleLineIndices, searchQuery])
// Compute match count and notify parent
const matchCount = useMemo(() => countSearchMatches(code, searchQuery || ''), [code, searchQuery])
useEffect(() => {
onMatchCountChange?.(matchCount)
}, [matchCount, onMatchCountChange])
// Pre-compute highlighted lines with search for visible indices (for gutter mode)
const highlightedVisibleLines = useMemo(() => {
const lang = languages[language] || languages.javascript
if (!searchQuery?.trim()) {
return visibleLineIndices.map((idx) => ({
lineNumber: idx + 1,
html: highlight(displayLines[idx], lang, language) || '&nbsp;',
}))
}
return visibleLineIndices.map((idx) => {
let html = highlight(displayLines[idx], lang, language)
const matchCounter = { count: cumulativeMatches[idx] }
html = applySearchHighlighting(html, searchQuery, currentMatchIndex, matchCounter)
return { lineNumber: idx + 1, html: html || '&nbsp;' }
})
}, [
displayLines,
language,
visibleLineIndices,
searchQuery,
currentMatchIndex,
cumulativeMatches,
])
// Pre-compute simple highlighted code (for no-gutter mode)
const highlightedCode = useMemo(() => {
const lang = languages[language] || languages.javascript
const visibleCode = visibleLineIndices.map((idx) => displayLines[idx]).join('\n')
let html = highlight(visibleCode, lang, language)
if (searchQuery?.trim()) {
const matchCounter = { count: 0 }
html = applySearchHighlighting(html, searchQuery, currentMatchIndex, matchCounter)
}
return html
}, [displayLines, language, visibleLineIndices, searchQuery, currentMatchIndex])
// Determine whitespace class based on wrap setting
const whitespaceClass = wrapText ? 'whitespace-pre-wrap break-words' : 'whitespace-pre'
const collapseColumnWidth = showCollapseColumn ? COLLAPSE_COLUMN_WIDTH : 0
// Grid-based rendering for gutter alignment (works with wrap)
if (showGutter) {
// Special rendering path: when wrapping with gutter, render per-line rows so gutter stays aligned.
if (showGutter && wrapText) {
const lines = code.split('\n')
const gutterWidth = calculateGutterWidth(lines.length)
const matchCounter = { count: 0 }
return (
<Container className={className}>
<Content className='code-editor-theme' editorRef={contentRef}>
@@ -1112,40 +686,37 @@ function ViewerInner({
paddingTop: '8px',
paddingBottom: '8px',
display: 'grid',
gridTemplateColumns: showCollapseColumn
? `${gutterWidth}px ${collapseColumnWidth}px 1fr`
: `${gutterWidth}px 1fr`,
gridTemplateColumns: `${gutterWidth}px 1fr`,
}}
>
{highlightedVisibleLines.map(({ lineNumber, html }) => {
const idx = lineNumber - 1
const isCollapsible = collapsibleLines.has(idx)
const isCollapsed = collapsedLines.has(idx)
{lines.map((line, idx) => {
let perLineHighlighted = highlight(
line,
languages[language] || languages.javascript,
language
)
// Apply search highlighting if query exists
if (searchQuery?.trim()) {
perLineHighlighted = applySearchHighlighting(
perLineHighlighted,
searchQuery,
currentMatchIndex,
matchCounter
)
}
return (
<Fragment key={idx}>
<div
className='select-none pr-0.5 text-right text-[var(--text-muted)] text-xs tabular-nums leading-[21px] dark:text-[#a8a8a8]'
style={gutterStyle}
style={{ transform: 'translateY(0.25px)', ...gutterStyle }}
>
{lineNumber}
{idx + 1}
</div>
{showCollapseColumn && (
<div className='ml-1 flex items-start justify-end'>
{isCollapsible && (
<CollapseButton
isCollapsed={isCollapsed}
onClick={() => toggleCollapse(idx)}
/>
)}
</div>
)}
<pre
className={cn(
'm-0 min-w-0 pr-2 pl-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]',
whitespaceClass
)}
dangerouslySetInnerHTML={{ __html: html }}
className='m-0 min-w-0 whitespace-pre-wrap break-words pr-2 pl-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]'
dangerouslySetInnerHTML={{ __html: perLineHighlighted || '&nbsp;' }}
/>
</Fragment>
)
@@ -1156,16 +727,69 @@ function ViewerInner({
)
}
// Simple display without gutter
// Apply syntax highlighting
let highlightedCode = highlight(code, languages[language] || languages.javascript, language)
// Apply search highlighting if query exists
if (searchQuery?.trim()) {
const matchCounter = { count: 0 }
highlightedCode = applySearchHighlighting(
highlightedCode,
searchQuery,
currentMatchIndex,
matchCounter
)
}
if (!showGutter) {
// Simple display without gutter
return (
<Container className={className}>
<Content className='code-editor-theme' editorRef={contentRef}>
<pre
className={cn(
whitespaceClass,
'p-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]'
)}
dangerouslySetInnerHTML={{ __html: highlightedCode }}
/>
</Content>
</Container>
)
}
// Calculate line numbers
const lineCount = code.split('\n').length
const gutterWidth = calculateGutterWidth(lineCount)
// Render line numbers
const lineNumbers = []
for (let i = 1; i <= lineCount; i++) {
lineNumbers.push(
<div
key={i}
className='text-right text-[var(--text-muted)] text-xs tabular-nums leading-[21px] dark:text-[#a8a8a8]'
>
{i}
</div>
)
}
return (
<Container className={className}>
<Content className='code-editor-theme' editorRef={contentRef}>
<Gutter width={gutterWidth} style={{ left: `${paddingLeft}px`, ...gutterStyle }}>
{lineNumbers}
</Gutter>
<Content
className='code-editor-theme'
paddingLeft={`${gutterWidth + paddingLeft}px`}
editorRef={contentRef}
>
<pre
className={cn(
whitespaceClass,
'p-2 font-mono text-[13px] text-[var(--text-primary)] leading-[21px] dark:text-[#eeeeee]'
)}
style={{ paddingLeft: paddingLeft > 0 ? paddingLeft : undefined }}
dangerouslySetInnerHTML={{ __html: highlightedCode }}
/>
</Content>
@@ -1176,9 +800,6 @@ function ViewerInner({
/**
* Readonly code viewer with optional gutter and syntax highlighting.
* Routes to either standard or virtualized implementation based on the `virtualized` prop.
*
* @param props - Component props
* @returns React component
*/
function Viewer({
code,
@@ -1193,7 +814,6 @@ function Viewer({
onMatchCountChange,
contentRef,
virtualized = false,
showCollapseColumn = false,
}: CodeViewerProps) {
const innerProps: ViewerInnerProps = {
code,
@@ -1207,7 +827,6 @@ function Viewer({
currentMatchIndex,
onMatchCountChange,
contentRef,
showCollapseColumn,
}
return virtualized ? <VirtualizedViewerInner {...innerProps} /> : <ViewerInner {...innerProps} />

View File

@@ -0,0 +1,146 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomAssignConversationParams {
accessToken: string
conversationId: string
admin_id: string
assignee_id: string
body?: string
}
export interface IntercomAssignConversationV2Response {
success: boolean
output: {
conversation: any
conversationId: string
admin_assignee_id: number | null
team_assignee_id: string | null
}
}
const assignConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to assign',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin performing the assignment',
},
assignee_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'The ID of the admin or team to assign the conversation to. Set to "0" to unassign.',
},
body: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional message to add when assigning (e.g., "Passing to the support team")',
},
},
request: {
url: (params: IntercomAssignConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/parts`),
method: 'POST',
headers: (params: IntercomAssignConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomAssignConversationParams) => {
const payload: any = {
message_type: 'assignment',
type: 'admin',
admin_id: params.admin_id,
assignee_id: params.assignee_id,
}
if (params.body) {
payload.body = params.body
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomAssignConversationParams, any>, 'params' | 'request'>
export const intercomAssignConversationV2Tool: ToolConfig<
IntercomAssignConversationParams,
IntercomAssignConversationV2Response
> = {
...assignConversationBase,
id: 'intercom_assign_conversation_v2',
name: 'Assign Conversation in Intercom',
description: 'Assign a conversation to an admin or team in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'assign_conversation')
}
const data = await response.json()
return {
success: true,
output: {
conversation: data,
conversationId: data.id,
admin_assignee_id: data.admin_assignee_id ?? null,
team_assignee_id: data.team_assignee_id ?? null,
},
}
},
outputs: {
conversation: {
type: 'object',
description: 'The assigned conversation object',
properties: {
id: { type: 'string', description: 'Unique identifier for the conversation' },
type: { type: 'string', description: 'Object type (conversation)' },
state: { type: 'string', description: 'State of the conversation' },
open: { type: 'boolean', description: 'Whether the conversation is open' },
admin_assignee_id: {
type: 'number',
description: 'ID of the assigned admin',
optional: true,
},
team_assignee_id: {
type: 'string',
description: 'ID of the assigned team',
optional: true,
},
created_at: { type: 'number', description: 'Unix timestamp when conversation was created' },
updated_at: {
type: 'number',
description: 'Unix timestamp when conversation was last updated',
},
},
},
conversationId: { type: 'string', description: 'ID of the assigned conversation' },
admin_assignee_id: {
type: 'number',
description: 'ID of the assigned admin',
optional: true,
},
team_assignee_id: { type: 'string', description: 'ID of the assigned team', optional: true },
},
}

View File

@@ -0,0 +1,115 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomAttachContactToCompanyParams {
accessToken: string
contactId: string
companyId: string
}
export interface IntercomAttachContactToCompanyV2Response {
success: boolean
output: {
company: any
companyId: string
name: string | null
}
}
const attachContactToCompanyBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to attach to the company',
},
companyId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the company to attach the contact to',
},
},
request: {
url: (params: IntercomAttachContactToCompanyParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/companies`),
method: 'POST',
headers: (params: IntercomAttachContactToCompanyParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomAttachContactToCompanyParams) => ({
id: params.companyId,
}),
},
} satisfies Pick<ToolConfig<IntercomAttachContactToCompanyParams, any>, 'params' | 'request'>
export const intercomAttachContactToCompanyV2Tool: ToolConfig<
IntercomAttachContactToCompanyParams,
IntercomAttachContactToCompanyV2Response
> = {
...attachContactToCompanyBase,
id: 'intercom_attach_contact_to_company_v2',
name: 'Attach Contact to Company in Intercom',
description: 'Attach a contact to a company in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'attach_contact_to_company')
}
const data = await response.json()
return {
success: true,
output: {
company: {
id: data.id,
type: data.type ?? 'company',
company_id: data.company_id ?? null,
name: data.name ?? null,
created_at: data.created_at ?? null,
updated_at: data.updated_at ?? null,
user_count: data.user_count ?? null,
session_count: data.session_count ?? null,
monthly_spend: data.monthly_spend ?? null,
plan: data.plan ?? null,
},
companyId: data.id,
name: data.name ?? null,
},
}
},
outputs: {
company: {
type: 'object',
description: 'The company object the contact was attached to',
properties: {
id: { type: 'string', description: 'Unique identifier for the company' },
type: { type: 'string', description: 'Object type (company)' },
company_id: { type: 'string', description: 'The company_id you defined' },
name: { type: 'string', description: 'Name of the company' },
created_at: { type: 'number', description: 'Unix timestamp when company was created' },
updated_at: { type: 'number', description: 'Unix timestamp when company was updated' },
user_count: { type: 'number', description: 'Number of users in the company' },
session_count: { type: 'number', description: 'Number of sessions' },
monthly_spend: { type: 'number', description: 'Monthly spend amount' },
plan: { type: 'object', description: 'Company plan details' },
},
},
companyId: { type: 'string', description: 'ID of the company' },
name: { type: 'string', description: 'Name of the company', optional: true },
},
}

View File

@@ -0,0 +1,129 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomCloseConversationParams {
accessToken: string
conversationId: string
admin_id: string
body?: string
}
export interface IntercomCloseConversationV2Response {
success: boolean
output: {
conversation: any
conversationId: string
state: string
}
}
const closeConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to close',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin performing the action',
},
body: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional closing message to add to the conversation',
},
},
request: {
url: (params: IntercomCloseConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/parts`),
method: 'POST',
headers: (params: IntercomCloseConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomCloseConversationParams) => {
const payload: any = {
message_type: 'close',
type: 'admin',
admin_id: params.admin_id,
}
if (params.body) {
payload.body = params.body
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomCloseConversationParams, any>, 'params' | 'request'>
export const intercomCloseConversationV2Tool: ToolConfig<
IntercomCloseConversationParams,
IntercomCloseConversationV2Response
> = {
...closeConversationBase,
id: 'intercom_close_conversation_v2',
name: 'Close Conversation in Intercom',
description: 'Close a conversation in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'close_conversation')
}
const data = await response.json()
return {
success: true,
output: {
conversation: {
id: data.id,
type: data.type ?? 'conversation',
state: data.state ?? 'closed',
open: data.open ?? false,
read: data.read ?? false,
created_at: data.created_at ?? null,
updated_at: data.updated_at ?? null,
},
conversationId: data.id,
state: data.state ?? 'closed',
},
}
},
outputs: {
conversation: {
type: 'object',
description: 'The closed conversation object',
properties: {
id: { type: 'string', description: 'Unique identifier for the conversation' },
type: { type: 'string', description: 'Object type (conversation)' },
state: { type: 'string', description: 'State of the conversation (closed)' },
open: { type: 'boolean', description: 'Whether the conversation is open (false)' },
read: { type: 'boolean', description: 'Whether the conversation has been read' },
created_at: { type: 'number', description: 'Unix timestamp when conversation was created' },
updated_at: {
type: 'number',
description: 'Unix timestamp when conversation was last updated',
},
},
},
conversationId: { type: 'string', description: 'ID of the closed conversation' },
state: { type: 'string', description: 'State of the conversation (closed)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomCreateCompany')
@@ -60,7 +60,7 @@ const createCompanyBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
company_id: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomCreateContact')
@@ -88,7 +88,7 @@ const intercomCreateContactBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
role: {

View File

@@ -0,0 +1,148 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('IntercomCreateEvent')
export interface IntercomCreateEventParams {
accessToken: string
event_name: string
created_at?: number
user_id?: string
email?: string
id?: string
metadata?: string
}
export interface IntercomCreateEventV2Response {
success: boolean
output: {
accepted: boolean
}
}
const createEventBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
event_name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'The name of the event (e.g., "order-completed"). Use past-tense verb-noun format for readability.',
},
created_at: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description:
'Unix timestamp for when the event occurred. Strongly recommended for uniqueness.',
},
user_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Your identifier for the user (external_id)',
},
email: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Email address of the user. Use only if your app uses email to uniquely identify users.',
},
id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The Intercom contact ID',
},
metadata: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON object with up to 10 metadata key-value pairs about the event (e.g., {"order_value": 99.99})',
},
},
request: {
url: () => buildIntercomUrl('/events'),
method: 'POST',
headers: (params: IntercomCreateEventParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomCreateEventParams) => {
const payload: any = {
event_name: params.event_name,
}
if (params.created_at) {
payload.created_at = params.created_at
} else {
payload.created_at = Math.floor(Date.now() / 1000)
}
if (params.user_id) {
payload.user_id = params.user_id
}
if (params.email) {
payload.email = params.email
}
if (params.id) {
payload.id = params.id
}
if (params.metadata) {
try {
payload.metadata = JSON.parse(params.metadata)
} catch (error) {
logger.warn('Failed to parse metadata, ignoring', { error })
}
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomCreateEventParams, any>, 'params' | 'request'>
export const intercomCreateEventV2Tool: ToolConfig<
IntercomCreateEventParams,
IntercomCreateEventV2Response
> = {
...createEventBase,
id: 'intercom_create_event_v2',
name: 'Create Event in Intercom',
description: 'Track a custom event for a contact in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'create_event')
}
return {
success: true,
output: {
accepted: true,
},
}
},
outputs: {
accepted: {
type: 'boolean',
description: 'Whether the event was accepted (202 Accepted)',
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
export interface IntercomCreateMessageParams {
accessToken: string
@@ -40,7 +40,7 @@ const createMessageBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
message_type: {

View File

@@ -0,0 +1,131 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomCreateNoteParams {
accessToken: string
contactId: string
body: string
admin_id?: string
}
export interface IntercomCreateNoteV2Response {
success: boolean
output: {
id: string
body: string
created_at: number
type: string
author: any | null
contact: any | null
}
}
const createNoteBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to add the note to',
},
body: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The text content of the note',
},
admin_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The ID of the admin creating the note',
},
},
request: {
url: (params: IntercomCreateNoteParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/notes`),
method: 'POST',
headers: (params: IntercomCreateNoteParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomCreateNoteParams) => {
const payload: any = {
body: params.body,
}
if (params.admin_id) {
payload.admin_id = params.admin_id
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomCreateNoteParams, any>, 'params' | 'request'>
export const intercomCreateNoteV2Tool: ToolConfig<
IntercomCreateNoteParams,
IntercomCreateNoteV2Response
> = {
...createNoteBase,
id: 'intercom_create_note_v2',
name: 'Create Note in Intercom',
description: 'Add a note to a specific contact',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'create_note')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
body: data.body,
created_at: data.created_at,
type: data.type ?? 'note',
author: data.author ?? null,
contact: data.contact ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the note' },
body: { type: 'string', description: 'The text content of the note' },
created_at: { type: 'number', description: 'Unix timestamp when the note was created' },
type: { type: 'string', description: 'Object type (note)' },
author: {
type: 'object',
description: 'The admin who created the note',
optional: true,
properties: {
type: { type: 'string', description: 'Author type (admin)' },
id: { type: 'string', description: 'Author ID' },
name: { type: 'string', description: 'Author name' },
email: { type: 'string', description: 'Author email' },
},
},
contact: {
type: 'object',
description: 'The contact the note was created for',
optional: true,
properties: {
type: { type: 'string', description: 'Contact type' },
id: { type: 'string', description: 'Contact ID' },
},
},
},
}

View File

@@ -0,0 +1,97 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomCreateTagParams {
accessToken: string
name: string
id?: string
}
export interface IntercomCreateTagV2Response {
success: boolean
output: {
id: string
name: string
type: string
}
}
const createTagBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description:
'The name of the tag. Will create a new tag if not found, or update the name if id is provided.',
},
id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The ID of an existing tag to update. Omit to create a new tag.',
},
},
request: {
url: () => buildIntercomUrl('/tags'),
method: 'POST',
headers: (params: IntercomCreateTagParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomCreateTagParams) => {
const payload: any = {
name: params.name,
}
if (params.id) {
payload.id = params.id
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomCreateTagParams, any>, 'params' | 'request'>
export const intercomCreateTagV2Tool: ToolConfig<
IntercomCreateTagParams,
IntercomCreateTagV2Response
> = {
...createTagBase,
id: 'intercom_create_tag_v2',
name: 'Create Tag in Intercom',
description: 'Create a new tag or update an existing tag name',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'create_tag')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
type: data.type ?? 'tag',
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the tag' },
name: { type: 'string', description: 'Name of the tag' },
type: { type: 'string', description: 'Object type (tag)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomCreateTicket')
@@ -41,7 +41,7 @@ const createTicketBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
ticket_type_id: {

View File

@@ -1,5 +1,5 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
export interface IntercomDeleteContactParams {
accessToken: string
@@ -23,7 +23,7 @@ const intercomDeleteContactBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {

View File

@@ -0,0 +1,101 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomDetachContactFromCompanyParams {
accessToken: string
contactId: string
companyId: string
}
export interface IntercomDetachContactFromCompanyV2Response {
success: boolean
output: {
company: any
companyId: string
name: string | null
}
}
const detachContactFromCompanyBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to detach from the company',
},
companyId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the company to detach the contact from',
},
},
request: {
url: (params: IntercomDetachContactFromCompanyParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/companies/${params.companyId}`),
method: 'DELETE',
headers: (params: IntercomDetachContactFromCompanyParams) => ({
Authorization: `Bearer ${params.accessToken}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
},
} satisfies Pick<ToolConfig<IntercomDetachContactFromCompanyParams, any>, 'params' | 'request'>
export const intercomDetachContactFromCompanyV2Tool: ToolConfig<
IntercomDetachContactFromCompanyParams,
IntercomDetachContactFromCompanyV2Response
> = {
...detachContactFromCompanyBase,
id: 'intercom_detach_contact_from_company_v2',
name: 'Detach Contact from Company in Intercom',
description: 'Remove a contact from a company in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'detach_contact_from_company')
}
const data = await response.json()
return {
success: true,
output: {
company: {
id: data.id,
type: data.type ?? 'company',
company_id: data.company_id ?? null,
name: data.name ?? null,
},
companyId: data.id,
name: data.name ?? null,
},
}
},
outputs: {
company: {
type: 'object',
description: 'The company object the contact was detached from',
properties: {
id: { type: 'string', description: 'Unique identifier for the company' },
type: { type: 'string', description: 'Object type (company)' },
company_id: { type: 'string', description: 'The company_id you defined' },
name: { type: 'string', description: 'Name of the company' },
},
},
companyId: { type: 'string', description: 'ID of the company' },
name: { type: 'string', description: 'Name of the company', optional: true },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomGetCompany')
@@ -25,7 +25,7 @@ const getCompanyBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
companyId: {

View File

@@ -1,5 +1,5 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
export interface IntercomGetContactParams {
accessToken: string
@@ -22,7 +22,7 @@ const intercomGetContactBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomGetConversation')
@@ -27,7 +27,7 @@ const getConversationBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {

View File

@@ -1,5 +1,5 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
export interface IntercomGetTicketParams {
accessToken: string
@@ -31,7 +31,7 @@ const getTicketBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
ticketId: {

View File

@@ -1,24 +1,28 @@
// Contact tools
// Company tools
export { intercomAssignConversationV2Tool } from './assign_conversation'
export { intercomAttachContactToCompanyV2Tool } from './attach_contact_to_company'
export { intercomCloseConversationV2Tool } from './close_conversation'
export { intercomCreateCompanyTool, intercomCreateCompanyV2Tool } from './create_company'
export { intercomCreateContactTool, intercomCreateContactV2Tool } from './create_contact'
// Message tools
export { intercomCreateEventV2Tool } from './create_event'
export { intercomCreateMessageTool, intercomCreateMessageV2Tool } from './create_message'
// Ticket tools
export { intercomCreateNoteV2Tool } from './create_note'
export { intercomCreateTagV2Tool } from './create_tag'
export { intercomCreateTicketTool, intercomCreateTicketV2Tool } from './create_ticket'
export { intercomDeleteContactTool, intercomDeleteContactV2Tool } from './delete_contact'
export { intercomDetachContactFromCompanyV2Tool } from './detach_contact_from_company'
export { intercomGetCompanyTool, intercomGetCompanyV2Tool } from './get_company'
export { intercomGetContactTool, intercomGetContactV2Tool } from './get_contact'
// Conversation tools
export { intercomGetConversationTool, intercomGetConversationV2Tool } from './get_conversation'
export { intercomGetTicketTool, intercomGetTicketV2Tool } from './get_ticket'
export { intercomListAdminsV2Tool } from './list_admins'
export { intercomListCompaniesTool, intercomListCompaniesV2Tool } from './list_companies'
export { intercomListContactsTool, intercomListContactsV2Tool } from './list_contacts'
export {
intercomListConversationsTool,
intercomListConversationsV2Tool,
} from './list_conversations'
export { intercomListTagsV2Tool } from './list_tags'
export { intercomOpenConversationV2Tool } from './open_conversation'
export {
intercomReplyConversationTool,
intercomReplyConversationV2Tool,
@@ -28,4 +32,9 @@ export {
intercomSearchConversationsTool,
intercomSearchConversationsV2Tool,
} from './search_conversations'
export { intercomSnoozeConversationV2Tool } from './snooze_conversation'
export { intercomTagContactV2Tool } from './tag_contact'
export { intercomTagConversationV2Tool } from './tag_conversation'
export { intercomUntagContactV2Tool } from './untag_contact'
export { intercomUpdateContactTool, intercomUpdateContactV2Tool } from './update_contact'
export { intercomUpdateTicketV2Tool } from './update_ticket'

View File

@@ -0,0 +1,125 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomListAdminsParams {
accessToken: string
}
interface IntercomAdmin {
type: string
id: string
name: string
email: string
job_title: string | null
away_mode_enabled: boolean
away_mode_reassign: boolean
has_inbox_seat: boolean
team_ids: number[]
avatar: {
type: string
image_url: string | null
} | null
email_verified: boolean | null
}
export interface IntercomListAdminsV2Response {
success: boolean
output: {
admins: IntercomAdmin[]
type: string
}
}
const listAdminsBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
},
request: {
url: () => buildIntercomUrl('/admins'),
method: 'GET',
headers: (params: IntercomListAdminsParams) => ({
Authorization: `Bearer ${params.accessToken}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
},
} satisfies Pick<ToolConfig<IntercomListAdminsParams, any>, 'params' | 'request'>
export const intercomListAdminsV2Tool: ToolConfig<
IntercomListAdminsParams,
IntercomListAdminsV2Response
> = {
...listAdminsBase,
id: 'intercom_list_admins_v2',
name: 'List Admins from Intercom',
description: 'Fetch a list of all admins for the workspace',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'list_admins')
}
const data = await response.json()
return {
success: true,
output: {
admins: data.admins ?? [],
type: data.type ?? 'admin.list',
},
}
},
outputs: {
admins: {
type: 'array',
description: 'Array of admin objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique identifier for the admin' },
type: { type: 'string', description: 'Object type (admin)' },
name: { type: 'string', description: 'Name of the admin' },
email: { type: 'string', description: 'Email of the admin' },
job_title: { type: 'string', description: 'Job title of the admin', optional: true },
away_mode_enabled: {
type: 'boolean',
description: 'Whether admin is in away mode',
},
away_mode_reassign: {
type: 'boolean',
description: 'Whether to reassign conversations when away',
},
has_inbox_seat: {
type: 'boolean',
description: 'Whether admin has a paid inbox seat',
},
team_ids: {
type: 'array',
description: 'List of team IDs the admin belongs to',
},
avatar: {
type: 'object',
description: 'Avatar information',
optional: true,
},
email_verified: {
type: 'boolean',
description: 'Whether email is verified',
optional: true,
},
},
},
},
type: { type: 'string', description: 'Object type (admin.list)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomListCompanies')
@@ -29,7 +29,7 @@ const listCompaniesBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
per_page: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomListContacts')
@@ -28,7 +28,7 @@ const listContactsBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
per_page: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomListConversations')
@@ -30,7 +30,7 @@ const listConversationsBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
per_page: {

View File

@@ -0,0 +1,86 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomListTagsParams {
accessToken: string
}
interface IntercomTag {
type: string
id: string
name: string
}
export interface IntercomListTagsV2Response {
success: boolean
output: {
tags: IntercomTag[]
type: string
}
}
const listTagsBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
},
request: {
url: () => buildIntercomUrl('/tags'),
method: 'GET',
headers: (params: IntercomListTagsParams) => ({
Authorization: `Bearer ${params.accessToken}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
},
} satisfies Pick<ToolConfig<IntercomListTagsParams, any>, 'params' | 'request'>
export const intercomListTagsV2Tool: ToolConfig<
IntercomListTagsParams,
IntercomListTagsV2Response
> = {
...listTagsBase,
id: 'intercom_list_tags_v2',
name: 'List Tags from Intercom',
description: 'Fetch a list of all tags in the workspace',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'list_tags')
}
const data = await response.json()
return {
success: true,
output: {
tags: data.tags ?? [],
type: data.type ?? 'tag.list',
},
}
},
outputs: {
tags: {
type: 'array',
description: 'Array of tag objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Unique identifier for the tag' },
type: { type: 'string', description: 'Object type (tag)' },
name: { type: 'string', description: 'Name of the tag' },
},
},
},
type: { type: 'string', description: 'Object type (list)' },
},
}

View File

@@ -0,0 +1,114 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomOpenConversationParams {
accessToken: string
conversationId: string
admin_id: string
}
export interface IntercomOpenConversationV2Response {
success: boolean
output: {
conversation: any
conversationId: string
state: string
}
}
const openConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to open',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin performing the action',
},
},
request: {
url: (params: IntercomOpenConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/parts`),
method: 'POST',
headers: (params: IntercomOpenConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomOpenConversationParams) => ({
message_type: 'open',
type: 'admin',
admin_id: params.admin_id,
}),
},
} satisfies Pick<ToolConfig<IntercomOpenConversationParams, any>, 'params' | 'request'>
export const intercomOpenConversationV2Tool: ToolConfig<
IntercomOpenConversationParams,
IntercomOpenConversationV2Response
> = {
...openConversationBase,
id: 'intercom_open_conversation_v2',
name: 'Open Conversation in Intercom',
description: 'Open a closed or snoozed conversation in Intercom',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'open_conversation')
}
const data = await response.json()
return {
success: true,
output: {
conversation: {
id: data.id,
type: data.type ?? 'conversation',
state: data.state ?? 'open',
open: data.open ?? true,
read: data.read ?? false,
created_at: data.created_at ?? null,
updated_at: data.updated_at ?? null,
},
conversationId: data.id,
state: data.state ?? 'open',
},
}
},
outputs: {
conversation: {
type: 'object',
description: 'The opened conversation object',
properties: {
id: { type: 'string', description: 'Unique identifier for the conversation' },
type: { type: 'string', description: 'Object type (conversation)' },
state: { type: 'string', description: 'State of the conversation (open)' },
open: { type: 'boolean', description: 'Whether the conversation is open (true)' },
read: { type: 'boolean', description: 'Whether the conversation has been read' },
created_at: { type: 'number', description: 'Unix timestamp when conversation was created' },
updated_at: {
type: 'number',
description: 'Unix timestamp when conversation was last updated',
},
},
},
conversationId: { type: 'string', description: 'ID of the opened conversation' },
state: { type: 'string', description: 'State of the conversation (open)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomReplyConversation')
@@ -31,7 +31,7 @@ const replyConversationBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomSearchContacts')
@@ -87,7 +87,7 @@ const searchContactsBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
query: {

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomSearchConversations')
@@ -41,7 +41,7 @@ const searchConversationsBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
query: {

View File

@@ -0,0 +1,124 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomSnoozeConversationParams {
accessToken: string
conversationId: string
admin_id: string
snoozed_until: number
}
export interface IntercomSnoozeConversationV2Response {
success: boolean
output: {
conversation: any
conversationId: string
state: string
snoozed_until: number | null
}
}
const snoozeConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to snooze',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin performing the action',
},
snoozed_until: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Unix timestamp for when the conversation should reopen',
},
},
request: {
url: (params: IntercomSnoozeConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/reply`),
method: 'POST',
headers: (params: IntercomSnoozeConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomSnoozeConversationParams) => ({
message_type: 'snoozed',
admin_id: params.admin_id,
snoozed_until: params.snoozed_until,
}),
},
} satisfies Pick<ToolConfig<IntercomSnoozeConversationParams, any>, 'params' | 'request'>
export const intercomSnoozeConversationV2Tool: ToolConfig<
IntercomSnoozeConversationParams,
IntercomSnoozeConversationV2Response
> = {
...snoozeConversationBase,
id: 'intercom_snooze_conversation_v2',
name: 'Snooze Conversation in Intercom',
description: 'Snooze a conversation to reopen at a future time',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'snooze_conversation')
}
const data = await response.json()
return {
success: true,
output: {
conversation: data,
conversationId: data.id,
state: data.state ?? 'snoozed',
snoozed_until: data.snoozed_until ?? null,
},
}
},
outputs: {
conversation: {
type: 'object',
description: 'The snoozed conversation object',
properties: {
id: { type: 'string', description: 'Unique identifier for the conversation' },
type: { type: 'string', description: 'Object type (conversation)' },
state: { type: 'string', description: 'State of the conversation (snoozed)' },
open: { type: 'boolean', description: 'Whether the conversation is open' },
snoozed_until: {
type: 'number',
description: 'Unix timestamp when conversation will reopen',
optional: true,
},
created_at: { type: 'number', description: 'Unix timestamp when conversation was created' },
updated_at: {
type: 'number',
description: 'Unix timestamp when conversation was last updated',
},
},
},
conversationId: { type: 'string', description: 'ID of the snoozed conversation' },
state: { type: 'string', description: 'State of the conversation (snoozed)' },
snoozed_until: {
type: 'number',
description: 'Unix timestamp when conversation will reopen',
optional: true,
},
},
}

View File

@@ -0,0 +1,89 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomTagContactParams {
accessToken: string
contactId: string
tagId: string
}
export interface IntercomTagContactV2Response {
success: boolean
output: {
id: string
name: string
type: string
}
}
const tagContactBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to tag',
},
tagId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the tag to apply',
},
},
request: {
url: (params: IntercomTagContactParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/tags`),
method: 'POST',
headers: (params: IntercomTagContactParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomTagContactParams) => ({
id: params.tagId,
}),
},
} satisfies Pick<ToolConfig<IntercomTagContactParams, any>, 'params' | 'request'>
export const intercomTagContactV2Tool: ToolConfig<
IntercomTagContactParams,
IntercomTagContactV2Response
> = {
...tagContactBase,
id: 'intercom_tag_contact_v2',
name: 'Tag Contact in Intercom',
description: 'Add a tag to a specific contact',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'tag_contact')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
type: data.type ?? 'tag',
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the tag' },
name: { type: 'string', description: 'Name of the tag' },
type: { type: 'string', description: 'Object type (tag)' },
},
}

View File

@@ -0,0 +1,97 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomTagConversationParams {
accessToken: string
conversationId: string
tagId: string
admin_id: string
}
export interface IntercomTagConversationV2Response {
success: boolean
output: {
id: string
name: string
type: string
}
}
const tagConversationBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the conversation to tag',
},
tagId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the tag to apply',
},
admin_id: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the admin applying the tag',
},
},
request: {
url: (params: IntercomTagConversationParams) =>
buildIntercomUrl(`/conversations/${params.conversationId}/tags`),
method: 'POST',
headers: (params: IntercomTagConversationParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomTagConversationParams) => ({
id: params.tagId,
admin_id: params.admin_id,
}),
},
} satisfies Pick<ToolConfig<IntercomTagConversationParams, any>, 'params' | 'request'>
export const intercomTagConversationV2Tool: ToolConfig<
IntercomTagConversationParams,
IntercomTagConversationV2Response
> = {
...tagConversationBase,
id: 'intercom_tag_conversation_v2',
name: 'Tag Conversation in Intercom',
description: 'Add a tag to a specific conversation',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'tag_conversation')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
type: data.type ?? 'tag',
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the tag' },
name: { type: 'string', description: 'Name of the tag' },
type: { type: 'string', description: 'Object type (tag)' },
},
}

View File

@@ -2,14 +2,13 @@ import { createLogger } from '@sim/logger'
const logger = createLogger('Intercom')
// Base params for Intercom API
export interface IntercomBaseParams {
accessToken: string // OAuth token or API token (hidden)
accessToken: string
}
export interface IntercomPaginationParams {
per_page?: number
starting_after?: string // Cursor for pagination
starting_after?: string
}
export interface IntercomPagingInfo {
@@ -33,12 +32,10 @@ export interface IntercomResponse<T> {
}
}
// Helper function to build Intercom API URLs
export function buildIntercomUrl(path: string): string {
return `https://api.intercom.io${path}`
}
// Helper function for consistent error handling
export function handleIntercomError(data: any, status: number, operation: string): never {
logger.error(`Intercom API request failed for ${operation}`, { data, status })

View File

@@ -0,0 +1,87 @@
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
export interface IntercomUntagContactParams {
accessToken: string
contactId: string
tagId: string
}
export interface IntercomUntagContactV2Response {
success: boolean
output: {
id: string
name: string
type: string
}
}
const untagContactBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the contact to untag',
},
tagId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the tag to remove',
},
},
request: {
url: (params: IntercomUntagContactParams) =>
buildIntercomUrl(`/contacts/${params.contactId}/tags/${params.tagId}`),
method: 'DELETE',
headers: (params: IntercomUntagContactParams) => ({
Authorization: `Bearer ${params.accessToken}`,
Accept: 'application/json',
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
},
} satisfies Pick<ToolConfig<IntercomUntagContactParams, any>, 'params' | 'request'>
export const intercomUntagContactV2Tool: ToolConfig<
IntercomUntagContactParams,
IntercomUntagContactV2Response
> = {
...untagContactBase,
id: 'intercom_untag_contact_v2',
name: 'Untag Contact in Intercom',
description: 'Remove a tag from a specific contact',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'untag_contact')
}
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name,
type: data.type ?? 'tag',
},
}
},
outputs: {
id: { type: 'string', description: 'Unique identifier for the tag that was removed' },
name: { type: 'string', description: 'Name of the tag that was removed' },
type: { type: 'string', description: 'Object type (tag)' },
},
}

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
import { buildIntercomUrl, handleIntercomError } from './types'
const logger = createLogger('IntercomUpdateContact')
@@ -38,7 +38,7 @@ const intercomUpdateContactBase = {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
visibility: 'user-only',
description: 'Intercom API access token',
},
contactId: {

View File

@@ -0,0 +1,193 @@
import { createLogger } from '@sim/logger'
import { buildIntercomUrl, handleIntercomError } from '@/tools/intercom/types'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('IntercomUpdateTicket')
export interface IntercomUpdateTicketParams {
accessToken: string
ticketId: string
ticket_attributes?: string
open?: boolean
is_shared?: boolean
snoozed_until?: number
admin_id?: string
assignee_id?: string
}
export interface IntercomUpdateTicketV2Response {
success: boolean
output: {
ticket: any
ticketId: string
ticket_state: string
}
}
const updateTicketBase = {
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Intercom API access token',
},
ticketId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The ID of the ticket to update',
},
ticket_attributes: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'JSON object with ticket attributes (e.g., {"_default_title_":"New Title","_default_description_":"Updated description"})',
},
open: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Set to false to close the ticket, true to keep it open',
},
is_shared: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the ticket is visible to users',
},
snoozed_until: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Unix timestamp for when the ticket should reopen',
},
admin_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'The ID of the admin performing the update (needed for workflows and attribution)',
},
assignee_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The ID of the admin or team to assign the ticket to. Set to "0" to unassign.',
},
},
request: {
url: (params: IntercomUpdateTicketParams) => buildIntercomUrl(`/tickets/${params.ticketId}`),
method: 'PUT',
headers: (params: IntercomUpdateTicketParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
'Intercom-Version': '2.14',
}),
body: (params: IntercomUpdateTicketParams) => {
const payload: any = {}
if (params.ticket_attributes) {
try {
payload.ticket_attributes = JSON.parse(params.ticket_attributes)
} catch (error) {
logger.error('Failed to parse ticket_attributes', { error })
throw new Error('ticket_attributes must be a valid JSON object')
}
}
if (params.open !== undefined) {
payload.open = params.open
}
if (params.is_shared !== undefined) {
payload.is_shared = params.is_shared
}
if (params.snoozed_until !== undefined) {
payload.snoozed_until = params.snoozed_until
}
if (params.admin_id) {
payload.admin_id = params.admin_id
}
if (params.assignee_id) {
payload.assignee_id = params.assignee_id
}
return payload
},
},
} satisfies Pick<ToolConfig<IntercomUpdateTicketParams, any>, 'params' | 'request'>
export const intercomUpdateTicketV2Tool: ToolConfig<
IntercomUpdateTicketParams,
IntercomUpdateTicketV2Response
> = {
...updateTicketBase,
id: 'intercom_update_ticket_v2',
name: 'Update Ticket in Intercom',
description: 'Update a ticket in Intercom (change state, assignment, attributes)',
version: '2.0.0',
transformResponse: async (response: Response) => {
if (!response.ok) {
const data = await response.json()
handleIntercomError(data, response.status, 'update_ticket')
}
const data = await response.json()
return {
success: true,
output: {
ticket: {
id: data.id,
type: data.type ?? 'ticket',
ticket_id: data.ticket_id ?? null,
ticket_state: data.ticket_state ?? null,
ticket_attributes: data.ticket_attributes ?? null,
open: data.open ?? null,
is_shared: data.is_shared ?? null,
snoozed_until: data.snoozed_until ?? null,
admin_assignee_id: data.admin_assignee_id ?? null,
team_assignee_id: data.team_assignee_id ?? null,
created_at: data.created_at ?? null,
updated_at: data.updated_at ?? null,
},
ticketId: data.id,
ticket_state: data.ticket_state ?? null,
},
}
},
outputs: {
ticket: {
type: 'object',
description: 'The updated ticket object',
properties: {
id: { type: 'string', description: 'Unique identifier for the ticket' },
type: { type: 'string', description: 'Object type (ticket)' },
ticket_id: { type: 'string', description: 'Ticket ID shown in Intercom UI' },
ticket_state: { type: 'string', description: 'State of the ticket' },
ticket_attributes: { type: 'object', description: 'Attributes of the ticket' },
open: { type: 'boolean', description: 'Whether the ticket is open' },
is_shared: { type: 'boolean', description: 'Whether the ticket is visible to users' },
snoozed_until: {
type: 'number',
description: 'Unix timestamp when ticket will reopen',
optional: true,
},
admin_assignee_id: { type: 'string', description: 'ID of assigned admin', optional: true },
team_assignee_id: { type: 'string', description: 'ID of assigned team', optional: true },
created_at: { type: 'number', description: 'Unix timestamp when ticket was created' },
updated_at: { type: 'number', description: 'Unix timestamp when ticket was last updated' },
},
},
ticketId: { type: 'string', description: 'ID of the updated ticket' },
ticket_state: { type: 'string', description: 'Current state of the ticket' },
},
}

View File

@@ -158,3 +158,300 @@ export const kalshiAmendOrderTool: ToolConfig<KalshiAmendOrderParams, KalshiAmen
},
},
}
export interface KalshiAmendOrderV2Params extends KalshiAuthParams {
orderId: string // Order ID to amend (required)
ticker: string // Market ticker (required)
side: string // 'yes' or 'no' (required)
action: string // 'buy' or 'sell' (required)
clientOrderId?: string // Original client order ID (optional in V2)
updatedClientOrderId?: string // New client order ID (optional in V2)
count?: string // Updated quantity
yesPrice?: string // Updated yes price in cents (1-99)
noPrice?: string // Updated no price in cents (1-99)
yesPriceDollars?: string // Updated yes price in dollars
noPriceDollars?: string // Updated no price in dollars
countFp?: string // Count in fixed-point for fractional contracts
}
export interface KalshiAmendOrderV2Order {
order_id: string
user_id: string | null
ticker: string
event_ticker: string
status: string
side: string
type: string
yes_price: number | null
no_price: number | null
action: string
count: number
remaining_count: number
created_time: string
expiration_time: string | null
order_group_id: string | null
client_order_id: string | null
place_count: number | null
decrease_count: number | null
queue_position: number | null
maker_fill_count: number | null
taker_fill_count: number | null
maker_fees: number | null
taker_fees: number | null
last_update_time: string | null
take_profit_order_id: string | null
stop_loss_order_id: string | null
amend_count: number | null
amend_taker_fill_count: number | null
}
export interface KalshiAmendOrderV2Response {
success: boolean
output: {
old_order: KalshiAmendOrderV2Order
order: KalshiAmendOrderV2Order
}
}
export const kalshiAmendOrderV2Tool: ToolConfig<
KalshiAmendOrderV2Params,
KalshiAmendOrderV2Response
> = {
id: 'kalshi_amend_order_v2',
name: 'Amend Order on Kalshi V2',
description:
'Modify the price or quantity of an existing order on Kalshi (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The order ID to amend',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker',
},
side: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Side of the order: 'yes' or 'no'",
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Action type: 'buy' or 'sell'",
},
clientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The original client-specified order ID',
},
updatedClientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'The new client-specified order ID after amendment',
},
count: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated quantity for the order',
},
yesPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated yes price in cents (1-99)',
},
noPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated no price in cents (1-99)',
},
yesPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated yes price in dollars (e.g., "0.56")',
},
noPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated no price in dollars (e.g., "0.56")',
},
countFp: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Count in fixed-point for fractional contracts',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId}/amend`),
method: 'POST',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId}/amend`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'POST', path)
},
body: (params) => {
const body: Record<string, any> = {
ticker: params.ticker,
side: params.side.toLowerCase(),
action: params.action.toLowerCase(),
}
if (params.clientOrderId) body.client_order_id = params.clientOrderId
if (params.updatedClientOrderId) body.updated_client_order_id = params.updatedClientOrderId
if (params.count) body.count = Number.parseInt(params.count, 10)
if (params.yesPrice) body.yes_price = Number.parseInt(params.yesPrice, 10)
if (params.noPrice) body.no_price = Number.parseInt(params.noPrice, 10)
if (params.yesPriceDollars) body.yes_price_dollars = params.yesPriceDollars
if (params.noPriceDollars) body.no_price_dollars = params.noPriceDollars
if (params.countFp) body.count_fp = params.countFp
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'amend_order_v2')
}
const mapOrder = (order: any): KalshiAmendOrderV2Order => ({
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
ticker: order.ticker ?? null,
event_ticker: order.event_ticker ?? null,
status: order.status ?? null,
side: order.side ?? null,
type: order.type ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
action: order.action ?? null,
count: order.count ?? null,
remaining_count: order.remaining_count ?? null,
created_time: order.created_time ?? null,
expiration_time: order.expiration_time ?? null,
order_group_id: order.order_group_id ?? null,
client_order_id: order.client_order_id ?? null,
place_count: order.place_count ?? null,
decrease_count: order.decrease_count ?? null,
queue_position: order.queue_position ?? null,
maker_fill_count: order.maker_fill_count ?? null,
taker_fill_count: order.taker_fill_count ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees: order.taker_fees ?? null,
last_update_time: order.last_update_time ?? null,
take_profit_order_id: order.take_profit_order_id ?? null,
stop_loss_order_id: order.stop_loss_order_id ?? null,
amend_count: order.amend_count ?? null,
amend_taker_fill_count: order.amend_taker_fill_count ?? null,
})
return {
success: true,
output: {
old_order: mapOrder(data.old_order || {}),
order: mapOrder(data.order || {}),
},
}
},
outputs: {
old_order: {
type: 'object',
description: 'The original order object before amendment',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
status: { type: 'string', description: 'Order status' },
side: { type: 'string', description: 'Order side (yes/no)' },
type: { type: 'string', description: 'Order type (limit/market)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
action: { type: 'string', description: 'Action (buy/sell)' },
count: { type: 'number', description: 'Number of contracts' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
created_time: { type: 'string', description: 'Order creation time' },
expiration_time: { type: 'string', description: 'Order expiration time' },
order_group_id: { type: 'string', description: 'Order group ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
place_count: { type: 'number', description: 'Place count' },
decrease_count: { type: 'number', description: 'Decrease count' },
queue_position: { type: 'number', description: 'Queue position' },
maker_fill_count: { type: 'number', description: 'Maker fill count' },
taker_fill_count: { type: 'number', description: 'Taker fill count' },
maker_fees: { type: 'number', description: 'Maker fees' },
taker_fees: { type: 'number', description: 'Taker fees' },
last_update_time: { type: 'string', description: 'Last update time' },
take_profit_order_id: { type: 'string', description: 'Take profit order ID' },
stop_loss_order_id: { type: 'string', description: 'Stop loss order ID' },
amend_count: { type: 'number', description: 'Amend count' },
amend_taker_fill_count: { type: 'number', description: 'Amend taker fill count' },
},
},
order: {
type: 'object',
description: 'The amended order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
status: { type: 'string', description: 'Order status' },
side: { type: 'string', description: 'Order side (yes/no)' },
type: { type: 'string', description: 'Order type (limit/market)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
action: { type: 'string', description: 'Action (buy/sell)' },
count: { type: 'number', description: 'Number of contracts' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
created_time: { type: 'string', description: 'Order creation time' },
expiration_time: { type: 'string', description: 'Order expiration time' },
order_group_id: { type: 'string', description: 'Order group ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
place_count: { type: 'number', description: 'Place count' },
decrease_count: { type: 'number', description: 'Decrease count' },
queue_position: { type: 'number', description: 'Queue position' },
maker_fill_count: { type: 'number', description: 'Maker fill count' },
taker_fill_count: { type: 'number', description: 'Taker fill count' },
maker_fees: { type: 'number', description: 'Maker fees' },
taker_fees: { type: 'number', description: 'Taker fees' },
last_update_time: { type: 'string', description: 'Last update time' },
take_profit_order_id: { type: 'string', description: 'Take profit order ID' },
stop_loss_order_id: { type: 'string', description: 'Stop loss order ID' },
amend_count: { type: 'number', description: 'Amend count' },
amend_taker_fill_count: { type: 'number', description: 'Amend taker fill count' },
},
},
},
}

View File

@@ -78,3 +78,193 @@ export const kalshiCancelOrderTool: ToolConfig<KalshiCancelOrderParams, KalshiCa
},
},
}
export interface KalshiCancelOrderV2Params extends KalshiAuthParams {
orderId: string // Order ID to cancel (required)
}
export interface KalshiCancelOrderV2Response {
success: boolean
output: {
order: {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
reduced_by: number
reduced_by_fp: string | null
}
}
export const kalshiCancelOrderV2Tool: ToolConfig<
KalshiCancelOrderV2Params,
KalshiCancelOrderV2Response
> = {
id: 'kalshi_cancel_order_v2',
name: 'Cancel Order on Kalshi V2',
description: 'Cancel an existing order on Kalshi (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The order ID to cancel',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId}`),
method: 'DELETE',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId}`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'DELETE', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'cancel_order_v2')
}
const order = data.order || {}
return {
success: true,
output: {
order: {
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
},
reduced_by: data.reduced_by ?? 0,
reduced_by_fp: data.reduced_by_fp ?? null,
},
}
},
outputs: {
order: {
type: 'object',
description: 'The canceled order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
reduced_by: {
type: 'number',
description: 'Number of contracts canceled',
},
reduced_by_fp: {
type: 'string',
description: 'Number of contracts canceled in fixed-point format',
},
},
}

View File

@@ -209,3 +209,344 @@ export const kalshiCreateOrderTool: ToolConfig<KalshiCreateOrderParams, KalshiCr
},
},
}
export interface KalshiCreateOrderV2Params extends KalshiAuthParams {
ticker: string // Market ticker (required)
side: string // 'yes' or 'no' (required)
action: string // 'buy' or 'sell' (required)
count?: string // Number of contracts (optional - provide count or countFp)
type?: string // 'limit' or 'market' (default: limit)
yesPrice?: string // Yes price in cents (1-99)
noPrice?: string // No price in cents (1-99)
yesPriceDollars?: string // Yes price in dollars (e.g., "0.56")
noPriceDollars?: string // No price in dollars (e.g., "0.56")
clientOrderId?: string // Custom order identifier
expirationTs?: string // Unix timestamp expiration
timeInForce?: string // 'fill_or_kill', 'good_till_canceled', 'immediate_or_cancel'
buyMaxCost?: string // Maximum cost in cents
postOnly?: string // 'true' or 'false' - maker-only orders
reduceOnly?: string // 'true' or 'false' - position reduction only
selfTradePreventionType?: string // 'taker_at_cross' or 'maker'
orderGroupId?: string // Associated order group
countFp?: string // Count in fixed-point (for fractional contracts)
cancelOrderOnPause?: string // 'true' or 'false' - cancel on market pause
subaccount?: string // Subaccount to use for the order
}
export interface KalshiCreateOrderV2Response {
success: boolean
output: {
order: {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
}
}
export const kalshiCreateOrderV2Tool: ToolConfig<
KalshiCreateOrderV2Params,
KalshiCreateOrderV2Response
> = {
id: 'kalshi_create_order_v2',
name: 'Create Order on Kalshi V2',
description: 'Create a new order on a Kalshi prediction market (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker (e.g., KXBTC-24DEC31)',
},
side: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Side of the order: 'yes' or 'no'",
},
action: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Action type: 'buy' or 'sell'",
},
count: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of contracts (provide count or countFp)',
},
type: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Order type: 'limit' or 'market' (default: limit)",
},
yesPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Yes price in cents (1-99)',
},
noPrice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'No price in cents (1-99)',
},
yesPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Yes price in dollars (e.g., "0.56")',
},
noPriceDollars: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'No price in dollars (e.g., "0.56")',
},
clientOrderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Custom order identifier',
},
expirationTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Unix timestamp for order expiration',
},
timeInForce: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Time in force: 'fill_or_kill', 'good_till_canceled', 'immediate_or_cancel'",
},
buyMaxCost: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum cost in cents (auto-enables fill_or_kill)',
},
postOnly: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' for maker-only orders",
},
reduceOnly: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' for position reduction only",
},
selfTradePreventionType: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Self-trade prevention: 'taker_at_cross' or 'maker'",
},
orderGroupId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Associated order group ID',
},
countFp: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Count in fixed-point for fractional contracts',
},
cancelOrderOnPause: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Set to 'true' to cancel order on market pause",
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount to use for the order',
},
},
request: {
url: () => buildKalshiUrl('/portfolio/orders'),
method: 'POST',
headers: (params) => {
const path = '/trade-api/v2/portfolio/orders'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'POST', path)
},
body: (params) => {
const body: Record<string, any> = {
ticker: params.ticker,
side: params.side.toLowerCase(),
action: params.action.toLowerCase(),
}
// count or count_fp must be provided (but not both required)
if (params.count) body.count = Number.parseInt(params.count, 10)
if (params.countFp) body.count_fp = params.countFp
if (params.type) body.type = params.type.toLowerCase()
if (params.yesPrice) body.yes_price = Number.parseInt(params.yesPrice, 10)
if (params.noPrice) body.no_price = Number.parseInt(params.noPrice, 10)
if (params.yesPriceDollars) body.yes_price_dollars = params.yesPriceDollars
if (params.noPriceDollars) body.no_price_dollars = params.noPriceDollars
if (params.clientOrderId) body.client_order_id = params.clientOrderId
if (params.expirationTs) body.expiration_ts = Number.parseInt(params.expirationTs, 10)
if (params.timeInForce) body.time_in_force = params.timeInForce
if (params.buyMaxCost) body.buy_max_cost = Number.parseInt(params.buyMaxCost, 10)
if (params.postOnly) body.post_only = params.postOnly === 'true'
if (params.reduceOnly) body.reduce_only = params.reduceOnly === 'true'
if (params.selfTradePreventionType)
body.self_trade_prevention_type = params.selfTradePreventionType
if (params.orderGroupId) body.order_group_id = params.orderGroupId
if (params.cancelOrderOnPause)
body.cancel_order_on_pause = params.cancelOrderOnPause === 'true'
if (params.subaccount) body.subaccount = params.subaccount
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'create_order_v2')
}
const order = data.order || {}
return {
success: true,
output: {
order: {
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
},
},
}
},
outputs: {
order: {
type: 'object',
description: 'The created order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
},
}

View File

@@ -66,3 +66,78 @@ export const kalshiGetBalanceTool: ToolConfig<KalshiGetBalanceParams, KalshiGetB
portfolioValue: { type: 'number', description: 'Portfolio value in cents' },
},
}
/**
* V2 Params for Get Balance
*/
export interface KalshiGetBalanceV2Params extends KalshiAuthParams {}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetBalanceV2Response {
success: boolean
output: {
balance: number
portfolio_value: number
updated_ts: number | null
}
}
export const kalshiGetBalanceV2Tool: ToolConfig<
KalshiGetBalanceV2Params,
KalshiGetBalanceV2Response
> = {
id: 'kalshi_get_balance_v2',
name: 'Get Balance from Kalshi V2',
description:
'Retrieve your account balance and portfolio value from Kalshi (V2 - exact API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
},
request: {
url: () => buildKalshiUrl('/portfolio/balance'),
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/balance'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_balance_v2')
}
return {
success: true,
output: {
balance: data.balance ?? 0,
portfolio_value: data.portfolio_value ?? 0,
updated_ts: data.updated_ts ?? null,
},
}
},
outputs: {
balance: { type: 'number', description: 'Account balance in cents' },
portfolio_value: { type: 'number', description: 'Portfolio value in cents' },
updated_ts: { type: 'number', description: 'Unix timestamp of last update (milliseconds)' },
},
}

View File

@@ -102,3 +102,246 @@ export const kalshiGetCandlesticksTool: ToolConfig<
},
},
}
/**
* BidAskDistribution - OHLC data for yes_bid and yes_ask
*/
export interface BidAskDistribution {
open: number | null
open_dollars: string | null
low: number | null
low_dollars: string | null
high: number | null
high_dollars: string | null
close: number | null
close_dollars: string | null
}
/**
* PriceDistribution - Extended OHLC data for price field
*/
export interface PriceDistribution {
open: number | null
open_dollars: string | null
low: number | null
low_dollars: string | null
high: number | null
high_dollars: string | null
close: number | null
close_dollars: string | null
mean: number | null
mean_dollars: string | null
previous: number | null
previous_dollars: string | null
min: number | null
min_dollars: string | null
max: number | null
max_dollars: string | null
}
/**
* V2 Get Candlesticks Tool - Returns exact Kalshi API response structure
*/
export interface KalshiGetCandlesticksV2Response {
success: boolean
output: {
ticker: string
candlesticks: Array<{
end_period_ts: number | null
yes_bid: BidAskDistribution
yes_ask: BidAskDistribution
price: PriceDistribution
volume: number | null
volume_fp: string | null
open_interest: number | null
open_interest_fp: string | null
}>
}
}
export const kalshiGetCandlesticksV2Tool: ToolConfig<
KalshiGetCandlesticksParams,
KalshiGetCandlesticksV2Response
> = {
id: 'kalshi_get_candlesticks_v2',
name: 'Get Market Candlesticks from Kalshi V2',
description: 'Retrieve OHLC candlestick data for a specific market (V2 - full API response)',
version: '2.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker',
},
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker (e.g., KXBTC-24DEC31)',
},
startTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Start timestamp (Unix seconds)',
},
endTs: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'End timestamp (Unix seconds)',
},
periodInterval: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Period interval: 1 (1min), 60 (1hour), or 1440 (1day)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('start_ts', params.startTs.toString())
queryParams.append('end_ts', params.endTs.toString())
queryParams.append('period_interval', params.periodInterval.toString())
const query = queryParams.toString()
const url = buildKalshiUrl(
`/series/${params.seriesTicker}/markets/${params.ticker}/candlesticks`
)
return `${url}?${query}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_candlesticks_v2')
}
const mapBidAsk = (obj: Record<string, unknown> | null): BidAskDistribution => ({
open: (obj?.open as number) ?? null,
open_dollars: (obj?.open_dollars as string) ?? null,
low: (obj?.low as number) ?? null,
low_dollars: (obj?.low_dollars as string) ?? null,
high: (obj?.high as number) ?? null,
high_dollars: (obj?.high_dollars as string) ?? null,
close: (obj?.close as number) ?? null,
close_dollars: (obj?.close_dollars as string) ?? null,
})
const mapPrice = (obj: Record<string, unknown> | null): PriceDistribution => ({
open: (obj?.open as number) ?? null,
open_dollars: (obj?.open_dollars as string) ?? null,
low: (obj?.low as number) ?? null,
low_dollars: (obj?.low_dollars as string) ?? null,
high: (obj?.high as number) ?? null,
high_dollars: (obj?.high_dollars as string) ?? null,
close: (obj?.close as number) ?? null,
close_dollars: (obj?.close_dollars as string) ?? null,
mean: (obj?.mean as number) ?? null,
mean_dollars: (obj?.mean_dollars as string) ?? null,
previous: (obj?.previous as number) ?? null,
previous_dollars: (obj?.previous_dollars as string) ?? null,
min: (obj?.min as number) ?? null,
min_dollars: (obj?.min_dollars as string) ?? null,
max: (obj?.max as number) ?? null,
max_dollars: (obj?.max_dollars as string) ?? null,
})
const candlesticks = (data.candlesticks || []).map((c: Record<string, unknown>) => ({
end_period_ts: (c.end_period_ts as number) ?? null,
yes_bid: mapBidAsk(c.yes_bid as Record<string, unknown> | null),
yes_ask: mapBidAsk(c.yes_ask as Record<string, unknown> | null),
price: mapPrice(c.price as Record<string, unknown> | null),
volume: (c.volume as number) ?? null,
volume_fp: (c.volume_fp as string) ?? null,
open_interest: (c.open_interest as number) ?? null,
open_interest_fp: (c.open_interest_fp as string) ?? null,
}))
return {
success: true,
output: {
ticker: data.ticker ?? null,
candlesticks,
},
}
},
outputs: {
ticker: {
type: 'string',
description: 'Market ticker',
},
candlesticks: {
type: 'array',
description: 'Array of OHLC candlestick data with nested bid/ask/price objects',
properties: {
end_period_ts: { type: 'number', description: 'End period timestamp (Unix)' },
yes_bid: {
type: 'object',
description: 'Yes bid OHLC data',
properties: {
open: { type: 'number', description: 'Open price (cents)' },
open_dollars: { type: 'string', description: 'Open price (dollars)' },
low: { type: 'number', description: 'Low price (cents)' },
low_dollars: { type: 'string', description: 'Low price (dollars)' },
high: { type: 'number', description: 'High price (cents)' },
high_dollars: { type: 'string', description: 'High price (dollars)' },
close: { type: 'number', description: 'Close price (cents)' },
close_dollars: { type: 'string', description: 'Close price (dollars)' },
},
},
yes_ask: {
type: 'object',
description: 'Yes ask OHLC data',
properties: {
open: { type: 'number', description: 'Open price (cents)' },
open_dollars: { type: 'string', description: 'Open price (dollars)' },
low: { type: 'number', description: 'Low price (cents)' },
low_dollars: { type: 'string', description: 'Low price (dollars)' },
high: { type: 'number', description: 'High price (cents)' },
high_dollars: { type: 'string', description: 'High price (dollars)' },
close: { type: 'number', description: 'Close price (cents)' },
close_dollars: { type: 'string', description: 'Close price (dollars)' },
},
},
price: {
type: 'object',
description: 'Trade price OHLC data with additional statistics',
properties: {
open: { type: 'number', description: 'Open price (cents)' },
open_dollars: { type: 'string', description: 'Open price (dollars)' },
low: { type: 'number', description: 'Low price (cents)' },
low_dollars: { type: 'string', description: 'Low price (dollars)' },
high: { type: 'number', description: 'High price (cents)' },
high_dollars: { type: 'string', description: 'High price (dollars)' },
close: { type: 'number', description: 'Close price (cents)' },
close_dollars: { type: 'string', description: 'Close price (dollars)' },
mean: { type: 'number', description: 'Mean price (cents)' },
mean_dollars: { type: 'string', description: 'Mean price (dollars)' },
previous: { type: 'number', description: 'Previous price (cents)' },
previous_dollars: { type: 'string', description: 'Previous price (dollars)' },
min: { type: 'number', description: 'Min price (cents)' },
min_dollars: { type: 'string', description: 'Min price (dollars)' },
max: { type: 'number', description: 'Max price (cents)' },
max_dollars: { type: 'string', description: 'Max price (dollars)' },
},
},
volume: { type: 'number', description: 'Volume (contracts)' },
volume_fp: { type: 'string', description: 'Volume (fixed-point string)' },
open_interest: { type: 'number', description: 'Open interest (contracts)' },
open_interest_fp: { type: 'string', description: 'Open interest (fixed-point string)' },
},
},
},
}

View File

@@ -73,3 +73,179 @@ export const kalshiGetEventTool: ToolConfig<KalshiGetEventParams, KalshiGetEvent
},
},
}
/**
* V2 Params for Get Event
*/
export interface KalshiGetEventV2Params {
eventTicker: string
withNestedMarkets?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetEventV2Response {
success: boolean
output: {
event: {
event_ticker: string
series_ticker: string
title: string
sub_title: string | null
mutually_exclusive: boolean
category: string
collateral_return_type: string | null
strike_date: string | null
strike_period: string | null
available_on_brokers: boolean | null
product_metadata: Record<string, unknown> | null
markets: Array<{
ticker: string
event_ticker: string
market_type: string
title: string
subtitle: string | null
yes_sub_title: string | null
no_sub_title: string | null
open_time: string
close_time: string
expiration_time: string
status: string
yes_bid: number
yes_ask: number
no_bid: number
no_ask: number
last_price: number
previous_yes_bid: number | null
previous_yes_ask: number | null
previous_price: number | null
volume: number
volume_24h: number
liquidity: number | null
open_interest: number | null
result: string | null
cap_strike: number | null
floor_strike: number | null
}> | null
}
}
}
export const kalshiGetEventV2Tool: ToolConfig<KalshiGetEventV2Params, KalshiGetEventV2Response> = {
id: 'kalshi_get_event_v2',
name: 'Get Event from Kalshi V2',
description: 'Retrieve details of a specific event by ticker (V2 - exact API response)',
version: '2.0.0',
params: {
eventTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The event ticker',
},
withNestedMarkets: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include nested markets in response (true/false)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.withNestedMarkets)
queryParams.append('with_nested_markets', params.withNestedMarkets)
const query = queryParams.toString()
const url = buildKalshiUrl(`/events/${params.eventTicker}`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_event_v2')
}
const event = data.event || {}
const markets =
event.markets?.map((m: Record<string, unknown>) => ({
ticker: m.ticker ?? null,
event_ticker: m.event_ticker ?? null,
market_type: m.market_type ?? null,
title: m.title ?? null,
subtitle: m.subtitle ?? null,
yes_sub_title: m.yes_sub_title ?? null,
no_sub_title: m.no_sub_title ?? null,
open_time: m.open_time ?? null,
close_time: m.close_time ?? null,
expiration_time: m.expiration_time ?? null,
status: m.status ?? null,
yes_bid: m.yes_bid ?? 0,
yes_ask: m.yes_ask ?? 0,
no_bid: m.no_bid ?? 0,
no_ask: m.no_ask ?? 0,
last_price: m.last_price ?? 0,
previous_yes_bid: m.previous_yes_bid ?? null,
previous_yes_ask: m.previous_yes_ask ?? null,
previous_price: m.previous_price ?? null,
volume: m.volume ?? 0,
volume_24h: m.volume_24h ?? 0,
liquidity: m.liquidity ?? null,
open_interest: m.open_interest ?? null,
result: m.result ?? null,
cap_strike: m.cap_strike ?? null,
floor_strike: m.floor_strike ?? null,
})) ?? null
return {
success: true,
output: {
event: {
event_ticker: event.event_ticker ?? null,
series_ticker: event.series_ticker ?? null,
title: event.title ?? null,
sub_title: event.sub_title ?? null,
mutually_exclusive: event.mutually_exclusive ?? false,
category: event.category ?? null,
collateral_return_type: event.collateral_return_type ?? null,
strike_date: event.strike_date ?? null,
strike_period: event.strike_period ?? null,
available_on_brokers: event.available_on_brokers ?? null,
product_metadata: event.product_metadata ?? null,
markets,
},
},
}
},
outputs: {
event: {
type: 'object',
description: 'Event object with full details matching Kalshi API response',
properties: {
event_ticker: { type: 'string', description: 'Event ticker' },
series_ticker: { type: 'string', description: 'Series ticker' },
title: { type: 'string', description: 'Event title' },
sub_title: { type: 'string', description: 'Event subtitle' },
mutually_exclusive: { type: 'boolean', description: 'Mutually exclusive markets' },
category: { type: 'string', description: 'Event category' },
collateral_return_type: { type: 'string', description: 'Collateral return type' },
strike_date: { type: 'string', description: 'Strike date' },
strike_period: { type: 'string', description: 'Strike period' },
available_on_brokers: { type: 'boolean', description: 'Available on brokers' },
product_metadata: { type: 'object', description: 'Product metadata' },
markets: { type: 'array', description: 'Nested markets (if requested)' },
},
},
},
}

View File

@@ -106,3 +106,197 @@ export const kalshiGetEventsTool: ToolConfig<KalshiGetEventsParams, KalshiGetEve
},
},
}
/**
* V2 Params for Get Events
*/
export interface KalshiGetEventsV2Params extends KalshiPaginationParams {
status?: string
seriesTicker?: string
withNestedMarkets?: string
withMilestones?: string
minCloseTs?: number
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetEventsV2Response {
success: boolean
output: {
events: Array<{
event_ticker: string
series_ticker: string
title: string
sub_title: string | null
mutually_exclusive: boolean
category: string
collateral_return_type: string | null
strike_date: string | null
strike_period: string | null
available_on_brokers: boolean | null
product_metadata: Record<string, unknown> | null
markets: Array<Record<string, unknown>> | null
}>
milestones: Array<{
event_ticker: string
milestone_type: string
milestone_date: string
milestone_title: string | null
}> | null
cursor: string | null
}
}
export const kalshiGetEventsV2Tool: ToolConfig<KalshiGetEventsV2Params, KalshiGetEventsV2Response> =
{
id: 'kalshi_get_events_v2',
name: 'Get Events from Kalshi V2',
description:
'Retrieve a list of events from Kalshi with optional filtering (V2 - exact API response)',
version: '2.0.0',
params: {
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (open, closed, settled)',
},
seriesTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by series ticker',
},
withNestedMarkets: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include nested markets in response (true/false)',
},
withMilestones: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include milestones in response (true/false)',
},
minCloseTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum close timestamp (Unix seconds)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-200, default: 200)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.append('status', params.status)
if (params.seriesTicker) queryParams.append('series_ticker', params.seriesTicker)
if (params.withNestedMarkets)
queryParams.append('with_nested_markets', params.withNestedMarkets)
if (params.withMilestones) queryParams.append('with_milestones', params.withMilestones)
if (params.minCloseTs !== undefined)
queryParams.append('min_close_ts', params.minCloseTs.toString())
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/events')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_events_v2')
}
const events = (data.events || []).map((e: Record<string, unknown>) => ({
event_ticker: e.event_ticker ?? null,
series_ticker: e.series_ticker ?? null,
title: e.title ?? null,
sub_title: e.sub_title ?? null,
mutually_exclusive: e.mutually_exclusive ?? false,
category: e.category ?? null,
collateral_return_type: e.collateral_return_type ?? null,
strike_date: e.strike_date ?? null,
strike_period: e.strike_period ?? null,
available_on_brokers: e.available_on_brokers ?? null,
product_metadata: e.product_metadata ?? null,
markets: e.markets ?? null,
}))
const milestones = data.milestones
? (data.milestones as Array<Record<string, unknown>>).map((m) => ({
event_ticker: (m.event_ticker as string) ?? '',
milestone_type: (m.milestone_type as string) ?? '',
milestone_date: (m.milestone_date as string) ?? '',
milestone_title: (m.milestone_title as string | null) ?? null,
}))
: null
return {
success: true,
output: {
events,
milestones,
cursor: data.cursor ?? null,
},
}
},
outputs: {
events: {
type: 'array',
description: 'Array of event objects',
properties: {
event_ticker: { type: 'string', description: 'Event ticker' },
series_ticker: { type: 'string', description: 'Series ticker' },
title: { type: 'string', description: 'Event title' },
sub_title: { type: 'string', description: 'Event subtitle' },
mutually_exclusive: { type: 'boolean', description: 'Mutually exclusive markets' },
category: { type: 'string', description: 'Event category' },
collateral_return_type: { type: 'string', description: 'Collateral return type' },
strike_date: { type: 'string', description: 'Strike date' },
strike_period: { type: 'string', description: 'Strike period' },
available_on_brokers: { type: 'boolean', description: 'Available on brokers' },
product_metadata: { type: 'object', description: 'Product metadata' },
markets: { type: 'array', description: 'Nested markets (if requested)' },
},
},
milestones: {
type: 'array',
description: 'Array of milestone objects (if requested)',
properties: {
event_ticker: { type: 'string', description: 'Event ticker' },
milestone_type: { type: 'string', description: 'Milestone type' },
milestone_date: { type: 'string', description: 'Milestone date' },
milestone_title: { type: 'string', description: 'Milestone title' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -59,3 +59,74 @@ export const kalshiGetExchangeStatusTool: ToolConfig<
},
},
}
/**
* V2 Params for Get Exchange Status
*/
export type KalshiGetExchangeStatusV2Params = Record<string, never>
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetExchangeStatusV2Response {
success: boolean
output: {
exchange_active: boolean
trading_active: boolean
exchange_estimated_resume_time: string | null
}
}
export const kalshiGetExchangeStatusV2Tool: ToolConfig<
KalshiGetExchangeStatusV2Params,
KalshiGetExchangeStatusV2Response
> = {
id: 'kalshi_get_exchange_status_v2',
name: 'Get Exchange Status from Kalshi V2',
description: 'Retrieve the current status of the Kalshi exchange (V2 - exact API response)',
version: '2.0.0',
params: {},
request: {
url: () => {
return buildKalshiUrl('/exchange/status')
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_exchange_status_v2')
}
return {
success: true,
output: {
exchange_active: data.exchange_active ?? false,
trading_active: data.trading_active ?? false,
exchange_estimated_resume_time: data.exchange_estimated_resume_time ?? null,
},
}
},
outputs: {
exchange_active: {
type: 'boolean',
description: 'Whether the exchange is active',
},
trading_active: {
type: 'boolean',
description: 'Whether trading is active',
},
exchange_estimated_resume_time: {
type: 'string',
description: 'Estimated time when exchange will resume (if inactive)',
},
},
}

View File

@@ -131,3 +131,198 @@ export const kalshiGetFillsTool: ToolConfig<KalshiGetFillsParams, KalshiGetFills
},
},
}
/**
* V2 Params for Get Fills - fixes limit max to 200, adds subaccount
*/
export interface KalshiGetFillsV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
orderId?: string
minTs?: number
maxTs?: number
subaccount?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetFillsV2Response {
success: boolean
output: {
fills: Array<{
fill_id: string
trade_id: string
order_id: string
client_order_id: string | null
ticker: string
market_ticker: string
side: string
action: string
count: number
count_fp: string | null
price: number | null
yes_price: number
no_price: number
yes_price_fixed: string | null
no_price_fixed: string | null
is_taker: boolean
created_time: string | null
ts: number | null
}>
cursor: string | null
}
}
export const kalshiGetFillsV2Tool: ToolConfig<KalshiGetFillsV2Params, KalshiGetFillsV2Response> = {
id: 'kalshi_get_fills_v2',
name: 'Get Fills from Kalshi V2',
description: "Retrieve your portfolio's fills/trades from Kalshi (V2 - exact API response)",
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker',
},
orderId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by order ID',
},
minTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp (Unix milliseconds)',
},
maxTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp (Unix milliseconds)',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount to get fills for',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-200, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.orderId) queryParams.append('order_id', params.orderId)
if (params.minTs !== undefined) queryParams.append('min_ts', params.minTs.toString())
if (params.maxTs !== undefined) queryParams.append('max_ts', params.maxTs.toString())
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/fills')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/fills'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_fills_v2')
}
const fills = (data.fills || []).map((f: Record<string, unknown>) => ({
fill_id: f.fill_id ?? null,
trade_id: f.trade_id ?? null,
order_id: f.order_id ?? null,
client_order_id: f.client_order_id ?? null,
ticker: f.ticker ?? null,
market_ticker: f.market_ticker ?? null,
side: f.side ?? null,
action: f.action ?? null,
count: f.count ?? 0,
count_fp: f.count_fp ?? null,
price: f.price ?? null,
yes_price: f.yes_price ?? 0,
no_price: f.no_price ?? 0,
yes_price_fixed: f.yes_price_fixed ?? null,
no_price_fixed: f.no_price_fixed ?? null,
is_taker: f.is_taker ?? false,
created_time: f.created_time ?? null,
ts: f.ts ?? null,
}))
return {
success: true,
output: {
fills,
cursor: data.cursor ?? null,
},
}
},
outputs: {
fills: {
type: 'array',
description: 'Array of fill/trade objects with all API fields',
properties: {
fill_id: { type: 'string', description: 'Fill ID' },
trade_id: { type: 'string', description: 'Trade ID (same as fill_id)' },
order_id: { type: 'string', description: 'Order ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
market_ticker: { type: 'string', description: 'Market ticker (legacy)' },
side: { type: 'string', description: 'Side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
count: { type: 'number', description: 'Number of contracts' },
count_fp: { type: 'string', description: 'Count (fixed-point)' },
price: { type: 'number', description: 'Price (deprecated)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_fixed: { type: 'string', description: 'Yes price in dollars' },
no_price_fixed: { type: 'string', description: 'No price in dollars' },
is_taker: { type: 'boolean', description: 'Whether fill was taker' },
created_time: { type: 'string', description: 'Fill creation time' },
ts: { type: 'number', description: 'Unix timestamp (milliseconds)' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -58,3 +58,219 @@ export const kalshiGetMarketTool: ToolConfig<KalshiGetMarketParams, KalshiGetMar
},
},
}
/**
* V2 Get Market Tool - Returns exact Kalshi API response structure
*/
export interface KalshiGetMarketV2Response {
success: boolean
output: {
market: {
ticker: string
event_ticker: string
market_type: string
title: string
subtitle: string | null
yes_sub_title: string | null
no_sub_title: string | null
open_time: string | null
close_time: string | null
expected_expiration_time: string | null
expiration_time: string | null
latest_expiration_time: string | null
settlement_timer_seconds: number | null
status: string
response_price_units: string | null
notional_value: number | null
tick_size: number | null
yes_bid: number | null
yes_ask: number | null
no_bid: number | null
no_ask: number | null
last_price: number | null
previous_yes_bid: number | null
previous_yes_ask: number | null
previous_price: number | null
volume: number | null
volume_24h: number | null
liquidity: number | null
open_interest: number | null
result: string | null
cap_strike: number | null
floor_strike: number | null
can_close_early: boolean | null
expiration_value: string | null
category: string | null
risk_limit_cents: number | null
strike_type: string | null
rules_primary: string | null
rules_secondary: string | null
settlement_source_url: string | null
custom_strike: object | null
underlying: string | null
settlement_value: number | null
cfd_contract_size: number | null
yes_fee_fp: number | null
no_fee_fp: number | null
last_price_fp: number | null
yes_bid_fp: number | null
yes_ask_fp: number | null
no_bid_fp: number | null
no_ask_fp: number | null
}
}
}
export const kalshiGetMarketV2Tool: ToolConfig<KalshiGetMarketParams, KalshiGetMarketV2Response> = {
id: 'kalshi_get_market_v2',
name: 'Get Market from Kalshi V2',
description:
'Retrieve details of a specific prediction market by ticker (V2 - full API response)',
version: '2.0.0',
params: {
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The market ticker (e.g., "KXBTC-24DEC31")',
},
},
request: {
url: (params) => buildKalshiUrl(`/markets/${params.ticker}`),
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_market_v2')
}
const m = data.market || {}
return {
success: true,
output: {
market: {
ticker: m.ticker ?? null,
event_ticker: m.event_ticker ?? null,
market_type: m.market_type ?? null,
title: m.title ?? null,
subtitle: m.subtitle ?? null,
yes_sub_title: m.yes_sub_title ?? null,
no_sub_title: m.no_sub_title ?? null,
open_time: m.open_time ?? null,
close_time: m.close_time ?? null,
expected_expiration_time: m.expected_expiration_time ?? null,
expiration_time: m.expiration_time ?? null,
latest_expiration_time: m.latest_expiration_time ?? null,
settlement_timer_seconds: m.settlement_timer_seconds ?? null,
status: m.status ?? null,
response_price_units: m.response_price_units ?? null,
notional_value: m.notional_value ?? null,
tick_size: m.tick_size ?? null,
yes_bid: m.yes_bid ?? null,
yes_ask: m.yes_ask ?? null,
no_bid: m.no_bid ?? null,
no_ask: m.no_ask ?? null,
last_price: m.last_price ?? null,
previous_yes_bid: m.previous_yes_bid ?? null,
previous_yes_ask: m.previous_yes_ask ?? null,
previous_price: m.previous_price ?? null,
volume: m.volume ?? null,
volume_24h: m.volume_24h ?? null,
liquidity: m.liquidity ?? null,
open_interest: m.open_interest ?? null,
result: m.result ?? null,
cap_strike: m.cap_strike ?? null,
floor_strike: m.floor_strike ?? null,
can_close_early: m.can_close_early ?? null,
expiration_value: m.expiration_value ?? null,
category: m.category ?? null,
risk_limit_cents: m.risk_limit_cents ?? null,
strike_type: m.strike_type ?? null,
rules_primary: m.rules_primary ?? null,
rules_secondary: m.rules_secondary ?? null,
settlement_source_url: m.settlement_source_url ?? null,
custom_strike: m.custom_strike ?? null,
underlying: m.underlying ?? null,
settlement_value: m.settlement_value ?? null,
cfd_contract_size: m.cfd_contract_size ?? null,
yes_fee_fp: m.yes_fee_fp ?? null,
no_fee_fp: m.no_fee_fp ?? null,
last_price_fp: m.last_price_fp ?? null,
yes_bid_fp: m.yes_bid_fp ?? null,
yes_ask_fp: m.yes_ask_fp ?? null,
no_bid_fp: m.no_bid_fp ?? null,
no_ask_fp: m.no_ask_fp ?? null,
},
},
}
},
outputs: {
market: {
type: 'object',
description: 'Market object with all API fields',
properties: {
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
market_type: { type: 'string', description: 'Market type' },
title: { type: 'string', description: 'Market title' },
subtitle: { type: 'string', description: 'Market subtitle' },
yes_sub_title: { type: 'string', description: 'Yes outcome subtitle' },
no_sub_title: { type: 'string', description: 'No outcome subtitle' },
open_time: { type: 'string', description: 'Market open time' },
close_time: { type: 'string', description: 'Market close time' },
expected_expiration_time: { type: 'string', description: 'Expected expiration time' },
expiration_time: { type: 'string', description: 'Expiration time' },
latest_expiration_time: { type: 'string', description: 'Latest expiration time' },
settlement_timer_seconds: { type: 'number', description: 'Settlement timer in seconds' },
status: { type: 'string', description: 'Market status' },
response_price_units: { type: 'string', description: 'Response price units' },
notional_value: { type: 'number', description: 'Notional value' },
tick_size: { type: 'number', description: 'Tick size' },
yes_bid: { type: 'number', description: 'Current yes bid price' },
yes_ask: { type: 'number', description: 'Current yes ask price' },
no_bid: { type: 'number', description: 'Current no bid price' },
no_ask: { type: 'number', description: 'Current no ask price' },
last_price: { type: 'number', description: 'Last trade price' },
previous_yes_bid: { type: 'number', description: 'Previous yes bid' },
previous_yes_ask: { type: 'number', description: 'Previous yes ask' },
previous_price: { type: 'number', description: 'Previous price' },
volume: { type: 'number', description: 'Total volume' },
volume_24h: { type: 'number', description: '24-hour volume' },
liquidity: { type: 'number', description: 'Market liquidity' },
open_interest: { type: 'number', description: 'Open interest' },
result: { type: 'string', description: 'Market result' },
cap_strike: { type: 'number', description: 'Cap strike' },
floor_strike: { type: 'number', description: 'Floor strike' },
can_close_early: { type: 'boolean', description: 'Can close early' },
expiration_value: { type: 'string', description: 'Expiration value' },
category: { type: 'string', description: 'Market category' },
risk_limit_cents: { type: 'number', description: 'Risk limit in cents' },
strike_type: { type: 'string', description: 'Strike type' },
rules_primary: { type: 'string', description: 'Primary rules' },
rules_secondary: { type: 'string', description: 'Secondary rules' },
settlement_source_url: { type: 'string', description: 'Settlement source URL' },
custom_strike: { type: 'object', description: 'Custom strike object' },
underlying: { type: 'string', description: 'Underlying asset' },
settlement_value: { type: 'number', description: 'Settlement value' },
cfd_contract_size: { type: 'number', description: 'CFD contract size' },
yes_fee_fp: { type: 'number', description: 'Yes fee (fixed-point)' },
no_fee_fp: { type: 'number', description: 'No fee (fixed-point)' },
last_price_fp: { type: 'number', description: 'Last price (fixed-point)' },
yes_bid_fp: { type: 'number', description: 'Yes bid (fixed-point)' },
yes_ask_fp: { type: 'number', description: 'Yes ask (fixed-point)' },
no_bid_fp: { type: 'number', description: 'No bid (fixed-point)' },
no_ask_fp: { type: 'number', description: 'No ask (fixed-point)' },
},
},
},
}

View File

@@ -105,3 +105,341 @@ export const kalshiGetMarketsTool: ToolConfig<KalshiGetMarketsParams, KalshiGetM
},
},
}
/**
* V2 Get Markets Tool - Returns exact Kalshi API response structure with all params
*/
export interface KalshiGetMarketsV2Params extends KalshiPaginationParams {
status?: string // unopened, open, closed, settled
seriesTicker?: string
eventTicker?: string
minCreatedTs?: number
maxCreatedTs?: number
minUpdatedTs?: number
minCloseTs?: number
maxCloseTs?: number
minSettledTs?: number
maxSettledTs?: number
tickers?: string // comma-separated list
mveFilter?: string // display or all
}
export interface KalshiGetMarketsV2Response {
success: boolean
output: {
markets: Array<{
ticker: string
event_ticker: string
market_type: string
title: string
subtitle: string | null
yes_sub_title: string | null
no_sub_title: string | null
open_time: string | null
close_time: string | null
expected_expiration_time: string | null
expiration_time: string | null
latest_expiration_time: string | null
settlement_timer_seconds: number | null
status: string
response_price_units: string | null
notional_value: number | null
tick_size: number | null
yes_bid: number | null
yes_ask: number | null
no_bid: number | null
no_ask: number | null
last_price: number | null
previous_yes_bid: number | null
previous_yes_ask: number | null
previous_price: number | null
volume: number | null
volume_24h: number | null
liquidity: number | null
open_interest: number | null
result: string | null
cap_strike: number | null
floor_strike: number | null
can_close_early: boolean | null
expiration_value: string | null
category: string | null
risk_limit_cents: number | null
strike_type: string | null
rules_primary: string | null
rules_secondary: string | null
settlement_source_url: string | null
custom_strike: object | null
underlying: string | null
settlement_value: number | null
cfd_contract_size: number | null
yes_fee_fp: number | null
no_fee_fp: number | null
last_price_fp: number | null
yes_bid_fp: number | null
yes_ask_fp: number | null
no_bid_fp: number | null
no_ask_fp: number | null
}>
cursor: string | null
}
}
export const kalshiGetMarketsV2Tool: ToolConfig<
KalshiGetMarketsV2Params,
KalshiGetMarketsV2Response
> = {
id: 'kalshi_get_markets_v2',
name: 'Get Markets from Kalshi V2',
description:
'Retrieve a list of prediction markets from Kalshi with all filtering options (V2 - full API response)',
version: '2.0.0',
params: {
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (unopened, open, closed, settled)',
},
seriesTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by series ticker',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker',
},
minCreatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum created timestamp (Unix seconds)',
},
maxCreatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum created timestamp (Unix seconds)',
},
minUpdatedTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum updated timestamp (Unix seconds)',
},
minCloseTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum close timestamp (Unix seconds)',
},
maxCloseTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum close timestamp (Unix seconds)',
},
minSettledTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum settled timestamp (Unix seconds)',
},
maxSettledTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum settled timestamp (Unix seconds)',
},
tickers: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of tickers to filter',
},
mveFilter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'MVE filter (display or all)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.status) queryParams.append('status', params.status)
if (params.seriesTicker) queryParams.append('series_ticker', params.seriesTicker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.minCreatedTs) queryParams.append('min_created_ts', params.minCreatedTs.toString())
if (params.maxCreatedTs) queryParams.append('max_created_ts', params.maxCreatedTs.toString())
if (params.minUpdatedTs) queryParams.append('min_updated_ts', params.minUpdatedTs.toString())
if (params.minCloseTs) queryParams.append('min_close_ts', params.minCloseTs.toString())
if (params.maxCloseTs) queryParams.append('max_close_ts', params.maxCloseTs.toString())
if (params.minSettledTs) queryParams.append('min_settled_ts', params.minSettledTs.toString())
if (params.maxSettledTs) queryParams.append('max_settled_ts', params.maxSettledTs.toString())
if (params.tickers) queryParams.append('tickers', params.tickers)
if (params.mveFilter) queryParams.append('mve_filter', params.mveFilter)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/markets')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_markets_v2')
}
const markets = (data.markets || []).map((m: Record<string, unknown>) => ({
ticker: m.ticker ?? null,
event_ticker: m.event_ticker ?? null,
market_type: m.market_type ?? null,
title: m.title ?? null,
subtitle: m.subtitle ?? null,
yes_sub_title: m.yes_sub_title ?? null,
no_sub_title: m.no_sub_title ?? null,
open_time: m.open_time ?? null,
close_time: m.close_time ?? null,
expected_expiration_time: m.expected_expiration_time ?? null,
expiration_time: m.expiration_time ?? null,
latest_expiration_time: m.latest_expiration_time ?? null,
settlement_timer_seconds: m.settlement_timer_seconds ?? null,
status: m.status ?? null,
response_price_units: m.response_price_units ?? null,
notional_value: m.notional_value ?? null,
tick_size: m.tick_size ?? null,
yes_bid: m.yes_bid ?? null,
yes_ask: m.yes_ask ?? null,
no_bid: m.no_bid ?? null,
no_ask: m.no_ask ?? null,
last_price: m.last_price ?? null,
previous_yes_bid: m.previous_yes_bid ?? null,
previous_yes_ask: m.previous_yes_ask ?? null,
previous_price: m.previous_price ?? null,
volume: m.volume ?? null,
volume_24h: m.volume_24h ?? null,
liquidity: m.liquidity ?? null,
open_interest: m.open_interest ?? null,
result: m.result ?? null,
cap_strike: m.cap_strike ?? null,
floor_strike: m.floor_strike ?? null,
can_close_early: m.can_close_early ?? null,
expiration_value: m.expiration_value ?? null,
category: m.category ?? null,
risk_limit_cents: m.risk_limit_cents ?? null,
strike_type: m.strike_type ?? null,
rules_primary: m.rules_primary ?? null,
rules_secondary: m.rules_secondary ?? null,
settlement_source_url: m.settlement_source_url ?? null,
custom_strike: m.custom_strike ?? null,
underlying: m.underlying ?? null,
settlement_value: m.settlement_value ?? null,
cfd_contract_size: m.cfd_contract_size ?? null,
yes_fee_fp: m.yes_fee_fp ?? null,
no_fee_fp: m.no_fee_fp ?? null,
last_price_fp: m.last_price_fp ?? null,
yes_bid_fp: m.yes_bid_fp ?? null,
yes_ask_fp: m.yes_ask_fp ?? null,
no_bid_fp: m.no_bid_fp ?? null,
no_ask_fp: m.no_ask_fp ?? null,
}))
return {
success: true,
output: {
markets,
cursor: data.cursor ?? null,
},
}
},
outputs: {
markets: {
type: 'array',
description: 'Array of market objects with all API fields',
properties: {
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
market_type: { type: 'string', description: 'Market type' },
title: { type: 'string', description: 'Market title' },
subtitle: { type: 'string', description: 'Market subtitle' },
yes_sub_title: { type: 'string', description: 'Yes outcome subtitle' },
no_sub_title: { type: 'string', description: 'No outcome subtitle' },
open_time: { type: 'string', description: 'Market open time' },
close_time: { type: 'string', description: 'Market close time' },
expected_expiration_time: { type: 'string', description: 'Expected expiration time' },
expiration_time: { type: 'string', description: 'Expiration time' },
latest_expiration_time: { type: 'string', description: 'Latest expiration time' },
settlement_timer_seconds: { type: 'number', description: 'Settlement timer in seconds' },
status: { type: 'string', description: 'Market status' },
response_price_units: { type: 'string', description: 'Response price units' },
notional_value: { type: 'number', description: 'Notional value' },
tick_size: { type: 'number', description: 'Tick size' },
yes_bid: { type: 'number', description: 'Current yes bid price' },
yes_ask: { type: 'number', description: 'Current yes ask price' },
no_bid: { type: 'number', description: 'Current no bid price' },
no_ask: { type: 'number', description: 'Current no ask price' },
last_price: { type: 'number', description: 'Last trade price' },
previous_yes_bid: { type: 'number', description: 'Previous yes bid' },
previous_yes_ask: { type: 'number', description: 'Previous yes ask' },
previous_price: { type: 'number', description: 'Previous price' },
volume: { type: 'number', description: 'Total volume' },
volume_24h: { type: 'number', description: '24-hour volume' },
liquidity: { type: 'number', description: 'Market liquidity' },
open_interest: { type: 'number', description: 'Open interest' },
result: { type: 'string', description: 'Market result' },
cap_strike: { type: 'number', description: 'Cap strike' },
floor_strike: { type: 'number', description: 'Floor strike' },
can_close_early: { type: 'boolean', description: 'Can close early' },
expiration_value: { type: 'string', description: 'Expiration value' },
category: { type: 'string', description: 'Market category' },
risk_limit_cents: { type: 'number', description: 'Risk limit in cents' },
strike_type: { type: 'string', description: 'Strike type' },
rules_primary: { type: 'string', description: 'Primary rules' },
rules_secondary: { type: 'string', description: 'Secondary rules' },
settlement_source_url: { type: 'string', description: 'Settlement source URL' },
custom_strike: { type: 'object', description: 'Custom strike object' },
underlying: { type: 'string', description: 'Underlying asset' },
settlement_value: { type: 'number', description: 'Settlement value' },
cfd_contract_size: { type: 'number', description: 'CFD contract size' },
yes_fee_fp: { type: 'number', description: 'Yes fee (fixed-point)' },
no_fee_fp: { type: 'number', description: 'No fee (fixed-point)' },
last_price_fp: { type: 'number', description: 'Last price (fixed-point)' },
yes_bid_fp: { type: 'number', description: 'Yes bid (fixed-point)' },
yes_ask_fp: { type: 'number', description: 'Yes ask (fixed-point)' },
no_bid_fp: { type: 'number', description: 'No bid (fixed-point)' },
no_ask_fp: { type: 'number', description: 'No ask (fixed-point)' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -71,3 +71,178 @@ export const kalshiGetOrderTool: ToolConfig<KalshiGetOrderParams, KalshiGetOrder
},
},
}
export interface KalshiGetOrderV2Params extends KalshiAuthParams {
orderId: string // Order ID to retrieve (required)
}
export interface KalshiGetOrderV2Response {
success: boolean
output: {
order: {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
}
}
export const kalshiGetOrderV2Tool: ToolConfig<KalshiGetOrderV2Params, KalshiGetOrderV2Response> = {
id: 'kalshi_get_order_v2',
name: 'Get Order from Kalshi V2',
description: 'Retrieve details of a specific order by ID from Kalshi (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
orderId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The order ID to retrieve',
},
},
request: {
url: (params) => buildKalshiUrl(`/portfolio/orders/${params.orderId}`),
method: 'GET',
headers: (params) => {
const path = `/trade-api/v2/portfolio/orders/${params.orderId}`
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_order_v2')
}
const order = data.order || {}
return {
success: true,
output: {
order: {
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
},
},
}
},
outputs: {
order: {
type: 'object',
description: 'Order object with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
},
}

View File

@@ -63,3 +63,135 @@ export const kalshiGetOrderbookTool: ToolConfig<
},
},
}
/**
* V2 Get Orderbook Tool - Returns exact Kalshi API response structure with depth param
* API returns tuple arrays: [price, count] for orderbook, [dollars_string, count] for _dollars variants
*/
export interface KalshiGetOrderbookV2Params {
ticker: string
depth?: number // Number of price levels to return
}
export interface KalshiGetOrderbookV2Response {
success: boolean
output: {
orderbook: {
yes: Array<[number, number]> // [price_in_cents, count]
no: Array<[number, number]> // [price_in_cents, count]
yes_dollars: Array<[string, number]> // [dollars_string, count]
no_dollars: Array<[string, number]> // [dollars_string, count]
}
orderbook_fp: {
yes_dollars: Array<[string, string]> // [dollars_string, fp_count_string]
no_dollars: Array<[string, string]> // [dollars_string, fp_count_string]
}
}
}
export const kalshiGetOrderbookV2Tool: ToolConfig<
KalshiGetOrderbookV2Params,
KalshiGetOrderbookV2Response
> = {
id: 'kalshi_get_orderbook_v2',
name: 'Get Market Orderbook from Kalshi V2',
description:
'Retrieve the orderbook (yes and no bids) for a specific market (V2 - includes depth and fp fields)',
version: '2.0.0',
params: {
ticker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Market ticker (e.g., KXBTC-24DEC31)',
},
depth: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of price levels to return (default: all)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.depth) queryParams.append('depth', params.depth.toString())
const query = queryParams.toString()
const url = buildKalshiUrl(`/markets/${params.ticker}/orderbook`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_orderbook_v2')
}
const orderbook = data.orderbook || {}
const orderbookFp = data.orderbook_fp || {}
return {
success: true,
output: {
orderbook: {
yes: orderbook.yes ?? [],
no: orderbook.no ?? [],
yes_dollars: orderbook.yes_dollars ?? [],
no_dollars: orderbook.no_dollars ?? [],
},
orderbook_fp: {
yes_dollars: orderbookFp.yes_dollars ?? [],
no_dollars: orderbookFp.no_dollars ?? [],
},
},
}
},
outputs: {
orderbook: {
type: 'object',
description: 'Orderbook with yes/no bids (legacy integer counts)',
properties: {
yes: {
type: 'array',
description: 'Yes side bids as tuples [price_cents, count]',
},
no: {
type: 'array',
description: 'No side bids as tuples [price_cents, count]',
},
yes_dollars: {
type: 'array',
description: 'Yes side bids as tuples [dollars_string, count]',
},
no_dollars: {
type: 'array',
description: 'No side bids as tuples [dollars_string, count]',
},
},
},
orderbook_fp: {
type: 'object',
description: 'Orderbook with fixed-point counts (preferred)',
properties: {
yes_dollars: {
type: 'array',
description: 'Yes side bids as tuples [dollars_string, fp_count_string]',
},
no_dollars: {
type: 'array',
description: 'No side bids as tuples [dollars_string, fp_count_string]',
},
},
},
},
}

View File

@@ -123,3 +123,250 @@ export const kalshiGetOrdersTool: ToolConfig<KalshiGetOrdersParams, KalshiGetOrd
},
},
}
export interface KalshiGetOrdersV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
status?: string // resting, canceled, executed
minTs?: string // Minimum timestamp filter (Unix timestamp)
maxTs?: string // Maximum timestamp filter (Unix timestamp)
subaccount?: string // Subaccount to filter orders
}
export interface KalshiOrderV2 {
order_id: string
user_id: string | null
client_order_id: string | null
ticker: string
side: string
action: string
type: string
status: string
yes_price: number | null
no_price: number | null
yes_price_dollars: string | null
no_price_dollars: string | null
fill_count: number | null
fill_count_fp: string | null
remaining_count: number | null
remaining_count_fp: string | null
initial_count: number | null
initial_count_fp: string | null
taker_fees: number | null
maker_fees: number | null
taker_fees_dollars: string | null
maker_fees_dollars: string | null
taker_fill_cost: number | null
maker_fill_cost: number | null
taker_fill_cost_dollars: string | null
maker_fill_cost_dollars: string | null
queue_position: number | null
expiration_time: string | null
created_time: string | null
last_update_time: string | null
self_trade_prevention_type: string | null
order_group_id: string | null
cancel_order_on_pause: boolean | null
}
export interface KalshiGetOrdersV2Response {
success: boolean
output: {
orders: KalshiOrderV2[]
cursor: string | null
}
}
export const kalshiGetOrdersV2Tool: ToolConfig<KalshiGetOrdersV2Params, KalshiGetOrdersV2Response> =
{
id: 'kalshi_get_orders_v2',
name: 'Get Orders from Kalshi V2',
description:
'Retrieve your orders from Kalshi with optional filtering (V2 with full API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker (max 10 comma-separated)',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (resting, canceled, executed)',
},
minTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp filter (Unix timestamp)',
},
maxTs: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp filter (Unix timestamp)',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount to filter orders',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-200, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.status) queryParams.append('status', params.status)
if (params.minTs) queryParams.append('min_ts', params.minTs)
if (params.maxTs) queryParams.append('max_ts', params.maxTs)
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/orders')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/orders'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_orders_v2')
}
const rawOrders = data.orders || []
const orders: KalshiOrderV2[] = rawOrders.map((order: any) => ({
order_id: order.order_id ?? null,
user_id: order.user_id ?? null,
client_order_id: order.client_order_id ?? null,
ticker: order.ticker ?? null,
side: order.side ?? null,
action: order.action ?? null,
type: order.type ?? null,
status: order.status ?? null,
yes_price: order.yes_price ?? null,
no_price: order.no_price ?? null,
yes_price_dollars: order.yes_price_dollars ?? null,
no_price_dollars: order.no_price_dollars ?? null,
fill_count: order.fill_count ?? null,
fill_count_fp: order.fill_count_fp ?? null,
remaining_count: order.remaining_count ?? null,
remaining_count_fp: order.remaining_count_fp ?? null,
initial_count: order.initial_count ?? null,
initial_count_fp: order.initial_count_fp ?? null,
taker_fees: order.taker_fees ?? null,
maker_fees: order.maker_fees ?? null,
taker_fees_dollars: order.taker_fees_dollars ?? null,
maker_fees_dollars: order.maker_fees_dollars ?? null,
taker_fill_cost: order.taker_fill_cost ?? null,
maker_fill_cost: order.maker_fill_cost ?? null,
taker_fill_cost_dollars: order.taker_fill_cost_dollars ?? null,
maker_fill_cost_dollars: order.maker_fill_cost_dollars ?? null,
queue_position: order.queue_position ?? null,
expiration_time: order.expiration_time ?? null,
created_time: order.created_time ?? null,
last_update_time: order.last_update_time ?? null,
self_trade_prevention_type: order.self_trade_prevention_type ?? null,
order_group_id: order.order_group_id ?? null,
cancel_order_on_pause: order.cancel_order_on_pause ?? null,
}))
return {
success: true,
output: {
orders,
cursor: data.cursor ?? null,
},
}
},
outputs: {
orders: {
type: 'array',
description: 'Array of order objects with full API response fields',
properties: {
order_id: { type: 'string', description: 'Order ID' },
user_id: { type: 'string', description: 'User ID' },
client_order_id: { type: 'string', description: 'Client order ID' },
ticker: { type: 'string', description: 'Market ticker' },
side: { type: 'string', description: 'Order side (yes/no)' },
action: { type: 'string', description: 'Action (buy/sell)' },
type: { type: 'string', description: 'Order type (limit/market)' },
status: { type: 'string', description: 'Order status (resting/canceled/executed)' },
yes_price: { type: 'number', description: 'Yes price in cents' },
no_price: { type: 'number', description: 'No price in cents' },
yes_price_dollars: { type: 'string', description: 'Yes price in dollars' },
no_price_dollars: { type: 'string', description: 'No price in dollars' },
fill_count: { type: 'number', description: 'Filled contract count' },
fill_count_fp: { type: 'string', description: 'Filled count (fixed-point)' },
remaining_count: { type: 'number', description: 'Remaining contracts' },
remaining_count_fp: { type: 'string', description: 'Remaining count (fixed-point)' },
initial_count: { type: 'number', description: 'Initial contract count' },
initial_count_fp: { type: 'string', description: 'Initial count (fixed-point)' },
taker_fees: { type: 'number', description: 'Taker fees in cents' },
maker_fees: { type: 'number', description: 'Maker fees in cents' },
taker_fees_dollars: { type: 'string', description: 'Taker fees in dollars' },
maker_fees_dollars: { type: 'string', description: 'Maker fees in dollars' },
taker_fill_cost: { type: 'number', description: 'Taker fill cost in cents' },
maker_fill_cost: { type: 'number', description: 'Maker fill cost in cents' },
taker_fill_cost_dollars: { type: 'string', description: 'Taker fill cost in dollars' },
maker_fill_cost_dollars: { type: 'string', description: 'Maker fill cost in dollars' },
queue_position: { type: 'number', description: 'Queue position (deprecated)' },
expiration_time: { type: 'string', description: 'Order expiration time' },
created_time: { type: 'string', description: 'Order creation time' },
last_update_time: { type: 'string', description: 'Last update time' },
self_trade_prevention_type: { type: 'string', description: 'Self-trade prevention type' },
order_group_id: { type: 'string', description: 'Order group ID' },
cancel_order_on_pause: { type: 'boolean', description: 'Cancel on market pause' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -126,3 +126,195 @@ export const kalshiGetPositionsTool: ToolConfig<
},
},
}
/**
* V2 Params for Get Positions - removes invalid settlementStatus, adds countFilter and subaccount
*/
export interface KalshiGetPositionsV2Params extends KalshiAuthParams, KalshiPaginationParams {
ticker?: string
eventTicker?: string
countFilter?: string
subaccount?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetPositionsV2Response {
success: boolean
output: {
market_positions: Array<{
ticker: string
event_ticker: string
event_title: string | null
market_title: string | null
position: number
market_exposure: number | null
realized_pnl: number | null
total_traded: number | null
resting_orders_count: number | null
fees_paid: number | null
}>
event_positions: Array<{
event_ticker: string
event_exposure: number
realized_pnl: number | null
total_cost: number | null
}> | null
cursor: string | null
}
}
export const kalshiGetPositionsV2Tool: ToolConfig<
KalshiGetPositionsV2Params,
KalshiGetPositionsV2Response
> = {
id: 'kalshi_get_positions_v2',
name: 'Get Positions from Kalshi V2',
description: 'Retrieve your open positions from Kalshi (V2 - exact API response)',
version: '2.0.0',
params: {
keyId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your Kalshi API Key ID',
},
privateKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Your RSA Private Key (PEM format)',
},
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker',
},
eventTicker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by event ticker (max 10 comma-separated)',
},
countFilter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by count (all, positive, negative). Default: all',
},
subaccount: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Subaccount to get positions for',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.eventTicker) queryParams.append('event_ticker', params.eventTicker)
if (params.countFilter) queryParams.append('count_filter', params.countFilter)
if (params.subaccount) queryParams.append('subaccount', params.subaccount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/portfolio/positions')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: (params) => {
const path = '/trade-api/v2/portfolio/positions'
return buildKalshiAuthHeaders(params.keyId, params.privateKey, 'GET', path)
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_positions_v2')
}
const marketPositions = (data.market_positions || []).map((p: Record<string, unknown>) => ({
ticker: p.ticker ?? null,
event_ticker: p.event_ticker ?? null,
event_title: p.event_title ?? null,
market_title: p.market_title ?? null,
position: p.position ?? 0,
market_exposure: p.market_exposure ?? null,
realized_pnl: p.realized_pnl ?? null,
total_traded: p.total_traded ?? null,
resting_orders_count: p.resting_orders_count ?? null,
fees_paid: p.fees_paid ?? null,
}))
const eventPositions = data.event_positions
? (data.event_positions as Array<Record<string, unknown>>).map((p) => ({
event_ticker: (p.event_ticker as string) ?? '',
event_exposure: (p.event_exposure as number) ?? 0,
realized_pnl: (p.realized_pnl as number | null) ?? null,
total_cost: (p.total_cost as number | null) ?? null,
}))
: null
return {
success: true,
output: {
market_positions: marketPositions,
event_positions: eventPositions,
cursor: data.cursor ?? null,
},
}
},
outputs: {
market_positions: {
type: 'array',
description: 'Array of market position objects',
properties: {
ticker: { type: 'string', description: 'Market ticker' },
event_ticker: { type: 'string', description: 'Event ticker' },
event_title: { type: 'string', description: 'Event title' },
market_title: { type: 'string', description: 'Market title' },
position: { type: 'number', description: 'Position size' },
market_exposure: { type: 'number', description: 'Market exposure' },
realized_pnl: { type: 'number', description: 'Realized P&L' },
total_traded: { type: 'number', description: 'Total traded' },
resting_orders_count: { type: 'number', description: 'Resting orders count' },
fees_paid: { type: 'number', description: 'Fees paid' },
},
},
event_positions: {
type: 'array',
description: 'Array of event position objects',
properties: {
event_ticker: { type: 'string', description: 'Event ticker' },
event_exposure: { type: 'number', description: 'Event exposure' },
realized_pnl: { type: 'number', description: 'Realized P&L' },
total_cost: { type: 'number', description: 'Total cost' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -65,3 +65,141 @@ export const kalshiGetSeriesByTickerTool: ToolConfig<
},
},
}
/**
* V2 Params for Get Series by Ticker
*/
export interface KalshiGetSeriesByTickerV2Params {
seriesTicker: string
includeVolume?: string
}
/**
* V2 Response matching Kalshi API exactly
*/
export interface KalshiGetSeriesByTickerV2Response {
success: boolean
output: {
series: {
ticker: string
title: string
frequency: string
category: string
tags: string[] | null
settlement_sources: Array<{
name: string
url: string
}> | null
contract_url: string | null
contract_terms_url: string | null
fee_type: string | null
fee_multiplier: number | null
additional_prohibitions: string[] | null
product_metadata: Record<string, unknown> | null
volume: number | null
volume_fp: number | null
}
}
}
export const kalshiGetSeriesByTickerV2Tool: ToolConfig<
KalshiGetSeriesByTickerV2Params,
KalshiGetSeriesByTickerV2Response
> = {
id: 'kalshi_get_series_by_ticker_v2',
name: 'Get Series by Ticker from Kalshi V2',
description: 'Retrieve details of a specific market series by ticker (V2 - exact API response)',
version: '2.0.0',
params: {
seriesTicker: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Series ticker',
},
includeVolume: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Include volume data in response (true/false)',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.includeVolume) queryParams.append('include_volume', params.includeVolume)
const query = queryParams.toString()
const url = buildKalshiUrl(`/series/${params.seriesTicker}`)
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_series_by_ticker_v2')
}
const series = data.series || data
const settlementSources = series.settlement_sources
? (series.settlement_sources as Array<Record<string, unknown>>).map((s) => ({
name: (s.name as string) ?? null,
url: (s.url as string) ?? null,
}))
: null
return {
success: true,
output: {
series: {
ticker: series.ticker ?? null,
title: series.title ?? null,
frequency: series.frequency ?? null,
category: series.category ?? null,
tags: series.tags ?? null,
settlement_sources: settlementSources,
contract_url: series.contract_url ?? null,
contract_terms_url: series.contract_terms_url ?? null,
fee_type: series.fee_type ?? null,
fee_multiplier: series.fee_multiplier ?? null,
additional_prohibitions: series.additional_prohibitions ?? null,
product_metadata: series.product_metadata ?? null,
volume: series.volume ?? null,
volume_fp: series.volume_fp ?? null,
},
},
}
},
outputs: {
series: {
type: 'object',
description: 'Series object with full details matching Kalshi API response',
properties: {
ticker: { type: 'string', description: 'Series ticker' },
title: { type: 'string', description: 'Series title' },
frequency: { type: 'string', description: 'Event frequency' },
category: { type: 'string', description: 'Series category' },
tags: { type: 'array', description: 'Series tags' },
settlement_sources: { type: 'array', description: 'Settlement sources' },
contract_url: { type: 'string', description: 'Contract URL' },
contract_terms_url: { type: 'string', description: 'Contract terms URL' },
fee_type: { type: 'string', description: 'Fee type' },
fee_multiplier: { type: 'number', description: 'Fee multiplier' },
additional_prohibitions: { type: 'array', description: 'Additional prohibitions' },
product_metadata: { type: 'object', description: 'Product metadata' },
volume: { type: 'number', description: 'Series volume' },
volume_fp: { type: 'number', description: 'Volume (fixed-point)' },
},
},
},
}

View File

@@ -80,3 +80,138 @@ export const kalshiGetTradesTool: ToolConfig<KalshiGetTradesParams, KalshiGetTra
},
},
}
/**
* V2 Get Trades Tool - Returns exact Kalshi API response structure with additional params
*/
export interface KalshiGetTradesV2Params extends KalshiPaginationParams {
ticker?: string // Filter by market ticker
minTs?: number // Minimum timestamp (Unix seconds)
maxTs?: number // Maximum timestamp (Unix seconds)
}
export interface KalshiGetTradesV2Response {
success: boolean
output: {
trades: Array<{
trade_id: string | null
ticker: string
yes_price: number | null
no_price: number | null
count: number | null
count_fp: number | null
created_time: string | null
taker_side: string | null
}>
cursor: string | null
}
}
export const kalshiGetTradesV2Tool: ToolConfig<KalshiGetTradesV2Params, KalshiGetTradesV2Response> =
{
id: 'kalshi_get_trades_v2',
name: 'Get Trades from Kalshi V2',
description:
'Retrieve recent trades with additional filtering options (V2 - includes trade_id and count_fp)',
version: '2.0.0',
params: {
ticker: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by market ticker',
},
minTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Minimum timestamp (Unix seconds)',
},
maxTs: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Maximum timestamp (Unix seconds)',
},
limit: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Number of results (1-1000, default: 100)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.ticker) queryParams.append('ticker', params.ticker)
if (params.minTs) queryParams.append('min_ts', params.minTs.toString())
if (params.maxTs) queryParams.append('max_ts', params.maxTs.toString())
if (params.limit) queryParams.append('limit', params.limit)
if (params.cursor) queryParams.append('cursor', params.cursor)
const query = queryParams.toString()
const url = buildKalshiUrl('/markets/trades')
return query ? `${url}?${query}` : url
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handleKalshiError(data, response.status, 'get_trades_v2')
}
const trades = (data.trades || []).map((t: Record<string, unknown>) => ({
trade_id: t.trade_id ?? null,
ticker: t.ticker ?? null,
yes_price: t.yes_price ?? null,
no_price: t.no_price ?? null,
count: t.count ?? null,
count_fp: t.count_fp ?? null,
created_time: t.created_time ?? null,
taker_side: t.taker_side ?? null,
}))
return {
success: true,
output: {
trades,
cursor: data.cursor ?? null,
},
}
},
outputs: {
trades: {
type: 'array',
description: 'Array of trade objects with trade_id and count_fp',
properties: {
trade_id: { type: 'string', description: 'Trade ID' },
ticker: { type: 'string', description: 'Market ticker' },
yes_price: { type: 'number', description: 'Yes price' },
no_price: { type: 'number', description: 'No price' },
count: { type: 'number', description: 'Number of contracts' },
count_fp: { type: 'number', description: 'Count (fixed-point)' },
created_time: { type: 'string', description: 'Trade creation time' },
taker_side: { type: 'string', description: 'Taker side (yes/no)' },
},
},
cursor: {
type: 'string',
description: 'Pagination cursor for fetching more results',
},
},
}

View File

@@ -1,17 +1,17 @@
export { kalshiAmendOrderTool } from './amend_order'
export { kalshiCancelOrderTool } from './cancel_order'
export { kalshiCreateOrderTool } from './create_order'
export { kalshiGetBalanceTool } from './get_balance'
export { kalshiGetCandlesticksTool } from './get_candlesticks'
export { kalshiGetEventTool } from './get_event'
export { kalshiGetEventsTool } from './get_events'
export { kalshiGetExchangeStatusTool } from './get_exchange_status'
export { kalshiGetFillsTool } from './get_fills'
export { kalshiGetMarketTool } from './get_market'
export { kalshiGetMarketsTool } from './get_markets'
export { kalshiGetOrderTool } from './get_order'
export { kalshiGetOrderbookTool } from './get_orderbook'
export { kalshiGetOrdersTool } from './get_orders'
export { kalshiGetPositionsTool } from './get_positions'
export { kalshiGetSeriesByTickerTool } from './get_series_by_ticker'
export { kalshiGetTradesTool } from './get_trades'
export { kalshiAmendOrderTool, kalshiAmendOrderV2Tool } from './amend_order'
export { kalshiCancelOrderTool, kalshiCancelOrderV2Tool } from './cancel_order'
export { kalshiCreateOrderTool, kalshiCreateOrderV2Tool } from './create_order'
export { kalshiGetBalanceTool, kalshiGetBalanceV2Tool } from './get_balance'
export { kalshiGetCandlesticksTool, kalshiGetCandlesticksV2Tool } from './get_candlesticks'
export { kalshiGetEventTool, kalshiGetEventV2Tool } from './get_event'
export { kalshiGetEventsTool, kalshiGetEventsV2Tool } from './get_events'
export { kalshiGetExchangeStatusTool, kalshiGetExchangeStatusV2Tool } from './get_exchange_status'
export { kalshiGetFillsTool, kalshiGetFillsV2Tool } from './get_fills'
export { kalshiGetMarketTool, kalshiGetMarketV2Tool } from './get_market'
export { kalshiGetMarketsTool, kalshiGetMarketsV2Tool } from './get_markets'
export { kalshiGetOrderTool, kalshiGetOrderV2Tool } from './get_order'
export { kalshiGetOrderbookTool, kalshiGetOrderbookV2Tool } from './get_orderbook'
export { kalshiGetOrdersTool, kalshiGetOrdersV2Tool } from './get_orders'
export { kalshiGetPositionsTool, kalshiGetPositionsV2Tool } from './get_positions'
export { kalshiGetSeriesByTickerTool, kalshiGetSeriesByTickerV2Tool } from './get_series_by_ticker'
export { kalshiGetTradesTool, kalshiGetTradesV2Tool } from './get_trades'

View File

@@ -0,0 +1,206 @@
import type { PolymarketActivity } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
export interface PolymarketGetActivityParams {
user: string
limit?: string
offset?: string
market?: string
eventId?: string
type?: string
start?: number
end?: number
sortBy?: string
sortDirection?: string
side?: string
}
export interface PolymarketGetActivityResponse {
success: boolean
output: {
activity: PolymarketActivity[]
}
}
export const polymarketGetActivityTool: ToolConfig<
PolymarketGetActivityParams,
PolymarketGetActivityResponse
> = {
id: 'polymarket_get_activity',
name: 'Get Activity from Polymarket',
description:
'Retrieve on-chain activity for a user including trades, splits, merges, redemptions, rewards, and conversions',
version: '1.0.0',
params: {
user: {
type: 'string',
required: true,
description: 'User wallet address (0x-prefixed)',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Maximum results (default: 100, max: 500)',
visibility: 'user-or-llm',
},
offset: {
type: 'string',
required: false,
description: 'Pagination offset (default: 0, max: 10000)',
visibility: 'user-or-llm',
},
market: {
type: 'string',
required: false,
description: 'Comma-separated condition IDs (mutually exclusive with eventId)',
visibility: 'user-or-llm',
},
eventId: {
type: 'string',
required: false,
description: 'Comma-separated event IDs (mutually exclusive with market)',
visibility: 'user-or-llm',
},
type: {
type: 'string',
required: false,
description:
'Activity type filter: TRADE, SPLIT, MERGE, REDEEM, REWARD, CONVERSION, MAKER_REBATE',
visibility: 'user-or-llm',
},
start: {
type: 'number',
required: false,
description: 'Start timestamp (Unix seconds)',
visibility: 'user-or-llm',
},
end: {
type: 'number',
required: false,
description: 'End timestamp (Unix seconds)',
visibility: 'user-or-llm',
},
sortBy: {
type: 'string',
required: false,
description: 'Sort field: TIMESTAMP, TOKENS, or CASH (default: TIMESTAMP)',
visibility: 'user-or-llm',
},
sortDirection: {
type: 'string',
required: false,
description: 'Sort direction: ASC or DESC (default: DESC)',
visibility: 'user-or-llm',
},
side: {
type: 'string',
required: false,
description: 'Trade side filter: BUY or SELL (only applies to trades)',
visibility: 'user-or-llm',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('user', params.user)
if (params.limit) queryParams.append('limit', params.limit)
if (params.offset) queryParams.append('offset', params.offset)
if (params.market) queryParams.append('market', params.market)
if (params.eventId) queryParams.append('eventId', params.eventId)
if (params.type) queryParams.append('type', params.type)
if (params.start != null && !Number.isNaN(params.start))
queryParams.append('start', String(params.start))
if (params.end != null && !Number.isNaN(params.end))
queryParams.append('end', String(params.end))
if (params.sortBy) queryParams.append('sortBy', params.sortBy)
if (params.sortDirection) queryParams.append('sortDirection', params.sortDirection)
if (params.side) queryParams.append('side', params.side)
return `${buildDataUrl('/activity')}?${queryParams.toString()}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handlePolymarketError(data, response.status, 'get_activity')
}
const activityList = Array.isArray(data) ? data : []
const activity: PolymarketActivity[] = activityList.map((a: any) => ({
proxyWallet: a.proxyWallet ?? null,
timestamp: a.timestamp ?? 0,
conditionId: a.conditionId ?? '',
type: a.type ?? '',
size: a.size ?? 0,
usdcSize: a.usdcSize ?? 0,
transactionHash: a.transactionHash ?? null,
price: a.price ?? null,
asset: a.asset ?? null,
side: a.side ?? null,
outcomeIndex: a.outcomeIndex ?? null,
title: a.title ?? null,
slug: a.slug ?? null,
icon: a.icon ?? null,
eventSlug: a.eventSlug ?? null,
outcome: a.outcome ?? null,
name: a.name ?? null,
pseudonym: a.pseudonym ?? null,
bio: a.bio ?? null,
profileImage: a.profileImage ?? null,
profileImageOptimized: a.profileImageOptimized ?? null,
}))
return {
success: true,
output: {
activity,
},
}
},
outputs: {
activity: {
type: 'array',
description: 'Array of activity entries',
items: {
type: 'object',
properties: {
proxyWallet: { type: 'string', description: 'User proxy wallet address' },
timestamp: { type: 'number', description: 'Unix timestamp of activity' },
conditionId: { type: 'string', description: 'Market condition ID' },
type: {
type: 'string',
description: 'Activity type (TRADE, SPLIT, MERGE, REDEEM, REWARD, CONVERSION)',
},
size: { type: 'number', description: 'Size in tokens' },
usdcSize: { type: 'number', description: 'Size in USDC' },
transactionHash: { type: 'string', description: 'Blockchain transaction hash' },
price: { type: 'number', description: 'Price (for trades)' },
asset: { type: 'string', description: 'Asset/token ID' },
side: { type: 'string', description: 'Trade side (BUY/SELL)' },
outcomeIndex: { type: 'number', description: 'Outcome index' },
title: { type: 'string', description: 'Market title' },
slug: { type: 'string', description: 'Market slug' },
icon: { type: 'string', description: 'Market icon URL' },
eventSlug: { type: 'string', description: 'Event slug' },
outcome: { type: 'string', description: 'Outcome name' },
name: { type: 'string', description: 'User display name' },
pseudonym: { type: 'string', description: 'User pseudonym' },
bio: { type: 'string', description: 'User bio' },
profileImage: { type: 'string', description: 'User profile image URL' },
profileImageOptimized: { type: 'string', description: 'Optimized profile image URL' },
},
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketEvent } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketEvent } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetEventParams {
eventId?: string // Event ID
@@ -71,6 +71,26 @@ export const polymarketGetEventTool: ToolConfig<
event: {
type: 'object',
description: 'Event object with details',
properties: {
id: { type: 'string', description: 'Event ID' },
ticker: { type: 'string', description: 'Event ticker' },
slug: { type: 'string', description: 'Event slug' },
title: { type: 'string', description: 'Event title' },
description: { type: 'string', description: 'Event description' },
startDate: { type: 'string', description: 'Start date' },
creationDate: { type: 'string', description: 'Creation date' },
endDate: { type: 'string', description: 'End date' },
image: { type: 'string', description: 'Event image URL' },
icon: { type: 'string', description: 'Event icon URL' },
active: { type: 'boolean', description: 'Whether event is active' },
closed: { type: 'boolean', description: 'Whether event is closed' },
archived: { type: 'boolean', description: 'Whether event is archived' },
liquidity: { type: 'number', description: 'Total liquidity' },
volume: { type: 'number', description: 'Total volume' },
openInterest: { type: 'number', description: 'Open interest' },
commentCount: { type: 'number', description: 'Comment count' },
markets: { type: 'array', description: 'Array of markets in this event' },
},
},
},
}

View File

@@ -1,12 +1,12 @@
import type { PolymarketEvent, PolymarketPaginationParams } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketEvent, PolymarketPaginationParams } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetEventsParams extends PolymarketPaginationParams {
closed?: string // 'true' or 'false' - filter for closed/active events
order?: string // sort field (e.g., 'volume', 'liquidity', 'startDate', 'endDate')
ascending?: string // 'true' or 'false' - sort direction
tagId?: string // filter by tag ID
closed?: string
order?: string
ascending?: string
tagId?: string
}
export interface PolymarketGetEventsResponse {
@@ -29,7 +29,7 @@ export const polymarketGetEventsTool: ToolConfig<
closed: {
type: 'string',
required: false,
description: 'Filter by closed status (true/false). Use false for active events only.',
description: 'Filter by closed status (true/false). Use false for open events only.',
visibility: 'user-or-llm',
},
order: {
@@ -71,13 +71,11 @@ export const polymarketGetEventsTool: ToolConfig<
if (params.order) queryParams.append('order', params.order)
if (params.ascending) queryParams.append('ascending', params.ascending)
if (params.tagId) queryParams.append('tag_id', params.tagId)
// Default limit to 50 to prevent browser crashes from large data sets
queryParams.append('limit', params.limit || '50')
if (params.offset) queryParams.append('offset', params.offset)
const query = queryParams.toString()
const url = buildGammaUrl('/events')
return `${url}?${query}`
return `${url}?${queryParams.toString()}`
},
method: 'GET',
headers: () => ({
@@ -92,7 +90,6 @@ export const polymarketGetEventsTool: ToolConfig<
handlePolymarketError(data, response.status, 'get_events')
}
// Response is an array of events
const events = Array.isArray(data) ? data : []
return {
@@ -107,6 +104,26 @@ export const polymarketGetEventsTool: ToolConfig<
events: {
type: 'array',
description: 'Array of event objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Event ID' },
ticker: { type: 'string', description: 'Event ticker' },
slug: { type: 'string', description: 'Event slug' },
title: { type: 'string', description: 'Event title' },
description: { type: 'string', description: 'Event description' },
startDate: { type: 'string', description: 'Start date' },
endDate: { type: 'string', description: 'End date' },
image: { type: 'string', description: 'Event image URL' },
icon: { type: 'string', description: 'Event icon URL' },
active: { type: 'boolean', description: 'Whether event is active' },
closed: { type: 'boolean', description: 'Whether event is closed' },
archived: { type: 'boolean', description: 'Whether event is archived' },
liquidity: { type: 'number', description: 'Total liquidity' },
volume: { type: 'number', description: 'Total volume' },
markets: { type: 'array', description: 'Array of markets in this event' },
},
},
},
},
}

View File

@@ -0,0 +1,132 @@
import type { PolymarketMarketHolders } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
export interface PolymarketGetHoldersParams {
market: string
limit?: string
minBalance?: string
}
export interface PolymarketGetHoldersResponse {
success: boolean
output: {
holders: PolymarketMarketHolders[]
}
}
export const polymarketGetHoldersTool: ToolConfig<
PolymarketGetHoldersParams,
PolymarketGetHoldersResponse
> = {
id: 'polymarket_get_holders',
name: 'Get Market Holders from Polymarket',
description: 'Retrieve top holders of a specific market token',
version: '1.0.0',
params: {
market: {
type: 'string',
required: true,
description: 'Comma-separated list of condition IDs',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Number of holders to return (0-20, default: 20)',
visibility: 'user-or-llm',
},
minBalance: {
type: 'string',
required: false,
description: 'Minimum balance threshold (default: 1)',
visibility: 'user-or-llm',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('market', params.market)
if (params.limit) queryParams.append('limit', params.limit)
if (params.minBalance) queryParams.append('minBalance', params.minBalance)
return `${buildDataUrl('/holders')}?${queryParams.toString()}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handlePolymarketError(data, response.status, 'get_holders')
}
const marketHolders = Array.isArray(data) ? data : []
const holders: PolymarketMarketHolders[] = marketHolders.map((mh: any) => ({
token: mh.token ?? '',
holders: (mh.holders ?? []).map((h: any) => ({
proxyWallet: h.proxyWallet ?? '',
bio: h.bio ?? null,
asset: h.asset ?? '',
pseudonym: h.pseudonym ?? null,
amount: h.amount ?? 0,
displayUsernamePublic: h.displayUsernamePublic ?? false,
outcomeIndex: h.outcomeIndex ?? 0,
name: h.name ?? null,
profileImage: h.profileImage ?? null,
profileImageOptimized: h.profileImageOptimized ?? null,
})),
}))
return {
success: true,
output: {
holders,
},
}
},
outputs: {
holders: {
type: 'array',
description: 'Array of market holder groups by token',
items: {
type: 'object',
properties: {
token: { type: 'string', description: 'Token/asset ID' },
holders: {
type: 'array',
description: 'Array of holders for this token',
items: {
type: 'object',
properties: {
proxyWallet: { type: 'string', description: 'Holder wallet address' },
bio: { type: 'string', description: 'Holder bio' },
asset: { type: 'string', description: 'Asset ID' },
pseudonym: { type: 'string', description: 'Holder pseudonym' },
amount: { type: 'number', description: 'Amount held' },
displayUsernamePublic: {
type: 'boolean',
description: 'Whether username is publicly displayed',
},
outcomeIndex: { type: 'number', description: 'Outcome index' },
name: { type: 'string', description: 'Holder display name' },
profileImage: { type: 'string', description: 'Profile image URL' },
profileImageOptimized: {
type: 'string',
description: 'Optimized profile image URL',
},
},
},
},
},
},
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetLastTradePriceParams {
tokenId: string // The token ID (CLOB token ID from market)
@@ -9,6 +9,7 @@ export interface PolymarketGetLastTradePriceResponse {
success: boolean
output: {
price: string
side: string
}
}
@@ -52,7 +53,8 @@ export const polymarketGetLastTradePriceTool: ToolConfig<
return {
success: true,
output: {
price: typeof data === 'string' ? data : data.price || '',
price: data.price ?? '',
side: data.side ?? '',
},
}
},
@@ -62,5 +64,9 @@ export const polymarketGetLastTradePriceTool: ToolConfig<
type: 'string',
description: 'Last trade price',
},
side: {
type: 'string',
description: 'Side of the last trade (BUY or SELL)',
},
},
}

View File

@@ -0,0 +1,143 @@
import type { PolymarketLeaderboardEntry } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
export interface PolymarketGetLeaderboardParams {
category?: string
timePeriod?: string
orderBy?: string
limit?: string
offset?: string
user?: string
userName?: string
}
export interface PolymarketGetLeaderboardResponse {
success: boolean
output: {
leaderboard: PolymarketLeaderboardEntry[]
}
}
export const polymarketGetLeaderboardTool: ToolConfig<
PolymarketGetLeaderboardParams,
PolymarketGetLeaderboardResponse
> = {
id: 'polymarket_get_leaderboard',
name: 'Get Leaderboard from Polymarket',
description: 'Retrieve trader leaderboard rankings by profit/loss or volume',
version: '1.0.0',
params: {
category: {
type: 'string',
required: false,
description:
'Category filter: OVERALL, POLITICS, SPORTS, CRYPTO, CULTURE, MENTIONS, WEATHER, ECONOMICS, TECH, FINANCE (default: OVERALL)',
visibility: 'user-or-llm',
},
timePeriod: {
type: 'string',
required: false,
description: 'Time period: DAY, WEEK, MONTH, ALL (default: DAY)',
visibility: 'user-or-llm',
},
orderBy: {
type: 'string',
required: false,
description: 'Order by: PNL or VOL (default: PNL)',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Number of results (1-50, default: 25)',
visibility: 'user-or-llm',
},
offset: {
type: 'string',
required: false,
description: 'Pagination offset (0-1000, default: 0)',
visibility: 'user-or-llm',
},
user: {
type: 'string',
required: false,
description: 'Filter by specific user wallet address',
visibility: 'user-or-llm',
},
userName: {
type: 'string',
required: false,
description: 'Filter by username',
visibility: 'user-or-llm',
},
},
request: {
url: (params) => {
const queryParams = new URLSearchParams()
if (params.category) queryParams.append('category', params.category)
if (params.timePeriod) queryParams.append('timePeriod', params.timePeriod)
if (params.orderBy) queryParams.append('orderBy', params.orderBy)
if (params.limit) queryParams.append('limit', params.limit)
if (params.offset) queryParams.append('offset', params.offset)
if (params.user) queryParams.append('user', params.user)
if (params.userName) queryParams.append('userName', params.userName)
const query = queryParams.toString()
return query ? `${buildDataUrl('/v1/leaderboard')}?${query}` : buildDataUrl('/v1/leaderboard')
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
handlePolymarketError(data, response.status, 'get_leaderboard')
}
const entries = Array.isArray(data) ? data : []
const leaderboard: PolymarketLeaderboardEntry[] = entries.map((entry: any) => ({
rank: entry.rank ?? '',
proxyWallet: entry.proxyWallet ?? '',
userName: entry.userName ?? null,
vol: entry.vol ?? 0,
pnl: entry.pnl ?? 0,
profileImage: entry.profileImage ?? null,
xUsername: entry.xUsername ?? null,
verifiedBadge: entry.verifiedBadge ?? false,
}))
return {
success: true,
output: {
leaderboard,
},
}
},
outputs: {
leaderboard: {
type: 'array',
description: 'Array of leaderboard entries',
items: {
type: 'object',
properties: {
rank: { type: 'string', description: 'Leaderboard rank position' },
proxyWallet: { type: 'string', description: 'User proxy wallet address' },
userName: { type: 'string', description: 'User display name' },
vol: { type: 'number', description: 'Trading volume' },
pnl: { type: 'number', description: 'Profit and loss' },
profileImage: { type: 'string', description: 'User profile image URL' },
xUsername: { type: 'string', description: 'Twitter/X username' },
verifiedBadge: { type: 'boolean', description: 'Whether user has verified badge' },
},
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketMarket } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketMarket } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetMarketParams {
marketId?: string // Market ID
@@ -71,6 +71,30 @@ export const polymarketGetMarketTool: ToolConfig<
market: {
type: 'object',
description: 'Market object with details',
properties: {
id: { type: 'string', description: 'Market ID' },
question: { type: 'string', description: 'Market question' },
conditionId: { type: 'string', description: 'Condition ID' },
slug: { type: 'string', description: 'Market slug' },
resolutionSource: { type: 'string', description: 'Resolution source' },
endDate: { type: 'string', description: 'End date' },
startDate: { type: 'string', description: 'Start date' },
image: { type: 'string', description: 'Market image URL' },
icon: { type: 'string', description: 'Market icon URL' },
description: { type: 'string', description: 'Market description' },
outcomes: { type: 'string', description: 'Outcomes JSON string' },
outcomePrices: { type: 'string', description: 'Outcome prices JSON string' },
volume: { type: 'string', description: 'Total volume' },
liquidity: { type: 'string', description: 'Total liquidity' },
active: { type: 'boolean', description: 'Whether market is active' },
closed: { type: 'boolean', description: 'Whether market is closed' },
archived: { type: 'boolean', description: 'Whether market is archived' },
volumeNum: { type: 'number', description: 'Volume as number' },
liquidityNum: { type: 'number', description: 'Liquidity as number' },
clobTokenIds: { type: 'array', description: 'CLOB token IDs' },
acceptingOrders: { type: 'boolean', description: 'Whether accepting orders' },
negRisk: { type: 'boolean', description: 'Whether negative risk' },
},
},
},
}

View File

@@ -1,12 +1,12 @@
import type { PolymarketMarket, PolymarketPaginationParams } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketMarket, PolymarketPaginationParams } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetMarketsParams extends PolymarketPaginationParams {
closed?: string // 'true' or 'false' - filter for closed/active markets
order?: string // sort field - use camelCase (e.g., 'volumeNum', 'liquidityNum', 'startDate', 'endDate')
ascending?: string // 'true' or 'false' - sort direction
tagId?: string // filter by tag ID
closed?: string
order?: string
ascending?: string
tagId?: string
}
export interface PolymarketGetMarketsResponse {
@@ -29,7 +29,7 @@ export const polymarketGetMarketsTool: ToolConfig<
closed: {
type: 'string',
required: false,
description: 'Filter by closed status (true/false). Use false for active markets only.',
description: 'Filter by closed status (true/false). Use false for open markets only.',
visibility: 'user-or-llm',
},
order: {
@@ -71,13 +71,11 @@ export const polymarketGetMarketsTool: ToolConfig<
if (params.order) queryParams.append('order', params.order)
if (params.ascending) queryParams.append('ascending', params.ascending)
if (params.tagId) queryParams.append('tag_id', params.tagId)
// Default limit to 50 to prevent browser crashes from large data sets
queryParams.append('limit', params.limit || '50')
if (params.offset) queryParams.append('offset', params.offset)
const query = queryParams.toString()
const url = buildGammaUrl('/markets')
return `${url}?${query}`
return `${url}?${queryParams.toString()}`
},
method: 'GET',
headers: () => ({
@@ -107,6 +105,26 @@ export const polymarketGetMarketsTool: ToolConfig<
markets: {
type: 'array',
description: 'Array of market objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Market ID' },
question: { type: 'string', description: 'Market question' },
conditionId: { type: 'string', description: 'Condition ID' },
slug: { type: 'string', description: 'Market slug' },
endDate: { type: 'string', description: 'End date' },
image: { type: 'string', description: 'Market image URL' },
outcomes: { type: 'string', description: 'Outcomes JSON string' },
outcomePrices: { type: 'string', description: 'Outcome prices JSON string' },
volume: { type: 'string', description: 'Total volume' },
liquidity: { type: 'string', description: 'Total liquidity' },
active: { type: 'boolean', description: 'Whether market is active' },
closed: { type: 'boolean', description: 'Whether market is closed' },
volumeNum: { type: 'number', description: 'Volume as number' },
liquidityNum: { type: 'number', description: 'Liquidity as number' },
clobTokenIds: { type: 'array', description: 'CLOB token IDs' },
},
},
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetMidpointParams {
tokenId: string // The token ID (CLOB token ID from market)

View File

@@ -1,6 +1,6 @@
import type { PolymarketOrderBook } from '@/tools/polymarket/types'
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketOrderBook } from './types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetOrderbookParams {
tokenId: string // The token ID (CLOB token ID from market)
@@ -50,10 +50,22 @@ export const polymarketGetOrderbookTool: ToolConfig<
handlePolymarketError(data, response.status, 'get_orderbook')
}
const orderbook: PolymarketOrderBook = {
market: data.market ?? '',
asset_id: data.asset_id ?? '',
hash: data.hash ?? '',
timestamp: data.timestamp ?? '',
bids: data.bids ?? [],
asks: data.asks ?? [],
min_order_size: data.min_order_size ?? '0',
tick_size: data.tick_size ?? '0',
neg_risk: data.neg_risk ?? false,
}
return {
success: true,
output: {
orderbook: data,
orderbook,
},
}
},
@@ -62,6 +74,37 @@ export const polymarketGetOrderbookTool: ToolConfig<
orderbook: {
type: 'object',
description: 'Order book with bids and asks arrays',
properties: {
market: { type: 'string', description: 'Market identifier' },
asset_id: { type: 'string', description: 'Asset token ID' },
hash: { type: 'string', description: 'Order book hash' },
timestamp: { type: 'string', description: 'Timestamp' },
bids: {
type: 'array',
description: 'Bid orders',
items: {
type: 'object',
properties: {
price: { type: 'string', description: 'Bid price' },
size: { type: 'string', description: 'Bid size' },
},
},
},
asks: {
type: 'array',
description: 'Ask orders',
items: {
type: 'object',
properties: {
price: { type: 'string', description: 'Ask price' },
size: { type: 'string', description: 'Ask size' },
},
},
},
min_order_size: { type: 'string', description: 'Minimum order size' },
tick_size: { type: 'string', description: 'Tick size' },
neg_risk: { type: 'boolean', description: 'Whether negative risk' },
},
},
},
}

View File

@@ -1,10 +1,19 @@
import type { PolymarketPosition } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPosition } from './types'
import { buildDataUrl, handlePolymarketError } from './types'
export interface PolymarketGetPositionsParams {
user: string // Wallet address (required)
market?: string // Optional market filter
user: string
market?: string
eventId?: string
sizeThreshold?: string
redeemable?: string
mergeable?: string
sortBy?: string
sortDirection?: string
title?: string
limit?: string
offset?: string
}
export interface PolymarketGetPositionsResponse {
@@ -33,7 +42,63 @@ export const polymarketGetPositionsTool: ToolConfig<
market: {
type: 'string',
required: false,
description: 'Optional market ID to filter positions',
description:
'Condition IDs to filter positions (comma-separated, mutually exclusive with eventId)',
visibility: 'user-or-llm',
},
eventId: {
type: 'string',
required: false,
description: 'Event ID to filter positions (mutually exclusive with market)',
visibility: 'user-or-llm',
},
sizeThreshold: {
type: 'string',
required: false,
description: 'Minimum position size threshold (default: 1)',
visibility: 'user-or-llm',
},
redeemable: {
type: 'string',
required: false,
description: 'Filter for redeemable positions only (true/false)',
visibility: 'user-or-llm',
},
mergeable: {
type: 'string',
required: false,
description: 'Filter for mergeable positions only (true/false)',
visibility: 'user-or-llm',
},
sortBy: {
type: 'string',
required: false,
description:
'Sort field (TOKENS, CURRENT, INITIAL, CASHPNL, PERCENTPNL, TITLE, RESOLVING, PRICE, AVGPRICE)',
visibility: 'user-or-llm',
},
sortDirection: {
type: 'string',
required: false,
description: 'Sort direction (ASC or DESC)',
visibility: 'user-or-llm',
},
title: {
type: 'string',
required: false,
description: 'Search filter by title',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Number of results per page',
visibility: 'user-or-llm',
},
offset: {
type: 'string',
required: false,
description: 'Pagination offset',
visibility: 'user-or-llm',
},
},
@@ -43,6 +108,15 @@ export const polymarketGetPositionsTool: ToolConfig<
const queryParams = new URLSearchParams()
queryParams.append('user', params.user)
if (params.market) queryParams.append('market', params.market)
if (params.eventId) queryParams.append('eventId', params.eventId)
if (params.sizeThreshold) queryParams.append('sizeThreshold', params.sizeThreshold)
if (params.redeemable) queryParams.append('redeemable', params.redeemable)
if (params.mergeable) queryParams.append('mergeable', params.mergeable)
if (params.sortBy) queryParams.append('sortBy', params.sortBy)
if (params.sortDirection) queryParams.append('sortDirection', params.sortDirection)
if (params.title) queryParams.append('title', params.title)
if (params.limit) queryParams.append('limit', params.limit)
if (params.offset) queryParams.append('offset', params.offset)
return `${buildDataUrl('/positions')}?${queryParams.toString()}`
},
@@ -59,8 +133,34 @@ export const polymarketGetPositionsTool: ToolConfig<
handlePolymarketError(data, response.status, 'get_positions')
}
// Response is an array of positions
const positions = Array.isArray(data) ? data : []
const rawPositions = Array.isArray(data) ? data : []
const positions: PolymarketPosition[] = rawPositions.map((p: Record<string, unknown>) => ({
proxyWallet: (p.proxyWallet as string) ?? null,
asset: (p.asset as string) ?? '',
conditionId: (p.conditionId as string) ?? '',
size: (p.size as number) ?? 0,
avgPrice: (p.avgPrice as number) ?? 0,
initialValue: (p.initialValue as number) ?? 0,
currentValue: (p.currentValue as number) ?? 0,
cashPnl: (p.cashPnl as number) ?? 0,
percentPnl: (p.percentPnl as number) ?? 0,
totalBought: (p.totalBought as number) ?? 0,
realizedPnl: (p.realizedPnl as number) ?? 0,
percentRealizedPnl: (p.percentRealizedPnl as number) ?? 0,
curPrice: (p.curPrice as number) ?? 0,
redeemable: (p.redeemable as boolean) ?? false,
mergeable: (p.mergeable as boolean) ?? false,
title: (p.title as string) ?? null,
slug: (p.slug as string) ?? null,
icon: (p.icon as string) ?? null,
eventSlug: (p.eventSlug as string) ?? null,
outcome: (p.outcome as string) ?? null,
outcomeIndex: (p.outcomeIndex as number) ?? null,
oppositeOutcome: (p.oppositeOutcome as string) ?? null,
oppositeAsset: (p.oppositeAsset as string) ?? null,
endDate: (p.endDate as string) ?? null,
negativeRisk: (p.negativeRisk as boolean) ?? false,
}))
return {
success: true,
@@ -74,6 +174,36 @@ export const polymarketGetPositionsTool: ToolConfig<
positions: {
type: 'array',
description: 'Array of position objects',
items: {
type: 'object',
properties: {
proxyWallet: { type: 'string', description: 'Proxy wallet address' },
asset: { type: 'string', description: 'Asset token ID' },
conditionId: { type: 'string', description: 'Condition ID' },
size: { type: 'number', description: 'Position size' },
avgPrice: { type: 'number', description: 'Average price' },
initialValue: { type: 'number', description: 'Initial value' },
currentValue: { type: 'number', description: 'Current value' },
cashPnl: { type: 'number', description: 'Cash profit/loss' },
percentPnl: { type: 'number', description: 'Percent profit/loss' },
totalBought: { type: 'number', description: 'Total bought' },
realizedPnl: { type: 'number', description: 'Realized profit/loss' },
percentRealizedPnl: { type: 'number', description: 'Percent realized profit/loss' },
curPrice: { type: 'number', description: 'Current price' },
redeemable: { type: 'boolean', description: 'Whether position is redeemable' },
mergeable: { type: 'boolean', description: 'Whether position is mergeable' },
title: { type: 'string', description: 'Market title' },
slug: { type: 'string', description: 'Market slug' },
icon: { type: 'string', description: 'Market icon URL' },
eventSlug: { type: 'string', description: 'Event slug' },
outcome: { type: 'string', description: 'Outcome name' },
outcomeIndex: { type: 'number', description: 'Outcome index' },
oppositeOutcome: { type: 'string', description: 'Opposite outcome name' },
oppositeAsset: { type: 'string', description: 'Opposite asset token ID' },
endDate: { type: 'string', description: 'End date' },
negativeRisk: { type: 'boolean', description: 'Whether negative risk' },
},
},
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetPriceParams {
tokenId: string // The token ID (CLOB token ID from market)

View File

@@ -1,6 +1,6 @@
import type { PolymarketPriceHistoryEntry } from '@/tools/polymarket/types'
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPriceHistoryEntry } from './types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetPriceHistoryParams {
tokenId: string
@@ -99,7 +99,14 @@ export const polymarketGetPriceHistoryTool: ToolConfig<
outputs: {
history: {
type: 'array',
description: 'Array of price history entries with timestamp (t) and price (p)',
description: 'Array of price history entries',
items: {
type: 'object',
properties: {
t: { type: 'number', description: 'Unix timestamp' },
p: { type: 'number', description: 'Price at timestamp' },
},
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketPaginationParams, PolymarketSeries } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPaginationParams, PolymarketSeries } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetSeriesParams extends PolymarketPaginationParams {}
@@ -97,6 +97,26 @@ export const polymarketGetSeriesTool: ToolConfig<
series: {
type: 'array',
description: 'Array of series objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Series ID' },
ticker: { type: 'string', description: 'Series ticker' },
slug: { type: 'string', description: 'Series slug' },
title: { type: 'string', description: 'Series title' },
seriesType: { type: 'string', description: 'Series type' },
recurrence: { type: 'string', description: 'Recurrence pattern' },
image: { type: 'string', description: 'Series image URL' },
icon: { type: 'string', description: 'Series icon URL' },
active: { type: 'boolean', description: 'Whether series is active' },
closed: { type: 'boolean', description: 'Whether series is closed' },
archived: { type: 'boolean', description: 'Whether series is archived' },
featured: { type: 'boolean', description: 'Whether series is featured' },
volume: { type: 'number', description: 'Total volume' },
liquidity: { type: 'number', description: 'Total liquidity' },
eventCount: { type: 'number', description: 'Number of events in series' },
},
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketSeries } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketSeries } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetSeriesByIdParams {
seriesId: string // Series ID (required)
@@ -58,6 +58,25 @@ export const polymarketGetSeriesByIdTool: ToolConfig<
series: {
type: 'object',
description: 'Series object with details',
properties: {
id: { type: 'string', description: 'Series ID' },
ticker: { type: 'string', description: 'Series ticker' },
slug: { type: 'string', description: 'Series slug' },
title: { type: 'string', description: 'Series title' },
seriesType: { type: 'string', description: 'Series type' },
recurrence: { type: 'string', description: 'Recurrence pattern' },
image: { type: 'string', description: 'Series image URL' },
icon: { type: 'string', description: 'Series icon URL' },
active: { type: 'boolean', description: 'Whether series is active' },
closed: { type: 'boolean', description: 'Whether series is closed' },
archived: { type: 'boolean', description: 'Whether series is archived' },
featured: { type: 'boolean', description: 'Whether series is featured' },
volume: { type: 'number', description: 'Total volume' },
liquidity: { type: 'number', description: 'Total liquidity' },
commentCount: { type: 'number', description: 'Comment count' },
eventCount: { type: 'number', description: 'Number of events in series' },
events: { type: 'array', description: 'Array of events in this series' },
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketSpread } from '@/tools/polymarket/types'
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketSpread } from './types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetSpreadParams {
tokenId: string // The token ID (CLOB token ID from market)
@@ -53,7 +53,9 @@ export const polymarketGetSpreadTool: ToolConfig<
return {
success: true,
output: {
spread: data,
spread: {
spread: data.spread ?? '',
},
},
}
},
@@ -61,7 +63,10 @@ export const polymarketGetSpreadTool: ToolConfig<
outputs: {
spread: {
type: 'object',
description: 'Bid-ask spread with bid and ask prices',
description: 'Spread value between bid and ask',
properties: {
spread: { type: 'string', description: 'The spread value' },
},
},
},
}

View File

@@ -1,6 +1,6 @@
import type { PolymarketPaginationParams, PolymarketTag } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPaginationParams, PolymarketTag } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketGetTagsParams extends PolymarketPaginationParams {}
@@ -71,7 +71,17 @@ export const polymarketGetTagsTool: ToolConfig<PolymarketGetTagsParams, Polymark
outputs: {
tags: {
type: 'array',
description: 'Array of tag objects with id, label, and slug',
description: 'Array of tag objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Tag ID' },
label: { type: 'string', description: 'Tag label' },
slug: { type: 'string', description: 'Tag slug' },
createdAt: { type: 'string', description: 'Creation timestamp' },
updatedAt: { type: 'string', description: 'Last update timestamp' },
},
},
},
},
}

View File

@@ -1,5 +1,5 @@
import { buildClobUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import { buildClobUrl, handlePolymarketError } from './types'
export interface PolymarketGetTickSizeParams {
tokenId: string // The token ID (CLOB token ID from market)

View File

@@ -1,10 +1,17 @@
import type { PolymarketTrade } from '@/tools/polymarket/types'
import { buildDataUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPaginationParams, PolymarketTrade } from './types'
import { buildDataUrl, handlePolymarketError } from './types'
export interface PolymarketGetTradesParams extends PolymarketPaginationParams {
user?: string // Optional user wallet address
market?: string // Optional market filter
export interface PolymarketGetTradesParams {
user?: string
market?: string
eventId?: string
side?: string
takerOnly?: string
filterType?: string
filterAmount?: string
limit?: string
offset?: string
}
export interface PolymarketGetTradesResponse {
@@ -33,13 +40,43 @@ export const polymarketGetTradesTool: ToolConfig<
market: {
type: 'string',
required: false,
description: 'Market ID to filter trades',
description: 'Market/condition ID to filter trades (mutually exclusive with eventId)',
visibility: 'user-or-llm',
},
eventId: {
type: 'string',
required: false,
description: 'Event ID to filter trades (mutually exclusive with market)',
visibility: 'user-or-llm',
},
side: {
type: 'string',
required: false,
description: 'Trade direction filter (BUY or SELL)',
visibility: 'user-or-llm',
},
takerOnly: {
type: 'string',
required: false,
description: 'Filter for taker trades only (true/false, default: true)',
visibility: 'user-or-llm',
},
filterType: {
type: 'string',
required: false,
description: 'Filter type (CASH or TOKENS) - requires filterAmount',
visibility: 'user-or-llm',
},
filterAmount: {
type: 'string',
required: false,
description: 'Filter amount threshold - requires filterType',
visibility: 'user-or-llm',
},
limit: {
type: 'string',
required: false,
description: 'Number of results per page (max 50)',
description: 'Number of results per page (default: 100, max: 10000)',
visibility: 'user-or-llm',
},
offset: {
@@ -55,13 +92,16 @@ export const polymarketGetTradesTool: ToolConfig<
const queryParams = new URLSearchParams()
if (params.user) queryParams.append('user', params.user)
if (params.market) queryParams.append('market', params.market)
// Default limit to 50 to prevent browser crashes from large data sets
queryParams.append('limit', params.limit || '50')
if (params.eventId) queryParams.append('eventId', params.eventId)
if (params.side) queryParams.append('side', params.side.toUpperCase())
if (params.takerOnly) queryParams.append('takerOnly', params.takerOnly)
if (params.filterType) queryParams.append('filterType', params.filterType.toUpperCase())
if (params.filterAmount) queryParams.append('filterAmount', params.filterAmount)
if (params.limit) queryParams.append('limit', params.limit)
if (params.offset) queryParams.append('offset', params.offset)
const query = queryParams.toString()
const url = buildDataUrl('/trades')
return `${url}?${query}`
return `${url}?${queryParams.toString()}`
},
method: 'GET',
headers: () => ({
@@ -76,8 +116,28 @@ export const polymarketGetTradesTool: ToolConfig<
handlePolymarketError(data, response.status, 'get_trades')
}
// Response is an array of trades
const trades = Array.isArray(data) ? data : []
const rawTrades = Array.isArray(data) ? data : []
const trades: PolymarketTrade[] = rawTrades.map((t: Record<string, unknown>) => ({
proxyWallet: (t.proxyWallet as string) ?? null,
side: (t.side as string) ?? '',
asset: (t.asset as string) ?? '',
conditionId: (t.conditionId as string) ?? '',
size: (t.size as number) ?? 0,
price: (t.price as number) ?? 0,
timestamp: (t.timestamp as number) ?? 0,
title: (t.title as string) ?? null,
slug: (t.slug as string) ?? null,
icon: (t.icon as string) ?? null,
eventSlug: (t.eventSlug as string) ?? null,
outcome: (t.outcome as string) ?? null,
outcomeIndex: (t.outcomeIndex as number) ?? null,
name: (t.name as string) ?? null,
pseudonym: (t.pseudonym as string) ?? null,
bio: (t.bio as string) ?? null,
profileImage: (t.profileImage as string) ?? null,
profileImageOptimized: (t.profileImageOptimized as string) ?? null,
transactionHash: (t.transactionHash as string) ?? null,
}))
return {
success: true,
@@ -91,6 +151,30 @@ export const polymarketGetTradesTool: ToolConfig<
trades: {
type: 'array',
description: 'Array of trade objects',
items: {
type: 'object',
properties: {
proxyWallet: { type: 'string', description: 'Proxy wallet address' },
side: { type: 'string', description: 'Trade side (BUY or SELL)' },
asset: { type: 'string', description: 'Asset token ID' },
conditionId: { type: 'string', description: 'Condition ID' },
size: { type: 'number', description: 'Trade size' },
price: { type: 'number', description: 'Trade price' },
timestamp: { type: 'number', description: 'Unix timestamp' },
title: { type: 'string', description: 'Market title' },
slug: { type: 'string', description: 'Market slug' },
icon: { type: 'string', description: 'Market icon URL' },
eventSlug: { type: 'string', description: 'Event slug' },
outcome: { type: 'string', description: 'Outcome name' },
outcomeIndex: { type: 'number', description: 'Outcome index' },
name: { type: 'string', description: 'Trader name' },
pseudonym: { type: 'string', description: 'Trader pseudonym' },
bio: { type: 'string', description: 'Trader bio' },
profileImage: { type: 'string', description: 'Profile image URL' },
profileImageOptimized: { type: 'string', description: 'Optimized profile image URL' },
transactionHash: { type: 'string', description: 'Transaction hash' },
},
},
},
},
}

View File

@@ -1,18 +1,20 @@
export * from './get_event'
export * from './get_events'
export * from './get_last_trade_price'
export * from './get_market'
export * from './get_markets'
export * from './get_midpoint'
export * from './get_orderbook'
export * from './get_positions'
export * from './get_price'
export * from './get_price_history'
export * from './get_series'
export * from './get_series_by_id'
export * from './get_spread'
export * from './get_tags'
export * from './get_tick_size'
export * from './get_trades'
export * from './search'
export * from './types'
export { polymarketGetActivityTool } from './get_activity'
export { polymarketGetEventTool } from './get_event'
export { polymarketGetEventsTool } from './get_events'
export { polymarketGetHoldersTool } from './get_holders'
export { polymarketGetLastTradePriceTool } from './get_last_trade_price'
export { polymarketGetLeaderboardTool } from './get_leaderboard'
export { polymarketGetMarketTool } from './get_market'
export { polymarketGetMarketsTool } from './get_markets'
export { polymarketGetMidpointTool } from './get_midpoint'
export { polymarketGetOrderbookTool } from './get_orderbook'
export { polymarketGetPositionsTool } from './get_positions'
export { polymarketGetPriceTool } from './get_price'
export { polymarketGetPriceHistoryTool } from './get_price_history'
export { polymarketGetSeriesTool } from './get_series'
export { polymarketGetSeriesByIdTool } from './get_series_by_id'
export { polymarketGetSpreadTool } from './get_spread'
export { polymarketGetTagsTool } from './get_tags'
export { polymarketGetTickSizeTool } from './get_tick_size'
export { polymarketGetTradesTool } from './get_trades'
export { polymarketSearchTool } from './search'

View File

@@ -1,9 +1,22 @@
import type { PolymarketSearchResult } from '@/tools/polymarket/types'
import { buildGammaUrl, handlePolymarketError } from '@/tools/polymarket/types'
import type { ToolConfig } from '@/tools/types'
import type { PolymarketPaginationParams, PolymarketSearchResult } from './types'
import { buildGammaUrl, handlePolymarketError } from './types'
export interface PolymarketSearchParams extends PolymarketPaginationParams {
query: string // Search term (required)
export interface PolymarketSearchParams {
query: string
limit?: string
page?: string
cache?: string
eventsStatus?: string
limitPerType?: string
eventsTag?: string
sort?: string
ascending?: string
searchTags?: string
searchProfiles?: string
recurrence?: string
excludeTagId?: string
keepClosedMarkets?: string
}
export interface PolymarketSearchResponse {
@@ -32,10 +45,76 @@ export const polymarketSearchTool: ToolConfig<PolymarketSearchParams, Polymarket
description: 'Number of results per page (max 50)',
visibility: 'user-or-llm',
},
offset: {
page: {
type: 'string',
required: false,
description: 'Pagination offset',
description: 'Page number for pagination (1-indexed)',
visibility: 'user-or-llm',
},
cache: {
type: 'string',
required: false,
description: 'Enable caching (true/false)',
visibility: 'user-or-llm',
},
eventsStatus: {
type: 'string',
required: false,
description: 'Filter events by status',
visibility: 'user-or-llm',
},
limitPerType: {
type: 'string',
required: false,
description: 'Limit results per type (markets, events, profiles)',
visibility: 'user-or-llm',
},
eventsTag: {
type: 'string',
required: false,
description: 'Filter by event tags (comma-separated)',
visibility: 'user-or-llm',
},
sort: {
type: 'string',
required: false,
description: 'Sort field',
visibility: 'user-or-llm',
},
ascending: {
type: 'string',
required: false,
description: 'Sort direction (true for ascending, false for descending)',
visibility: 'user-or-llm',
},
searchTags: {
type: 'string',
required: false,
description: 'Include tags in search results (true/false)',
visibility: 'user-or-llm',
},
searchProfiles: {
type: 'string',
required: false,
description: 'Include profiles in search results (true/false)',
visibility: 'user-or-llm',
},
recurrence: {
type: 'string',
required: false,
description: 'Filter by recurrence type',
visibility: 'user-or-llm',
},
excludeTagId: {
type: 'string',
required: false,
description: 'Exclude events with these tag IDs (comma-separated)',
visibility: 'user-or-llm',
},
keepClosedMarkets: {
type: 'string',
required: false,
description: 'Include closed markets in results (0 or 1)',
visibility: 'user-or-llm',
},
},
@@ -44,9 +123,20 @@ export const polymarketSearchTool: ToolConfig<PolymarketSearchParams, Polymarket
url: (params) => {
const queryParams = new URLSearchParams()
queryParams.append('q', params.query)
// Default limit to 50 to prevent browser crashes from large data sets
queryParams.append('limit', params.limit || '50')
if (params.offset) queryParams.append('offset', params.offset)
if (params.page) queryParams.append('page', params.page)
if (params.cache) queryParams.append('cache', params.cache)
if (params.eventsStatus) queryParams.append('events_status', params.eventsStatus)
if (params.limitPerType) queryParams.append('limit_per_type', params.limitPerType)
if (params.eventsTag) queryParams.append('events_tag', params.eventsTag)
if (params.sort) queryParams.append('sort', params.sort)
if (params.ascending) queryParams.append('ascending', params.ascending)
if (params.searchTags) queryParams.append('search_tags', params.searchTags)
if (params.searchProfiles) queryParams.append('search_profiles', params.searchProfiles)
if (params.recurrence) queryParams.append('recurrence', params.recurrence)
if (params.excludeTagId) queryParams.append('exclude_tag_id', params.excludeTagId)
if (params.keepClosedMarkets)
queryParams.append('keep_closed_markets', params.keepClosedMarkets)
return `${buildGammaUrl('/public-search')}?${queryParams.toString()}`
},
@@ -63,11 +153,11 @@ export const polymarketSearchTool: ToolConfig<PolymarketSearchParams, Polymarket
handlePolymarketError(data, response.status, 'search')
}
// Response contains markets, events, and profiles arrays
const results: PolymarketSearchResult = {
markets: data.markets || [],
events: data.events || [],
profiles: data.profiles || [],
markets: data.markets ?? [],
events: data.events ?? [],
tags: data.tags ?? [],
profiles: data.profiles ?? [],
}
return {
@@ -81,7 +171,13 @@ export const polymarketSearchTool: ToolConfig<PolymarketSearchParams, Polymarket
outputs: {
results: {
type: 'object',
description: 'Search results containing markets, events, and profiles arrays',
description: 'Search results containing markets, events, tags, and profiles arrays',
properties: {
markets: { type: 'array', description: 'Array of matching market objects' },
events: { type: 'array', description: 'Array of matching event objects' },
tags: { type: 'array', description: 'Array of matching tag objects' },
profiles: { type: 'array', description: 'Array of matching profile objects' },
},
},
},
}

View File

@@ -92,6 +92,11 @@ export interface PolymarketTag {
id: string
label: string
slug: string
createdAt?: string
updatedAt?: string
forceShow?: boolean
forceHide?: boolean
isCarousel?: boolean
}
export interface PolymarketOrderBookEntry {
@@ -106,11 +111,13 @@ export interface PolymarketOrderBook {
timestamp: string
bids: PolymarketOrderBookEntry[]
asks: PolymarketOrderBookEntry[]
min_order_size: string
tick_size: string
neg_risk: boolean
}
export interface PolymarketPrice {
price: string
side: string
}
export interface PolymarketPriceHistoryEntry {
@@ -143,31 +150,125 @@ export interface PolymarketSeries {
export interface PolymarketSearchResult {
markets: PolymarketMarket[]
events: PolymarketEvent[]
profiles: any[]
tags: PolymarketTag[]
profiles: PolymarketProfile[]
}
export interface PolymarketProfile {
id: string
name: string | null
pseudonym: string | null
bio: string | null
profileImage: string | null
profileImageOptimized: string | null
walletAddress: string
}
export interface PolymarketSpread {
bid: string
ask: string
spread: string
}
export interface PolymarketPosition {
market: string
asset_id: string
size: string
value: string
proxyWallet: string | null
asset: string
conditionId: string
size: number
avgPrice: number
initialValue: number
currentValue: number
cashPnl: number
percentPnl: number
totalBought: number
realizedPnl: number
percentRealizedPnl: number
curPrice: number
redeemable: boolean
mergeable: boolean
title: string | null
slug: string | null
icon: string | null
eventSlug: string | null
outcome: string | null
outcomeIndex: number | null
oppositeOutcome: string | null
oppositeAsset: string | null
endDate: string | null
negativeRisk: boolean
}
export interface PolymarketTrade {
id: string
market: string
asset_id: string
proxyWallet: string | null
side: string
size: string
price: string
timestamp: string
maker: string
taker: string
asset: string
conditionId: string
size: number
price: number
timestamp: number
title: string | null
slug: string | null
icon: string | null
eventSlug: string | null
outcome: string | null
outcomeIndex: number | null
name: string | null
pseudonym: string | null
bio: string | null
profileImage: string | null
profileImageOptimized: string | null
transactionHash: string | null
}
export interface PolymarketActivity {
proxyWallet: string | null
timestamp: number
conditionId: string
type: string
size: number
usdcSize: number
transactionHash: string | null
price: number | null
asset: string | null
side: string | null
outcomeIndex: number | null
title: string | null
slug: string | null
icon: string | null
eventSlug: string | null
outcome: string | null
name: string | null
pseudonym: string | null
bio: string | null
profileImage: string | null
profileImageOptimized: string | null
}
export interface PolymarketLeaderboardEntry {
rank: string
proxyWallet: string
userName: string | null
vol: number
pnl: number
profileImage: string | null
xUsername: string | null
verifiedBadge: boolean
}
export interface PolymarketHolder {
proxyWallet: string
bio: string | null
asset: string
pseudonym: string | null
amount: number
displayUsernamePublic: boolean
outcomeIndex: number
name: string | null
profileImage: string | null
profileImageOptimized: string | null
}
export interface PolymarketMarketHolders {
token: string
holders: PolymarketHolder[]
}
export function handlePolymarketError(data: any, status: number, operation: string): never {

View File

@@ -663,16 +663,23 @@ import {
incidentioWorkflowsUpdateTool,
} from '@/tools/incidentio'
import {
intercomAssignConversationV2Tool,
intercomAttachContactToCompanyV2Tool,
intercomCloseConversationV2Tool,
intercomCreateCompanyTool,
intercomCreateCompanyV2Tool,
intercomCreateContactTool,
intercomCreateContactV2Tool,
intercomCreateEventV2Tool,
intercomCreateMessageTool,
intercomCreateMessageV2Tool,
intercomCreateNoteV2Tool,
intercomCreateTagV2Tool,
intercomCreateTicketTool,
intercomCreateTicketV2Tool,
intercomDeleteContactTool,
intercomDeleteContactV2Tool,
intercomDetachContactFromCompanyV2Tool,
intercomGetCompanyTool,
intercomGetCompanyV2Tool,
intercomGetContactTool,
@@ -681,20 +688,28 @@ import {
intercomGetConversationV2Tool,
intercomGetTicketTool,
intercomGetTicketV2Tool,
intercomListAdminsV2Tool,
intercomListCompaniesTool,
intercomListCompaniesV2Tool,
intercomListContactsTool,
intercomListContactsV2Tool,
intercomListConversationsTool,
intercomListConversationsV2Tool,
intercomListTagsV2Tool,
intercomOpenConversationV2Tool,
intercomReplyConversationTool,
intercomReplyConversationV2Tool,
intercomSearchContactsTool,
intercomSearchContactsV2Tool,
intercomSearchConversationsTool,
intercomSearchConversationsV2Tool,
intercomSnoozeConversationV2Tool,
intercomTagContactV2Tool,
intercomTagConversationV2Tool,
intercomUntagContactV2Tool,
intercomUpdateContactTool,
intercomUpdateContactV2Tool,
intercomUpdateTicketV2Tool,
} from '@/tools/intercom'
import { jinaReadUrlTool, jinaSearchTool } from '@/tools/jina'
import {
@@ -746,22 +761,39 @@ import {
} from '@/tools/jsm'
import {
kalshiAmendOrderTool,
kalshiAmendOrderV2Tool,
kalshiCancelOrderTool,
kalshiCancelOrderV2Tool,
kalshiCreateOrderTool,
kalshiCreateOrderV2Tool,
kalshiGetBalanceTool,
kalshiGetBalanceV2Tool,
kalshiGetCandlesticksTool,
kalshiGetCandlesticksV2Tool,
kalshiGetEventsTool,
kalshiGetEventsV2Tool,
kalshiGetEventTool,
kalshiGetEventV2Tool,
kalshiGetExchangeStatusTool,
kalshiGetExchangeStatusV2Tool,
kalshiGetFillsTool,
kalshiGetFillsV2Tool,
kalshiGetMarketsTool,
kalshiGetMarketsV2Tool,
kalshiGetMarketTool,
kalshiGetMarketV2Tool,
kalshiGetOrderbookTool,
kalshiGetOrderbookV2Tool,
kalshiGetOrdersTool,
kalshiGetOrdersV2Tool,
kalshiGetOrderTool,
kalshiGetOrderV2Tool,
kalshiGetPositionsTool,
kalshiGetPositionsV2Tool,
kalshiGetSeriesByTickerTool,
kalshiGetSeriesByTickerV2Tool,
kalshiGetTradesTool,
kalshiGetTradesV2Tool,
} from '@/tools/kalshi'
import {
knowledgeCreateDocumentTool,
@@ -1072,9 +1104,12 @@ import {
pipedriveUpdateLeadTool,
} from '@/tools/pipedrive'
import {
polymarketGetActivityTool,
polymarketGetEventsTool,
polymarketGetEventTool,
polymarketGetHoldersTool,
polymarketGetLastTradePriceTool,
polymarketGetLeaderboardTool,
polymarketGetMarketsTool,
polymarketGetMarketTool,
polymarketGetMidpointTool,
@@ -1815,22 +1850,39 @@ export const tools: Record<string, ToolConfig> = {
jsm_get_approvals: jsmGetApprovalsTool,
jsm_answer_approval: jsmAnswerApprovalTool,
kalshi_get_markets: kalshiGetMarketsTool,
kalshi_get_markets_v2: kalshiGetMarketsV2Tool,
kalshi_get_market: kalshiGetMarketTool,
kalshi_get_market_v2: kalshiGetMarketV2Tool,
kalshi_get_events: kalshiGetEventsTool,
kalshi_get_events_v2: kalshiGetEventsV2Tool,
kalshi_get_event: kalshiGetEventTool,
kalshi_get_event_v2: kalshiGetEventV2Tool,
kalshi_get_balance: kalshiGetBalanceTool,
kalshi_get_balance_v2: kalshiGetBalanceV2Tool,
kalshi_get_positions: kalshiGetPositionsTool,
kalshi_get_positions_v2: kalshiGetPositionsV2Tool,
kalshi_get_orders: kalshiGetOrdersTool,
kalshi_get_orders_v2: kalshiGetOrdersV2Tool,
kalshi_get_order: kalshiGetOrderTool,
kalshi_get_order_v2: kalshiGetOrderV2Tool,
kalshi_get_orderbook: kalshiGetOrderbookTool,
kalshi_get_orderbook_v2: kalshiGetOrderbookV2Tool,
kalshi_get_trades: kalshiGetTradesTool,
kalshi_get_trades_v2: kalshiGetTradesV2Tool,
kalshi_get_candlesticks: kalshiGetCandlesticksTool,
kalshi_get_candlesticks_v2: kalshiGetCandlesticksV2Tool,
kalshi_get_fills: kalshiGetFillsTool,
kalshi_get_fills_v2: kalshiGetFillsV2Tool,
kalshi_get_series_by_ticker: kalshiGetSeriesByTickerTool,
kalshi_get_series_by_ticker_v2: kalshiGetSeriesByTickerV2Tool,
kalshi_get_exchange_status: kalshiGetExchangeStatusTool,
kalshi_get_exchange_status_v2: kalshiGetExchangeStatusV2Tool,
kalshi_create_order: kalshiCreateOrderTool,
kalshi_create_order_v2: kalshiCreateOrderV2Tool,
kalshi_cancel_order: kalshiCancelOrderTool,
kalshi_cancel_order_v2: kalshiCancelOrderV2Tool,
kalshi_amend_order: kalshiAmendOrderTool,
kalshi_amend_order_v2: kalshiAmendOrderV2Tool,
polymarket_get_markets: polymarketGetMarketsTool,
polymarket_get_market: polymarketGetMarketTool,
polymarket_get_events: polymarketGetEventsTool,
@@ -1848,6 +1900,9 @@ export const tools: Record<string, ToolConfig> = {
polymarket_get_tick_size: polymarketGetTickSizeTool,
polymarket_get_positions: polymarketGetPositionsTool,
polymarket_get_trades: polymarketGetTradesTool,
polymarket_get_activity: polymarketGetActivityTool,
polymarket_get_leaderboard: polymarketGetLeaderboardTool,
polymarket_get_holders: polymarketGetHoldersTool,
slack_message: slackMessageTool,
slack_message_reader: slackMessageReaderTool,
slack_list_channels: slackListChannelsTool,
@@ -3086,8 +3141,23 @@ export const tools: Record<string, ToolConfig> = {
intercom_create_ticket_v2: intercomCreateTicketV2Tool,
intercom_get_ticket: intercomGetTicketTool,
intercom_get_ticket_v2: intercomGetTicketV2Tool,
intercom_update_ticket_v2: intercomUpdateTicketV2Tool,
intercom_create_message: intercomCreateMessageTool,
intercom_create_message_v2: intercomCreateMessageV2Tool,
intercom_list_admins_v2: intercomListAdminsV2Tool,
intercom_close_conversation_v2: intercomCloseConversationV2Tool,
intercom_open_conversation_v2: intercomOpenConversationV2Tool,
intercom_snooze_conversation_v2: intercomSnoozeConversationV2Tool,
intercom_assign_conversation_v2: intercomAssignConversationV2Tool,
intercom_list_tags_v2: intercomListTagsV2Tool,
intercom_create_tag_v2: intercomCreateTagV2Tool,
intercom_tag_contact_v2: intercomTagContactV2Tool,
intercom_untag_contact_v2: intercomUntagContactV2Tool,
intercom_tag_conversation_v2: intercomTagConversationV2Tool,
intercom_create_note_v2: intercomCreateNoteV2Tool,
intercom_create_event_v2: intercomCreateEventV2Tool,
intercom_attach_contact_to_company_v2: intercomAttachContactToCompanyV2Tool,
intercom_detach_contact_from_company_v2: intercomDetachContactFromCompanyV2Tool,
sentry_issues_list: listIssuesTool,
sentry_issues_get: getIssueTool,
sentry_issues_update: updateIssueTool,