pandora-cli-skills 1.1.65 → 1.1.66

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/SKILL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: pandora-cli-skills
3
3
  summary: Canonical skill and operator guide for Pandora CLI including mirror, polymarket, resolve, and LP flows.
4
- version: 1.1.65
4
+ version: 1.1.66
5
5
  ---
6
6
 
7
7
  # Pandora CLI & Skills
@@ -1890,7 +1890,7 @@ const commandContracts = [
1890
1890
  name: 'mirror.plan',
1891
1891
  summary: 'Generate mirror sizing and distribution plan from a Polymarket source market.',
1892
1892
  usage:
1893
- 'pandora [--output table|json] mirror plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
1893
+ 'pandora [--output table|json] mirror plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
1894
1894
  emits: ['mirror.plan', 'mirror.plan.help'],
1895
1895
  dataSchema: '#/definitions/MirrorPlanPayload',
1896
1896
  mcpExposed: true,
@@ -1911,6 +1911,7 @@ const commandContracts = [
1911
1911
  'max-liquidity-usdc': numberSchema('Maximum liquidity in USDC.', { minimum: 0 }),
1912
1912
  'with-rules': booleanSchema('Include market rules payloads.'),
1913
1913
  'include-similarity': booleanSchema('Include similarity diagnostics.'),
1914
+ 'min-close-lead-seconds': integerSchema('Lead time before targetTimestamp when Pandora trading closes.', { minimum: 1 }),
1914
1915
  'polymarket-gamma-url': stringSchema('Polymarket Gamma API base URL.'),
1915
1916
  'polymarket-gamma-mock-url': stringSchema('Polymarket Gamma mock URL.'),
1916
1917
  'polymarket-mock-url': stringSchema('Polymarket mock CLOB URL.'),
@@ -1923,7 +1924,7 @@ const commandContracts = [
1923
1924
  name: 'mirror.deploy',
1924
1925
  summary: 'Deploy a mirror market from selector or plan in dry-run or execute mode.',
1925
1926
  usage:
1926
- 'pandora [--output table|json] mirror deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--oracle <address>] [--factory <address>] [--usdc <address>] [--distribution-yes <parts>] [--distribution-no <parts>] [--sources <url...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--min-close-lead-seconds <n>]',
1927
+ 'pandora [--output table|json] mirror deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--oracle <address>] [--factory <address>] [--usdc <address>] [--distribution-yes <parts>] [--distribution-no <parts>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--min-close-lead-seconds <n>]',
1927
1928
  emits: ['mirror.deploy', 'mirror.deploy.help'],
1928
1929
  dataSchema: '#/definitions/MirrorDeployPayload',
1929
1930
  mcpExposed: true,
@@ -1963,6 +1964,13 @@ const commandContracts = [
1963
1964
  'distribution-no': numberSchema('Initial NO distribution parts.', { minimum: 0 }),
1964
1965
  sources: flexibleArraySchema(stringSchema(), 'Source URL list.'),
1965
1966
  'validation-ticket': stringSchema('Ticket returned by agent.market.validate for the exact final payload (CLI execute mode).'),
1967
+ 'target-timestamp': {
1968
+ description: 'Explicit target timestamp override.',
1969
+ anyOf: [
1970
+ integerSchema('Explicit target timestamp in unix seconds.', { minimum: 1 }),
1971
+ stringSchema('Explicit ISO date/time override for target timestamp.'),
1972
+ ],
1973
+ },
1966
1974
  'manifest-file': stringSchema('Mirror manifest path.'),
1967
1975
  'polymarket-host': stringSchema('Polymarket host override.'),
1968
1976
  'polymarket-gamma-url': stringSchema('Polymarket Gamma API base URL.'),
@@ -2091,7 +2099,7 @@ const commandContracts = [
2091
2099
  name: 'mirror.go',
2092
2100
  summary: 'Run mirror deploy, verify, and optional sync workflow.',
2093
2101
  usage:
2094
- 'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--max-open-exposure-usdc <amount>] [--max-trades-per-day <n>] [--polymarket-rpc-url <url>] [--sources <url...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--dotenv-path <path>] [--rpc-url <url>] [--private-key <hex>]',
2102
+ 'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--max-open-exposure-usdc <amount>] [--max-trades-per-day <n>] [--polymarket-rpc-url <url>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--dotenv-path <path>] [--rpc-url <url>] [--private-key <hex>]',
2095
2103
  emits: ['mirror.go', 'mirror.go.help'],
2096
2104
  dataSchema: '#/definitions/MirrorDeployPayload',
2097
2105
  mcpExposed: true,
@@ -2130,6 +2138,13 @@ const commandContracts = [
2130
2138
  'polymarket-rpc-url': stringSchema('Polygon RPC URL for Polymarket preflight.'),
2131
2139
  sources: flexibleArraySchema(stringSchema(), 'Independent public source URL list.'),
2132
2140
  'validation-ticket': stringSchema('Ticket returned by agent.market.validate for the exact final payload (CLI execute mode).'),
2141
+ 'target-timestamp': {
2142
+ description: 'Explicit target timestamp override.',
2143
+ anyOf: [
2144
+ integerSchema('Explicit target timestamp in unix seconds.', { minimum: 1 }),
2145
+ stringSchema('Explicit ISO date/time override for target timestamp.'),
2146
+ ],
2147
+ },
2133
2148
  'manifest-file': stringSchema('Mirror manifest path.'),
2134
2149
  'dotenv-path': stringSchema('Env file path.'),
2135
2150
  'rpc-url': commonFlags.rpcUrl,
@@ -4,7 +4,7 @@
4
4
  * @type {string}
5
5
  */
6
6
  const MIRROR_GO_USAGE =
7
- 'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--trust-deploy] [--skip-gate] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>]';
7
+ 'pandora [--output table|json] mirror go --polymarket-market-id <id>|--polymarket-slug <slug> [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--paper|--dry-run|--execute-live|--execute] [--auto-sync] [--sync-once] [--sync-interval-ms <ms>] [--hedge-ratio <n>] [--no-hedge] [--max-rebalance-usdc <n>] [--max-hedge-usdc <n>] [--max-open-exposure-usdc <n>] [--max-trades-per-day <n>] [--cooldown-ms <ms>] [--chain-id <id>] [--rpc-url <url>] [--polymarket-rpc-url <url>] [--private-key <hex>] [--funder <address>] [--usdc <address>] [--oracle <address>] [--factory <address>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--trust-deploy] [--skip-gate] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>]';
8
8
 
9
9
  /**
10
10
  * Canonical usage string for `mirror sync`.
@@ -94,10 +94,10 @@ function createRunMirrorCommand(deps) {
94
94
  ' browse --min-yes-pct <n> --max-yes-pct <n> --min-volume-24h <n> [--closes-after <date>|--end-date-after <date|72h>] [--closes-before <date>|--end-date-before <date|72h>] [--question-contains <text>|--keyword <text>] [--slug <text>] [--category sports|crypto|politics|entertainment] [--exclude-sports] [--sort-by volume24h|liquidity|endDate] [--limit <n>] [--chain-id <id>] [--polymarket-tag-id <id>] [--polymarket-tag-ids <csv>] [--sport-tag-id <id>] [--sport-tag-ids <csv>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
95
95
  );
96
96
  console.log(
97
- ' plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
97
+ ' plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
98
98
  );
99
99
  console.log(
100
- ' deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--oracle <address>] [--factory <address>] [--usdc <address>] [--distribution-yes <parts>] [--distribution-no <parts>] [--sources <url...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--min-close-lead-seconds <n>]',
100
+ ' deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--chain-id <id>] [--rpc-url <url>] [--private-key <hex>] [--oracle <address>] [--factory <address>] [--usdc <address>] [--distribution-yes <parts>] [--distribution-no <parts>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>] [--min-close-lead-seconds <n>]',
101
101
  );
102
102
  console.log(
103
103
  ' verify --pandora-market-address <address>|--market-address <address> --polymarket-market-id <id>|--polymarket-slug <slug> [--trust-deploy] [--manifest-file <path>] [--include-similarity] [--with-rules] [--allow-rule-mismatch] [--polymarket-host <url>] [--polymarket-gamma-url <url>] [--polymarket-gamma-mock-url <url>] [--polymarket-mock-url <url>]',
@@ -24,12 +24,12 @@ module.exports = async function handleMirrorDeploy({ shared, context, deps }) {
24
24
  context.outputMode,
25
25
  'mirror.deploy.help',
26
26
  commandHelpPayload(
27
- 'pandora [--output table|json] mirror deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--sources <url...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
27
+ 'pandora [--output table|json] mirror deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
28
28
  ),
29
29
  );
30
30
  } else {
31
31
  console.log(
32
- 'Usage: pandora [--output table|json] mirror deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--sources <url...>] [--validation-ticket <ticket>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
32
+ 'Usage: pandora [--output table|json] mirror deploy --plan-file <path>|--polymarket-market-id <id>|--polymarket-slug <slug> --dry-run|--execute [--liquidity-usdc <n>] [--fee-tier <500-50000>] [--max-imbalance <n>] [--arbiter <address>] [--category <n>] [--sources <url...>] [--validation-ticket <ticket>] [--target-timestamp <unix|iso>] [--manifest-file <path>] [--min-close-lead-seconds <n>]',
33
33
  );
34
34
  }
35
35
  return;
@@ -22,12 +22,12 @@ module.exports = async function handleMirrorPlan({ shared, context, deps }) {
22
22
  context.outputMode,
23
23
  'mirror.plan.help',
24
24
  commandHelpPayload(
25
- 'pandora [--output table|json] mirror plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--polymarket-gamma-url <url>]',
25
+ 'pandora [--output table|json] mirror plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>] [--polymarket-gamma-url <url>]',
26
26
  ),
27
27
  );
28
28
  } else {
29
29
  console.log(
30
- 'Usage: pandora [--output table|json] mirror plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--polymarket-gamma-url <url>]',
30
+ 'Usage: pandora [--output table|json] mirror plan --source polymarket --polymarket-market-id <id>|--polymarket-slug <slug> [--chain-id <id>] [--target-slippage-bps <n>] [--turnover-target <n>] [--depth-slippage-bps <n>] [--safety-multiplier <n>] [--min-liquidity-usdc <n>] [--max-liquidity-usdc <n>] [--with-rules] [--include-similarity] [--min-close-lead-seconds <n>] [--polymarket-gamma-url <url>]',
31
31
  );
32
32
  }
33
33
  return;
@@ -19,6 +19,27 @@ const { round } = require('./shared/utils.cjs');
19
19
  const MIRROR_PLAN_SCHEMA_VERSION = '1.0.0';
20
20
  const MIRROR_DEPLOY_SCHEMA_VERSION = '1.0.0';
21
21
  const MIRROR_BROWSE_SCHEMA_VERSION = '1.0.0';
22
+ const DEFAULT_MIRROR_MIN_CLOSE_LEAD_SECONDS = 3600;
23
+ const MIRROR_SPORT_TIMING_PROFILES = Object.freeze({
24
+ basketball: Object.freeze({
25
+ key: 'basketball',
26
+ expectedDurationMinutes: 150,
27
+ resolutionBufferMinutes: 30,
28
+ minimumTradingBufferMinutes: 90,
29
+ }),
30
+ soccer: Object.freeze({
31
+ key: 'soccer',
32
+ expectedDurationMinutes: 120,
33
+ resolutionBufferMinutes: 20,
34
+ minimumTradingBufferMinutes: 30,
35
+ }),
36
+ sports: Object.freeze({
37
+ key: 'sports',
38
+ expectedDurationMinutes: 150,
39
+ resolutionBufferMinutes: 30,
40
+ minimumTradingBufferMinutes: 45,
41
+ }),
42
+ });
22
43
 
23
44
  function createServiceError(code, message, details = undefined) {
24
45
  const err = new Error(message);
@@ -50,6 +71,150 @@ function normalizeComparableText(value) {
50
71
  .trim();
51
72
  }
52
73
 
74
+ function toUnixSeconds(value) {
75
+ if (value === null || value === undefined || value === '') return null;
76
+ const numeric = Number(value);
77
+ if (Number.isFinite(numeric)) {
78
+ return numeric > 1e12 ? Math.floor(numeric / 1000) : Math.floor(numeric);
79
+ }
80
+ const parsed = Date.parse(String(value));
81
+ if (Number.isNaN(parsed)) return null;
82
+ return Math.floor(parsed / 1000);
83
+ }
84
+
85
+ function formatTimestampIso(value) {
86
+ const unixSeconds = toUnixSeconds(value);
87
+ return Number.isFinite(unixSeconds) ? new Date(unixSeconds * 1000).toISOString() : null;
88
+ }
89
+
90
+ function parseMirrorTargetTimestampInput(value) {
91
+ if (value === null || value === undefined || value === '') return null;
92
+ const unixSeconds = toUnixSeconds(value);
93
+ return Number.isFinite(unixSeconds) && unixSeconds > 0 ? unixSeconds : null;
94
+ }
95
+
96
+ function collectMirrorTimingTextPool(sourceMarket) {
97
+ const raw = sourceMarket && sourceMarket.raw && typeof sourceMarket.raw === 'object' ? sourceMarket.raw : {};
98
+ const tagEntries = []
99
+ .concat(Array.isArray(raw.tags) ? raw.tags : [])
100
+ .concat(Array.isArray(raw.tag_ids) ? raw.tag_ids : [])
101
+ .concat(Array.isArray(raw.tagIds) ? raw.tagIds : []);
102
+ const tagText = [];
103
+ for (const entry of tagEntries) {
104
+ if (entry === null || entry === undefined) continue;
105
+ if (typeof entry === 'string' || typeof entry === 'number') {
106
+ tagText.push(String(entry));
107
+ continue;
108
+ }
109
+ if (typeof entry === 'object') {
110
+ for (const key of ['name', 'slug', 'label', 'title', 'group', 'topic', 'category', 'shortName', 'short_name']) {
111
+ if (entry[key]) tagText.push(String(entry[key]));
112
+ }
113
+ }
114
+ }
115
+ return [
116
+ sourceMarket && sourceMarket.question,
117
+ sourceMarket && sourceMarket.eventTitle,
118
+ sourceMarket && sourceMarket.eventSlug,
119
+ sourceMarket && sourceMarket.slug,
120
+ raw.sport,
121
+ raw.sport_type,
122
+ raw.league,
123
+ raw.competition,
124
+ ...tagText,
125
+ ]
126
+ .map((value) => normalizeComparableText(value))
127
+ .filter(Boolean)
128
+ .join(' ');
129
+ }
130
+
131
+ function inferMirrorSportTimingProfile(sourceMarket) {
132
+ const haystack = collectMirrorTimingTextPool(sourceMarket);
133
+ if (!haystack) return null;
134
+ if (/\b(nba|basketball)\b/.test(haystack)) {
135
+ return MIRROR_SPORT_TIMING_PROFILES.basketball;
136
+ }
137
+ if (/\b(soccer|football|premier league|epl|uefa|fifa|mls|la liga|serie a|bundesliga|champions league)\b/.test(haystack)) {
138
+ return MIRROR_SPORT_TIMING_PROFILES.soccer;
139
+ }
140
+ if (/\b(sport|sports|nfl|nhl|mlb|tennis|ufc|mma|formula 1|f1|cricket)\b/.test(haystack)) {
141
+ return MIRROR_SPORT_TIMING_PROFILES.sports;
142
+ }
143
+ return null;
144
+ }
145
+
146
+ function buildMirrorTimingData(sourceMarket, minCloseLeadSecondsInput) {
147
+ const minCloseLeadSeconds = Number.isFinite(Number(minCloseLeadSecondsInput))
148
+ ? Math.max(0, Math.trunc(Number(minCloseLeadSecondsInput)))
149
+ : DEFAULT_MIRROR_MIN_CLOSE_LEAD_SECONDS;
150
+ const sourceTimestamp = toUnixSeconds(sourceMarket && sourceMarket.closeTimestamp);
151
+ const eventStartTimestamp = toUnixSeconds(sourceMarket && sourceMarket.eventStartTimestamp) || sourceTimestamp;
152
+ const sourceCloseTimestamp = toUnixSeconds(sourceMarket && sourceMarket.sourceCloseTimestamp);
153
+ const timestampSource = String(
154
+ (sourceMarket && sourceMarket.timestampSource)
155
+ || (sourceMarket && sourceMarket.eventStartTimestamp ? 'game_start_time' : sourceMarket && sourceMarket.closeTimestamp ? 'source_timestamp' : ''),
156
+ ).trim() || null;
157
+ const profile = inferMirrorSportTimingProfile(sourceMarket);
158
+ const warnings = [];
159
+ let suggestedTargetTimestamp = sourceTimestamp;
160
+ let expectedEndTimestamp = null;
161
+ let tradingCutoffTimestamp = null;
162
+ let reason = null;
163
+
164
+ if (profile && eventStartTimestamp) {
165
+ expectedEndTimestamp = eventStartTimestamp + (profile.expectedDurationMinutes * 60);
166
+ const baseSuggestedTargetTimestamp = expectedEndTimestamp + (profile.resolutionBufferMinutes * 60);
167
+ const minimumTradingCutoffTimestamp = expectedEndTimestamp + (profile.minimumTradingBufferMinutes * 60);
168
+ suggestedTargetTimestamp = Math.max(baseSuggestedTargetTimestamp, minimumTradingCutoffTimestamp + minCloseLeadSeconds);
169
+ tradingCutoffTimestamp = suggestedTargetTimestamp - minCloseLeadSeconds;
170
+
171
+ if (timestampSource === 'game_start_time') {
172
+ warnings.push('Polymarket provided game_start_time, which is the event start. Mirror deploy should use a later targetTimestamp that covers event completion and a buffer.');
173
+ }
174
+ if (tradingCutoffTimestamp <= expectedEndTimestamp) {
175
+ warnings.push('With the current close lead, trading would stop before the expected regulation end. Increase targetTimestamp or reduce min-close-lead-seconds.');
176
+ } else if (tradingCutoffTimestamp < minimumTradingCutoffTimestamp) {
177
+ warnings.push('With the current close lead, trading would stop too close to the expected finish and may not cover overtime or stoppage time.');
178
+ }
179
+
180
+ reason = `Suggested targetTimestamp uses ${profile.key} timing defaults: expected duration ${profile.expectedDurationMinutes}m, resolution buffer ${profile.resolutionBufferMinutes}m, and trading cutoff buffer ${profile.minimumTradingBufferMinutes}m before the close lead.`;
181
+ } else {
182
+ if (timestampSource === 'game_start_time' && sourceTimestamp) {
183
+ warnings.push('Only a sports start time was available from Polymarket. Review targetTimestamp manually if you deploy this market.');
184
+ }
185
+ if (sourceTimestamp) {
186
+ tradingCutoffTimestamp = sourceTimestamp - minCloseLeadSeconds;
187
+ }
188
+ }
189
+
190
+ return {
191
+ sourceTimestamp,
192
+ sourceTimestampIso: formatTimestampIso(sourceTimestamp),
193
+ sourceTimestampKind: timestampSource,
194
+ sourceCloseTimestamp,
195
+ sourceCloseTimestampIso: formatTimestampIso(sourceCloseTimestamp),
196
+ eventStartTimestamp,
197
+ eventStartTimestampIso: formatTimestampIso(eventStartTimestamp),
198
+ expectedEndTimestamp,
199
+ expectedEndTimestampIso: formatTimestampIso(expectedEndTimestamp),
200
+ suggestedTargetTimestamp,
201
+ suggestedTargetTimestampIso: formatTimestampIso(suggestedTargetTimestamp),
202
+ tradingCutoffTimestamp,
203
+ tradingCutoffTimestampIso: formatTimestampIso(tradingCutoffTimestamp),
204
+ minCloseLeadSeconds,
205
+ profile: profile
206
+ ? {
207
+ sport: profile.key,
208
+ expectedDurationMinutes: profile.expectedDurationMinutes,
209
+ resolutionBufferMinutes: profile.resolutionBufferMinutes,
210
+ minimumTradingBufferMinutes: profile.minimumTradingBufferMinutes,
211
+ }
212
+ : null,
213
+ reason,
214
+ warnings,
215
+ };
216
+ }
217
+
53
218
  function hasPandoraBinaryRules(value) {
54
219
  const text = String(value || '');
55
220
  return /(^|\n)\s*YES\s*:/i.test(text) && /(^|\n)\s*NO\s*:/i.test(text);
@@ -366,6 +531,14 @@ function buildPlanDigest(planData) {
366
531
  sourceCount: planData.rules.sourceCount || null,
367
532
  }
368
533
  : null,
534
+ timing: planData.timing
535
+ ? {
536
+ sourceTimestamp: planData.timing.sourceTimestamp || null,
537
+ sourceTimestampKind: planData.timing.sourceTimestampKind || null,
538
+ suggestedTargetTimestamp: planData.timing.suggestedTargetTimestamp || null,
539
+ minCloseLeadSeconds: planData.timing.minCloseLeadSeconds || null,
540
+ }
541
+ : null,
369
542
  liquidityRecommendation: planData.liquidityRecommendation,
370
543
  distributionHint: planData.distributionHint,
371
544
  }));
@@ -412,6 +585,12 @@ async function buildMirrorPlan(options = {}) {
412
585
 
413
586
  const distribution = computeDistributionHint(sourceYesProbability === null ? 0.5 : sourceYesProbability);
414
587
  const rules = buildRuleTemplate(sourceMarket);
588
+ const timing = buildMirrorTimingData(
589
+ sourceMarket,
590
+ Number.isFinite(Number(options.minCloseLeadSeconds))
591
+ ? Number(options.minCloseLeadSeconds)
592
+ : DEFAULT_MIRROR_MIN_CLOSE_LEAD_SECONDS,
593
+ );
415
594
 
416
595
  let match = { best: null, diagnostics: [] };
417
596
  try {
@@ -440,6 +619,8 @@ async function buildMirrorPlan(options = {}) {
440
619
  for (const item of distribution.diagnostics || []) diagnostics.push(item);
441
620
  for (const item of rules.diagnostics || []) diagnostics.push(item);
442
621
  for (const item of match.diagnostics || []) diagnostics.push(item);
622
+ for (const item of timing.warnings || []) diagnostics.push(item);
623
+ if (timing.reason) diagnostics.push(timing.reason);
443
624
 
444
625
  const topMatch = match.best
445
626
  ? {
@@ -464,6 +645,9 @@ async function buildMirrorPlan(options = {}) {
464
645
  question: sourceMarket.question,
465
646
  description: options.withRules ? sourceMarket.description : undefined,
466
647
  closeTimestamp: sourceMarket.closeTimestamp,
648
+ eventStartTimestamp: sourceMarket.eventStartTimestamp || null,
649
+ sourceCloseTimestamp: sourceMarket.sourceCloseTimestamp || null,
650
+ timestampSource: sourceMarket.timestampSource || null,
467
651
  yesPct: sourceMarket.yesPct,
468
652
  noPct: sourceMarket.noPct,
469
653
  volume24hUsd: round(sourceMarket.volume24hUsd, 6),
@@ -481,6 +665,7 @@ async function buildMirrorPlan(options = {}) {
481
665
  proposedPandoraRules: rules.rulesText,
482
666
  sourceCount: normalizeSources(options.sources).length,
483
667
  },
668
+ timing,
484
669
  similarity: topMatch ? topMatch.similarity : null,
485
670
  sizingInputs: {
486
671
  V24: round(sourceMarket.volume24hUsd, 6),
@@ -545,7 +730,25 @@ async function deployMirror(options = {}) {
545
730
 
546
731
  const diagnostics = [];
547
732
  const question = String(planData.sourceMarket && planData.sourceMarket.question ? planData.sourceMarket.question : '').trim();
548
- const targetTimestamp = Number(planData.sourceMarket && planData.sourceMarket.closeTimestamp);
733
+ const minCloseLeadSeconds = Number.isFinite(Number(options.minCloseLeadSeconds))
734
+ ? Number(options.minCloseLeadSeconds)
735
+ : Number(planData.timing && planData.timing.minCloseLeadSeconds);
736
+ const effectiveTiming = planData.timing && typeof planData.timing === 'object'
737
+ ? {
738
+ ...planData.timing,
739
+ ...(Number.isFinite(Number(minCloseLeadSeconds))
740
+ ? { minCloseLeadSeconds: Number(minCloseLeadSeconds) }
741
+ : {}),
742
+ }
743
+ : buildMirrorTimingData(planData.sourceMarket || {}, minCloseLeadSeconds);
744
+ const suggestedTargetTimestamp = parseMirrorTargetTimestampInput(
745
+ effectiveTiming && effectiveTiming.suggestedTargetTimestamp,
746
+ );
747
+ const fallbackTargetTimestamp = parseMirrorTargetTimestampInput(
748
+ planData.sourceMarket && planData.sourceMarket.closeTimestamp,
749
+ );
750
+ const explicitTargetTimestamp = parseMirrorTargetTimestampInput(options.targetTimestamp);
751
+ const targetTimestamp = explicitTargetTimestamp || suggestedTargetTimestamp || fallbackTargetTimestamp;
549
752
  const refreshedRuleTemplate = buildRuleTemplate({
550
753
  ...(planData.sourceMarket || {}),
551
754
  description:
@@ -569,6 +772,17 @@ async function deployMirror(options = {}) {
569
772
  suggestedRules: refreshedRuleTemplate.rulesText,
570
773
  });
571
774
  diagnostics.push(...refreshedRuleTemplate.diagnostics);
775
+ if (explicitTargetTimestamp && suggestedTargetTimestamp && explicitTargetTimestamp < suggestedTargetTimestamp) {
776
+ diagnostics.push(
777
+ `Explicit --target-timestamp (${explicitTargetTimestamp}) is earlier than the suggested sports-safe target (${suggestedTargetTimestamp}). Trading may close before overtime or late completion.`,
778
+ );
779
+ } else if (!explicitTargetTimestamp && suggestedTargetTimestamp && suggestedTargetTimestamp !== fallbackTargetTimestamp) {
780
+ diagnostics.push(
781
+ `Using suggested targetTimestamp ${suggestedTargetTimestamp} instead of raw source timestamp ${fallbackTargetTimestamp}.`,
782
+ );
783
+ }
784
+ diagnostics.push(...(Array.isArray(effectiveTiming && effectiveTiming.warnings) ? effectiveTiming.warnings : []));
785
+ if (effectiveTiming && effectiveTiming.reason) diagnostics.push(effectiveTiming.reason);
572
786
 
573
787
  const liquidityUsdc =
574
788
  options.liquidityUsdc !== null && options.liquidityUsdc !== undefined
@@ -608,9 +822,9 @@ async function deployMirror(options = {}) {
608
822
  rules: sourceRulesText,
609
823
  sources,
610
824
  targetTimestamp,
611
- minCloseLeadSeconds: Number.isFinite(Number(options.minCloseLeadSeconds))
612
- ? Number(options.minCloseLeadSeconds)
613
- : 3600,
825
+ minCloseLeadSeconds: Number.isFinite(Number(minCloseLeadSeconds))
826
+ ? Number(minCloseLeadSeconds)
827
+ : DEFAULT_MIRROR_MIN_CLOSE_LEAD_SECONDS,
614
828
  liquidityUsdc,
615
829
  distributionYes,
616
830
  distributionNo,
@@ -696,6 +910,12 @@ async function deployMirror(options = {}) {
696
910
  generatedAt: new Date().toISOString(),
697
911
  planDigest: planData.planDigest || buildPlanDigest(planData),
698
912
  deploymentArgs: deployPayload.deploymentArgs,
913
+ timing: {
914
+ ...(effectiveTiming || {}),
915
+ selectedTargetTimestamp: targetTimestamp,
916
+ selectedTargetTimestampIso: formatTimestampIso(targetTimestamp),
917
+ overrideApplied: Boolean(explicitTargetTimestamp),
918
+ },
699
919
  dryRun: deployPayload.mode === 'dry-run',
700
920
  requiredValidation: deployPayload.requiredValidation || validationGate.requiredValidation || null,
701
921
  agentValidation: deployPayload.agentValidation || validationGate.agentValidation || null,
@@ -1,5 +1,5 @@
1
1
  const { MIN_AMM_FEE_TIER, MAX_AMM_FEE_TIER } = require('../shared/constants.cjs');
2
- const { normalizeMirrorPathForMcp, validateMirrorUrl } = require('./mirror_parser_guard.cjs');
2
+ const { normalizeMirrorPathForMcp, parseMirrorTargetTimestamp, validateMirrorUrl } = require('./mirror_parser_guard.cjs');
3
3
 
4
4
  const MAX_UINT24 = 16_777_215;
5
5
  const DISTRIBUTION_SCALE = 1_000_000_000;
@@ -122,6 +122,7 @@ function createParseMirrorDeployFlags(deps) {
122
122
  distributionNoPct: null,
123
123
  rules: null,
124
124
  validationTicket: null,
125
+ targetTimestamp: null,
125
126
  polymarketHost: null,
126
127
  polymarketGammaUrl: null,
127
128
  polymarketGammaMockUrl: null,
@@ -258,6 +259,15 @@ function createParseMirrorDeployFlags(deps) {
258
259
  i += 1;
259
260
  continue;
260
261
  }
262
+ if (token === '--target-timestamp') {
263
+ options.targetTimestamp = parseMirrorTargetTimestamp(
264
+ requireFlagValue(args, i, '--target-timestamp'),
265
+ '--target-timestamp',
266
+ CliError,
267
+ );
268
+ i += 1;
269
+ continue;
270
+ }
261
271
  if (token === '--distribution-yes') {
262
272
  options.distributionYes = parseDistributionUnits(
263
273
  requireFlagValue(args, i, '--distribution-yes'),
@@ -1,5 +1,5 @@
1
1
  const { MIN_AMM_FEE_TIER, MAX_AMM_FEE_TIER } = require('../shared/constants.cjs');
2
- const { normalizeMirrorPathForMcp, validateMirrorUrl } = require('./mirror_parser_guard.cjs');
2
+ const { normalizeMirrorPathForMcp, parseMirrorTargetTimestamp, validateMirrorUrl } = require('./mirror_parser_guard.cjs');
3
3
 
4
4
  const MAX_UINT24 = 16_777_215;
5
5
  const DISTRIBUTION_SCALE = 1_000_000_000;
@@ -135,6 +135,7 @@ function createParseMirrorGoFlags(deps) {
135
135
  sources: [],
136
136
  sourcesProvided: false,
137
137
  validationTicket: null,
138
+ targetTimestamp: null,
138
139
  manifestFile: null,
139
140
  trustDeploy: false,
140
141
  forceGate: false,
@@ -380,6 +381,15 @@ function createParseMirrorGoFlags(deps) {
380
381
  i += 1;
381
382
  continue;
382
383
  }
384
+ if (token === '--target-timestamp') {
385
+ options.targetTimestamp = parseMirrorTargetTimestamp(
386
+ requireFlagValue(args, i, '--target-timestamp'),
387
+ '--target-timestamp',
388
+ CliError,
389
+ );
390
+ i += 1;
391
+ continue;
392
+ }
383
393
  if (token === '--manifest-file') {
384
394
  options.manifestFile = normalizeMirrorPathForMcp(
385
395
  requireFlagValue(args, i, '--manifest-file'),
@@ -34,8 +34,25 @@ function validateMirrorUrl(rawValue, flagName, CliError, isSecureHttpUrlOrLocal)
34
34
  return value;
35
35
  }
36
36
 
37
+ function parseMirrorTargetTimestamp(rawValue, flagName, CliError) {
38
+ const value = String(rawValue || '').trim();
39
+ const numeric = Number(value);
40
+ if (Number.isFinite(numeric) && numeric > 0) {
41
+ return Math.trunc(numeric > 1e12 ? numeric / 1000 : numeric);
42
+ }
43
+ const parsed = Date.parse(value);
44
+ if (!Number.isNaN(parsed)) {
45
+ return Math.floor(parsed / 1000);
46
+ }
47
+ throw new CliError(
48
+ 'INVALID_FLAG_VALUE',
49
+ `${flagName} must be a unix timestamp in seconds (or milliseconds) or an ISO date/time string.`,
50
+ );
51
+ }
52
+
37
53
  module.exports = {
38
54
  normalizeMirrorPathForMcp,
39
55
  defaultMirrorWorkspacePath,
56
+ parseMirrorTargetTimestamp,
40
57
  validateMirrorUrl,
41
58
  };
@@ -34,6 +34,7 @@ function createParseMirrorPlanFlags(deps) {
34
34
  maxLiquidityUsdc: 50_000,
35
35
  withRules: false,
36
36
  includeSimilarity: false,
37
+ minCloseLeadSeconds: 3600,
37
38
  polymarketHost: null,
38
39
  polymarketGammaUrl: null,
39
40
  polymarketGammaMockUrl: null,
@@ -111,6 +112,14 @@ function createParseMirrorPlanFlags(deps) {
111
112
  options.includeSimilarity = true;
112
113
  continue;
113
114
  }
115
+ if (token === '--min-close-lead-seconds') {
116
+ options.minCloseLeadSeconds = parsePositiveInteger(
117
+ requireFlagValue(args, i, '--min-close-lead-seconds'),
118
+ '--min-close-lead-seconds',
119
+ );
120
+ i += 1;
121
+ continue;
122
+ }
114
123
  if (token === '--polymarket-host') {
115
124
  options.polymarketHost = validateMirrorUrl(
116
125
  requireFlagValue(args, i, '--polymarket-host'),
@@ -14,17 +14,18 @@ function toTimestampSeconds(value) {
14
14
  return Math.floor(parsed / 1000);
15
15
  }
16
16
 
17
- function resolvePolymarketTimestampValue(row) {
17
+ function resolvePolymarketEventStartValue(row) {
18
+ if (!row || typeof row !== 'object') return null;
19
+ return row.game_start_time || row.gameStartTime || null;
20
+ }
21
+
22
+ function resolvePolymarketCloseFallbackValue(row) {
18
23
  if (!row || typeof row !== 'object') return null;
19
- return (
20
- row.game_start_time ||
21
- row.gameStartTime ||
22
- row.endDateIso ||
23
- row.end_date_iso ||
24
- row.endDate ||
25
- row.closedTime ||
26
- null
27
- );
24
+ return row.endDateIso || row.end_date_iso || row.endDate || row.closedTime || null;
25
+ }
26
+
27
+ function resolvePolymarketTimestampValue(row) {
28
+ return resolvePolymarketEventStartValue(row) || resolvePolymarketCloseFallbackValue(row);
28
29
  }
29
30
 
30
31
  function normalizeTokens(tokens) {
@@ -105,12 +106,17 @@ function mapGammaRow(row) {
105
106
  const marketId = row && (row.conditionId || row.condition_id || row.id || row.questionID) ? String(
106
107
  row.conditionId || row.condition_id || row.id || row.questionID,
107
108
  ) : null;
109
+ const eventStartTimestamp = toTimestampSeconds(resolvePolymarketEventStartValue(row));
110
+ const sourceCloseTimestamp = toTimestampSeconds(resolvePolymarketCloseFallbackValue(row));
108
111
  const closeTimestamp = toTimestampSeconds(resolvePolymarketTimestampValue(row));
109
112
  return {
110
113
  legId: `polymarket:${String(marketId || '')}`,
111
114
  venue: 'polymarket',
112
115
  marketId,
113
116
  question: row && (row.question || row.title || row.description) ? String(row.question || row.title || row.description) : null,
117
+ eventStartTimestamp,
118
+ sourceCloseTimestamp,
119
+ timestampSource: eventStartTimestamp ? 'game_start_time' : sourceCloseTimestamp ? 'end_date_iso' : null,
114
120
  closeTimestamp,
115
121
  yesPct: mapped.yes,
116
122
  noPct: mapped.no,
@@ -129,11 +135,16 @@ function mapPolymarketRow(row) {
129
135
  const mapped = normalizeTokens(row.tokens || []);
130
136
  const question = row.question || row.description || null;
131
137
  const marketId = row.condition_id || row.question_id || null;
138
+ const eventStartTimestamp = toTimestampSeconds(resolvePolymarketEventStartValue(row));
139
+ const sourceCloseTimestamp = toTimestampSeconds(resolvePolymarketCloseFallbackValue(row));
132
140
  return {
133
141
  legId: `polymarket:${String(marketId || '')}`,
134
142
  venue: 'polymarket',
135
143
  marketId,
136
144
  question,
145
+ eventStartTimestamp,
146
+ sourceCloseTimestamp,
147
+ timestampSource: eventStartTimestamp ? 'game_start_time' : sourceCloseTimestamp ? 'end_date_iso' : null,
137
148
  closeTimestamp: toTimestampSeconds(resolvePolymarketTimestampValue(row)),
138
149
  yesPct: mapped.yes,
139
150
  noPct: mapped.no,
@@ -49,11 +49,14 @@ function toTimestampSeconds(value) {
49
49
  return numeric > 1e12 ? Math.floor(numeric / 1000) : Math.floor(numeric);
50
50
  }
51
51
 
52
- function resolvePolymarketEventTimestamp(row) {
52
+ function resolvePolymarketEventStartTimestampValue(row) {
53
+ if (!row || typeof row !== 'object') return null;
54
+ return row.game_start_time || row.gameStartTime || null;
55
+ }
56
+
57
+ function resolvePolymarketCloseFallbackValue(row) {
53
58
  if (!row || typeof row !== 'object') return null;
54
59
  return (
55
- row.game_start_time ||
56
- row.gameStartTime ||
57
60
  row.end_date_iso ||
58
61
  row.endDateIso ||
59
62
  row.endDate ||
@@ -64,6 +67,10 @@ function resolvePolymarketEventTimestamp(row) {
64
67
  );
65
68
  }
66
69
 
70
+ function resolvePolymarketEventTimestamp(row) {
71
+ return resolvePolymarketEventStartTimestampValue(row) || resolvePolymarketCloseFallbackValue(row);
72
+ }
73
+
67
74
  function normalizeHostList(hostInput) {
68
75
  const rawValues = Array.isArray(hostInput) ? hostInput : String(hostInput || '').split(',');
69
76
  const hosts = rawValues
@@ -424,6 +431,13 @@ function normalizeMarketRow(row) {
424
431
  eventSlug: toStringOrNull(row && (row.event_slug || row.eventSlug)),
425
432
  eventTitle: toStringOrNull(row && (row.event_title || row.eventTitle)),
426
433
  description: rulesSections.length ? rulesSections.join('\n\n') : null,
434
+ eventStartTimestamp: toTimestampSeconds(resolvePolymarketEventStartTimestampValue(row)),
435
+ sourceCloseTimestamp: toTimestampSeconds(resolvePolymarketCloseFallbackValue(row)),
436
+ timestampSource: resolvePolymarketEventStartTimestampValue(row)
437
+ ? 'game_start_time'
438
+ : resolvePolymarketCloseFallbackValue(row)
439
+ ? 'end_date_iso'
440
+ : null,
427
441
  closeTimestamp: toTimestampSeconds(resolvePolymarketEventTimestamp(row)),
428
442
  yesPct: tokens.yes,
429
443
  noPct: tokens.no,
@@ -795,6 +795,7 @@ function buildSchemaPayload() {
795
795
  type: 'object',
796
796
  properties: {
797
797
  sourceMarket: { type: 'object' },
798
+ timing: { type: 'object' },
798
799
  liquidityRecommendation: { type: 'object' },
799
800
  distributionHint: { type: 'object' },
800
801
  rules: { type: 'object' },
@@ -808,6 +809,7 @@ function buildSchemaPayload() {
808
809
  mode: { enum: ['dry-run', 'execute'] },
809
810
  planDigest: { type: ['string', 'null'] },
810
811
  deploymentArgs: { type: ['object', 'null'] },
812
+ timing: { type: ['object', 'null'] },
811
813
  dryRun: { type: ['boolean', 'null'] },
812
814
  requiredValidation: { type: ['object', 'null'] },
813
815
  agentValidation: { type: ['object', 'null'] },
package/cli/pandora.cjs CHANGED
@@ -2195,6 +2195,7 @@ function renderAutopilotTable(data) {
2195
2195
  }
2196
2196
 
2197
2197
  function renderMirrorPlanTable(data) {
2198
+ const timing = data.timing || {};
2198
2199
  printTable(
2199
2200
  ['Field', 'Value'],
2200
2201
  [
@@ -2202,6 +2203,10 @@ function renderMirrorPlanTable(data) {
2202
2203
  ['sourceMarketId', data.sourceMarket ? data.sourceMarket.marketId : ''],
2203
2204
  ['sourceSlug', data.sourceMarket ? data.sourceMarket.slug || '' : ''],
2204
2205
  ['sourceYesPct', data.sourceMarket && data.sourceMarket.yesPct !== null ? data.sourceMarket.yesPct : ''],
2206
+ ['sourceTimestampKind', timing.sourceTimestampKind || (data.sourceMarket ? data.sourceMarket.timestampSource || '' : '')],
2207
+ ['eventStartAt', timing.eventStartTimestampIso || ''],
2208
+ ['suggestedTargetAt', timing.suggestedTargetTimestampIso || ''],
2209
+ ['tradingCutoffAt', timing.tradingCutoffTimestampIso || ''],
2205
2210
  ['recommendedLiquidityUsdc', data.liquidityRecommendation ? data.liquidityRecommendation.liquidityUsdc : ''],
2206
2211
  ['distributionYes', data.distributionHint ? data.distributionHint.distributionYes : ''],
2207
2212
  ['distributionNo', data.distributionHint ? data.distributionHint.distributionNo : ''],
@@ -2369,6 +2374,8 @@ function renderMirrorDeployTable(data) {
2369
2374
  ['pollTxHash', data.tx && data.tx.pollTxHash ? data.tx.pollTxHash : ''],
2370
2375
  ['approveTxHash', data.tx && data.tx.approveTxHash ? data.tx.approveTxHash : ''],
2371
2376
  ['marketTxHash', data.tx && data.tx.marketTxHash ? data.tx.marketTxHash : ''],
2377
+ ['targetTimestamp', data.timing && data.timing.selectedTargetTimestampIso ? data.timing.selectedTargetTimestampIso : ''],
2378
+ ['tradingCutoffAt', data.timing && data.timing.tradingCutoffTimestampIso ? data.timing.tradingCutoffTimestampIso : ''],
2372
2379
  ['seedOddsMatch', data.postDeployChecks && data.postDeployChecks.seedOddsMatch !== null ? (data.postDeployChecks.seedOddsMatch ? 'yes' : 'no') : ''],
2373
2380
  ['seedDiffPct', data.postDeployChecks && data.postDeployChecks.diffPct !== null ? data.postDeployChecks.diffPct : ''],
2374
2381
  ['blockedLiveSync', data.postDeployChecks && data.postDeployChecks.blockedLiveSync ? 'yes' : 'no'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pandora-cli-skills",
3
- "version": "1.1.65",
3
+ "version": "1.1.66",
4
4
  "description": "Pandora CLI & Skills",
5
5
  "main": "cli/pandora.cjs",
6
6
  "bin": {