@candledottv/agent-sdk 0.6.9 → 0.6.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -37,6 +37,7 @@ const candle = new CandleClient({
37
37
  const market = await candle.getMarket("solana", "So11...mint")
38
38
  const quote = await candle.getQuote("solana", "So11...mint", { side: "buy", amountIn: "1000000000" })
39
39
  const feed = await candle.getFeed("new", "solana")
40
+ const card = await candle.getTokenCard("solana", "So11...mint")
40
41
  const events = await candle.getTokenEvents("solana", "So11...mint", { kind: "signal", since: "2026-10-28T00:00:00Z" })
41
42
  const verdict = await candle.verify("hood", "0xToken")
42
43
 
@@ -49,10 +50,76 @@ const request = candle.expandPreset(presets, "solana-open-sol", {
49
50
  })
50
51
  ```
51
52
 
52
- The API key is attached as `x-api-key` on every request when configured. The keyed methods
53
+ The API key is attached as `x-api-key` when configured, except on public `tokenPages` reads. The keyed methods
53
54
  (`launch`, `launchAsync`, `dryRunLaunch`, `getLaunchJob`, `reportActivity`, `uploadImage`)
54
55
  refuse to fetch without one and throw a plain `Error` locally instead of a server 401.
55
56
 
57
+ ## Token page About (unreleased source)
58
+
59
+ `candle.tokenPages` reads About and immutable section history, and submits claims, section edits,
60
+ restores, flags and immutable updates. These methods need a human-owned SDK release before they are available in the
61
+ published package. Token page switches default off; a server can return `404` or `feature_off`.
62
+
63
+ | Method | Access | Result |
64
+ | --- | --- | --- |
65
+ | `about(token)` | Public, no key sent | `TokenAbout`, with public feature switches |
66
+ | `burns(token, { cursor, limit })` | Public, CNDL only | `AboutBurnHistory` |
67
+ | `history(token, section, { cursor, limit })` | Public, no key sent | `AboutHistory`, including tombstones |
68
+ | `capabilities(token)` | `token-pages:read` | Private `AboutCapabilities` |
69
+ | `creatorChallenge(token)` | `token-pages:write` | Exact message, challenge ID and expiry |
70
+ | `claim(token, body, options)` | `token-pages:write` | Claim result and idempotency key |
71
+ | `setSection(token, section, body, options)` | `token-pages:write` | New version ID/revision and idempotency key |
72
+ | `restoreSection(token, section, body, options)` | `token-pages:write` | New version ID/revision and idempotency key |
73
+ | `updates(token, { cursor, limit })` | Public, no key sent | `AboutUpdates`, newest first with tombstones |
74
+ | `postUpdate(token, { text }, options)` | `token-pages:write`, current 0.5% | Immutable post ID/sequence and idempotency key |
75
+ | `deletePost(token, id, options)` | `token-pages:write`, author account | Soft delete, including after selling |
76
+ | `seen(token)` | `token-pages:read` | Own `AboutSeen` unread position |
77
+ | `markSeen(token, visibleUpdateSequence, options)` | `token-pages:write` | Monotonic seen result and idempotency key; call after rendering visible updates |
78
+ | `flag(token, body, options)` | `token-pages:write` | Flag result and idempotency key |
79
+
80
+ The token is `{ chain: "solana" | "hood", address: string }`; sections are `thesis`, `tokenomics`
81
+ and `lore`. History and burn pagination accepts a returned opaque cursor and a limit from 1 to 50.
82
+ Revisions and raw amounts remain strings. Reads bypass caches; capabilities are never folded into
83
+ public About. Hidden/removed history bodies stay null. The API validates strict bodies, text limits,
84
+ fresh eligibility and restrictions on every write. Existing keys gain no scope automatically.
85
+ Claim/edit/restore require 0.5% of current supply; any positive eligible balance can flag.
86
+
87
+ ```ts
88
+ const token = { chain: "solana" as const, address: mint }
89
+ const about = await candle.tokenPages.about(token)
90
+ const capabilities = await candle.tokenPages.capabilities(token)
91
+ // Persist this key and the draft before sending; a rerun must reuse both.
92
+ const idempotencyKey = crypto.randomUUID()
93
+ if (about.features.edit && capabilities.canEdit) {
94
+ const saved = await candle.tokenPages.setSection(token, "thesis", {
95
+ text: "Our thesis",
96
+ baseRevision: about.sections.thesis?.revision ?? "0",
97
+ }, { idempotencyKey })
98
+ console.log(saved.data.revision, saved.idempotencyKey)
99
+ }
100
+ ```
101
+
102
+ Claim explicitly with `{ kind: "holder" }` or `{ kind: "creator", challengeId?, signature? }`.
103
+ A known creator may need no additional signature. Otherwise call `creatorChallenge`, sign the exact
104
+ UTF-8 message locally and pass its challenge ID and signature in a separate creator claim. Solana
105
+ signatures use base64; Hood uses the 65-byte `0x` message signature. No method signs, creates a
106
+ transaction, requests a private key, implicitly claims or automatically restores on conflict.
107
+ Challenges are attempted once and expire after five minutes.
108
+
109
+ Restore takes `{ versionId, baseRevision }` and appends a new version; it never rewrites history.
110
+ Flag takes `{ contentId, reason, detail? }` for an exact version, with reason `spam`, `scam`, `abuse`,
111
+ `misleading`, `unsafe_link` or `other`. Restoring a removed/unsafe version is refused.
112
+
113
+ Every content write sends `Idempotency-Key` from `options.idempotencyKey` or a generated UUID and
114
+ returns `{ data, idempotencyKey }`. A connection or interrupted-body failure retries once with the
115
+ same key and serialized body. HTTP refusals are not retried. `TokenPageError` extends `CandleApiError`:
116
+ branch on `code` and `status`, retain `idempotencyKey`, inspect `latest` for `revision_conflict`, and
117
+ respect `retryAfterHeader` on rate limits. Two missing answers or an invalid success produce
118
+ `OUTCOME_UNKNOWN`; explicitly replay the same request/key to recover its result. A conflict leaves
119
+ the caller's draft untouched. Feature-off, missing scope/auth, insufficient holding, expired/replayed
120
+ proof, unsafe text, unavailable eligibility and old-server errors remain refusals for the caller to
121
+ handle. Do not log content bodies, credentials or creator signatures.
122
+
56
123
  ## Embedded launches with built-in idempotent retries
57
124
 
58
125
  `launch()` fills in a `clientLaunchId` (`"sdk-" + crypto.randomUUID()`) when you omit one, and
package/dist/client.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * payload (`getQuotePairs`, `getPresets`, `getMarket`, `getLaunchJob`, `getAgentProfile`)
16
16
  * return the useful inner object; methods whose top-level body IS the useful object
17
17
  * (`launch`, `dryRunLaunch`, `getQuote`, `getFeed`, `verify`) return the parsed body.
18
- * - `x-api-key` is attached to EVERY request when the client has a key. The write/keyed
18
+ * - `x-api-key` is attached when the client has a key, except on public tokenPages reads. The write/keyed
19
19
  * endpoints (launch, dry run, jobs, activity, uploads) refuse to fetch at all without one,
20
20
  * throwing a plain `Error` that names the missing option, so a misconfigured agent fails
21
21
  * fast and locally instead of with a server 401.
@@ -50,6 +50,7 @@ import { type PerpsFillsPage, type PerpsFundingPage, type PerpsHistoryOptions }
50
50
  import type { AgentPlansResult, PlanPrice, PlanTable } from "./plans";
51
51
  import type { SecretStore } from "./secret-store";
52
52
  import type { CandleStaking } from "./staking";
53
+ import { type ClusterResult } from "./token-clusters";
53
54
  import { type WalletChain } from "./wallet-import";
54
55
  export type Chain = "solana" | "hood";
55
56
  /**
@@ -494,6 +495,7 @@ export interface TokenEventGroup {
494
495
  visibility: TokenEventGroupVisibility;
495
496
  }
496
497
  export interface TokenEventWallet {
498
+ /** Display text (first4…last4) for free/keyless reads; full address for Pro/Max keys. */
497
499
  address: string;
498
500
  name: string | null;
499
501
  walletKind: "dev" | "kol" | "smart" | "agent" | "tracked";
@@ -539,6 +541,203 @@ export interface TokenEventsPage {
539
541
  events: TokenEvent[];
540
542
  nextCursor: string | null;
541
543
  }
544
+ /**
545
+ * One hacc card fact (BE-2117 `/v1/tokens/:mint/card-facts`, carried whole by the card route).
546
+ * `state: "ok"` has a `value`, `source`, `as_of` and `basis`; anything else has `value: null` and
547
+ * `display: "–"`. A percent fact's `value` is 0 to 100.
548
+ */
549
+ export interface TokenCardFact {
550
+ value: number | null;
551
+ display: string;
552
+ state: "ok" | "pending" | "unavailable";
553
+ source: string | null;
554
+ as_of: string | null;
555
+ basis: string | null;
556
+ note: string | null;
557
+ }
558
+ /** A same-name candidate: another token on a venue whose ticker or name matches. */
559
+ export interface TokenCardSameNameCandidate {
560
+ mint: string;
561
+ chain: Chain;
562
+ ticker: string | null;
563
+ name: string | null;
564
+ venue: string | null;
565
+ market_cap_usd: number | null;
566
+ liquidity_usd: number | null;
567
+ pair_created_at: string | null;
568
+ match: "ticker" | "name" | "both" | "self";
569
+ found_by: "dexscreener" | "hacc" | "both";
570
+ }
571
+ /** hacc's same-name block: which same-name token leads on live market cap, then liquidity. */
572
+ export interface TokenCardSameName {
573
+ state: "found" | "none" | "unavailable";
574
+ source: string;
575
+ display: string;
576
+ leader: TokenCardSameNameCandidate | null;
577
+ subject: (TokenCardSameNameCandidate & {
578
+ rank: number | null;
579
+ }) | null;
580
+ trailing: boolean | null;
581
+ others: TokenCardSameNameCandidate[];
582
+ detail: string | null;
583
+ }
584
+ /** hacc's card facts for one token, as `GET /v1/tokens/:mint/card-facts` answers them (BE-2117). */
585
+ export interface TokenCardFacts {
586
+ mint: string;
587
+ chain: Chain;
588
+ as_of: string;
589
+ cache_s: number;
590
+ fields: Record<"bundles" | "snipers" | "insiders" | "fresh_1d" | "fresh_7d" | "avg_wallet_age", TokenCardFact>;
591
+ launch: {
592
+ state: "ok" | "pending" | "failed" | "not_read";
593
+ source: string | null;
594
+ computed_at: string | null;
595
+ deployer: string | null;
596
+ first_buy_at: string | null;
597
+ rows_read: number | null;
598
+ covered_s: number | null;
599
+ truncated: boolean | null;
600
+ detail: string | null;
601
+ };
602
+ holders: {
603
+ source: string | null;
604
+ sampled: number;
605
+ known_age: number;
606
+ detail: string | null;
607
+ };
608
+ same_name: TokenCardSameName;
609
+ ath: {
610
+ source: string;
611
+ stored_usd: number | null;
612
+ stored_at: string | null;
613
+ usable_usd: number | null;
614
+ check: "confirmed" | "unconfirmed" | "over_ceiling" | "below_live" | "unverifiable" | "none";
615
+ };
616
+ vendor_reads: Record<"dexscreener" | "helius" | "cvc" | "blockscout" | "birdeye" | "other", number>;
617
+ cached: boolean;
618
+ }
619
+ /**
620
+ * GET /api/v1/tokens/:chain/:mint/card (BE-2118): the data behind a token card, the same for every
621
+ * caller and every plan. Facts only, never a score. A field with no source is `null` (show an en
622
+ * dash), and `sources` names where each block came from.
623
+ */
624
+ /**
625
+ * Creator metadata edit (BE-2140): the four links Candle keeps for a token. In an edit, a string
626
+ * sets a link and `""` or `null` removes it; a key left out is unchanged.
627
+ */
628
+ export type TokenMetadataSocialKey = "website" | "twitter" | "telegram" | "discord";
629
+ export type TokenMetadataSocials = Partial<Record<TokenMetadataSocialKey, string>>;
630
+ export interface EditTokenMetadataRequest {
631
+ chain: Chain;
632
+ mint: string;
633
+ /** Replaces the description; `""` clears it. At most 500 characters, safe for on-chain JSON. */
634
+ description?: string;
635
+ /** Any subset of the four links; each at most 200 characters, an absolute http(s) URL. */
636
+ socials?: Partial<Record<TokenMetadataSocialKey, string | null>>;
637
+ }
638
+ /** `PATCH /api/v1/tokens/:chain/:mint/metadata`'s payload: the token's state after the edit. */
639
+ export interface TokenMetadataEditResult {
640
+ chain: Chain;
641
+ mint: string;
642
+ /** False when the edit re-sent the current values; no version was added. */
643
+ changed: boolean;
644
+ /** The newest edit version, or null when the token has never been edited. */
645
+ version: number | null;
646
+ description: string;
647
+ socials: TokenMetadataSocials;
648
+ /** Always `candle` today: the edit is Candle's copy, nothing is written on chain. */
649
+ chainState: "candle" | "onchain" | "pending" | "failed" | "superseded";
650
+ chainTx: string | null;
651
+ }
652
+ export interface TokenMetadataHistoryItem {
653
+ version: number;
654
+ createdAt: string;
655
+ /** `description` and `socials.<key>`. */
656
+ changedFields: string[];
657
+ description: string;
658
+ socials: TokenMetadataSocials;
659
+ prevDescription: string;
660
+ prevSocials: TokenMetadataSocials;
661
+ chainState: TokenMetadataEditResult["chainState"];
662
+ chainTx: string | null;
663
+ /** The published agent's name, or null for the neutral "Agent". Never an account or key. */
664
+ author: {
665
+ kind: "agent";
666
+ name: string | null;
667
+ };
668
+ }
669
+ export interface TokenMetadataHistory {
670
+ chain: Chain;
671
+ mint: string;
672
+ /** Newest first. */
673
+ items: TokenMetadataHistoryItem[];
674
+ /** Pass as `cursor` for the next page; null on the last. */
675
+ nextCursor: string | null;
676
+ }
677
+ export interface TokenCard {
678
+ chain: Chain;
679
+ mint: string;
680
+ generatedAt: number;
681
+ identity: {
682
+ name: string | null;
683
+ symbol: string | null;
684
+ launchpad: string | null;
685
+ isCandle: boolean | null;
686
+ };
687
+ market: {
688
+ priceUsd: number | null;
689
+ marketCapUsd: number | null;
690
+ liquidityUsd: number | null;
691
+ change5mPct: number | null;
692
+ volume5mUsd: number | null;
693
+ buys5m: number | null;
694
+ sells5m: number | null;
695
+ updatedAt: number | null;
696
+ };
697
+ /** The highest daily price since launch times current supply; null when no source covers it. */
698
+ ath: {
699
+ marketCapUsd: number;
700
+ at: number;
701
+ precision: "day";
702
+ basis: "price times current supply (FDV)";
703
+ } | null;
704
+ /** The forensics route's report, or why there is none (`not_covered` carries its coverage). */
705
+ forensics: {
706
+ state: "ok";
707
+ report: unknown;
708
+ } | {
709
+ state: "not_covered";
710
+ coverage: unknown;
711
+ } | {
712
+ state: "unavailable";
713
+ };
714
+ /** Tracked-wallet trades, signals and callouts in the last hour, at the free tier. */
715
+ events: {
716
+ state: "ok";
717
+ windowMinutes: 60;
718
+ trackedBuys: number;
719
+ trackedSells: number;
720
+ signals: number;
721
+ callouts: number;
722
+ names: string[];
723
+ } | {
724
+ state: "off" | "unavailable";
725
+ };
726
+ hacc: {
727
+ state: "ok";
728
+ facts: TokenCardFacts;
729
+ } | {
730
+ state: "off" | "unavailable";
731
+ };
732
+ sameName: TokenCardSameName | {
733
+ state: "off" | "unavailable";
734
+ };
735
+ sources: Record<"identity" | "market" | "ath" | "forensics" | "events" | "sameName" | "hacc", string>;
736
+ /** Candle's token page, carrying the card source tag when one was given. */
737
+ links: {
738
+ token: string;
739
+ };
740
+ }
542
741
  /** GET /api/v1/markets/:chain/:mint, unwrapped from `{ success, market }`. */
543
742
  export interface MarketState {
544
743
  /** Additive Release A fields are optional when connected to an older API. */
@@ -746,6 +945,11 @@ export interface AgentProfile {
746
945
  username: string | null;
747
946
  launches: number;
748
947
  launchesViaApi: number;
948
+ /**
949
+ * Billing tier. Private: present only when the request is authenticated as this account (its own
950
+ * agent key); absent for any other caller.
951
+ */
952
+ tier?: "free" | "believer" | "pro" | "max";
749
953
  }
750
954
  /** Claimed fee income on public board entries and records. Older snapshots omit these fields. */
751
955
  export interface AgentFeeIncome {
@@ -3076,6 +3280,8 @@ export declare class CrossOriginRedirectError extends Error {
3076
3280
  */
3077
3281
  export declare function keyedRedirectGuard(doFetch: typeof fetch): typeof fetch;
3078
3282
  export declare class CandleClient {
3283
+ /** About reads and scoped writes. No implicit signing, claiming or conflict resolution. */
3284
+ readonly tokenPages: import("./token-pages").CandleTokenPages;
3079
3285
  private readonly apiUrl;
3080
3286
  private readonly apiKey?;
3081
3287
  private readonly fetchImpl;
@@ -3136,7 +3342,49 @@ export declare class CandleClient {
3136
3342
  limit?: number;
3137
3343
  before?: string;
3138
3344
  }): Promise<TokenEventsPage>;
3345
+ /**
3346
+ * The token card for one token (`GET /api/v1/tokens/:chain/:mint/card`): market, the all-time
3347
+ * high since launch, forensics coverage, hacc's safety and same-name facts, and tracked wallets
3348
+ * in the last hour. Free for every plan and the same for every caller; no key is needed. `tag`
3349
+ * is a card source tag (`v1.<group>.<poster>.<card>`) carried on `links.token` for counting card
3350
+ * arrivals; it credits nobody, and the route ignores a malformed one. The route is off until the
3351
+ * deployment sets `TOKEN_CARD_ENABLED` on; off it answers 503 `TOKEN_CARD_DISABLED`.
3352
+ */
3353
+ getTokenCard(chain: Chain, mint: string, opts?: {
3354
+ tag?: string;
3355
+ }): Promise<TokenCard>;
3356
+ /**
3357
+ * Edit Candle's copy of a token's description and links, as the token's creator
3358
+ * (`PATCH /api/v1/tokens/:chain/:mint/metadata`, BE-2140). Needs a key with `launch:write` whose
3359
+ * account launched the token. Name, symbol and image cannot be changed, and nothing is written
3360
+ * on chain: Candle's page and API change, wallets reading on-chain metadata do not. A Hood token
3361
+ * that carries its metadata on chain is refused (`ONCHAIN_METADATA_TOKEN`), as is a TEE-launched
3362
+ * Hood token (`TEE_LAUNCHED_HOOD_TOKEN`). Every call sends an Idempotency-Key (a fresh one unless
3363
+ * `idempotencyKey` is given); retrying with the same key and request returns the first result and
3364
+ * never edits twice. Off until the deployment sets `TOKEN_METADATA_EDIT_ENABLED`; off it answers
3365
+ * 503 `METADATA_EDIT_DISABLED`.
3366
+ */
3367
+ editTokenMetadata(request: EditTokenMetadataRequest, opts?: {
3368
+ idempotencyKey?: string;
3369
+ }): Promise<TokenMetadataEditResult>;
3370
+ /** A token's metadata edits, newest first (`GET .../metadata/history`). Public; no key is needed. */
3371
+ tokenMetadataHistory(params: {
3372
+ chain: Chain;
3373
+ mint: string;
3374
+ cursor?: string;
3375
+ limit?: number;
3376
+ }): Promise<TokenMetadataHistory>;
3139
3377
  verify(chain: Chain, mint: string): Promise<VerifyResult>;
3378
+ /**
3379
+ * BE-2006: optional third-party cluster analysis for a Solana token. Resolves the same
3380
+ * `ClusterResult` union as the API and `candle token clusters`, and never throws for an
3381
+ * unavailable analysis: unavailable is a normal result with a `reason`, not zero clustered supply.
3382
+ * A request that fails, times out or returns an unusable body resolves reason `transport_error`.
3383
+ * One request, no retry, no key sent, nothing cached here. Hood resolves `unsupported_chain`.
3384
+ */
3385
+ getTokenClusters(mint: string, opts?: {
3386
+ chain?: Chain;
3387
+ }): Promise<ClusterResult>;
3140
3388
  getOwnProfile(): Promise<OwnAgentProfile>;
3141
3389
  updateOwnProfile(patch: OwnAgentProfilePatch): Promise<{
3142
3390
  success: true;
@@ -4158,7 +4406,7 @@ export declare function __setEvmReceiptWaitForTest(opts: {
4158
4406
  pollMs?: number;
4159
4407
  } | null): void;
4160
4408
  /** This build's own version. Kept in lockstep with package.json by the release-bump CI guard. */
4161
- export declare const SDK_VERSION = "0.6.9";
4409
+ export declare const SDK_VERSION = "0.6.11";
4162
4410
  /** Test seam: the once-per-process latch would otherwise weld the suite's first case to the rest. */
4163
4411
  export declare function __resetSdkUpdateNoticeForTest(): void;
4164
4412
  //# sourceMappingURL=client.d.ts.map