@rackbops/plugin-tracker 0.31.2 → 0.31.3

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 (2) hide show
  1. package/dist/plugin.js +63 -6
  2. package/package.json +3 -3
package/dist/plugin.js CHANGED
@@ -3094,6 +3094,57 @@ function sourcesExpiry(now, deadline) {
3094
3094
  };
3095
3095
  }
3096
3096
 
3097
+ // node_modules/@rackbops/docket-types/dist/research-web.js
3098
+ var FIRECRAWL_TOOLS = [
3099
+ "mcp__firecrawl__firecrawl_search",
3100
+ "mcp__firecrawl__firecrawl_scrape"
3101
+ ];
3102
+ var WEB_ALLOWED_TOOLS = [...DEFAULT_ALLOWED_TOOLS, ...FIRECRAWL_TOOLS];
3103
+ function firecrawlLines() {
3104
+ return [
3105
+ "Read pages with WebFetch first. When a page refuses WebFetch (a 403, a block, a CAPTCHA " + "or challenge page), read it with `firecrawl_scrape` instead. Always pass " + '`proxy: "basic"` to it; never "stealth", "enhanced" or "auto", and nothing else that ' + "disguises the request. Scrape one page at a time: never several pages of the same site " + "in parallel. Never pass any eBay URL to any tool, Firecrawl included. You may use " + "`firecrawl_search` as you would web search. If Firecrawl is refused too, the page did " + "not open: handle it as a page that refused you, as above."
3106
+ ];
3107
+ }
3108
+ function firecrawlChecks() {
3109
+ return [
3110
+ "- To open a source, use WebFetch first; when a page refuses it (a 403, a block, a CAPTCHA), " + 'read it with `firecrawl_scrape`, always with `proxy: "basic"` (never "stealth", ' + '"enhanced" or "auto", nothing that disguises the request), one page at a time, never ' + "several pages of one site in parallel. Never pass any eBay URL to any tool. You may use " + "`firecrawl_search` as you would web search."
3111
+ ];
3112
+ }
3113
+ var BGG_CATEGORIES = [
3114
+ { name: "Strategy", familyId: 5497 },
3115
+ { name: "Family", familyId: 5499 },
3116
+ { name: "Thematic", familyId: 5496 },
3117
+ { name: "Party", familyId: 5498 },
3118
+ { name: "Abstract", familyId: 4666 },
3119
+ { name: "Customizable", familyId: 4667 },
3120
+ { name: "Children's", familyId: 4665 },
3121
+ { name: "Wargames", familyId: 4664 }
3122
+ ];
3123
+ function bggCategoryUrl(familyId, year) {
3124
+ return "https://boardgamegeek.com/search/boardgame?sort=bggrating&advsearch=1" + `&range[yearpublished][min]=${year}&range[yearpublished][max]=${year}` + `&range[numvoters][min]=30&nosubtypes[0]=boardgameexpansion&familyids[0]=${familyId}`;
3125
+ }
3126
+ function categoryNames() {
3127
+ const names = BGG_CATEGORIES.map((c) => c.name);
3128
+ return `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
3129
+ }
3130
+ function categoryLines(second = false) {
3131
+ return [
3132
+ 'When the question asks about categories (or "each of the major" kinds of something) and ' + "the best source defines its own categories, use that site's categories and say so" + (second ? "" : " in the summary") + "; do not invent genres, and do not say that no source defines them. For " + `board games these are BoardGameGeek's ${BGG_CATEGORIES.length} category rankings: ` + `${categoryNames()}. A category with no entry that qualifies is answered "none yet", ` + "with the reason; never leave it out." + (second ? " In this update, the summary names any category whose pick changed." : ` ${COMPACT_RUN}`)
3133
+ ];
3134
+ }
3135
+ var COMPACT_RUN = `The summary itself names every category's pick in one compact run, such as "Strategy: X; ` + `Family: Y; ...; Children's: none yet (no 2026 game has 30 ratings)", so none is lost when ` + "a long answer is cut; the findings carry each pick's source.";
3136
+ function bggSearchHint() {
3137
+ const ids = BGG_CATEGORIES.map((c) => `${c.name} ${c.familyId}`).join(", ");
3138
+ return [
3139
+ "A hint you may use: a BoardGameGeek advanced search shows one category for one year, such " + `as ${bggCategoryUrl(5497, 2026)} (Strategy, 2026). ` + `Change the year and the \`familyids[0]\` value for the category: ${ids}. These ids were ` + "seen on the site, not documented by it: if one shows the wrong category, use that " + "category's own ranking page."
3140
+ ];
3141
+ }
3142
+ function categoryChecks() {
3143
+ return [
3144
+ "- Where the question asks about categories and the best source defines its own (for " + `board games, BoardGameGeek's ${BGG_CATEGORIES.length}: ${categoryNames()}), the answer ` + "uses them and says so. An answer that invents its own categories, or says no source " + "defines them, while the source does, is `revise`d onto the source's categories. A " + 'category with no qualifying entry is answered "none yet" with the reason, never left ' + "out: `revise` an answer that skips one. The summary names every category's pick in " + 'one compact run ("Strategy: X; Family: Y; ..."), since the findings may be cut from ' + "the DM: `revise` a summary that leaves a category to the findings alone."
3145
+ ];
3146
+ }
3147
+
3097
3148
  // node_modules/@rackbops/docket-types/dist/research.js
3098
3149
  function usesSentSources(config) {
3099
3150
  return typeof config.sources === "string" && config.sources.trim().toLowerCase() === "sent";
@@ -3187,9 +3238,13 @@ function earlierBlock(earlier) {
3187
3238
  function disallowed(sent) {
3188
3239
  return [...DEFAULT_DISALLOWED_TOOLS, ...sent ? SENT_DISALLOWED_TOOLS : WEB_DISALLOWED_TOOLS];
3189
3240
  }
3241
+ function allowed(webFirst) {
3242
+ return [...webFirst ? WEB_ALLOWED_TOOLS : DEFAULT_ALLOWED_TOOLS];
3243
+ }
3190
3244
  function researchJob(config, pages, earlier, options = {}) {
3191
3245
  const canWait = options.canWait === true && pages === undefined && earlier === undefined;
3192
3246
  const sent = pages !== undefined;
3247
+ const webFirst = !sent && earlier === undefined;
3193
3248
  const prompt = [
3194
3249
  "You are researching a one-off question for a person, who will read your answer as a " + "Discord DM.",
3195
3250
  "",
@@ -3203,8 +3258,10 @@ function researchJob(config, pages, earlier, options = {}) {
3203
3258
  ""
3204
3259
  ] : [],
3205
3260
  ...partsLines(earlier !== undefined),
3261
+ ...categoryLines(earlier !== undefined),
3206
3262
  "",
3207
3263
  ...bestSourceLines(sent, canWait),
3264
+ ...webFirst ? ["", ...firecrawlLines(), ...bggSearchHint()] : [],
3208
3265
  "",
3209
3266
  "Use web search and fetch pages to check facts. Prefer primary sources (manufacturers, " + "government, utilities, standards bodies). Every claim that a reader might act on needs a " + "source you actually opened. Say plainly where sources disagree or where you are unsure.",
3210
3267
  "",
@@ -3220,7 +3277,7 @@ function researchJob(config, pages, earlier, options = {}) {
3220
3277
  return {
3221
3278
  prompt,
3222
3279
  jsonSchema: ANSWER_SCHEMA,
3223
- allowedTools: [...DEFAULT_ALLOWED_TOOLS],
3280
+ allowedTools: allowed(webFirst),
3224
3281
  disallowedTools: disallowed(sent),
3225
3282
  maxTurns: RESEARCH_MAX_TURNS,
3226
3283
  maxBudgetUsd: RESEARCH_MAX_BUDGET_USD,
@@ -3238,7 +3295,7 @@ function partsLines(second) {
3238
3295
  function bestSourceLines(pages, canWait) {
3239
3296
  return [
3240
3297
  "For each part, decide first which source is the best one for it: the site people in that " + "field treat as the reference (for board games, BoardGameGeek; for listings and sold " + "prices, eBay; otherwise the maker, the standards body or the official record). The " + "answer should rest on it.",
3241
- (pages ? "BoardGameGeek and eBay are not opened from this run: do not try. When the best source " + "is one of them and no gathered page is from it, or a page refused you (a 403, a " + "CAPTCHA or challenge page, an empty or blocked body, a sign-in wall)" : "Open BoardGameGeek like any other site: an ordinary read, with nothing to get round a " + "refusal. eBay is not opened from this run: do not try it. When the best source is " + "eBay, or its page refused you (BoardGameGeek or any other: a 403, a CAPTCHA or " + "challenge page, an empty or blocked body, a sign-in wall)") + (canWait ? ", list it in `blocked` with its site, its URL only when that is the page's real " + "address (such as BoardGameGeek's ranking or browse page), never a guessed one, and " + "why; then answer from the best sources you could open. Never write in `summary` or " + '`uncertain` that you "didn\'t check" it: `blocked` is where that goes, and the ' + "person's own browser can gather it for a fuller pass." : ", answer from the best sources you could open and name the missing source in one " + "short `uncertain` line, such as " + (pages ? '"BoardGameGeek was not among the pages read"' : '"BoardGameGeek refused the request, so its ranking is not used"') + ": no fuller pass follows this answer, so that line is how the person learns of it.")
3298
+ (pages ? "BoardGameGeek and eBay are not opened from this run: do not try. When the best source " + "is one of them and no gathered page is from it, or a page refused you (a 403, a " + "CAPTCHA or challenge page, an empty or blocked body, a sign-in wall)" : "Open BoardGameGeek like any other site: an ordinary read, with nothing that disguises " + "the request; a page that refuses WebFetch gets one more try through " + "`firecrawl_scrape`, as below (0.11.0). eBay is not opened from this run: do not try it. When the best source is " + "eBay, or its page refused you (BoardGameGeek or any other: a 403, a CAPTCHA or " + "challenge page, an empty or blocked body, a sign-in wall)") + (canWait ? ", list it in `blocked` with its site, its URL only when that is the page's real " + "address (such as BoardGameGeek's ranking or browse page), never a guessed one, and " + "why; then answer from the best sources you could open. Never write in `summary` or " + '`uncertain` that you "didn\'t check" it: `blocked` is where that goes, and the ' + "person's own browser can gather it for a fuller pass." : ", answer from the best sources you could open and name the missing source in one " + "short `uncertain` line, such as " + (pages ? '"BoardGameGeek was not among the pages read"' : '"BoardGameGeek refused the request, so its ranking is not used"') + ": no fuller pass follows this answer, so that line is how the person learns of it.")
3242
3299
  ];
3243
3300
  }
3244
3301
  function reviewJob(config, draft, pages, earlier, options = {}) {
@@ -3262,8 +3319,8 @@ function reviewJob(config, draft, pages, earlier, options = {}) {
3262
3319
  "- A claim citing a gathered page's `url`: check it against that page's text above; " + "do not try to open it, and do not open BoardGameGeek or eBay at all.",
3263
3320
  "- A claim resting only on a BoardGameGeek or eBay address that is not one of the " + "gathered pages cannot be checked: move it to `uncertain`."
3264
3321
  ] : [],
3265
- ...earlier ? updateChecks() : answerChecks(),
3266
- ...earlier || sent ? [] : bestSourceChecks(canWait),
3322
+ ...earlier ? updateChecks() : [...answerChecks(), ...categoryChecks()],
3323
+ ...earlier || sent ? [] : [...bestSourceChecks(canWait), ...firecrawlChecks()],
3267
3324
  "",
3268
3325
  "Then give a verdict: `approve` if the draft stands as is and covers every part (return it " + "unchanged in `answer`); `revise` if it stands once corrected (return the corrected " + "answer in `answer`); `reject` if editing cannot fix it. List each problem you found in " + "`problems`, one line each."
3269
3326
  ].join(`
@@ -3271,7 +3328,7 @@ function reviewJob(config, draft, pages, earlier, options = {}) {
3271
3328
  return {
3272
3329
  prompt,
3273
3330
  jsonSchema: REVIEW_SCHEMA,
3274
- allowedTools: [...DEFAULT_ALLOWED_TOOLS],
3331
+ allowedTools: allowed(!sent && earlier === undefined),
3275
3332
  disallowedTools: disallowed(sent),
3276
3333
  maxTurns: REVIEW_MAX_TURNS,
3277
3334
  maxBudgetUsd: REVIEW_MAX_BUDGET_USD,
@@ -3289,7 +3346,7 @@ function answerChecks() {
3289
3346
  function bestSourceChecks(canWait) {
3290
3347
  return [
3291
3348
  "- Name the best source for each part: the site its field treats as the reference (for " + "board games, BoardGameGeek; for listings and sold prices, eBay). If the answer leans on " + "secondary sources while that source is neither among its findings' sources nor " + (canWait ? "in `blocked`, the verdict is `revise`: add it to `blocked` (its site, its URL only " + "when that is the page's real address, such as BoardGameGeek's ranking or browse " + "page, never a guessed one, and why) and keep the answer from the sources it has." : "named in `uncertain`, the verdict is `revise`: add one short `uncertain` line naming " + "it, and keep the answer from the sources it has."),
3292
- "- You may open BoardGameGeek like any site. eBay is never opened by these runs: a claim " + "resting only on an eBay address came from a search listing, not a page anyone opened, " + "so move it to `uncertain`.",
3349
+ "- You may open BoardGameGeek like any site. eBay is never opened by these runs, through " + "any tool (WebFetch, Firecrawl or another): a claim resting only on an eBay address, " + "whichever tool listed or fetched it, is not one an answer may rest on, so move it to " + "`uncertain`.",
3293
3350
  ...canWait ? [
3294
3351
  "- A page that refused is not read, so a claim must not say a source was checked " + `unless a finding cites it. A line saying a source was not checked ("I didn't ` + 'check BoardGameGeek") is no answer: move it to `blocked` on a `revise`.'
3295
3352
  ] : []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rackbops/plugin-tracker",
3
- "version": "0.31.2",
3
+ "version": "0.31.3",
4
4
  "description": "The task tracker for Rackbops Clerk: reminders, renewals, price drops, one-off research (on the web, or on pages your own browser gathers and sends in through the task API, with a fuller pass on pages the research could not open) and an interest scout (through city-hall, with their findings), a want-list watcher (listing pages you paste, BoardGameGeek through its API once its token is set, and eBay and BoardGameGeek listings you send in through the task API or that eBay's saved-search alert emails forwarded to your alert address carry, which the watcher never opens; with the model checking each new listing and its seller first, through city-hall) by slash command, delivered by DM with buttons, and a daily DM at each person's hour of what is due today and overdue, on docket's SQLite store (/allow, /register, /remind, /renewal, /price, /research, /scout, /want, /tasks, /task, /settings, /web), with a web area for your tasks, history and settings, a web editor to make, change, pause and delete your own tasks (research requests too, made but not changed, and scouts, made and changed), an admin view (with the DMs that did not arrive, raising a person's daily model ceiling, and daily model usage per person), an operator switch that turns the daily budgets off for the alpha, forget-me, and a JSON task API with personal API tokens",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",
@@ -11,8 +11,8 @@
11
11
  "directory": "plugins/tracker"
12
12
  },
13
13
  "devDependencies": {
14
- "@rackbops/docket-core": "^0.10.0",
15
- "@rackbops/docket-types": "^0.10.0",
14
+ "@rackbops/docket-core": "^0.11.0",
15
+ "@rackbops/docket-types": "^0.11.0",
16
16
  "@rackbops/styles": "^0.2.42"
17
17
  },
18
18
  "peerDependencies": {