@dshtrading/connector-polymarket 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/gamma.js CHANGED
@@ -1,4 +1,4 @@
1
- import { PolymarketError, bodySnippet } from "./errors.js";
1
+ import { PolymarketError, bodySnippet, networkError } from "./errors.js";
2
2
  //#region src/gamma.ts
3
3
  /**
4
4
  * Polymarket gamma 公开只读 API 的传输层。
@@ -27,6 +27,20 @@ const DEFAULT_TIMEOUT_MS = 1e4;
27
27
  * 且仍落在 200 的集合内。
28
28
  */
29
29
  const USER_AGENT = "dsh-trading-connector-polymarket/0.6.1";
30
+ /**
31
+ * 本次响应是否由 Cloudflare **共享缓存直接命中**(`cf-cache-status: HIT`)。
32
+ *
33
+ * 用途:工具面 `stale` 字段的**唯一判据**。`/events` 回
34
+ * `cache-control: public, max-age=300`,HIT 意味着拿到的是副本(实测 `age: 224`
35
+ * 的副本,见 spikes/impl-polymarket/agent-tools-ACCEPTANCE.md),最多可能比源站旧
36
+ * 5 分钟;MISS/DYNAMIC = 源站当次现算。
37
+ *
38
+ * 头缺失(本地夹具、非 Cloudflare 部署)返回 false——false 的语义是「没有观察到
39
+ * 共享缓存命中」,不是「已证明为源站实时值」,两者不可混为一谈(不猜)。
40
+ */
41
+ function isCdnCached(response) {
42
+ return response.headers.get("cf-cache-status")?.trim().toUpperCase() === "HIT";
43
+ }
30
44
  var GammaClient = class {
31
45
  timeoutMs;
32
46
  fetchImpl;
@@ -65,7 +79,7 @@ var GammaClient = class {
65
79
  signal: controller.signal
66
80
  });
67
81
  } catch (cause) {
68
- throw networkError(url.pathname, cause);
82
+ throw networkError(`polymarket gamma ${url.pathname}`, cause);
69
83
  } finally {
70
84
  clearTimeout(timer);
71
85
  }
@@ -80,14 +94,10 @@ var GammaClient = class {
80
94
  return {
81
95
  status: response.status,
82
96
  body,
83
- text
97
+ text,
98
+ headers: response.headers
84
99
  };
85
100
  }
86
101
  };
87
- function networkError(path, cause) {
88
- const reason = cause instanceof Error ? cause.message : String(cause);
89
- const timedOut = cause instanceof Error && (cause.name === "TimeoutError" || cause.name === "AbortError");
90
- return new PolymarketError("TRADING_NETWORK", `polymarket gamma ${path}: ${timedOut ? "request timed out" : "network failure"} — ${reason}`, cause);
91
- }
92
102
  //#endregion
93
- export { DEFAULT_TIMEOUT_MS, GAMMA_BASE_URL, GammaClient, USER_AGENT };
103
+ export { DEFAULT_TIMEOUT_MS, GAMMA_BASE_URL, GammaClient, USER_AGENT, isCdnCached };
package/lib/index.d.ts CHANGED
@@ -1,7 +1,10 @@
1
+ import { CLOB_BASE_URL, ClobClient, ClobPriceHistory, ClobPriceHistoryRequest, ClobTransportOptions, DEFAULT_PRICE_HISTORY_FIDELITY_MINUTES, PRICE_HISTORY_MAX_WINDOW_MS, assertPriceHistoryRange } from "./clob.js";
1
2
  import { DiscoveryResult, DiscoveryTrace, FOMC_SERIES_ID, MAX_BUCKETS, MIN_BUCKETS, NOISE_SLUGS, PROBABILITY_SUM_MAX, PROBABILITY_SUM_MIN, canonicalBucket, discoverFomcMeetings, parseMeeting } from "./discovery.js";
2
- import { PolymarketError, bodySnippet } from "./errors.js";
3
- import { DEFAULT_TIMEOUT_MS, GAMMA_BASE_URL, GammaClient, GammaResponse, GammaTransportOptions, USER_AGENT } from "./gamma.js";
3
+ import { PolymarketError, bodySnippet, networkError } from "./errors.js";
4
+ import { EventBucketQuote, EventDetail, EventMarketQuote, EventSearchPage, EventSummary, matchesKeyword, parseEventDetail, parseEventSummary, searchEventsInPage } from "./events.js";
5
+ import { DEFAULT_TIMEOUT_MS, GAMMA_BASE_URL, GammaClient, GammaResponse, GammaTransportOptions, USER_AGENT, isCdnCached } from "./gamma.js";
4
6
  import { SNAPSHOT_DIR, SNAPSHOT_FILE, SNAPSHOT_VERSION, SnapshotEntry, SnapshotStore, SnapshotStoreOptions, SnapshotTable, applySnapshot, defaultSnapshotPath, emptySnapshotTable, parseSnapshotTable } from "./snapshot.js";
5
- import { PREDICTION_EVENTS_LIMIT, PolymarketService, PolymarketServiceOptions, TRADING_PREDICTION_FEED_KEY, createPolymarketPredictionFeed } from "./service.js";
7
+ import { Freshness, MEETING_LOOKUP_TTL_MS, POLYMARKET_SOURCE, PREDICTION_EVENTS_LIMIT, PREDICTION_SEARCH_DEFAULT_LIMIT, PREDICTION_SEARCH_MAX_LIMIT, PolymarketService, PolymarketServiceOptions, PredictionEventDetailResult, PredictionEventSearchResult, PredictionFomcResult, TRADING_PREDICTION_FEED_KEY, createPolymarketPredictionFeed } from "./service.js";
8
+ import { createPolymarketTools } from "./tools.js";
6
9
  import { ApplyOverrides, Config, apply, createPolymarketService, inject, name } from "./plugin.js";
7
- export { type ApplyOverrides, Config, DEFAULT_TIMEOUT_MS, DiscoveryResult, DiscoveryTrace, FOMC_SERIES_ID, GAMMA_BASE_URL, GammaClient, GammaResponse, GammaTransportOptions, MAX_BUCKETS, MIN_BUCKETS, NOISE_SLUGS, PREDICTION_EVENTS_LIMIT, PROBABILITY_SUM_MAX, PROBABILITY_SUM_MIN, PolymarketError, PolymarketService, PolymarketServiceOptions, SNAPSHOT_DIR, SNAPSHOT_FILE, SNAPSHOT_VERSION, SnapshotEntry, SnapshotStore, SnapshotStoreOptions, SnapshotTable, TRADING_PREDICTION_FEED_KEY, USER_AGENT, apply, applySnapshot, bodySnippet, canonicalBucket, createPolymarketPredictionFeed, createPolymarketService, defaultSnapshotPath, discoverFomcMeetings, emptySnapshotTable, inject, name, parseMeeting, parseSnapshotTable };
10
+ export { type ApplyOverrides, CLOB_BASE_URL, ClobClient, ClobPriceHistory, ClobPriceHistoryRequest, ClobTransportOptions, Config, DEFAULT_PRICE_HISTORY_FIDELITY_MINUTES, DEFAULT_TIMEOUT_MS, DiscoveryResult, DiscoveryTrace, EventBucketQuote, EventDetail, EventMarketQuote, EventSearchPage, EventSummary, FOMC_SERIES_ID, Freshness, GAMMA_BASE_URL, GammaClient, GammaResponse, GammaTransportOptions, MAX_BUCKETS, MEETING_LOOKUP_TTL_MS, MIN_BUCKETS, NOISE_SLUGS, POLYMARKET_SOURCE, PREDICTION_EVENTS_LIMIT, PREDICTION_SEARCH_DEFAULT_LIMIT, PREDICTION_SEARCH_MAX_LIMIT, PRICE_HISTORY_MAX_WINDOW_MS, PROBABILITY_SUM_MAX, PROBABILITY_SUM_MIN, PolymarketError, PolymarketService, PolymarketServiceOptions, PredictionEventDetailResult, PredictionEventSearchResult, PredictionFomcResult, SNAPSHOT_DIR, SNAPSHOT_FILE, SNAPSHOT_VERSION, SnapshotEntry, SnapshotStore, SnapshotStoreOptions, SnapshotTable, TRADING_PREDICTION_FEED_KEY, USER_AGENT, apply, applySnapshot, assertPriceHistoryRange, bodySnippet, canonicalBucket, createPolymarketPredictionFeed, createPolymarketService, createPolymarketTools, defaultSnapshotPath, discoverFomcMeetings, emptySnapshotTable, inject, isCdnCached, matchesKeyword, name, networkError, parseEventDetail, parseEventSummary, parseMeeting, parseSnapshotTable, searchEventsInPage };
package/lib/index.js CHANGED
@@ -1,7 +1,10 @@
1
+ import { PolymarketError, bodySnippet, networkError } from "./errors.js";
2
+ import { DEFAULT_TIMEOUT_MS, GAMMA_BASE_URL, GammaClient, USER_AGENT, isCdnCached } from "./gamma.js";
3
+ import { CLOB_BASE_URL, ClobClient, DEFAULT_PRICE_HISTORY_FIDELITY_MINUTES, PRICE_HISTORY_MAX_WINDOW_MS, assertPriceHistoryRange } from "./clob.js";
1
4
  import { FOMC_SERIES_ID, MAX_BUCKETS, MIN_BUCKETS, NOISE_SLUGS, PROBABILITY_SUM_MAX, PROBABILITY_SUM_MIN, canonicalBucket, discoverFomcMeetings, parseMeeting } from "./discovery.js";
2
- import { PolymarketError, bodySnippet } from "./errors.js";
3
- import { DEFAULT_TIMEOUT_MS, GAMMA_BASE_URL, GammaClient, USER_AGENT } from "./gamma.js";
4
- import { PREDICTION_EVENTS_LIMIT, PolymarketService, TRADING_PREDICTION_FEED_KEY, createPolymarketPredictionFeed } from "./service.js";
5
+ import { matchesKeyword, parseEventDetail, parseEventSummary, searchEventsInPage } from "./events.js";
6
+ import { MEETING_LOOKUP_TTL_MS, POLYMARKET_SOURCE, PREDICTION_EVENTS_LIMIT, PREDICTION_SEARCH_DEFAULT_LIMIT, PREDICTION_SEARCH_MAX_LIMIT, PolymarketService, TRADING_PREDICTION_FEED_KEY, createPolymarketPredictionFeed } from "./service.js";
5
7
  import { SNAPSHOT_DIR, SNAPSHOT_FILE, SNAPSHOT_VERSION, SnapshotStore, applySnapshot, defaultSnapshotPath, emptySnapshotTable, parseSnapshotTable } from "./snapshot.js";
8
+ import { createPolymarketTools } from "./tools.js";
6
9
  import { Config, apply, createPolymarketService, inject, name } from "./plugin.js";
7
- export { Config, DEFAULT_TIMEOUT_MS, FOMC_SERIES_ID, GAMMA_BASE_URL, GammaClient, MAX_BUCKETS, MIN_BUCKETS, NOISE_SLUGS, PREDICTION_EVENTS_LIMIT, PROBABILITY_SUM_MAX, PROBABILITY_SUM_MIN, PolymarketError, PolymarketService, SNAPSHOT_DIR, SNAPSHOT_FILE, SNAPSHOT_VERSION, SnapshotStore, TRADING_PREDICTION_FEED_KEY, USER_AGENT, apply, applySnapshot, bodySnippet, canonicalBucket, createPolymarketPredictionFeed, createPolymarketService, defaultSnapshotPath, discoverFomcMeetings, emptySnapshotTable, inject, name, parseMeeting, parseSnapshotTable };
10
+ export { CLOB_BASE_URL, ClobClient, Config, DEFAULT_PRICE_HISTORY_FIDELITY_MINUTES, DEFAULT_TIMEOUT_MS, FOMC_SERIES_ID, GAMMA_BASE_URL, GammaClient, MAX_BUCKETS, MEETING_LOOKUP_TTL_MS, MIN_BUCKETS, NOISE_SLUGS, POLYMARKET_SOURCE, PREDICTION_EVENTS_LIMIT, PREDICTION_SEARCH_DEFAULT_LIMIT, PREDICTION_SEARCH_MAX_LIMIT, PRICE_HISTORY_MAX_WINDOW_MS, PROBABILITY_SUM_MAX, PROBABILITY_SUM_MIN, PolymarketError, PolymarketService, SNAPSHOT_DIR, SNAPSHOT_FILE, SNAPSHOT_VERSION, SnapshotStore, TRADING_PREDICTION_FEED_KEY, USER_AGENT, apply, applySnapshot, assertPriceHistoryRange, bodySnippet, canonicalBucket, createPolymarketPredictionFeed, createPolymarketService, createPolymarketTools, defaultSnapshotPath, discoverFomcMeetings, emptySnapshotTable, inject, isCdnCached, matchesKeyword, name, networkError, parseEventDetail, parseEventSummary, parseMeeting, parseSnapshotTable, searchEventsInPage };
package/lib/json.js ADDED
@@ -0,0 +1,63 @@
1
+ //#region src/json.ts
2
+ /**
3
+ * 上游 JSON 体的取值原语(discovery.ts 与 events.ts 共用,只此一份)。
4
+ *
5
+ * 为什么单独成模块:FOMC 发现面与事件检索面从同一份 gamma 半结构化体里取值,
6
+ * 「字符串数字算不算数字」「空串算不算文本」「字符串化数组怎么拆」这三个口径一旦
7
+ * 分叉,同一份上游数据会在两个工具面上给出不同答案。
8
+ */
9
+ function isRecord(value) {
10
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11
+ }
12
+ /** 上游把数字既发成 number 也发成 string(`"0.835"`),两种都读;其余一律 undefined。 */
13
+ function num(value) {
14
+ if (typeof value === "number" && Number.isFinite(value)) return value;
15
+ if (typeof value === "string" && value.trim() !== "") {
16
+ const parsed = Number(value);
17
+ if (Number.isFinite(parsed)) return parsed;
18
+ }
19
+ }
20
+ /** 非空字符串;`null` / 空串 / 纯空白一律 undefined(不当成空文本)。 */
21
+ function text(value) {
22
+ if (typeof value !== "string") return void 0;
23
+ const trimmed = value.trim();
24
+ return trimmed.length > 0 ? trimmed : void 0;
25
+ }
26
+ /**
27
+ * 字符串化的 JSON 数组(gamma 的 `outcomes` / `outcomePrices`,如
28
+ * `"[\"Yes\", \"No\"]"`)。解析失败或不是数组一律 undefined——不猜、不补空数组。
29
+ */
30
+ function parseStringArray(value) {
31
+ if (typeof value !== "string") return void 0;
32
+ let parsed;
33
+ try {
34
+ parsed = JSON.parse(value);
35
+ } catch {
36
+ return;
37
+ }
38
+ return Array.isArray(parsed) ? parsed : void 0;
39
+ }
40
+ /** 字符串化数组 → 数字数组(解析不出数字的项丢弃;全丢或非数组 = undefined)。 */
41
+ function parseNumberArray(value) {
42
+ const raw = parseStringArray(value);
43
+ if (raw === void 0) return void 0;
44
+ const parsed = [];
45
+ for (const item of raw) {
46
+ const value = num(item);
47
+ if (value !== void 0) parsed.push(value);
48
+ }
49
+ return parsed.length > 0 ? parsed : void 0;
50
+ }
51
+ /** 字符串化数组 → 非空字符串数组(同 {@link parseNumberArray} 的丢弃口径)。 */
52
+ function parseTextArray(value) {
53
+ const raw = parseStringArray(value);
54
+ if (raw === void 0) return void 0;
55
+ const parsed = [];
56
+ for (const item of raw) {
57
+ const value = text(item);
58
+ if (value !== void 0) parsed.push(value);
59
+ }
60
+ return parsed.length > 0 ? parsed : void 0;
61
+ }
62
+ //#endregion
63
+ export { isRecord, num, parseNumberArray, parseStringArray, parseTextArray, text };
package/lib/plugin.d.ts CHANGED
@@ -4,7 +4,7 @@ import { Context } from "@deepseek-ai/cordis";
4
4
  //#region src/plugin.d.ts
5
5
  /** Cordis 插件名 = patch 行 id(TEMPLATES §8)。 */
6
6
  declare const name = "dsh-trading-connector-polymarket";
7
- /** 本插件不注册工具、不依赖任何宿主服务(纯 provide)。 */
7
+ /** 注册 agent 工具面需要宿主 tools 服务(与 connector-jin10 同款;缺 inject 会让整棵树加载失败)。 */
8
8
  declare const inject: string[];
9
9
  interface Config {
10
10
  enabled: boolean;
@@ -19,9 +19,11 @@ declare const Config: Schema<Config>;
19
19
  interface ApplyOverrides {
20
20
  /** 注入的 fetch(单测用契约化替身)。 */
21
21
  readonly fetchImpl?: typeof globalThis.fetch;
22
- /** 基址覆盖(单测指向本地夹具服务)。 */
22
+ /** gamma 基址覆盖(单测指向本地夹具服务)。 */
23
23
  readonly baseUrl?: string;
24
- /** 时钟注入(单测固定 fetchedAt)。 */
24
+ /** CLOB 基址覆盖(同上;历史面与会议面可指向同一个夹具服务)。 */
25
+ readonly clobBaseUrl?: string;
26
+ /** 时钟注入(单测固定 fetchedAt / 推进缓存 TTL)。 */
25
27
  readonly now?: (() => number) | undefined;
26
28
  }
27
29
  /** 组装取数服务(测试可直接注入 fetchImpl,无需 mock 全局)。 */
@@ -29,6 +31,10 @@ declare function createPolymarketService(config: Config, overrides?: ApplyOverri
29
31
  /**
30
32
  * 插件钩子(宿主签名 `apply(ctx, config)`)。
31
33
  *
34
+ * **一个 service 实例供两处消费**:桥/面板面(`tradingPredictionFeed`)与 agent 工具面
35
+ * 共用下面这个 `service`——两份取数逻辑漂移是这类数据面最容易犯的错(同一场会议在面板
36
+ * 与工具里给出不同概率)。工具用同名先到先得注册,不覆盖别人的工具。
37
+ *
32
38
  * 不接收注入参数:测试用的 fetch/时钟替身走 {@link createPolymarketService},不往
33
39
  * 生产钩子的签名里加测试专用面(connector-jin10 同款纪律)。
34
40
  */
package/lib/plugin.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import { DEFAULT_TIMEOUT_MS, GammaClient } from "./gamma.js";
2
+ import { ClobClient } from "./clob.js";
2
3
  import { PolymarketService, TRADING_PREDICTION_FEED_KEY, createPolymarketPredictionFeed } from "./service.js";
3
4
  import { SnapshotStore } from "./snapshot.js";
5
+ import { createPolymarketTools } from "./tools.js";
4
6
  import Schema from "@deepseek-ai/schemastery";
5
7
  //#region src/plugin.ts
6
8
  /** Cordis 插件名 = patch 行 id(TEMPLATES §8)。 */
7
9
  const name = "dsh-trading-connector-polymarket";
8
- /** 本插件不注册工具、不依赖任何宿主服务(纯 provide)。 */
9
- const inject = [];
10
+ /** 注册 agent 工具面需要宿主 tools 服务(与 connector-jin10 同款;缺 inject 会让整棵树加载失败)。 */
11
+ const inject = ["tools"];
10
12
  const Config = Schema.object({
11
13
  enabled: Schema.boolean().default(true).description("是否启用 Polymarket 预测市场数据面"),
12
14
  timeoutMs: Schema.number().default(DEFAULT_TIMEOUT_MS).description("单次 gamma-api 请求超时(ms)"),
@@ -20,9 +22,15 @@ function createPolymarketService(config, overrides = {}) {
20
22
  ...overrides.fetchImpl !== void 0 ? { fetchImpl: overrides.fetchImpl } : {},
21
23
  ...overrides.baseUrl !== void 0 ? { baseUrl: overrides.baseUrl } : {}
22
24
  });
25
+ const clob = new ClobClient({
26
+ timeoutMs: config.timeoutMs,
27
+ ...overrides.fetchImpl !== void 0 ? { fetchImpl: overrides.fetchImpl } : {},
28
+ ...overrides.clobBaseUrl !== void 0 ? { baseUrl: overrides.clobBaseUrl } : {}
29
+ });
23
30
  const snapshots = config.snapshots ? new SnapshotStore(config.snapshotPath === "" ? {} : { filePath: config.snapshotPath }) : void 0;
24
31
  return new PolymarketService({
25
32
  gamma,
33
+ clob,
26
34
  ...snapshots !== void 0 ? { snapshots } : {},
27
35
  ...overrides.now !== void 0 ? { now: overrides.now } : {}
28
36
  });
@@ -30,12 +38,19 @@ function createPolymarketService(config, overrides = {}) {
30
38
  /**
31
39
  * 插件钩子(宿主签名 `apply(ctx, config)`)。
32
40
  *
41
+ * **一个 service 实例供两处消费**:桥/面板面(`tradingPredictionFeed`)与 agent 工具面
42
+ * 共用下面这个 `service`——两份取数逻辑漂移是这类数据面最容易犯的错(同一场会议在面板
43
+ * 与工具里给出不同概率)。工具用同名先到先得注册,不覆盖别人的工具。
44
+ *
33
45
  * 不接收注入参数:测试用的 fetch/时钟替身走 {@link createPolymarketService},不往
34
46
  * 生产钩子的签名里加测试专用面(connector-jin10 同款纪律)。
35
47
  */
36
48
  function apply(ctx, config) {
37
49
  if (!config.enabled) return;
38
- ctx.reflect.provide(TRADING_PREDICTION_FEED_KEY, createPolymarketPredictionFeed(createPolymarketService(config)));
50
+ const service = createPolymarketService(config);
51
+ ctx.reflect.provide(TRADING_PREDICTION_FEED_KEY, createPolymarketPredictionFeed(service));
52
+ const tools = ctx.tools;
53
+ for (const tool of createPolymarketTools(service)) if (tools.get(tool.name) === void 0) tools.register(tool);
39
54
  }
40
55
  //#endregion
41
56
  export { Config, apply, createPolymarketService, inject, name };
package/lib/service.d.ts CHANGED
@@ -1,12 +1,64 @@
1
+ import { ClobClient } from "./clob.js";
2
+ import { EventDetail, EventSummary } from "./events.js";
1
3
  import { GammaClient } from "./gamma.js";
2
4
  import { SnapshotStore } from "./snapshot.js";
3
- import { PredictionFeed, PredictionFeedService } from "@dshtrading/api";
5
+ import { PredictionFeed, PredictionFeedService, PredictionMeeting, PredictionPriceHistory, PredictionPriceHistoryRequest } from "@dshtrading/api";
4
6
  //#region src/service.d.ts
5
7
  declare const TRADING_PREDICTION_FEED_KEY = "tradingPredictionFeed";
6
- /** 单次取数的候选上限(上游 limit 上限 100,证据 Q1.4)。 */
8
+ /** 单次取数的候选上限(上游 limit 上限 100,证据 Q1.4;`limit=500` 与 `limit=100` 同结果)。 */
7
9
  declare const PREDICTION_EVENTS_LIMIT = 100;
10
+ /**
11
+ * 会议发现结果的缓存窗口(ms)——**短 TTL,只挡同一刷新周期内的重复发现**。
12
+ *
13
+ * 历史面必须先按 `meetingId` 找到会议才能拿到各档 token;面板每换一个事件、每重绘
14
+ * 一次都会要历史,不缓存就是「面板每个事件打一次 gamma」(`/events` 还有
15
+ * `max-age=300` 的 CDN 缓存,重复打只是白等一份同一副本)。60 秒的依据:会议集合是
16
+ * 以天为单位变化的(新会议按周出现、结算按会议日翻转),60s 足够短到不掩盖真实变化,
17
+ * 又足够长到覆盖一次面板交互的全部事件请求。
18
+ *
19
+ * **失败不入缓存**:网络抖动不会被记成「没有这场会议」,下一次重试照常打上游。
20
+ */
21
+ declare const MEETING_LOOKUP_TTL_MS = 60000;
22
+ /** 关键词检索的返回条数默认/上限(取数页恒为 {@link PREDICTION_EVENTS_LIMIT})。 */
23
+ declare const PREDICTION_SEARCH_DEFAULT_LIMIT = 20;
24
+ declare const PREDICTION_SEARCH_MAX_LIMIT = 50;
25
+ /** 工具面 `source` 字段的唯一取值:数据来自哪个读取面(公开只读,无密钥)。 */
26
+ declare const POLYMARKET_SOURCE = "gamma-api.polymarket.com /events (public read-only upstream)";
27
+ /**
28
+ * 工具面出参的时间/出处/新鲜度三件套。
29
+ *
30
+ * `stale` 的判据只有一个(见 {@link isCdnCached}):本次响应是否由共享 CDN 直接命中。
31
+ * 它**不是**「数据旧了」的推测,而是「拿到的是副本」的实证。
32
+ */
33
+ interface Freshness {
34
+ /** 本进程的取数时刻(ISO 8601 UTC;上游无响应级时间戳,数据时间戳只能由取数侧记录)。 */
35
+ readonly observedAt: string;
36
+ readonly source: string;
37
+ readonly stale: boolean;
38
+ }
39
+ /** FOMC 工具面结果(可按 meetingId 收窄)。 */
40
+ interface PredictionFomcResult extends Freshness {
41
+ readonly meetings: readonly PredictionMeeting[];
42
+ }
43
+ /** 关键词检索结果:`filtering` 恒为本地过滤标记(上游无检索参数,见 events.ts 头注)。 */
44
+ interface PredictionEventSearchResult extends Freshness {
45
+ readonly filtering: 'local-page-filter';
46
+ /** 本次实际请求的取数页大小(恒为 {@link PREDICTION_EVENTS_LIMIT})。 */
47
+ readonly pageLimit: number;
48
+ /** 本次扫描的上游事件数。 */
49
+ readonly scanned: number;
50
+ /** 关键词命中数(截断到返回条数**之前**)。 */
51
+ readonly matched: number;
52
+ readonly events: readonly EventSummary[];
53
+ }
54
+ /** 单事件详情结果。 */
55
+ interface PredictionEventDetailResult extends Freshness {
56
+ readonly event: EventDetail;
57
+ }
8
58
  interface PolymarketServiceOptions {
9
59
  readonly gamma: GammaClient;
60
+ /** CLOB 历史传输层(`/prices-history`)。 */
61
+ readonly clob: ClobClient;
10
62
  /** 快照存储;缺省 = 不做 Δ 标注(服务仍可用)。 */
11
63
  readonly snapshots?: SnapshotStore | undefined;
12
64
  /** 时钟注入(测试固定 fetchedAt 用;缺省 Date.now)。 */
@@ -21,19 +73,85 @@ interface PolymarketServiceOptions {
21
73
  */
22
74
  declare class PolymarketService {
23
75
  private readonly gamma;
76
+ private readonly clob;
24
77
  private readonly snapshots;
25
78
  private readonly now;
79
+ private meetingLookup;
26
80
  constructor(options: PolymarketServiceOptions);
27
81
  /**
28
- * FOMC 决议路径全量会议(含已结算;按 endDate 升序)。
82
+ * `@dshtrading/api` 形状的取数面(桥/面板消费)。只是把 {@link fomcMeetings} 的结果
83
+ * 投影成 `PredictionFeed`——`fetchedAt` 与工具面的 `observedAt` 是同一个取数时刻,
84
+ * 两个字段名各归各自的契约,不是两次取数。
85
+ */
86
+ listFomcMeetings(options?: {
87
+ cacheBust?: boolean | undefined;
88
+ }): Promise<PredictionFeed>;
89
+ /**
90
+ * FOMC 决议路径全量会议(含已结算;按 endDate 升序)——**取数逻辑的唯一实现**。
29
91
  *
30
92
  * **失败必须响亮**:传输失败、HTTP 403(Cloudflare 1010 的 text/plain 体)、
31
93
  * 非 JSON 体、非数组体一律抛结构化错误——绝不返回 `{meetings: []}`。空数组
32
94
  * 只代表「上游确实返回了空集合」这一种真实语义。
95
+ *
96
+ * `meetingId` 命中 event id 或 slug(精确匹配);给了但一场都没匹配到时抛
97
+ * TRADING_SYMBOL_NOT_FOUND——「上游说查不到」与「上游故障」是两件事,消费方不该把
98
+ * 前者当基础设施抖动去重试。
33
99
  */
34
- listFomcMeetings(options?: {
100
+ fomcMeetings(options?: {
35
101
  cacheBust?: boolean | undefined;
36
- }): Promise<PredictionFeed>;
102
+ meetingId?: string | undefined;
103
+ }): Promise<PredictionFomcResult>;
104
+ /**
105
+ * 关键词检索(中文或英文,大小写不敏感子串,命中事件标题或 slug——**不搜档位标签**,
106
+ * 理由见 events.ts 的 `matchesKeyword`)。
107
+ *
108
+ * 上游 `/events` **没有关键词参数**(实测 `q=`/`title=` 与无参数逐条同结果),所以
109
+ * 这里是单次拉「开盘中、24h 成交额降序」的一页再本地过滤,输出带
110
+ * `filtering: 'local-page-filter'` 与 `scanned`,让消费方一眼看出这是「一页 ∩ 关键词」,
111
+ * 不是上游检索。
112
+ *
113
+ * 空关键词**先抛错、零请求**:空串会匹配一切,静默回「最热 N 条」冒充检索命中就是编造。
114
+ */
115
+ searchEvents(options: {
116
+ keyword: string;
117
+ limit?: number | undefined;
118
+ }): Promise<PredictionEventSearchResult>;
119
+ /**
120
+ * 单事件详情(eventId 接受 event id 或 slug,精确匹配,**单次请求**)。
121
+ *
122
+ * 上游 200 空集 = 没有这个事件 → TRADING_SYMBOL_NOT_FOUND;传输/准入/契约失败照旧由
123
+ * 传输层结构化抛出,两者不互相冒充。
124
+ */
125
+ eventDetail(options: {
126
+ eventId: string;
127
+ }): Promise<PredictionEventDetailResult>;
128
+ /**
129
+ * 单场会议在请求窗口内的逐档历史读数(面板「事件对照」的回填来源)。
130
+ *
131
+ * 三段语义(每一段都对应一条验收要求,改这里前先读它们):
132
+ *
133
+ * 1. **空 ≠ 失败**。上游 token 不存在时回 `200 {"history":[]}`(Q5.2),与本窗口确实
134
+ * 没有读数**无法在响应上区分**。所以空 history 只产生 `points: []`、不抛错——
135
+ * 但也只是「这一档在这个窗口没有可用读数」,不等于「概率为 0」「没有变动」:
136
+ * 面板照实留白,不许画一条 0 的线。
137
+ * 2. **任一档失败必须响亮抛出**(`Promise.all` 全否)。少一档就是**把失败画成没有
138
+ * 变动**——用户看到的是一条平的线,而真实情况是「这一档我们没取到」。宁可整块
139
+ * 报错让面板显示失败,也不给一份看起来完整、实则缺档的图。
140
+ * 3. **拿不到 token 的档位不扇出**:`clobTokenId` 缺省 = 上游没给定位键(或解析失败),
141
+ * 没有 token 就没有任何可请求的对象,**不猜**、也不给这一档造一条空序列;它就
142
+ * 不出现在 `series` 里(调用方按 bucketKey 找不到 = 这一档没有历史可对照)。
143
+ *
144
+ * 请求参数在**打 gamma 之前**校验({@link assertPriceHistoryRange}:`fromMs < toMs`、
145
+ * 窗口 ≤ 336h、fidelity > 0):桥侧已有同值闸门,这里是纵深防御——让非法请求
146
+ * 一次上游请求都不产生,而不是拿一个必然失败的窗口去打两次网络。
147
+ * `fidelityMinutes` 缺省 5 分钟(面板口径,依据见 clob.ts 的同名常量)。
148
+ */
149
+ listPriceHistory(request: PredictionPriceHistoryRequest): Promise<PredictionPriceHistory>;
150
+ /** 一次 gamma `/events` + 发现规则(不做 Δ 标注:历史面不需要快照)。 */
151
+ private fetchMeetings;
152
+ /** 历史面的会议定位:短 TTL 缓存(见 {@link MEETING_LOOKUP_TTL_MS};失败不缓存)。 */
153
+ private meetingsForLookup;
154
+ private withFreshness;
37
155
  /**
38
156
  * Δ 标注:**快照失败不阻断取数**。
39
157
  *
@@ -49,4 +167,4 @@ declare class PolymarketService {
49
167
  /** host 平面服务面:桥与工具面共用的最小接口(@dshtrading/api 的 PredictionFeedService)。 */
50
168
  declare function createPolymarketPredictionFeed(service: PolymarketService): PredictionFeedService;
51
169
  //#endregion
52
- export { PREDICTION_EVENTS_LIMIT, PolymarketService, PolymarketServiceOptions, TRADING_PREDICTION_FEED_KEY, createPolymarketPredictionFeed };
170
+ export { Freshness, MEETING_LOOKUP_TTL_MS, POLYMARKET_SOURCE, PREDICTION_EVENTS_LIMIT, PREDICTION_SEARCH_DEFAULT_LIMIT, PREDICTION_SEARCH_MAX_LIMIT, PolymarketService, PolymarketServiceOptions, PredictionEventDetailResult, PredictionEventSearchResult, PredictionFomcResult, TRADING_PREDICTION_FEED_KEY, createPolymarketPredictionFeed };