@rackbops/docket-types 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -3
- package/dist/index.d.ts +16 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -4
- package/dist/index.js.map +1 -1
- package/dist/research-answer.d.ts +152 -0
- package/dist/research-answer.d.ts.map +1 -0
- package/dist/research-answer.js +210 -0
- package/dist/research-answer.js.map +1 -0
- package/dist/research.d.ts +87 -0
- package/dist/research.d.ts.map +1 -0
- package/dist/research.js +294 -0
- package/dist/research.js.map +1 -0
- package/dist/scout.d.ts +127 -0
- package/dist/scout.d.ts.map +1 -0
- package/dist/scout.js +313 -0
- package/dist/scout.js.map +1 -0
- package/dist/want-bgg.d.ts +40 -0
- package/dist/want-bgg.d.ts.map +1 -0
- package/dist/want-bgg.js +196 -0
- package/dist/want-bgg.js.map +1 -0
- package/dist/want-sources.d.ts +67 -0
- package/dist/want-sources.d.ts.map +1 -0
- package/dist/want-sources.js +196 -0
- package/dist/want-sources.js.map +1 -0
- package/dist/wantjudge.d.ts +111 -0
- package/dist/wantjudge.d.ts.map +1 -0
- package/dist/wantjudge.js +437 -0
- package/dist/wantjudge.js.map +1 -0
- package/dist/wantlist.d.ts +73 -0
- package/dist/wantlist.d.ts.map +1 -0
- package/dist/wantlist.js +185 -0
- package/dist/wantlist.js.map +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -11,11 +11,69 @@ The tracker's task types, built on `@rackbops/docket-core`'s contract. Shipped s
|
|
|
11
11
|
or the owner's pattern; `extractPrice`), appends it to the series and alerts once per crossing
|
|
12
12
|
of the drop line, measured from the first, last (default) or peak price seen; done stops it.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
- `research` (category 5, E8): execute lane, `once`, an optional deadline. Two `claude -p` Jobs
|
|
15
|
+
go through the runner:
|
|
16
|
+
- a research run: the web-search spike's prompt and answer schema, `{summary,
|
|
17
|
+
findings[{claim, sources[]}], uncertain[]}`, about 15 turns and 1 USD (item 61, proposed and
|
|
18
|
+
not yet decided);
|
|
19
|
+
- a reviewer run (item 62), queued as a follow-up, which checks the draft against its sources
|
|
20
|
+
and approves, revises or rejects it (8 turns and 0.5 USD, inferred).
|
|
21
|
+
|
|
22
|
+
On **approve** the stored draft goes out by DM to the owner and accepted recipients; on
|
|
23
|
+
**revise**, the reviewer's corrected answer. Each claim is saved as a finding with its first
|
|
24
|
+
source. A **rejected** answer is reported and nothing is saved. An answer the reviewer did not
|
|
25
|
+
pass is never sent; the draft stays in the task's state, which only the owner and admins see.
|
|
26
|
+
|
|
27
|
+
Model output is parsed, capped and cleaned before it reaches a message or a finding. No mention
|
|
28
|
+
can ping, no masked link can hide its target, only http(s) URLs that spell no mention count as
|
|
29
|
+
sources, and every DM stays under Discord's limit.
|
|
30
|
+
|
|
31
|
+
Failures:
|
|
32
|
+
- a schema miss, a malformed answer, a timeout or an error (including an Executor or `prepare`
|
|
33
|
+
that threw, or a Job not back in six hours): retried once;
|
|
34
|
+
- an auth failure: retried once, an hour later;
|
|
35
|
+
- a turn or budget cap: reported, not retried;
|
|
36
|
+
- a research run that would start past its deadline: makes no call and tells the owner.
|
|
37
|
+
|
|
38
|
+
A request costs up to 1.5 USD when every run succeeds, and up to about 3 USD with one retry per
|
|
39
|
+
phase. The CLI can end a run over its cap.
|
|
40
|
+
|
|
41
|
+
- `scout` (category 1, E9): execute lane, `calendar` every N days at the owner's hour (the host bounds N; the tracker
|
|
42
|
+
plugin allows 1 to 30). One
|
|
43
|
+
`claude -p` Job a run (WebSearch and WebFetch; 30 turns, 1.50 USD, 10 minutes, item 61 proposed)
|
|
44
|
+
asks for five to ten things through a gift lens; an item shown once is never shown again (the
|
|
45
|
+
state keeps the newest 300 digests, each item is also a finding).
|
|
46
|
+
- `wantlist` (category 2, E9): notify lane, `poll`, no model. Reads a source each run, keeps the
|
|
47
|
+
listings within the owner's limits and DMs the new ones with a Done button, each a finding.
|
|
48
|
+
Sources: `pageSource` (a pasted page's JSON-LD `ItemList`s and `Product`s, through the host's
|
|
49
|
+
Fetch port, never eBay) and `bggSource` (BGG's XML API, with the host's token).
|
|
50
|
+
- `wantjudge` (category 2, plan 5.4, item 63): the same watch on the execute lane. New listings
|
|
51
|
+
are read in plain code (no charge), then one Job has the model judge each one and its seller
|
|
52
|
+
(WebFetch only on the watch's own host, never WebSearch or the runner's files; 12 turns,
|
|
53
|
+
0.50 USD, 5 minutes). The DM puts the ones worth a look first with the model's note; a look
|
|
54
|
+
that fails for good sends the listings unchecked.
|
|
55
|
+
|
|
56
|
+
`reminder`, `renewal`, `price` and `wantlist` call no model.
|
|
57
|
+
|
|
58
|
+
`wantlistType(sources)` and `wantjudgeType(sources)` build the two watches over the sources a
|
|
59
|
+
host can run; `TASK_TYPES` has them over `pageSource` only. What the want-list types ask of a
|
|
60
|
+
host:
|
|
61
|
+
|
|
62
|
+
- its Fetch port refuses any hop to eBay when a read carries the `NEVER_EBAY` header, and does
|
|
63
|
+
not send that header on;
|
|
64
|
+
- its Fetch port reaches only public addresses, checked after the name lookup: the judge grants
|
|
65
|
+
the runner WebFetch on the watch's own host only because the host's reads already reach it;
|
|
66
|
+
- `bggSource` gets a Fetch that hands back the body as sent and follows no redirect, so BGG's
|
|
67
|
+
token never leaves BGG's host;
|
|
68
|
+
- the DM text names the tracker plugin's `/task history` command, and a judged watch with no
|
|
69
|
+
Fetch says "this bot reads no pages".
|
|
70
|
+
|
|
71
|
+
`wantjudgeType`'s `wait` (BGG's spacing, default `setTimeout`) and `bggSource`'s `now` (default
|
|
72
|
+
`Date.now`) can be injected; a host that keeps time behind its Clock passes them.
|
|
16
73
|
|
|
17
74
|
```ts
|
|
18
|
-
import { TASK_TYPES, reminder, renewal, price, TYPE_IDS, isTypeId } from "@rackbops/docket-types"
|
|
75
|
+
import { TASK_TYPES, reminder, renewal, price, research, scout, TYPE_IDS, isTypeId } from "@rackbops/docket-types"
|
|
76
|
+
import { bggSource, pageSource, wantjudgeType, wantlistType } from "@rackbops/docket-types"
|
|
19
77
|
```
|
|
20
78
|
|
|
21
79
|
`TASK_TYPES` is every shipped type keyed by id, ready for the dispatcher. Every type is declared
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The six categories of the plan (section 1.2) as type identifiers, and the types shipped so far:
|
|
5
5
|
* `reminder` with the reminder slice (Lepid-Labs/city-hall#7), `renewal` and `price` with the
|
|
6
|
-
* renewal and price-tracker slice (city-hall#11)
|
|
7
|
-
*
|
|
6
|
+
* renewal and price-tracker slice (city-hall#11), and `research` (category 5, E8), the first
|
|
7
|
+
* execute-lane type: a research run and a reviewer run through the runner. `scout` (category 1),
|
|
8
|
+
* `wantlist` and the judged `wantjudge` (category 2) came from the tracker plugin in 0.6.0 (E9,
|
|
9
|
+
* plan item 104).
|
|
8
10
|
*/
|
|
9
11
|
import type { TaskType } from "@rackbops/docket-core";
|
|
10
12
|
export type { Lane } from "@rackbops/docket-core";
|
|
@@ -13,8 +15,18 @@ export { money } from "./money.js";
|
|
|
13
15
|
export { BASELINE_RULES, type BaselineRule, DEFAULT_BASELINE, DEFAULT_DROP_PERCENT, MISSES_BEFORE_TELLING, type PriceConfig, type PriceState, price, reference, } from "./price.js";
|
|
14
16
|
export { DEFAULT_SNOOZE_MS, type ReminderConfig, reminder, snoozeUntil } from "./reminder.js";
|
|
15
17
|
export { decisionOf, isRenewalDecision, RENEWAL_DECISIONS, type RenewalConfig, type RenewalDecision, type RenewalState, renewal, } from "./renewal.js";
|
|
16
|
-
|
|
17
|
-
export
|
|
18
|
+
export { AUTH_RETRY_MS, deadlineOf, draftJson, MAX_CONTEXT_CHARS, MAX_QUESTION_CHARS, MAX_TRIES, RESEARCH_MAX_BUDGET_USD, RESEARCH_MAX_TURNS, RESEARCH_TIMEOUT_MS, REVIEW_MAX_BUDGET_USD, REVIEW_MAX_TURNS, REVIEW_TIMEOUT_MS, type ResearchConfig, type ResearchPhase, type ResearchState, research, researchJob, researchState, reviewJob, } from "./research.js";
|
|
19
|
+
export { ANSWER_SCHEMA, clean, fitMessage, MAX_MESSAGE_CHARS, parseAnswer, parseReview, REVIEW_SCHEMA, type ResearchAnswer, type ResearchFinding, type Review, renderAnswer, safeUrl, VERDICTS, type Verdict, ZWSP, } from "./research-answer.js";
|
|
20
|
+
export { itemKey, LENS_TEXT, LENSES, type Lens, lensOf, MAX_FOR_CHARS, MAX_INTEREST_CHARS, MAX_INTERESTS, MAX_ITEMS, MAX_SCOUT_NOTES, MAX_SHOWN, MIN_ITEMS, PROMPT_SHOWN, parseScout, renderScout, SCOUT_MAX_BUDGET_USD, SCOUT_MAX_TURNS, SCOUT_SCHEMA, SCOUT_TIMEOUT_MS, type ScoutConfig, type ScoutItem, type ScoutState, type ShownItem, scout, scoutJob, scoutState, } from "./scout.js";
|
|
21
|
+
export { BGG_ATTRIBUTION, BGG_HOST, BGG_SPACING_MS, type BggOptions, bggSource, bggThingUrl, MAX_SCANNED, parseBggThingId, parseMarketplace, } from "./want-bgg.js";
|
|
22
|
+
export { isEbayHost, type Listing, listingKey, listingsFromJsonLd, MAX_LISTINGS, NEVER_EBAY, pageSource, readListings, SOURCE_IDS, type Source, type SourceId, SourceMiss, SourceUnavailableError, } from "./want-sources.js";
|
|
23
|
+
export { FITS, type Fit, JUDGE_MAX_BUDGET_USD, JUDGE_MAX_TURNS, JUDGE_SCHEMA, JUDGE_TIMEOUT_MS, type JudgeOptions, type JudgeState, type JudgeVerdict, judgeHosts, judgeJob, judgeState, parseVerdicts, RETRY_GRACE_MS, renderJudged, renderUnchecked, wantjudgeType, } from "./wantjudge.js";
|
|
24
|
+
export { clip, inert, listingFinding, listingLine, MAX_DM_CHARS, MAX_NEW_PER_RUN, MAX_REPORTED, miss, type Polled, pollWant, priceText, renderWant, SHOWN_IN_DM, type WantConfig, type WantState, wantlistType, wantState, withinLimits, } from "./wantlist.js";
|
|
25
|
+
/**
|
|
26
|
+
* The six task categories the tracker starts with (plan section 1.2), as type identifiers, then
|
|
27
|
+
* `wantjudge`: category 2's watch with the model's look first (plan 5.4), a type of its own.
|
|
28
|
+
*/
|
|
29
|
+
export declare const TYPE_IDS: readonly ["reminder", "renewal", "price", "research", "scout", "wantlist", "wantjudge"];
|
|
18
30
|
export type TypeId = (typeof TYPE_IDS)[number];
|
|
19
31
|
/** True when `value` names a task type. Narrows a string read from storage or the wire. */
|
|
20
32
|
export declare function isTypeId(value: string): value is TypeId;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -3,19 +3,44 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The six categories of the plan (section 1.2) as type identifiers, and the types shipped so far:
|
|
5
5
|
* `reminder` with the reminder slice (Lepid-Labs/city-hall#7), `renewal` and `price` with the
|
|
6
|
-
* renewal and price-tracker slice (city-hall#11)
|
|
7
|
-
*
|
|
6
|
+
* renewal and price-tracker slice (city-hall#11), and `research` (category 5, E8), the first
|
|
7
|
+
* execute-lane type: a research run and a reviewer run through the runner. `scout` (category 1),
|
|
8
|
+
* `wantlist` and the judged `wantjudge` (category 2) came from the tracker plugin in 0.6.0 (E9,
|
|
9
|
+
* plan item 104).
|
|
8
10
|
*/
|
|
9
11
|
import { price } from "./price.js";
|
|
10
12
|
import { reminder } from "./reminder.js";
|
|
11
13
|
import { renewal } from "./renewal.js";
|
|
14
|
+
import { research } from "./research.js";
|
|
15
|
+
import { scout } from "./scout.js";
|
|
16
|
+
import { pageSource } from "./want-sources.js";
|
|
17
|
+
import { wantjudgeType } from "./wantjudge.js";
|
|
18
|
+
import { wantlistType } from "./wantlist.js";
|
|
12
19
|
export { extractPrice, jsonLdBlocks, parsePrice, priceFromJson, priceFromMeta, priceFromPattern, } from "./extract.js";
|
|
13
20
|
export { money } from "./money.js";
|
|
14
21
|
export { BASELINE_RULES, DEFAULT_BASELINE, DEFAULT_DROP_PERCENT, MISSES_BEFORE_TELLING, price, reference, } from "./price.js";
|
|
15
22
|
export { DEFAULT_SNOOZE_MS, reminder, snoozeUntil } from "./reminder.js";
|
|
16
23
|
export { decisionOf, isRenewalDecision, RENEWAL_DECISIONS, renewal, } from "./renewal.js";
|
|
17
|
-
|
|
18
|
-
export
|
|
24
|
+
export { AUTH_RETRY_MS, deadlineOf, draftJson, MAX_CONTEXT_CHARS, MAX_QUESTION_CHARS, MAX_TRIES, RESEARCH_MAX_BUDGET_USD, RESEARCH_MAX_TURNS, RESEARCH_TIMEOUT_MS, REVIEW_MAX_BUDGET_USD, REVIEW_MAX_TURNS, REVIEW_TIMEOUT_MS, research, researchJob, researchState, reviewJob, } from "./research.js";
|
|
25
|
+
export { ANSWER_SCHEMA, clean, fitMessage, MAX_MESSAGE_CHARS, parseAnswer, parseReview, REVIEW_SCHEMA, renderAnswer, safeUrl, VERDICTS, ZWSP, } from "./research-answer.js";
|
|
26
|
+
export { itemKey, LENS_TEXT, LENSES, lensOf, MAX_FOR_CHARS, MAX_INTEREST_CHARS, MAX_INTERESTS, MAX_ITEMS, MAX_SCOUT_NOTES, MAX_SHOWN, MIN_ITEMS, PROMPT_SHOWN, parseScout, renderScout, SCOUT_MAX_BUDGET_USD, SCOUT_MAX_TURNS, SCOUT_SCHEMA, SCOUT_TIMEOUT_MS, scout, scoutJob, scoutState, } from "./scout.js";
|
|
27
|
+
export { BGG_ATTRIBUTION, BGG_HOST, BGG_SPACING_MS, bggSource, bggThingUrl, MAX_SCANNED, parseBggThingId, parseMarketplace, } from "./want-bgg.js";
|
|
28
|
+
export { isEbayHost, listingKey, listingsFromJsonLd, MAX_LISTINGS, NEVER_EBAY, pageSource, readListings, SOURCE_IDS, SourceMiss, SourceUnavailableError, } from "./want-sources.js";
|
|
29
|
+
export { FITS, JUDGE_MAX_BUDGET_USD, JUDGE_MAX_TURNS, JUDGE_SCHEMA, JUDGE_TIMEOUT_MS, judgeHosts, judgeJob, judgeState, parseVerdicts, RETRY_GRACE_MS, renderJudged, renderUnchecked, wantjudgeType, } from "./wantjudge.js";
|
|
30
|
+
export { clip, inert, listingFinding, listingLine, MAX_DM_CHARS, MAX_NEW_PER_RUN, MAX_REPORTED, miss, pollWant, priceText, renderWant, SHOWN_IN_DM, wantlistType, wantState, withinLimits, } from "./wantlist.js";
|
|
31
|
+
/**
|
|
32
|
+
* The six task categories the tracker starts with (plan section 1.2), as type identifiers, then
|
|
33
|
+
* `wantjudge`: category 2's watch with the model's look first (plan 5.4), a type of its own.
|
|
34
|
+
*/
|
|
35
|
+
export const TYPE_IDS = [
|
|
36
|
+
"reminder",
|
|
37
|
+
"renewal",
|
|
38
|
+
"price",
|
|
39
|
+
"research",
|
|
40
|
+
"scout",
|
|
41
|
+
"wantlist",
|
|
42
|
+
"wantjudge",
|
|
43
|
+
];
|
|
19
44
|
/** True when `value` names a task type. Narrows a string read from storage or the wire. */
|
|
20
45
|
export function isTypeId(value) {
|
|
21
46
|
return TYPE_IDS.includes(value);
|
|
@@ -25,5 +50,10 @@ export const TASK_TYPES = Object.freeze({
|
|
|
25
50
|
reminder: reminder,
|
|
26
51
|
renewal: renewal,
|
|
27
52
|
price: price,
|
|
53
|
+
research: research,
|
|
54
|
+
scout: scout,
|
|
55
|
+
// Over the page source only: BGG needs a host's token, so a host with one builds its own.
|
|
56
|
+
wantlist: wantlistType({ page: pageSource }),
|
|
57
|
+
wantjudge: wantjudgeType({ page: pageSource }),
|
|
28
58
|
});
|
|
29
59
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A research answer as data (plan 5.6): what the model returned is parsed field by field, capped,
|
|
3
|
+
* and cleaned before any of it becomes message text or a finding. Nothing here trusts the shape
|
|
4
|
+
* the JSON schema promised -- the CLI enforces it, but a stored draft comes back from the task's
|
|
5
|
+
* state, and a reviewer could return a valid shape around hostile text. A page the agent read can
|
|
6
|
+
* carry an instruction; the most it can do here is be quoted as text.
|
|
7
|
+
*/
|
|
8
|
+
import { clean, ZWSP } from "@rackbops/docket-core";
|
|
9
|
+
/** The cleaner lives in core, so the dispatcher's own messages use it too; re-exported here. */
|
|
10
|
+
export { clean, ZWSP };
|
|
11
|
+
/** One claim and the pages it rests on. */
|
|
12
|
+
export interface ResearchFinding {
|
|
13
|
+
claim: string;
|
|
14
|
+
/** http(s) URLs the run says it opened. */
|
|
15
|
+
sources: string[];
|
|
16
|
+
}
|
|
17
|
+
/** The shape both runs return (the reviewer inside its verdict). */
|
|
18
|
+
export interface ResearchAnswer {
|
|
19
|
+
summary: string;
|
|
20
|
+
findings: ResearchFinding[];
|
|
21
|
+
uncertain: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare const MAX_SUMMARY_CHARS = 1200;
|
|
24
|
+
export declare const MAX_CLAIM_CHARS = 400;
|
|
25
|
+
export declare const MAX_URL_CHARS = 400;
|
|
26
|
+
export declare const MAX_FINDINGS = 12;
|
|
27
|
+
export declare const MAX_SOURCES_PER_FINDING = 4;
|
|
28
|
+
export declare const MAX_UNCERTAIN = 6;
|
|
29
|
+
export declare const MAX_UNCERTAIN_CHARS = 300;
|
|
30
|
+
/** Discord's DM limit is 2000 characters; the rest is headroom for the host's own framing. */
|
|
31
|
+
export declare const MAX_MESSAGE_CHARS = 1900;
|
|
32
|
+
/** The JSON schema the research run answers in: the spike's (docket-runner spike/cases.json). */
|
|
33
|
+
export declare const ANSWER_SCHEMA: {
|
|
34
|
+
readonly type: "object";
|
|
35
|
+
readonly properties: {
|
|
36
|
+
readonly summary: {
|
|
37
|
+
readonly type: "string";
|
|
38
|
+
readonly description: "The answer in at most 150 words.";
|
|
39
|
+
};
|
|
40
|
+
readonly findings: {
|
|
41
|
+
readonly type: "array";
|
|
42
|
+
readonly items: {
|
|
43
|
+
readonly type: "object";
|
|
44
|
+
readonly properties: {
|
|
45
|
+
readonly claim: {
|
|
46
|
+
readonly type: "string";
|
|
47
|
+
};
|
|
48
|
+
readonly sources: {
|
|
49
|
+
readonly type: "array";
|
|
50
|
+
readonly items: {
|
|
51
|
+
readonly type: "string";
|
|
52
|
+
readonly description: "A URL you opened.";
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
readonly required: readonly ["claim", "sources"];
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
readonly uncertain: {
|
|
60
|
+
readonly type: "array";
|
|
61
|
+
readonly items: {
|
|
62
|
+
readonly type: "string";
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
readonly required: readonly ["summary", "findings", "uncertain"];
|
|
67
|
+
};
|
|
68
|
+
/** An http(s) URL, as a URL parses it, or null. Never anything else (no `javascript:`). */
|
|
69
|
+
export declare function safeUrl(value: unknown): string | null;
|
|
70
|
+
/**
|
|
71
|
+
* The answer in `value`, capped and cleaned, or null when it is not one. A claim with no usable
|
|
72
|
+
* source is not a finding: it moves to `uncertain`, marked, since every claim a reader might act
|
|
73
|
+
* on needs a source the run opened.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseAnswer(value: unknown): ResearchAnswer | null;
|
|
76
|
+
/** The reviewer's three verdicts. */
|
|
77
|
+
export declare const VERDICTS: readonly ["approve", "revise", "reject"];
|
|
78
|
+
export type Verdict = (typeof VERDICTS)[number];
|
|
79
|
+
export interface Review {
|
|
80
|
+
verdict: Verdict;
|
|
81
|
+
/** What the reviewer found wrong; empty on a clean approval. */
|
|
82
|
+
problems: string[];
|
|
83
|
+
/** The answer to send: the draft as approved, or as the reviewer corrected it. */
|
|
84
|
+
answer: ResearchAnswer | null;
|
|
85
|
+
}
|
|
86
|
+
export declare const MAX_PROBLEMS = 5;
|
|
87
|
+
export declare const MAX_PROBLEM_CHARS = 300;
|
|
88
|
+
/** The JSON schema the reviewer answers in. */
|
|
89
|
+
export declare const REVIEW_SCHEMA: {
|
|
90
|
+
readonly type: "object";
|
|
91
|
+
readonly properties: {
|
|
92
|
+
readonly verdict: {
|
|
93
|
+
readonly type: "string";
|
|
94
|
+
readonly enum: readonly ["approve", "revise", "reject"];
|
|
95
|
+
readonly description: string;
|
|
96
|
+
};
|
|
97
|
+
readonly problems: {
|
|
98
|
+
readonly type: "array";
|
|
99
|
+
readonly items: {
|
|
100
|
+
readonly type: "string";
|
|
101
|
+
};
|
|
102
|
+
readonly description: "Each problem found, one line each; empty when there are none.";
|
|
103
|
+
};
|
|
104
|
+
readonly answer: {
|
|
105
|
+
readonly type: "object";
|
|
106
|
+
readonly properties: {
|
|
107
|
+
readonly summary: {
|
|
108
|
+
readonly type: "string";
|
|
109
|
+
readonly description: "The answer in at most 150 words.";
|
|
110
|
+
};
|
|
111
|
+
readonly findings: {
|
|
112
|
+
readonly type: "array";
|
|
113
|
+
readonly items: {
|
|
114
|
+
readonly type: "object";
|
|
115
|
+
readonly properties: {
|
|
116
|
+
readonly claim: {
|
|
117
|
+
readonly type: "string";
|
|
118
|
+
};
|
|
119
|
+
readonly sources: {
|
|
120
|
+
readonly type: "array";
|
|
121
|
+
readonly items: {
|
|
122
|
+
readonly type: "string";
|
|
123
|
+
readonly description: "A URL you opened.";
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
readonly required: readonly ["claim", "sources"];
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
readonly uncertain: {
|
|
131
|
+
readonly type: "array";
|
|
132
|
+
readonly items: {
|
|
133
|
+
readonly type: "string";
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
readonly required: readonly ["summary", "findings", "uncertain"];
|
|
138
|
+
readonly description: "The answer to send: the draft unchanged on approve, corrected on revise.";
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
readonly required: readonly ["verdict", "problems"];
|
|
142
|
+
};
|
|
143
|
+
/** The reviewer's verdict in `value`, cleaned, or null when it is not one. */
|
|
144
|
+
export declare function parseReview(value: unknown): Review | null;
|
|
145
|
+
/** Lines as one DM of at most `MAX_MESSAGE_CHARS`, whole lines dropped from the end. */
|
|
146
|
+
export declare function fitMessage(lines: string[]): string;
|
|
147
|
+
/**
|
|
148
|
+
* The DM a reviewed answer goes out as, at most `MAX_MESSAGE_CHARS`. Every piece of it was
|
|
149
|
+
* cleaned by `parseAnswer`; the title is the owner's own and is cleaned here.
|
|
150
|
+
*/
|
|
151
|
+
export declare function renderAnswer(title: string, answer: ResearchAnswer, review: Review): string;
|
|
152
|
+
//# sourceMappingURL=research-answer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"research-answer.d.ts","sourceRoot":"","sources":["../src/research-answer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A research answer as data (plan 5.6): what the model returned is parsed field by field, capped,
|
|
3
|
+
* and cleaned before any of it becomes message text or a finding. Nothing here trusts the shape
|
|
4
|
+
* the JSON schema promised -- the CLI enforces it, but a stored draft comes back from the task's
|
|
5
|
+
* state, and a reviewer could return a valid shape around hostile text. A page the agent read can
|
|
6
|
+
* carry an instruction; the most it can do here is be quoted as text.
|
|
7
|
+
*/
|
|
8
|
+
import { clean, ZWSP } from "@rackbops/docket-core";
|
|
9
|
+
/** The cleaner lives in core, so the dispatcher's own messages use it too; re-exported here. */
|
|
10
|
+
export { clean, ZWSP };
|
|
11
|
+
export const MAX_SUMMARY_CHARS = 1200;
|
|
12
|
+
export const MAX_CLAIM_CHARS = 400;
|
|
13
|
+
export const MAX_URL_CHARS = 400;
|
|
14
|
+
export const MAX_FINDINGS = 12;
|
|
15
|
+
export const MAX_SOURCES_PER_FINDING = 4;
|
|
16
|
+
export const MAX_UNCERTAIN = 6;
|
|
17
|
+
export const MAX_UNCERTAIN_CHARS = 300;
|
|
18
|
+
/** Discord's DM limit is 2000 characters; the rest is headroom for the host's own framing. */
|
|
19
|
+
export const MAX_MESSAGE_CHARS = 1900;
|
|
20
|
+
/** The JSON schema the research run answers in: the spike's (docket-runner spike/cases.json). */
|
|
21
|
+
export const ANSWER_SCHEMA = {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
summary: { type: "string", description: "The answer in at most 150 words." },
|
|
25
|
+
findings: {
|
|
26
|
+
type: "array",
|
|
27
|
+
items: {
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
claim: { type: "string" },
|
|
31
|
+
sources: {
|
|
32
|
+
type: "array",
|
|
33
|
+
items: { type: "string", description: "A URL you opened." },
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ["claim", "sources"],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
uncertain: { type: "array", items: { type: "string" } },
|
|
40
|
+
},
|
|
41
|
+
required: ["summary", "findings", "uncertain"],
|
|
42
|
+
};
|
|
43
|
+
/** An http(s) URL, as a URL parses it, or null. Never anything else (no `javascript:`). */
|
|
44
|
+
export function safeUrl(value) {
|
|
45
|
+
if (typeof value !== "string")
|
|
46
|
+
return null;
|
|
47
|
+
const trimmed = value.trim().replace(/^<|>$/g, "");
|
|
48
|
+
if (trimmed.length === 0 || trimmed.length > MAX_URL_CHARS)
|
|
49
|
+
return null;
|
|
50
|
+
let url;
|
|
51
|
+
try {
|
|
52
|
+
url = new URL(trimmed);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (url.protocol !== "https:" && url.protocol !== "http:")
|
|
58
|
+
return null;
|
|
59
|
+
if (url.username || url.password)
|
|
60
|
+
return null;
|
|
61
|
+
const text = url.toString();
|
|
62
|
+
// A URL is shown as is, so one that spells a mention could ping; it is no source.
|
|
63
|
+
if (/@(everyone|here)\b|<[@#]/i.test(decodeURIComponentSafe(text)))
|
|
64
|
+
return null;
|
|
65
|
+
return text;
|
|
66
|
+
}
|
|
67
|
+
function decodeURIComponentSafe(text) {
|
|
68
|
+
try {
|
|
69
|
+
return decodeURIComponent(text);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return text;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function isObj(v) {
|
|
76
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The answer in `value`, capped and cleaned, or null when it is not one. A claim with no usable
|
|
80
|
+
* source is not a finding: it moves to `uncertain`, marked, since every claim a reader might act
|
|
81
|
+
* on needs a source the run opened.
|
|
82
|
+
*/
|
|
83
|
+
export function parseAnswer(value) {
|
|
84
|
+
if (!isObj(value) || typeof value.summary !== "string" || !Array.isArray(value.findings)) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const summary = clean(value.summary, MAX_SUMMARY_CHARS);
|
|
88
|
+
if (summary.length === 0)
|
|
89
|
+
return null;
|
|
90
|
+
const findings = [];
|
|
91
|
+
const uncertain = [];
|
|
92
|
+
for (const f of value.findings) {
|
|
93
|
+
if (!isObj(f) || typeof f.claim !== "string")
|
|
94
|
+
continue;
|
|
95
|
+
const claim = clean(f.claim, MAX_CLAIM_CHARS, true);
|
|
96
|
+
if (claim.length === 0)
|
|
97
|
+
continue;
|
|
98
|
+
const raw = Array.isArray(f.sources) ? f.sources : [];
|
|
99
|
+
const sources = [...new Set(raw.map(safeUrl).filter((u) => u !== null))];
|
|
100
|
+
if (sources.length === 0)
|
|
101
|
+
uncertain.push(`${claim} (no source given)`);
|
|
102
|
+
else
|
|
103
|
+
findings.push({ claim, sources: sources.slice(0, MAX_SOURCES_PER_FINDING) });
|
|
104
|
+
}
|
|
105
|
+
const said = Array.isArray(value.uncertain) ? value.uncertain : [];
|
|
106
|
+
for (const u of said) {
|
|
107
|
+
if (typeof u !== "string")
|
|
108
|
+
continue;
|
|
109
|
+
const line = clean(u, MAX_UNCERTAIN_CHARS, true);
|
|
110
|
+
if (line.length > 0)
|
|
111
|
+
uncertain.push(line);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
summary,
|
|
115
|
+
findings: findings.slice(0, MAX_FINDINGS),
|
|
116
|
+
uncertain: uncertain.slice(0, MAX_UNCERTAIN),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** The reviewer's three verdicts. */
|
|
120
|
+
export const VERDICTS = ["approve", "revise", "reject"];
|
|
121
|
+
export const MAX_PROBLEMS = 5;
|
|
122
|
+
export const MAX_PROBLEM_CHARS = 300;
|
|
123
|
+
/** The JSON schema the reviewer answers in. */
|
|
124
|
+
export const REVIEW_SCHEMA = {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
verdict: {
|
|
128
|
+
type: "string",
|
|
129
|
+
enum: [...VERDICTS],
|
|
130
|
+
description: "approve: the draft stands as is. revise: it stands once corrected; put the corrected " +
|
|
131
|
+
"answer in `answer`. reject: it cannot be fixed by editing (wrong question, sources do " +
|
|
132
|
+
"not support it, or nothing usable).",
|
|
133
|
+
},
|
|
134
|
+
problems: {
|
|
135
|
+
type: "array",
|
|
136
|
+
items: { type: "string" },
|
|
137
|
+
description: "Each problem found, one line each; empty when there are none.",
|
|
138
|
+
},
|
|
139
|
+
answer: {
|
|
140
|
+
...ANSWER_SCHEMA,
|
|
141
|
+
description: "The answer to send: the draft unchanged on approve, corrected on revise.",
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
required: ["verdict", "problems"],
|
|
145
|
+
};
|
|
146
|
+
/** The reviewer's verdict in `value`, cleaned, or null when it is not one. */
|
|
147
|
+
export function parseReview(value) {
|
|
148
|
+
if (!isObj(value) || typeof value.verdict !== "string")
|
|
149
|
+
return null;
|
|
150
|
+
const verdict = VERDICTS.includes(value.verdict)
|
|
151
|
+
? value.verdict
|
|
152
|
+
: null;
|
|
153
|
+
if (!verdict)
|
|
154
|
+
return null;
|
|
155
|
+
const problems = (Array.isArray(value.problems) ? value.problems : [])
|
|
156
|
+
.filter((p) => typeof p === "string")
|
|
157
|
+
.map((p) => clean(p, MAX_PROBLEM_CHARS, true))
|
|
158
|
+
.filter((p) => p.length > 0)
|
|
159
|
+
.slice(0, MAX_PROBLEMS);
|
|
160
|
+
const answer = parseAnswer(value.answer);
|
|
161
|
+
// Approve or revise without a usable answer is not a verdict the type can act on.
|
|
162
|
+
if (verdict !== "reject" && !answer)
|
|
163
|
+
return null;
|
|
164
|
+
return { verdict, problems, answer: verdict === "reject" ? null : answer };
|
|
165
|
+
}
|
|
166
|
+
/** Fits `lines` into `max` characters, dropping whole lines from the end and saying so. */
|
|
167
|
+
function fit(lines, max) {
|
|
168
|
+
const out = [];
|
|
169
|
+
let length = 0;
|
|
170
|
+
for (const [i, line] of lines.entries()) {
|
|
171
|
+
const more = `(${lines.length - i} more lines not shown)`;
|
|
172
|
+
// Room for this line, and for the marker unless it is the last.
|
|
173
|
+
const reserve = i < lines.length - 1 ? more.length + 1 : 0;
|
|
174
|
+
if (length + line.length + 1 + reserve > max) {
|
|
175
|
+
out.push(more);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
out.push(line);
|
|
179
|
+
length += line.length + 1;
|
|
180
|
+
}
|
|
181
|
+
const text = out.join("\n");
|
|
182
|
+
return text.length > max ? `${text.slice(0, max - 3)}...` : text;
|
|
183
|
+
}
|
|
184
|
+
/** Lines as one DM of at most `MAX_MESSAGE_CHARS`, whole lines dropped from the end. */
|
|
185
|
+
export function fitMessage(lines) {
|
|
186
|
+
return fit(lines, MAX_MESSAGE_CHARS);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* The DM a reviewed answer goes out as, at most `MAX_MESSAGE_CHARS`. Every piece of it was
|
|
190
|
+
* cleaned by `parseAnswer`; the title is the owner's own and is cleaned here.
|
|
191
|
+
*/
|
|
192
|
+
export function renderAnswer(title, answer, review) {
|
|
193
|
+
const lines = [`Research: ${clean(title, 200, true)}`, "", answer.summary];
|
|
194
|
+
if (answer.findings.length > 0) {
|
|
195
|
+
lines.push("", "Findings:");
|
|
196
|
+
// Sources in <...> so Discord shows the link without an embed.
|
|
197
|
+
for (const f of answer.findings)
|
|
198
|
+
lines.push(`- ${f.claim} <${f.sources[0]}>`);
|
|
199
|
+
}
|
|
200
|
+
if (answer.uncertain.length > 0) {
|
|
201
|
+
lines.push("", "Uncertain:");
|
|
202
|
+
for (const u of answer.uncertain)
|
|
203
|
+
lines.push(`- ${u}`);
|
|
204
|
+
}
|
|
205
|
+
const note = review.verdict === "approve"
|
|
206
|
+
? "Checked by a reviewer run."
|
|
207
|
+
: `Checked and corrected by a reviewer run (${review.problems.length} problem(s) fixed).`;
|
|
208
|
+
return `${fit(lines, MAX_MESSAGE_CHARS - note.length - 2)}\n\n${note}`;
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=research-answer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"research-answer.js","sourceRoot":"","sources":["../src/research-answer.ts"],"names":[],"mappings":"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"}
|