@anthusai/papyrus 5.1.0 → 5.2.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/amplify/data/schema.js +2 -0
- package/amplify/data/schema.ts +2 -0
- package/amplify/functions/monitoring-view/handler.py +11 -0
- package/amplify/functions/monitoring-view/resource.js +24 -0
- package/amplify/functions/monitoring-view/resource.ts +34 -0
- package/amplify/functions/monitoring-view/schema.js +16 -0
- package/amplify/functions/monitoring-view/schema.ts +18 -0
- package/amplify/functions/monitoring-view/wire.js +37 -0
- package/amplify/functions/monitoring-view/wire.ts +47 -0
- package/amplify/publication/redirect-and-index.js +49 -0
- package/amplify/publication/redirect-rules.js +26 -10
- package/amplify/publication/redirect-rules.ts +36 -10
- package/amplify/publication/s3-projection.js +4 -2
- package/amplify/publication/s3-projection.ts +4 -2
- package/amplify/site-backend.js +4 -0
- package/amplify/site-backend.ts +4 -0
- package/components/monitoring-overview-view.tsx +240 -0
- package/components/monitoring-panel.tsx +40 -0
- package/components/newsroom-organizer.css +17 -0
- package/components/newsroom-references-view.tsx +14 -14
- package/components/publication-cyclotron-panel.tsx +10 -10
- package/components/reference-add-entry.tsx +75 -0
- package/components/reference-curation-controls.tsx +30 -21
- package/components/reference-cyclotron-priming.tsx +4 -4
- package/components/reference-cyclotron-suggestion.tsx +1 -1
- package/components/reference-priming.tsx +14 -10
- package/components/reference-section-suggestion.tsx +1 -1
- package/components/topic-steering-workspace.tsx +13 -2
- package/lib/category-repository.ts +1 -1
- package/lib/monitoring-client.ts +29 -0
- package/lib/monitoring-overview-fixture.ts +51 -0
- package/lib/monitoring-overview.ts +144 -0
- package/lib/relevance-status.ts +2 -2
- package/package.json +8 -2
- package/routes.manifest.json +1 -1
|
@@ -60,7 +60,7 @@ export function ReferenceSuggestionBlock({
|
|
|
60
60
|
<section aria-label="Suggestion" className="space-y-2 border-y border-foreground py-3" data-reference-suggestion={referenceLineageId}>
|
|
61
61
|
<h3 className="text-xs font-semibold uppercase tracking-wide text-foreground">Suggestion</h3>
|
|
62
62
|
<p className="text-sm text-foreground">
|
|
63
|
-
|
|
63
|
+
Papyrus suggests <span className="font-semibold">{decision.label}</span> ({suggestionConfidenceWords(decision)}).
|
|
64
64
|
</p>
|
|
65
65
|
<p className="text-xs text-foreground" data-reference-suggestion-notice>
|
|
66
66
|
A suggestion never changes the status until an editor acts. This reference is {statusLabel.toLowerCase()}.
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useState } from "react";
|
|
4
3
|
import { PrimingPanel, type PrimingPanelProps } from "cyclotron/components/priming-panel";
|
|
5
|
-
import { Button } from "./ui/button";
|
|
6
4
|
|
|
7
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* The "Import a file" option of the Add a reference area. Papyrus supplies the
|
|
7
|
+
* transport and configuration; Cyclotron owns the preview and label controls.
|
|
8
|
+
* It is only rendered by `ReferenceAddEntry`, never as a section of its own.
|
|
9
|
+
*/
|
|
8
10
|
export function ReferencePriming(props: PrimingPanelProps) {
|
|
9
|
-
const [open, setOpen] = useState(false);
|
|
10
11
|
return (
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
<PrimingPanel
|
|
13
|
+
{...props}
|
|
14
|
+
renderItem={props.renderItem ?? ((item) => (
|
|
15
|
+
<div className="space-y-2">
|
|
16
|
+
<p className="whitespace-pre-wrap text-sm">{String(item.values.abstract ?? item.values.text ?? "")}</p>
|
|
17
|
+
<p className="break-all text-xs text-muted-foreground">{String(item.values.source_uri ?? "")}</p>
|
|
18
|
+
</div>
|
|
19
|
+
))}
|
|
20
|
+
/>
|
|
17
21
|
);
|
|
18
22
|
}
|
|
@@ -40,7 +40,7 @@ export function SectionSuggestionBlock({
|
|
|
40
40
|
<section aria-label="Suggested Section" className="space-y-2 border-y border-foreground py-3" data-reference-section-suggestion={referenceLineageId}>
|
|
41
41
|
<h3 className="text-xs font-semibold uppercase tracking-wide text-foreground">Suggested Section</h3>
|
|
42
42
|
<p className="text-sm text-foreground">
|
|
43
|
-
|
|
43
|
+
Papyrus suggests <span className="font-semibold">{titleOf(sections, suggestion.sectionId)}</span>
|
|
44
44
|
{suggestion.confidence !== null ? ` (${formatPercentage(suggestion.confidence)})` : ""}.
|
|
45
45
|
</p>
|
|
46
46
|
{others.length ? (
|
|
@@ -14,6 +14,7 @@ import type { ReactNode, RefObject } from "react";
|
|
|
14
14
|
import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useTransition } from "react";
|
|
15
15
|
import amplifyOutputs from "papyrus-amplify-outputs";
|
|
16
16
|
import { EditorialChecksAdminPanel } from "./editorial-checks-admin-panel";
|
|
17
|
+
import { MonitoringPanel } from "./monitoring-panel";
|
|
17
18
|
import { SignupEntriesPanel } from "./signup-entries-panel";
|
|
18
19
|
import { pluginSettings, type PluginOutputs } from "../lib/plugin-settings";
|
|
19
20
|
import { generateClient } from "aws-amplify/data";
|
|
@@ -247,7 +248,7 @@ type AssignmentAction = "claim" | "release" | "complete" | "cancel" | "reopen" |
|
|
|
247
248
|
type ReportingPacketReviewDecision = "select" | "merge" | "brief" | "hold" | "kill";
|
|
248
249
|
type AssignmentDeskViewMode = "queue" | "budget";
|
|
249
250
|
type UserRoleAction = "grant" | "revoke";
|
|
250
|
-
type AdministrationPanel = "users" | "policies" | "sections" | "procedures" | "delegations" | "signups" | "editorial-checks";
|
|
251
|
+
type AdministrationPanel = "users" | "policies" | "sections" | "procedures" | "delegations" | "signups" | "editorial-checks" | "monitoring";
|
|
251
252
|
export type NewsDeskTab = "overview" | "desks" | "topics" | "concepts" | "references" | "messages" | "assignments" | "administration" | "search";
|
|
252
253
|
type LexicalRuleScope = "publication" | "corpus" | "classifier" | "category";
|
|
253
254
|
type AnalysisReindexMode = "online-update" | "classifier-retrain" | "scoped-topic-rebuild" | "entity-graph-rebuild" | "generated-analysis-rebuild";
|
|
@@ -4327,6 +4328,15 @@ function AdministrationDeskView({
|
|
|
4327
4328
|
<strong>Editorial checks</strong>
|
|
4328
4329
|
<span>Policies the article editor checks drafts against</span>
|
|
4329
4330
|
</Link>
|
|
4331
|
+
<Link
|
|
4332
|
+
href={administrationPanelHref("monitoring", isDemo)}
|
|
4333
|
+
data-news-desk-admin-nav="monitoring"
|
|
4334
|
+
data-active={administrationPanel === "monitoring" || undefined}
|
|
4335
|
+
onClick={() => onPanelChange("monitoring")}
|
|
4336
|
+
>
|
|
4337
|
+
<strong>Monitoring</strong>
|
|
4338
|
+
<span>The automated research pipeline, read only</span>
|
|
4339
|
+
</Link>
|
|
4330
4340
|
{signupPluginEnabled() ? (
|
|
4331
4341
|
<Link
|
|
4332
4342
|
href={administrationPanelHref("signups", isDemo)}
|
|
@@ -4342,6 +4352,7 @@ function AdministrationDeskView({
|
|
|
4342
4352
|
<div className="news-desk-settings-panel">
|
|
4343
4353
|
{administrationPanel === "signups" ? <SignupEntriesPanel /> : null}
|
|
4344
4354
|
{administrationPanel === "editorial-checks" ? <EditorialChecksAdminPanel /> : null}
|
|
4355
|
+
{administrationPanel === "monitoring" ? <MonitoringPanel isAdmin={canManageUsers} isDemo={isDemo} /> : null}
|
|
4345
4356
|
{administrationPanel === "delegations" ? <AdministrationDelegationsPanel disabled={disabled} /> : null}
|
|
4346
4357
|
{administrationPanel === "users" ? (
|
|
4347
4358
|
<AdministrationUsersPanel
|
|
@@ -12806,7 +12817,7 @@ function signupPluginEnabled(): boolean {
|
|
|
12806
12817
|
}
|
|
12807
12818
|
|
|
12808
12819
|
function normalizeAdministrationPanel(value: string | null | undefined): AdministrationPanel {
|
|
12809
|
-
if (value === "policies" || value === "sections" || value === "procedures" || value === "delegations" || value === "editorial-checks") return value;
|
|
12820
|
+
if (value === "policies" || value === "sections" || value === "procedures" || value === "delegations" || value === "editorial-checks" || value === "monitoring") return value;
|
|
12810
12821
|
if (value === "signups" && signupPluginEnabled()) return value;
|
|
12811
12822
|
return "users";
|
|
12812
12823
|
}
|
|
@@ -1700,7 +1700,7 @@ export function createDemoCategorySteeringDashboard(
|
|
|
1700
1700
|
question: "Should this candidate source become a reference for this publication?",
|
|
1701
1701
|
reviewControl: "thumbs",
|
|
1702
1702
|
reviewReason: "program",
|
|
1703
|
-
reviewDetail: "Every
|
|
1703
|
+
reviewDetail: "Every suggestion is reviewed while suggestions are getting started.",
|
|
1704
1704
|
version: 1,
|
|
1705
1705
|
}),
|
|
1706
1706
|
},
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { generateClient } from "aws-amplify/data";
|
|
4
|
+
import type { MonitoringResult } from "./monitoring-overview";
|
|
5
|
+
|
|
6
|
+
type GraphQLResult = { data?: { monitoringOverview?: unknown }; errors?: { message?: string; errorType?: string }[] };
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reads the monitoring overview through the site's own authenticated API (user pool, editor or admin).
|
|
10
|
+
* The browser holds no table name, key or secret: the function behind the query does the read.
|
|
11
|
+
*/
|
|
12
|
+
export async function loadMonitoringOverview(scope: "publication" | "all"): Promise<MonitoringResult> {
|
|
13
|
+
const client = generateClient({ authMode: "userPool" });
|
|
14
|
+
let result: GraphQLResult;
|
|
15
|
+
try {
|
|
16
|
+
result = (await client.graphql({
|
|
17
|
+
query: `query MonitoringOverview($scope: String) { monitoringOverview(scope: $scope) }`,
|
|
18
|
+
variables: { scope },
|
|
19
|
+
})) as GraphQLResult;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
// Amplify throws the response when AppSync returns errors.
|
|
22
|
+
result = (error && typeof error === "object" && "errors" in error ? error : { errors: [{ message: "failed" }] }) as GraphQLResult;
|
|
23
|
+
}
|
|
24
|
+
const refused = result.errors?.some((entry) => /unauthorized|not authorized|forbidden/i.test(`${entry.errorType ?? ""} ${entry.message ?? ""}`));
|
|
25
|
+
if (refused) return { error: "forbidden" };
|
|
26
|
+
if (result.errors?.length || result.data?.monitoringOverview === undefined || result.data.monitoringOverview === null) return { state: "unavailable" };
|
|
27
|
+
const raw = result.data.monitoringOverview;
|
|
28
|
+
return (typeof raw === "string" ? JSON.parse(raw) : raw) as MonitoringResult;
|
|
29
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { MonitoringOverview } from "./monitoring-overview";
|
|
2
|
+
|
|
3
|
+
/** A realistic overview for tests and the `?demo=1` newsroom. Public Hacker News titles only. */
|
|
4
|
+
export const FIXTURE_MONITORING_OVERVIEW: MonitoringOverview = {
|
|
5
|
+
state: "ready",
|
|
6
|
+
asOf: "2026-10-21T02:40:00Z",
|
|
7
|
+
scope: { mode: "publication", publicationId: "desk-a", site: "a.example.test", canViewAll: true },
|
|
8
|
+
feeds: [{ id: "hn", sourceUrl: "https://news.ycombinator.com/rss", intervalSeconds: 900, limit: 30, enabled: true,
|
|
9
|
+
lastStatus: "complete", lastFinishedAt: "2026-10-21T02:33:00Z", nextDueAt: "2026-10-21T02:50:00Z" }],
|
|
10
|
+
runs: [
|
|
11
|
+
{ runId: "r3", startedAt: "2026-10-21T02:33:00.000000Z", status: "complete", new: 2, revised: 1, unchanged: 27, itemCount: 30, errorCount: 0, errors: [] },
|
|
12
|
+
{ runId: "r1", startedAt: "2026-10-21T02:18:00.000000Z", status: "complete", new: 0, revised: 0, unchanged: 30, itemCount: 30, errorCount: 0, errors: [] },
|
|
13
|
+
{ runId: "r0", startedAt: "2026-10-21T02:03:00.000000Z", status: "partial", new: 1, revised: 0, unchanged: 28, itemCount: 30, errorCount: 1, errors: ["ConnectionError: timeout"] },
|
|
14
|
+
],
|
|
15
|
+
routerRuns: [{ runId: "q2", startedAt: "2026-10-21T02:37:00.000000Z", mode: "live", status: "stopped", stoppedBy: "request_ceiling_reached",
|
|
16
|
+
counts: { examined: 3, decided: 1, delivered: 1, deliveryFailed: 1, routed: 1 }, errorCount: 0 }],
|
|
17
|
+
router: { mode: "live", feedback: true, enabled: true, maxItemsPerRun: 5 },
|
|
18
|
+
usage: { day: "2026-10-21", decisionsToday: 2, decisionsTotal: 3, ceilings: { decisionsTotal: 300, decisionsPerDay: 100, items: 300 } },
|
|
19
|
+
hiddenNotRelevant: 1,
|
|
20
|
+
items: [
|
|
21
|
+
{ sourceId: "104", revision: 1, title: "Fresh Show HN", url: "https://example.org/104", discussionUrl: "https://news.ycombinator.com/item?id=104",
|
|
22
|
+
observedAt: "2026-10-21T02:30:00.000000Z", routing: "collected", routingNote: null, decidedAt: null,
|
|
23
|
+
relevance: [{ publicationId: "desk-a", site: "a.example.test", relevant: null, label: null, confidence: null, delivery: { state: null } }] },
|
|
24
|
+
{ sourceId: "105", revision: 1, title: "Broken request", url: "https://example.org/105", discussionUrl: "https://news.ycombinator.com/item?id=105",
|
|
25
|
+
observedAt: "2026-10-21T01:30:00.000000Z", routing: "error", routingNote: "invalid request: bad title", decidedAt: null,
|
|
26
|
+
relevance: [{ publicationId: "desk-a", site: "a.example.test", relevant: null, label: null, confidence: null, delivery: { state: null } }] },
|
|
27
|
+
{ sourceId: "101", revision: 1, title: "Quantum paper on arXiv", url: "https://example.org/101", discussionUrl: "https://news.ycombinator.com/item?id=101",
|
|
28
|
+
observedAt: "2026-10-21T00:10:00.000000Z", routing: "routed", routingNote: null, decidedAt: "2026-10-21T01:00:00Z",
|
|
29
|
+
relevance: [{ publicationId: "desk-a", site: "a.example.test", relevant: true, label: "include", confidence: 0.91,
|
|
30
|
+
delivery: { state: "delivered", referenceId: "ref-1", referenceLineageId: "lin-1", deliveredAt: "2026-10-21T01:00:05Z" } }] },
|
|
31
|
+
{ sourceId: "103", revision: 1, title: "Phishing kit leaks", url: "https://example.org/103", discussionUrl: "https://news.ycombinator.com/item?id=103",
|
|
32
|
+
observedAt: "2026-10-20T23:00:00.000000Z", routing: "decided", routingNote: null, decidedAt: "2026-10-20T23:10:00Z",
|
|
33
|
+
relevance: [{ publicationId: "desk-a", site: "a.example.test", relevant: true, label: "include", confidence: 0.7,
|
|
34
|
+
delivery: { state: "failed", error: "ClientError: throttled", attempts: 2 } }] },
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const FIXTURE_ALL_PIPELINE_OVERVIEW: MonitoringOverview = {
|
|
39
|
+
...FIXTURE_MONITORING_OVERVIEW,
|
|
40
|
+
scope: { mode: "all", publicationId: "desk-a", site: "a.example.test", canViewAll: true },
|
|
41
|
+
hiddenNotRelevant: 0,
|
|
42
|
+
items: FIXTURE_MONITORING_OVERVIEW.items.map((item) => item.sourceId !== "101" ? item : {
|
|
43
|
+
...item,
|
|
44
|
+
relevance: [
|
|
45
|
+
...item.relevance,
|
|
46
|
+
{ publicationId: "desk-b", site: "b.example.test", relevant: false, label: "exclude", confidence: 0.8, delivery: { state: "not_applicable" } },
|
|
47
|
+
{ publicationId: "desk-c", site: "desk-c", relevant: true, label: "include", confidence: null,
|
|
48
|
+
delivery: { state: "delivered", referenceId: "ref-2", referenceLineageId: "lin-2" } },
|
|
49
|
+
],
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/** Shapes and wording for the newsroom Monitoring page. The function in src/papyrus_content/monitoring_view.py produces them. */
|
|
2
|
+
import { getNewsroomNavHref } from "./newsroom-nav";
|
|
3
|
+
|
|
4
|
+
export type MonitoringDeliveryState = "delivered" | "failed" | "pending" | "not_applicable" | null;
|
|
5
|
+
|
|
6
|
+
export type MonitoringRelevance = {
|
|
7
|
+
publicationId: string;
|
|
8
|
+
site: string | null;
|
|
9
|
+
relevant: boolean | null;
|
|
10
|
+
label: string | null;
|
|
11
|
+
confidence: number | null;
|
|
12
|
+
delivery: {
|
|
13
|
+
state: MonitoringDeliveryState;
|
|
14
|
+
referenceId?: string | null;
|
|
15
|
+
referenceLineageId?: string | null;
|
|
16
|
+
deliveredAt?: string | null;
|
|
17
|
+
error?: string | null;
|
|
18
|
+
attempts?: number | null;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type MonitoringItem = {
|
|
23
|
+
sourceId: string;
|
|
24
|
+
revision: number;
|
|
25
|
+
title: string;
|
|
26
|
+
url: string | null;
|
|
27
|
+
discussionUrl: string | null;
|
|
28
|
+
observedAt: string | null;
|
|
29
|
+
routing: string;
|
|
30
|
+
routingNote: string | null;
|
|
31
|
+
decidedAt: string | null;
|
|
32
|
+
relevance: MonitoringRelevance[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type MonitoringRun = {
|
|
36
|
+
runId: string;
|
|
37
|
+
startedAt: string;
|
|
38
|
+
status: string;
|
|
39
|
+
new: number;
|
|
40
|
+
revised: number;
|
|
41
|
+
unchanged: number;
|
|
42
|
+
itemCount: number | null;
|
|
43
|
+
errorCount: number;
|
|
44
|
+
errors: (string | null)[];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type MonitoringRouterRun = {
|
|
48
|
+
runId: string;
|
|
49
|
+
startedAt: string;
|
|
50
|
+
mode: string;
|
|
51
|
+
status: string;
|
|
52
|
+
stoppedBy: string | null;
|
|
53
|
+
counts: Record<string, number>;
|
|
54
|
+
errorCount: number;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type MonitoringFeed = {
|
|
58
|
+
id: string;
|
|
59
|
+
sourceUrl: string | null;
|
|
60
|
+
intervalSeconds: number | null;
|
|
61
|
+
limit: number | null;
|
|
62
|
+
enabled: boolean;
|
|
63
|
+
lastStatus: string | null;
|
|
64
|
+
lastFinishedAt: string | null;
|
|
65
|
+
nextDueAt: string | null;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type MonitoringOverview = {
|
|
69
|
+
state: "ready" | "empty";
|
|
70
|
+
asOf: string;
|
|
71
|
+
notice?: "publication_not_routed";
|
|
72
|
+
scope: { mode: "publication" | "all"; publicationId: string | null; site: string | null; canViewAll: boolean };
|
|
73
|
+
feeds: MonitoringFeed[];
|
|
74
|
+
runs: MonitoringRun[];
|
|
75
|
+
routerRuns: MonitoringRouterRun[];
|
|
76
|
+
router: { mode: string; feedback: boolean; enabled: boolean; maxItemsPerRun: number | null } | null;
|
|
77
|
+
usage: {
|
|
78
|
+
day: string;
|
|
79
|
+
decisionsToday: number;
|
|
80
|
+
decisionsTotal: number;
|
|
81
|
+
ceilings: { decisionsTotal?: number; decisionsPerDay?: number; items?: number } | null;
|
|
82
|
+
} | null;
|
|
83
|
+
items: MonitoringItem[];
|
|
84
|
+
hiddenNotRelevant: number;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type MonitoringResult =
|
|
88
|
+
| MonitoringOverview
|
|
89
|
+
| { state: "not_configured" }
|
|
90
|
+
| { state: "unavailable" }
|
|
91
|
+
| { error: "forbidden" | "bad_request" };
|
|
92
|
+
|
|
93
|
+
const ROUTING_WORDS: Record<string, string> = {
|
|
94
|
+
collected: "Collected, waiting for the router",
|
|
95
|
+
"dry-run-validated": "Checked in a dry run, not sent",
|
|
96
|
+
decided: "Judged, delivery not finished",
|
|
97
|
+
routed: "Routed",
|
|
98
|
+
refused: "Refused by the shared Cyclotron",
|
|
99
|
+
uncertain: "Uncertain, needs a person",
|
|
100
|
+
error: "Error",
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export function routingLabel(routing: string): string {
|
|
104
|
+
return ROUTING_WORDS[routing] ?? routing;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Fixed-format UTC time so the page reads the same everywhere. */
|
|
108
|
+
export function formatUtc(iso: string | null | undefined): string {
|
|
109
|
+
if (!iso) return "never";
|
|
110
|
+
const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})/.exec(iso);
|
|
111
|
+
return match ? `${match[1]} ${match[2]} UTC` : iso;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function confidenceText(confidence: number | null): string {
|
|
115
|
+
return confidence === null ? "confidence not reported" : `${Math.round(confidence * 100)}% confidence`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function usageText(used: number, ceiling: number | undefined): string {
|
|
119
|
+
return ceiling === undefined ? `${used} used, no ceiling set` : `${used} of ${ceiling}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function everyText(seconds: number | null): string {
|
|
123
|
+
if (!seconds) return "on a schedule";
|
|
124
|
+
return seconds % 3600 === 0 ? `every ${seconds / 3600} h` : seconds % 60 === 0 ? `every ${seconds / 60} min` : `every ${seconds} s`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function referenceHref(lineageId: string, demo?: boolean): string {
|
|
128
|
+
return getNewsroomNavHref(`/newsroom/references/${encodeURIComponent(lineageId)}`, demo);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function describeRelevance(entry: MonitoringRelevance): string {
|
|
132
|
+
if (entry.relevant === null) return "Not judged yet";
|
|
133
|
+
return entry.relevant ? "Relevant" : "Not relevant";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function describeDelivery(entry: MonitoringRelevance): string {
|
|
137
|
+
switch (entry.delivery.state) {
|
|
138
|
+
case "delivered": return "In the References queue";
|
|
139
|
+
case "failed": return "Not in the References queue: delivery failed";
|
|
140
|
+
case "pending": return "Not in the References queue yet";
|
|
141
|
+
case "not_applicable": return "Nothing to deliver";
|
|
142
|
+
default: return "";
|
|
143
|
+
}
|
|
144
|
+
}
|
package/lib/relevance-status.ts
CHANGED
|
@@ -52,10 +52,10 @@ export const DEMO_CYCLOTRON_STATUS: CyclotronStatus = {
|
|
|
52
52
|
},
|
|
53
53
|
calibration: { saysSure: null, isRight: null, gapPoints: null, curve: [] },
|
|
54
54
|
reviewRate: {
|
|
55
|
-
state: "onboarding", rate: 1, reason: "
|
|
55
|
+
state: "onboarding", rate: 1, reason: "Getting started: every suggestion is reviewed until suggestions earn a lower review rate.",
|
|
56
56
|
auditFloor: 0.05, confidenceThreshold: 0.7, override: null,
|
|
57
57
|
nextCheckAfterDecisions: 99,
|
|
58
|
-
nextStep: "Steps down to 50% after 2 more windows of 100 decisions with accuracy on confident decisions of at least 85% (at least 20 reviewed) and a
|
|
58
|
+
nextStep: "Steps down to 50% after 2 more windows of 100 decisions with accuracy on confident decisions of at least 85% (at least 20 reviewed) and a gap under 5 points between how sure suggestions say they are and how often they are right. Next check in 99 decisions.",
|
|
59
59
|
expectedReviewsPerWeek: 1,
|
|
60
60
|
},
|
|
61
61
|
lastChange: null,
|
package/package.json
CHANGED
|
@@ -85,6 +85,8 @@
|
|
|
85
85
|
"./components/item-stream-view": "./components/item-stream-view.tsx",
|
|
86
86
|
"./components/markus-body-editor": "./components/markus-body-editor.tsx",
|
|
87
87
|
"./components/markus-ir-preview": "./components/markus-ir-preview.tsx",
|
|
88
|
+
"./components/monitoring-overview-view": "./components/monitoring-overview-view.tsx",
|
|
89
|
+
"./components/monitoring-panel": "./components/monitoring-panel.tsx",
|
|
88
90
|
"./components/news-desk-client-provider": "./components/news-desk-client-provider.tsx",
|
|
89
91
|
"./components/news-desk-page": "./components/news-desk-page.tsx",
|
|
90
92
|
"./components/news-desk-taxonomy-client": "./components/news-desk-taxonomy-client.ts",
|
|
@@ -118,6 +120,7 @@
|
|
|
118
120
|
"./components/reader-auth-state": "./components/reader-auth-state.ts",
|
|
119
121
|
"./components/reader-settings": "./components/reader-settings.ts",
|
|
120
122
|
"./components/reader-theme-control": "./components/reader-theme-control.tsx",
|
|
123
|
+
"./components/reference-add-entry": "./components/reference-add-entry.tsx",
|
|
121
124
|
"./components/reference-curation-controls": "./components/reference-curation-controls.tsx",
|
|
122
125
|
"./components/reference-cyclotron-priming": "./components/reference-cyclotron-priming.tsx",
|
|
123
126
|
"./components/reference-cyclotron-status": "./components/reference-cyclotron-status.tsx",
|
|
@@ -224,6 +227,9 @@
|
|
|
224
227
|
"./lib/markus-projection": "./lib/markus-projection.ts",
|
|
225
228
|
"./lib/markus-to-article": "./lib/markus-to-article.ts",
|
|
226
229
|
"./lib/media-route-response": "./lib/media-route-response.ts",
|
|
230
|
+
"./lib/monitoring-client": "./lib/monitoring-client.ts",
|
|
231
|
+
"./lib/monitoring-overview-fixture": "./lib/monitoring-overview-fixture.ts",
|
|
232
|
+
"./lib/monitoring-overview": "./lib/monitoring-overview.ts",
|
|
227
233
|
"./lib/news-desk-session": "./lib/news-desk-session.ts",
|
|
228
234
|
"./lib/newspaper-layout": "./lib/newspaper-layout.ts",
|
|
229
235
|
"./lib/newsroom-articles": "./lib/newsroom-articles.ts",
|
|
@@ -395,7 +401,7 @@
|
|
|
395
401
|
"optional": true
|
|
396
402
|
}
|
|
397
403
|
},
|
|
398
|
-
"version": "5.
|
|
404
|
+
"version": "5.2.0",
|
|
399
405
|
"dependencies": {
|
|
400
406
|
"@aws-amplify/adapter-nextjs": "^1.7.3",
|
|
401
407
|
"@aws-crypto/sha256-js": "5.2.0",
|
|
@@ -435,7 +441,7 @@
|
|
|
435
441
|
"class-variance-authority": "^0.7.1",
|
|
436
442
|
"clsx": "^2.1.1",
|
|
437
443
|
"cmdk": "^1.1.1",
|
|
438
|
-
"cyclotron": "https://github.com/AnthusAI/Cyclotron/releases/download/ui-v0.2.
|
|
444
|
+
"cyclotron": "https://github.com/AnthusAI/Cyclotron/releases/download/ui-v0.2.3/cyclotron-0.2.3.tgz",
|
|
439
445
|
"d3": "^7.9.0",
|
|
440
446
|
"esbuild": "^0.28.2",
|
|
441
447
|
"framer-motion": "^14.3.0",
|
package/routes.manifest.json
CHANGED