@ixo/openhands-sdk 0.1.0 → 0.3.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.
Files changed (106) hide show
  1. package/README.md +3 -4
  2. package/dist/cjs/chain/entityShape.d.ts +208 -0
  3. package/dist/cjs/chain/entityShape.d.ts.map +1 -0
  4. package/dist/cjs/chain/entityShape.js +201 -0
  5. package/dist/cjs/chain/entityShape.js.map +1 -0
  6. package/dist/cjs/chain/env.d.ts +73 -6
  7. package/dist/cjs/chain/env.d.ts.map +1 -1
  8. package/dist/cjs/chain/env.js +156 -14
  9. package/dist/cjs/chain/env.js.map +1 -1
  10. package/dist/cjs/config.d.ts +29 -3
  11. package/dist/cjs/config.d.ts.map +1 -1
  12. package/dist/cjs/config.js +7 -2
  13. package/dist/cjs/config.js.map +1 -1
  14. package/dist/cjs/data/agent.d.ts.map +1 -1
  15. package/dist/cjs/data/agent.js +5 -4
  16. package/dist/cjs/data/agent.js.map +1 -1
  17. package/dist/cjs/data/install.d.ts +36 -7
  18. package/dist/cjs/data/install.d.ts.map +1 -1
  19. package/dist/cjs/data/install.js +54 -13
  20. package/dist/cjs/data/install.js.map +1 -1
  21. package/dist/cjs/data/queue.d.ts.map +1 -1
  22. package/dist/cjs/data/queue.js +3 -2
  23. package/dist/cjs/data/queue.js.map +1 -1
  24. package/dist/cjs/data/receipt.d.ts.map +1 -1
  25. package/dist/cjs/data/receipt.js +6 -2
  26. package/dist/cjs/data/receipt.js.map +1 -1
  27. package/dist/cjs/data/threads.d.ts.map +1 -1
  28. package/dist/cjs/data/threads.js +8 -3
  29. package/dist/cjs/data/threads.js.map +1 -1
  30. package/dist/cjs/geo/geohash.d.ts +14 -0
  31. package/dist/cjs/geo/geohash.d.ts.map +1 -0
  32. package/dist/cjs/geo/geohash.js +64 -0
  33. package/dist/cjs/geo/geohash.js.map +1 -0
  34. package/dist/cjs/rubric/marketPlan.d.ts +144 -0
  35. package/dist/cjs/rubric/marketPlan.d.ts.map +1 -0
  36. package/dist/cjs/rubric/marketPlan.js +319 -0
  37. package/dist/cjs/rubric/marketPlan.js.map +1 -0
  38. package/dist/cjs/rubric/observation.d.ts +53 -0
  39. package/dist/cjs/rubric/observation.d.ts.map +1 -0
  40. package/dist/cjs/rubric/observation.js +38 -0
  41. package/dist/cjs/rubric/observation.js.map +1 -0
  42. package/dist/cjs/rubric/pingLink.d.ts +45 -0
  43. package/dist/cjs/rubric/pingLink.d.ts.map +1 -0
  44. package/dist/cjs/rubric/pingLink.js +74 -0
  45. package/dist/cjs/rubric/pingLink.js.map +1 -0
  46. package/dist/cjs/rubric/pingMatch.d.ts +73 -0
  47. package/dist/cjs/rubric/pingMatch.d.ts.map +1 -0
  48. package/dist/cjs/rubric/pingMatch.js +65 -0
  49. package/dist/cjs/rubric/pingMatch.js.map +1 -0
  50. package/dist/cjs/rubric/terms.d.ts +8 -0
  51. package/dist/cjs/rubric/terms.d.ts.map +1 -1
  52. package/dist/cjs/rubric/terms.js +2 -0
  53. package/dist/cjs/rubric/terms.js.map +1 -1
  54. package/dist/esm/chain/entityShape.d.ts +208 -0
  55. package/dist/esm/chain/entityShape.d.ts.map +1 -0
  56. package/dist/esm/chain/entityShape.js +184 -0
  57. package/dist/esm/chain/entityShape.js.map +1 -0
  58. package/dist/esm/chain/env.d.ts +73 -6
  59. package/dist/esm/chain/env.d.ts.map +1 -1
  60. package/dist/esm/chain/env.js +146 -12
  61. package/dist/esm/chain/env.js.map +1 -1
  62. package/dist/esm/config.d.ts +29 -3
  63. package/dist/esm/config.d.ts.map +1 -1
  64. package/dist/esm/config.js +7 -3
  65. package/dist/esm/config.js.map +1 -1
  66. package/dist/esm/data/agent.d.ts.map +1 -1
  67. package/dist/esm/data/agent.js +5 -4
  68. package/dist/esm/data/agent.js.map +1 -1
  69. package/dist/esm/data/install.d.ts +36 -7
  70. package/dist/esm/data/install.d.ts.map +1 -1
  71. package/dist/esm/data/install.js +52 -12
  72. package/dist/esm/data/install.js.map +1 -1
  73. package/dist/esm/data/queue.d.ts.map +1 -1
  74. package/dist/esm/data/queue.js +3 -2
  75. package/dist/esm/data/queue.js.map +1 -1
  76. package/dist/esm/data/receipt.d.ts.map +1 -1
  77. package/dist/esm/data/receipt.js +6 -2
  78. package/dist/esm/data/receipt.js.map +1 -1
  79. package/dist/esm/data/threads.d.ts.map +1 -1
  80. package/dist/esm/data/threads.js +8 -3
  81. package/dist/esm/data/threads.js.map +1 -1
  82. package/dist/esm/geo/geohash.d.ts +14 -0
  83. package/dist/esm/geo/geohash.d.ts.map +1 -0
  84. package/dist/esm/geo/geohash.js +60 -0
  85. package/dist/esm/geo/geohash.js.map +1 -0
  86. package/dist/esm/rubric/marketPlan.d.ts +144 -0
  87. package/dist/esm/rubric/marketPlan.d.ts.map +1 -0
  88. package/dist/esm/rubric/marketPlan.js +305 -0
  89. package/dist/esm/rubric/marketPlan.js.map +1 -0
  90. package/dist/esm/rubric/observation.d.ts +53 -0
  91. package/dist/esm/rubric/observation.d.ts.map +1 -0
  92. package/dist/esm/rubric/observation.js +33 -0
  93. package/dist/esm/rubric/observation.js.map +1 -0
  94. package/dist/esm/rubric/pingLink.d.ts +45 -0
  95. package/dist/esm/rubric/pingLink.d.ts.map +1 -0
  96. package/dist/esm/rubric/pingLink.js +69 -0
  97. package/dist/esm/rubric/pingLink.js.map +1 -0
  98. package/dist/esm/rubric/pingMatch.d.ts +73 -0
  99. package/dist/esm/rubric/pingMatch.d.ts.map +1 -0
  100. package/dist/esm/rubric/pingMatch.js +60 -0
  101. package/dist/esm/rubric/pingMatch.js.map +1 -0
  102. package/dist/esm/rubric/terms.d.ts +8 -0
  103. package/dist/esm/rubric/terms.d.ts.map +1 -1
  104. package/dist/esm/rubric/terms.js +2 -0
  105. package/dist/esm/rubric/terms.js.map +1 -1
  106. package/package.json +1 -1
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Ask the World: a prediction market turned into a plan of checks people can
3
+ * do in public places (docs/verified-work-ask-the-world-plan.md).
4
+ *
5
+ * A **plan** is a frozen document: the market it informs, the checks, where
6
+ * each happens and how many answers each place buys, what each check beats,
7
+ * and one line on what the plan cannot tell you. It is written once, stored
8
+ * at a content address, and named by every ask it spawns (`terms.plan`), so
9
+ * the report can be rebuilt from the chain alone. The asks are filed under
10
+ * one tag, `plan-<slug>`, the way a call's legs are filed under `call-<slug>`.
11
+ *
12
+ * The agent that calls the MCP is the one that reads the market and drafts
13
+ * the checks; this module never guesses them. What it does is refuse what
14
+ * must never be bought (the **market screen**), check that a draft is whole
15
+ * (`validatePlan`), and price it (`planCost`). All pure, so the MCP, the
16
+ * worker and the app agree on one answer.
17
+ */
18
+ /** The tag every ask of a plan is filed under. No colon: a tag is `[a-z0-9-]`. */
19
+ export declare const planTag: (slug: string) => string;
20
+ /** The slug back out of a tag, or null when the tag is not a plan's. */
21
+ export declare const slugOfPlanTag: (tag: string | null | undefined) => string | null;
22
+ /** A slug from free text: lowercase words joined by dashes, 3–40 characters, starting with a letter. */
23
+ export declare const planSlug: (text: string, suffix?: string) => string;
24
+ /** What a person hands over for a check: the same three kinds `ask_the_world` posts. */
25
+ export type PlanAct = "photo" | "observation";
26
+ export declare const PLAN_ACTS: readonly PlanAct[];
27
+ /** Where a check happens. One place becomes one ask. */
28
+ export interface PlanPlace {
29
+ /** What a person recognises: "Union Square, San Francisco". */
30
+ name: string;
31
+ lat: number;
32
+ lon: number;
33
+ /** How close the answer must be taken, in metres (20–20 000, as `ask_the_world`). */
34
+ withinM: number;
35
+ }
36
+ /** One line of the plan: an act, its places, how many answers per place, the pay, and what it beats. */
37
+ export interface PlanCheck {
38
+ /** Stable within the plan: c1, c2… */
39
+ id: string;
40
+ act: PlanAct;
41
+ /** What the person does, in plain words, 12–300 characters. */
42
+ text: string;
43
+ places: PlanPlace[];
44
+ /** Answers to buy at each place (the ask's quota), 1–10. */
45
+ perPlace: number;
46
+ /** What one approved answer earns, in dollars. */
47
+ payUsd: number;
48
+ /** How long each ask stays open, in hours. */
49
+ hours: number;
50
+ /** The existing feed this check improves on, in one line. Mandatory: no line, no check. */
51
+ beats: string;
52
+ }
53
+ /** The market a plan informs, as read when the plan was drafted. */
54
+ export interface PlanMarket {
55
+ venue: "polymarket" | "kalshi" | "other";
56
+ url: string;
57
+ question: string;
58
+ /** The resolution rules as the venue states them, trimmed. */
59
+ rules?: string;
60
+ /** When the market closes (ISO), if the venue says. */
61
+ closes?: string;
62
+ }
63
+ /** The frozen plan document. */
64
+ export interface MarketPlan {
65
+ kind: "openhands.market-plan";
66
+ v: 1;
67
+ slug: string;
68
+ market: PlanMarket;
69
+ checks: PlanCheck[];
70
+ /** What the plan cannot tell the trader. Mandatory. */
71
+ cannotTell: string;
72
+ /** ISO time the plan was drafted. */
73
+ createdAt: string;
74
+ }
75
+ export declare const MARKET_PLAN_KIND: "openhands.market-plan";
76
+ /** The limits a plan is held to. The per-ask cap and the per-plan cap are the MCP's; these are shape. */
77
+ export declare const PLAN_LIMITS: {
78
+ readonly checks: 12;
79
+ readonly placesPerCheck: 20;
80
+ readonly asksPerPlan: 120;
81
+ readonly perPlace: 10;
82
+ readonly textMin: 12;
83
+ readonly textMax: 300;
84
+ readonly withinMin: 20;
85
+ readonly withinMax: 20000;
86
+ readonly hoursMin: 1;
87
+ readonly hoursMax: 720;
88
+ /** The smallest pay worth a person's walk, in dollars. */
89
+ readonly payFloorUsd: 0.05;
90
+ readonly lineMin: 8;
91
+ readonly lineMax: 240;
92
+ };
93
+ /** How many asks a plan posts: one per place per check. */
94
+ export declare const planAskCount: (plan: Pick<MarketPlan, "checks">) => number;
95
+ /** How many answers a plan buys at most. */
96
+ export declare const planAnswerCount: (plan: Pick<MarketPlan, "checks">) => number;
97
+ /** The price of a plan, line by line. */
98
+ export interface PlanCost {
99
+ /** Per check: answers bought, pay, the check's total including the review of each answer. */
100
+ lines: {
101
+ id: string;
102
+ answers: number;
103
+ payUsd: number;
104
+ reviewUsd: number;
105
+ totalUsd: number;
106
+ }[];
107
+ /** Σ (pay + review) over every answer the plan can buy. */
108
+ workUsd: number;
109
+ /** The platform fee on top. */
110
+ feeUsd: number;
111
+ /** What the funder sends. */
112
+ totalUsd: number;
113
+ }
114
+ /**
115
+ * Price a plan: every answer it can buy costs its pay plus the review of that
116
+ * answer (the oracle's check reward), and the fee is a share of that work.
117
+ */
118
+ export declare const planCost: (plan: Pick<MarketPlan, "checks">, opts: {
119
+ reviewUsd: number | ((check: PlanCheck) => number);
120
+ feeRate: number;
121
+ }) => PlanCost;
122
+ /** One refusal: the rule that refused it, where, and a sentence a person can act on. */
123
+ export interface PlanRefusal {
124
+ rule: PlanRule;
125
+ /** The check it concerns, or null for the plan as a whole. */
126
+ check: string | null;
127
+ why: string;
128
+ }
129
+ export type PlanRule = "shape" | "in-play-sport" | "conflict-zone" | "polling-place" | "resolution-source" | "acts-on-outcome" | "non-public" | "faces" | "no-beats" | "no-cannot-tell";
130
+ /** Refusals for the market as a whole: a sports market or a conflict market is never scoped. */
131
+ export declare const screenMarket: (market: PlanMarket) => PlanRefusal[];
132
+ /** Refusals for one check, read against the market it informs. */
133
+ export declare const screenCheck: (check: PlanCheck) => PlanRefusal[];
134
+ /**
135
+ * Everything wrong with a plan, in the order a person would fix it: the
136
+ * market first, then the plan's own shape, then each check. An empty list is
137
+ * a plan that may be stored and funded.
138
+ */
139
+ export declare const validatePlan: (plan: MarketPlan) => PlanRefusal[];
140
+ /** A plan read back from the store, or null when the document is not a version 1 plan. */
141
+ export declare const readMarketPlan: (doc: unknown) => MarketPlan | null;
142
+ /** The words the asks of one check carry: the check, then its place, then the honest limit of a single look. */
143
+ export declare const askQuestionFor: (check: PlanCheck, place: PlanPlace) => string;
144
+ //# sourceMappingURL=marketPlan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marketPlan.d.ts","sourceRoot":"","sources":["../../../src/rubric/marketPlan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Ask the World: a prediction market turned into a plan of checks people can
3
+ * do in public places (docs/verified-work-ask-the-world-plan.md).
4
+ *
5
+ * A **plan** is a frozen document: the market it informs, the checks, where
6
+ * each happens and how many answers each place buys, what each check beats,
7
+ * and one line on what the plan cannot tell you. It is written once, stored
8
+ * at a content address, and named by every ask it spawns (`terms.plan`), so
9
+ * the report can be rebuilt from the chain alone. The asks are filed under
10
+ * one tag, `plan-<slug>`, the way a call's legs are filed under `call-<slug>`.
11
+ *
12
+ * The agent that calls the MCP is the one that reads the market and drafts
13
+ * the checks; this module never guesses them. What it does is refuse what
14
+ * must never be bought (the **market screen**), check that a draft is whole
15
+ * (`validatePlan`), and price it (`planCost`). All pure, so the MCP, the
16
+ * worker and the app agree on one answer.
17
+ */
18
+ /** The tag every ask of a plan is filed under. No colon: a tag is `[a-z0-9-]`. */
19
+ export const planTag = (slug) => `plan-${slug}`;
20
+ /** The slug back out of a tag, or null when the tag is not a plan's. */
21
+ export const slugOfPlanTag = (tag) => {
22
+ const match = /^plan-([a-z][a-z0-9-]{2,39})$/.exec(tag ?? "");
23
+ return match ? match[1] : null;
24
+ };
25
+ /** A slug from free text: lowercase words joined by dashes, 3–40 characters, starting with a letter. */
26
+ export const planSlug = (text, suffix = "") => {
27
+ const words = text
28
+ .toLowerCase()
29
+ .normalize("NFKD")
30
+ .replace(/[̀-ͯ]/g, "")
31
+ .replace(/[^a-z0-9]+/g, "-")
32
+ .replace(/^-+|-+$/g, "");
33
+ const start = words.replace(/^[^a-z]+/, "") || "plan";
34
+ const tail = suffix ? `-${suffix}` : "";
35
+ const room = 40 - tail.length;
36
+ const body = start.slice(0, room).replace(/-+$/, "");
37
+ return `${body.length >= 3 ? body : `plan-${body}`.slice(0, room)}${tail}`;
38
+ };
39
+ export const PLAN_ACTS = ["photo", "observation"];
40
+ export const MARKET_PLAN_KIND = "openhands.market-plan";
41
+ /** The limits a plan is held to. The per-ask cap and the per-plan cap are the MCP's; these are shape. */
42
+ export const PLAN_LIMITS = {
43
+ checks: 12,
44
+ placesPerCheck: 20,
45
+ asksPerPlan: 120,
46
+ perPlace: 10,
47
+ textMin: 12,
48
+ textMax: 300,
49
+ withinMin: 20,
50
+ withinMax: 20_000,
51
+ hoursMin: 1,
52
+ hoursMax: 720,
53
+ /** The smallest pay worth a person's walk, in dollars. */
54
+ payFloorUsd: 0.05,
55
+ lineMin: 8,
56
+ lineMax: 240,
57
+ };
58
+ /** How many asks a plan posts: one per place per check. */
59
+ export const planAskCount = (plan) => plan.checks.reduce((n, check) => n + check.places.length, 0);
60
+ /** How many answers a plan buys at most. */
61
+ export const planAnswerCount = (plan) => plan.checks.reduce((n, check) => n + check.places.length * check.perPlace, 0);
62
+ /** Cents, so totals add the way a person adds them. */
63
+ const cents = (usd) => Math.round(usd * 100) / 100;
64
+ /**
65
+ * Price a plan: every answer it can buy costs its pay plus the review of that
66
+ * answer (the oracle's check reward), and the fee is a share of that work.
67
+ */
68
+ export const planCost = (plan, opts) => {
69
+ const lines = plan.checks.map((check) => {
70
+ const answers = check.places.length * check.perPlace;
71
+ /* The review is priced per check: a photo is screened, a sentence is not, so the two cost differently. */
72
+ const reviewUsd = typeof opts.reviewUsd === "function" ? opts.reviewUsd(check) : opts.reviewUsd;
73
+ return { id: check.id, answers, payUsd: check.payUsd, reviewUsd, totalUsd: cents(answers * (check.payUsd + reviewUsd)) };
74
+ });
75
+ const workUsd = cents(lines.reduce((sum, line) => sum + line.totalUsd, 0));
76
+ const feeUsd = cents(workUsd * Math.max(0, opts.feeRate));
77
+ return { lines, workUsd, feeUsd, totalUsd: cents(workUsd + feeUsd) };
78
+ };
79
+ /*
80
+ * The market screen. Each list is a set of words that, found in a check or a
81
+ * market, mean the thing must not be bought. They are deliberately broad: a
82
+ * plan refused for a word that was innocent costs the agent one redraft; a
83
+ * plan that slips through can cost a person their safety or the network its
84
+ * standing. The refusal names the rule so the redraft is easy.
85
+ */
86
+ /** Sport and esports markets settled by play. A person at the venue is courtsiding, whatever they look at. */
87
+ const SPORT = [
88
+ /\bvs\.?\b/i,
89
+ /\bwin on \d{4}-\d{2}-\d{2}\b/i,
90
+ /\bo\/u\b/i,
91
+ /\bover\/under\b/i,
92
+ /\bspread:/i,
93
+ /\bhandicap\b/i,
94
+ /\bmap handicap\b/i,
95
+ /\bgame \d\b/i,
96
+ /\b(bo3|bo5)\b/i,
97
+ /\b(league|cup|open|championship|grand prix|playoffs?|super bowl|world series|stanley cup|nba finals)\b.*\b(win|winner|beat|score|goals?|points|sets?|maps?)\b/i,
98
+ /\b(counter-strike|dota|league of legends|lol:|valorant|esports?)\b/i,
99
+ ];
100
+ /** Places where nobody should be sent to look, whatever the money. */
101
+ const CONFLICT = [
102
+ /\biran\b/i,
103
+ /\bisrael\b/i,
104
+ /\bgaza\b/i,
105
+ /\bwest bank\b/i,
106
+ /\blebanon\b/i,
107
+ /\byemen\b/i,
108
+ /\bsyria\b/i,
109
+ /\bsudan\b/i,
110
+ /\bukrain/i,
111
+ /\brussia\b/i,
112
+ /\bhormuz\b/i,
113
+ /\bkharg\b/i,
114
+ /\bred sea\b/i,
115
+ /\btaiwan strait\b/i,
116
+ /\bceasefire\b/i,
117
+ /\bblockade\b/i,
118
+ /\binvade|invasion\b/i,
119
+ ];
120
+ /** Polling places: the law differs by state and by distance, and the network does not litigate it per ask. */
121
+ const POLLING = [/\bpolling (place|station|site|location)s?\b/i, /\bvoting (booth|centre|center|location)s?\b/i, /\bballot\b/i, /\bvoters?\b/i];
122
+ /** The thing that settles a market: never visit, touch or photograph it. */
123
+ const SOURCE = [
124
+ /\bsensor\b/i,
125
+ /\bweather station\b/i,
126
+ /\bthermometer\b/i,
127
+ /\b(rain|river|stream|tide|snow) gauge\b/i,
128
+ /\bgauge\b/i,
129
+ /\bofficial (count|counter|tally|scoreboard|reading)\b/i,
130
+ /\bcounting (room|centre|center)\b/i,
131
+ /\btally\b/i,
132
+ /\bscoreboard\b/i,
133
+ ];
134
+ /** Acts that change the world the market is about. A check looks; it never moves the outcome. */
135
+ const ACTS = [
136
+ /\bvote\b/i,
137
+ /\b(bet|wager)\b/i,
138
+ /\bprotest\b/i,
139
+ /\bblock(ade)?\b/i,
140
+ /\bsabotage\b/i,
141
+ /\bdamage\b/i,
142
+ /\bdeface\b/i,
143
+ /\bdestroy\b/i,
144
+ /\bremove the\b/i,
145
+ /\bbribe\b/i,
146
+ /\bthreaten\b/i,
147
+ /\bpersuade\b/i,
148
+ /\bconvince\b/i,
149
+ /\blobby\b/i,
150
+ /\binfluence\b/i,
151
+ /\bcampaign for\b/i,
152
+ ];
153
+ /** Places a person has no right to be, and information only an insider has. */
154
+ const PRIVATE = [
155
+ /\bbehind the counter\b/i,
156
+ /\bbackstage\b/i,
157
+ /\bstaff[- ]only\b/i,
158
+ /\brestricted (area|zone)\b/i,
159
+ /\bprivate (property|land|area|office)\b/i,
160
+ /\binside the (office|factory|warehouse|plant)\b/i,
161
+ /\bask (an? )?(employee|staff member|manager|worker) (for|about|what)\b/i,
162
+ /\binternal (numbers|figures|data|memo)\b/i,
163
+ /\bconfidential\b/i,
164
+ /\btrespass/i,
165
+ ];
166
+ /** Faces and anything that identifies a person. */
167
+ const FACES = [/(?<!\bno )\bfaces?\b/i, /\bselfie\b/i, /\bportrait\b/i, /\bidentify (the|a|who)\b/i, /\b(licen[cs]e|number) plates?\b/i, /\bwho (is|was) (inside|driving|in the)\b/i];
168
+ /** Photos of these need to say they take no faces. */
169
+ const CROWD = [/\bqueue\b/i, /\bline (of|outside|at)\b/i, /\bcrowd\b/i, /\bpeople\b/i, /\bshoppers\b/i, /\bcustomers\b/i, /\briders?\b/i, /\bpassengers?\b/i];
170
+ const NO_FACES = /\bno faces\b/i;
171
+ const hit = (patterns, text) => patterns.some((pattern) => pattern.test(text));
172
+ /** The market as one string the screen reads. */
173
+ const marketText = (market) => [market.question, market.rules ?? ""].join("\n");
174
+ /** The place names of a check, as one string. */
175
+ const placeText = (check) => check.places.map((place) => place.name).join("\n");
176
+ /** Refusals for the market as a whole: a sports market or a conflict market is never scoped. */
177
+ export const screenMarket = (market) => {
178
+ const refusals = [];
179
+ if (hit(SPORT, market.question)) {
180
+ refusals.push({ rule: "in-play-sport", check: null, why: "This market is settled by play. Sending people to a venue to report it is courtsiding, so the network does not scope sports markets." });
181
+ }
182
+ if (hit(CONFLICT, market.question)) {
183
+ refusals.push({ rule: "conflict-zone", check: null, why: "This market is about a conflict. The network sends nobody to look where looking can get them hurt." });
184
+ }
185
+ return refusals;
186
+ };
187
+ /** Refusals for one check, read against the market it informs. */
188
+ export const screenCheck = (check) => {
189
+ const refusals = [];
190
+ const text = check.text;
191
+ const where = placeText(check);
192
+ const id = check.id;
193
+ if (hit(CONFLICT, where)) {
194
+ refusals.push({ rule: "conflict-zone", check: id, why: "A place in this check is in a conflict area. Choose a place where looking is safe." });
195
+ }
196
+ if (hit(POLLING, `${text}\n${where}`)) {
197
+ refusals.push({ rule: "polling-place", check: id, why: "Polling places are refused: what may be recorded near one differs by state and distance." });
198
+ }
199
+ if (hit(SOURCE, text)) {
200
+ refusals.push({ rule: "resolution-source", check: id, why: "This check goes to an instrument or an official count. Never visit or photograph what settles a market." });
201
+ }
202
+ if (hit(ACTS, text)) {
203
+ refusals.push({ rule: "acts-on-outcome", check: id, why: "This check asks the person to do something that could change the outcome. A check only looks." });
204
+ }
205
+ if (hit(PRIVATE, text)) {
206
+ refusals.push({ rule: "non-public", check: id, why: "This check needs somewhere private or something only an insider knows. Ask only what anyone could see from a public place." });
207
+ }
208
+ if (hit(FACES, text)) {
209
+ refusals.push({ rule: "faces", check: id, why: "This check would record or identify a person. Ask for the thing, not the people." });
210
+ }
211
+ else if (check.act === "photo" && hit(CROWD, text) && !NO_FACES.test(text)) {
212
+ refusals.push({ rule: "faces", check: id, why: "A photo with people in it must say \"no faces\" in the check, so the person taking it frames it that way." });
213
+ }
214
+ if (check.beats.trim().length < PLAN_LIMITS.lineMin) {
215
+ refusals.push({ rule: "no-beats", check: id, why: "Say what existing feed this check improves on. A check that beats nothing is not worth buying." });
216
+ }
217
+ return refusals;
218
+ };
219
+ /** Refusals for the shape of one check: the numbers and lengths a plan must keep. */
220
+ const shapeOfCheck = (check) => {
221
+ const L = PLAN_LIMITS;
222
+ const out = [];
223
+ const bad = (why) => out.push({ rule: "shape", check: check.id, why });
224
+ if (!PLAN_ACTS.includes(check.act))
225
+ bad(`The act must be one of ${PLAN_ACTS.join(", ")}.`);
226
+ const text = check.text.trim();
227
+ if (text.length < L.textMin || text.length > L.textMax)
228
+ bad(`The check's words must be ${L.textMin}–${L.textMax} characters.`);
229
+ if (!check.places.length)
230
+ bad("A check needs at least one place.");
231
+ if (check.places.length > L.placesPerCheck)
232
+ bad(`A check holds at most ${L.placesPerCheck} places.`);
233
+ for (const place of check.places) {
234
+ if (!place.name?.trim())
235
+ bad("Every place needs a name a person recognises.");
236
+ if (!(Math.abs(place.lat) <= 90) || !(Math.abs(place.lon) <= 180) || (place.lat === 0 && place.lon === 0))
237
+ bad(`"${place.name}" has no usable coordinates.`);
238
+ if (!(place.withinM >= L.withinMin && place.withinM <= L.withinMax))
239
+ bad(`"${place.name}": the distance must be ${L.withinMin}–${L.withinMax} m.`);
240
+ }
241
+ if (!Number.isInteger(check.perPlace) || check.perPlace < 1 || check.perPlace > L.perPlace)
242
+ bad(`Answers per place must be a whole number from 1 to ${L.perPlace}.`);
243
+ if (!(check.payUsd >= L.payFloorUsd))
244
+ bad(`The pay must be at least $${L.payFloorUsd.toFixed(2)} an answer.`);
245
+ if (!(check.hours >= L.hoursMin && check.hours <= L.hoursMax))
246
+ bad(`A check stays open ${L.hoursMin}–${L.hoursMax} hours.`);
247
+ if (check.beats.trim().length > L.lineMax)
248
+ bad(`What the check beats fits in ${L.lineMax} characters.`);
249
+ return out;
250
+ };
251
+ /**
252
+ * Everything wrong with a plan, in the order a person would fix it: the
253
+ * market first, then the plan's own shape, then each check. An empty list is
254
+ * a plan that may be stored and funded.
255
+ */
256
+ export const validatePlan = (plan) => {
257
+ const L = PLAN_LIMITS;
258
+ const refusals = [...screenMarket(plan.market)];
259
+ const shape = (why) => refusals.push({ rule: "shape", check: null, why });
260
+ if (plan.kind !== MARKET_PLAN_KIND || plan.v !== 1)
261
+ shape("This is not a version 1 plan.");
262
+ if (!slugOfPlanTag(planTag(plan.slug)))
263
+ shape("The slug must be 3–40 characters of a–z, 0–9 and dashes, starting with a letter.");
264
+ if (!/^https?:\/\//.test(plan.market.url))
265
+ shape("The market needs its link.");
266
+ if (plan.market.question.trim().length < 8)
267
+ shape("The market needs its question.");
268
+ if (!plan.checks.length)
269
+ shape("A plan needs at least one check.");
270
+ if (plan.checks.length > L.checks)
271
+ shape(`A plan holds at most ${L.checks} checks.`);
272
+ if (planAskCount(plan) > L.asksPerPlan)
273
+ shape(`A plan posts at most ${L.asksPerPlan} asks (one per place per check).`);
274
+ const ids = new Set();
275
+ for (const check of plan.checks) {
276
+ if (ids.has(check.id))
277
+ shape(`Two checks share the id ${check.id}.`);
278
+ ids.add(check.id);
279
+ }
280
+ if (plan.cannotTell.trim().length < L.lineMin) {
281
+ refusals.push({ rule: "no-cannot-tell", check: null, why: "Say what this plan cannot tell the trader. A plan without its limit reads as a promise." });
282
+ }
283
+ for (const check of plan.checks)
284
+ refusals.push(...shapeOfCheck(check), ...screenCheck(check));
285
+ return refusals;
286
+ };
287
+ /** A plan read back from the store, or null when the document is not a version 1 plan. */
288
+ export const readMarketPlan = (doc) => {
289
+ if (!doc || typeof doc !== "object")
290
+ return null;
291
+ const plan = doc;
292
+ if (plan.kind !== MARKET_PLAN_KIND || plan.v !== 1)
293
+ return null;
294
+ if (typeof plan.slug !== "string" || !plan.market || !Array.isArray(plan.checks) || typeof plan.cannotTell !== "string")
295
+ return null;
296
+ return plan;
297
+ };
298
+ /** The words the asks of one check carry: the check, then its place, then the honest limit of a single look. */
299
+ export const askQuestionFor = (check, place) => {
300
+ const text = check.text.trim().replace(/\s+/g, " ");
301
+ const at = ` At ${place.name}.`;
302
+ const room = PLAN_LIMITS.textMax - at.length;
303
+ return (text.length > room ? `${text.slice(0, room - 1).trimEnd()}…` : text) + at;
304
+ };
305
+ //# sourceMappingURL=marketPlan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marketPlan.js","sourceRoot":"","sources":["../../../src/rubric/marketPlan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { type AskTerms } from "./terms.ts";
2
+ /**
3
+ * The terms of an observation bought from a stranger: what `ask_the_world`
4
+ * posts, and what every check of an Ask the World plan posts, so that the two
5
+ * can never drift apart (docs/verified-work-ask-the-world-plan.md, D2).
6
+ *
7
+ * A located ask carries the place gate, then the falsifiers (daylight always;
8
+ * weather on a photo only), then the relevance judge.
9
+ *
10
+ * **Weather only where the reviewer can weigh it.** On testnet, 2026-10-15, a
11
+ * typed observation ("It's dry and the streetlights are working", ask #44040)
12
+ * came back with location, daylight and the judge passed and the weather gate
13
+ * `unevaluated`, and the engine declines any decision with a gate it could
14
+ * not evaluate (reason code `weather`). The falsifier was meant to fail only
15
+ * an answer the world contradicts; on a typed answer it failed every one. A
16
+ * photo keeps it. The judge says its refusal
17
+ * first and the question last: the oracle fails any rule it cannot find proof
18
+ * for, so a sentence that never names its own failure reads as "prove it", and
19
+ * the question is a caller's 300 characters that the parser may cut.
20
+ */
21
+ export type ObservationMode = "observation" | "photo" | "url-check";
22
+ export interface ObservationInput {
23
+ /** The real-world question, 12–300 characters. */
24
+ question: string;
25
+ mode: ObservationMode;
26
+ /** Where the answer must be taken. Null only for a url-check. */
27
+ centre: {
28
+ lat: number;
29
+ lon: number;
30
+ } | null;
31
+ /** How close, in metres. */
32
+ metres: number;
33
+ /** A label for the place, frozen beside the coordinates. Never the contract: the gate measures from lat/lon. */
34
+ place?: string | null;
35
+ /** Who rules, named in the terms so the ask is a contract about who decides. */
36
+ oracle: {
37
+ kind: "oracle";
38
+ address: string;
39
+ rewardUixo: number;
40
+ };
41
+ /** The tag the ask is filed under. */
42
+ tag?: string;
43
+ /** The content address of the plan this ask is one check of. */
44
+ plan?: string;
45
+ }
46
+ /** The relevance line every observation is judged on. */
47
+ export declare const relevanceLine: (question: string) => string;
48
+ /**
49
+ * Terms in the parser's own key order, so they read back byte for byte:
50
+ * v, task, deliverable, campaign, plan, criteria, judge, via.
51
+ */
52
+ export declare function observationTerms(input: ObservationInput): AskTerms;
53
+ //# sourceMappingURL=observation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../../../src/rubric/observation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,KAAK,QAAQ,EAAkB,MAAM,YAAY,CAAC;AAE5F;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,OAAO,GAAG,WAAW,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,eAAe,CAAC;IACtB,iEAAiE;IACjE,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5C,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,gHAAgH;IAChH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gFAAgF;IAChF,MAAM,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,yDAAyD;AACzD,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,KAAG,MACqG,CAAC;AAEvJ;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,QAAQ,CAuBlE"}
@@ -0,0 +1,33 @@
1
+ import { DEFAULT_CONFIDENCE, deriveTerms } from "./terms.js";
2
+ /** The relevance line every observation is judged on. */
3
+ export const relevanceLine = (question) => `The answer addresses the question this ask asked. Fail this only if it answers something else, or is empty or evasive. The question: "${question}"`;
4
+ /**
5
+ * Terms in the parser's own key order, so they read back byte for byte:
6
+ * v, task, deliverable, campaign, plan, criteria, judge, via.
7
+ */
8
+ export function observationTerms(input) {
9
+ const base = deriveTerms(input.question, input.oracle);
10
+ const criteria = [...base.criteria];
11
+ if (input.centre) {
12
+ criteria.push({
13
+ id: `c${criteria.length + 1}`,
14
+ kind: "measure",
15
+ gate: { id: "within-radius", lat: input.centre.lat, lon: input.centre.lon, meters: input.metres, ...(input.place ? { place: input.place } : {}) }
16
+ });
17
+ if (input.mode === "photo")
18
+ criteria.push({ id: `c${criteria.length + 1}`, kind: "measure", gate: { id: "weather-consistent" } });
19
+ criteria.push({ id: `c${criteria.length + 1}`, kind: "measure", gate: { id: "daylight-consistent" } });
20
+ }
21
+ criteria.push({ id: `c${criteria.length + 1}`, kind: "judge", text: relevanceLine(input.question), confidence: DEFAULT_CONFIDENCE });
22
+ return {
23
+ v: base.v,
24
+ task: input.question,
25
+ deliverable: { type: input.mode === "photo" ? "photo" : "text", prompt: input.question },
26
+ ...(input.tag ? { campaign: input.tag } : {}),
27
+ ...(input.plan ? { plan: input.plan } : {}),
28
+ criteria,
29
+ judge: base.judge,
30
+ via: "mcp"
31
+ };
32
+ }
33
+ //# sourceMappingURL=observation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation.js","sourceRoot":"","sources":["../../../src/rubric/observation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAiC,MAAM,YAAY,CAAC;AA0C5F,yDAAyD;AACzD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAgB,EAAU,EAAE,CACxD,yIAAyI,QAAQ,GAAG,CAAC;AAEvJ;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAuB;IACtD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAgB,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YAC7B,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,EAAE,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;SAClJ,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,oBAAoB,EAAE,EAAE,CAAC,CAAC;QAClI,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,qBAAqB,EAAE,EAAE,CAAC,CAAC;IACzG,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC,CAAC;IACrI,OAAO;QACL,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,IAAI,EAAE,KAAK,CAAC,QAAQ;QACpB,WAAW,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,QAAQ,EAAE;QACxF,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,QAAQ;QACR,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,GAAG,EAAE,KAAK;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The signed personal link a ping carries (Ask the World, B11).
3
+ *
4
+ * A ping says "this ask, for this person, until this time". The link's token
5
+ * binds those three and the ping's own id under a secret only the network
6
+ * holds, so opening the link proves the ping was sent to that person: the
7
+ * submit page can then answer *as* them, reserve their slot, and file the
8
+ * claim in their name. The token proves who was pinged, nothing more; that
9
+ * a link is opened once is the registry's to enforce, because only it knows.
10
+ *
11
+ * Web Crypto HMAC-SHA-256, so the worker that mints and the app that checks
12
+ * run the same code.
13
+ */
14
+ /** How long a ping's link stays good: long enough to walk there, short enough that a forwarded link dies. */
15
+ export declare const PING_LINK_MINUTES = 30;
16
+ /** What a valid token says. */
17
+ export interface PingClaim {
18
+ /** The person's address. */
19
+ person: string;
20
+ /** The ask (collection id). */
21
+ ask: string;
22
+ /** The ping's id, so a second open can be refused. */
23
+ ping: string;
24
+ /** Expiry, epoch seconds. */
25
+ exp: number;
26
+ }
27
+ /** A token for one ping. The secret must be the one the app checks with. */
28
+ export declare function mintPingToken(claim: PingClaim, secret: string): Promise<string>;
29
+ export type PingTokenCheck = {
30
+ ok: true;
31
+ claim: PingClaim;
32
+ } | {
33
+ ok: false;
34
+ reason: "malformed" | "forged" | "expired" | "wrong-ask";
35
+ };
36
+ /**
37
+ * Read a token back. Forged, expired, malformed or meant for another ask: refused, and the
38
+ * reason says which, so the page can tell the person something true.
39
+ */
40
+ export declare function checkPingToken(token: string, secret: string, opts?: {
41
+ ask?: string;
42
+ now?: number;
43
+ graceSeconds?: number;
44
+ }): Promise<PingTokenCheck>;
45
+ //# sourceMappingURL=pingLink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pingLink.d.ts","sourceRoot":"","sources":["../../../src/rubric/pingLink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,6GAA6G;AAC7G,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,+BAA+B;AAC/B,MAAM,WAAW,SAAS;IACxB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;CACb;AAoBD,4EAA4E;AAC5E,wBAAsB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMrF;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAA;CAAE,CAAC;AAEtI;;;GAGG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAmB7J"}