@anthusai/papyrus 2.0.0-next.11 → 2.0.0-next.110
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/auth/resource.js +16 -1
- package/amplify/auth/resource.ts +19 -1
- package/amplify/data/function-handler.js +20 -0
- package/amplify/data/function-handler.ts +40 -0
- package/amplify/data/resource.js +152 -62
- package/amplify/data/resource.ts +158 -62
- package/amplify/data/schema.js +2101 -1987
- package/amplify/data/schema.ts +197 -70
- package/amplify/functions/assignment-action/data-access.js +2 -0
- package/amplify/functions/assignment-action/data-access.ts +2 -0
- package/amplify/functions/assignment-action/handler.ts +1 -1
- package/amplify/functions/automation-view/handler.py +17 -0
- package/amplify/functions/automation-view/resource.js +24 -0
- package/amplify/functions/automation-view/resource.ts +34 -0
- package/amplify/functions/automation-view/schema.js +26 -0
- package/amplify/functions/automation-view/schema.ts +28 -0
- package/amplify/functions/automation-view/wire.js +56 -0
- package/amplify/functions/automation-view/wire.ts +76 -0
- package/amplify/functions/content-actions/handler.py +64 -3
- package/amplify/functions/content-actions/requirements.txt +1 -0
- package/amplify/functions/content-actions/resource.js +1 -1
- package/amplify/functions/content-actions/resource.ts +1 -1
- package/amplify/functions/editorial-checks/handler.py +2 -2
- package/amplify/functions/editorial-checks/requirements.txt +1 -1
- package/amplify/functions/editorial-checks/resource.js +3 -3
- package/amplify/functions/editorial-checks/resource.ts +3 -3
- package/amplify/functions/editorial-checks/schema.js +38 -8
- package/amplify/functions/editorial-checks/schema.ts +38 -8
- package/amplify/functions/editorial-checks/wire.js +4 -0
- package/amplify/functions/editorial-checks/wire.ts +4 -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 +29 -0
- package/amplify/functions/monitoring-view/schema.ts +31 -0
- package/amplify/functions/monitoring-view/wire.js +64 -0
- package/amplify/functions/monitoring-view/wire.ts +81 -0
- package/amplify/functions/public-forms/core.js +247 -0
- package/amplify/functions/public-forms/core.ts +310 -0
- package/amplify/functions/public-forms/handler.ts +41 -0
- package/amplify/functions/public-forms/origins.js +35 -0
- package/amplify/functions/public-forms/origins.ts +43 -0
- package/amplify/functions/public-forms/resource.js +9 -0
- package/amplify/functions/public-forms/resource.ts +10 -0
- package/amplify/functions/public-forms/wire.js +25 -0
- package/amplify/functions/public-forms/wire.ts +32 -0
- package/amplify/functions/shared/editorial-context.ts +63 -4
- package/amplify/functions/shared/python-bundle.js +20 -14
- package/amplify/functions/shared/python-bundle.ts +25 -18
- package/amplify/plugins/newsletter/config.js +245 -0
- package/amplify/plugins/newsletter/config.ts +305 -0
- package/amplify/plugins/newsletter/function/handler.py +6 -0
- package/amplify/plugins/newsletter/function/requirements.txt +5 -0
- package/amplify/plugins/newsletter/ops/handler.py +12 -0
- package/amplify/plugins/newsletter/ops/resource.js +27 -0
- package/amplify/plugins/newsletter/ops/resource.ts +37 -0
- package/amplify/plugins/newsletter/papyrus.plugin.js +263 -0
- package/amplify/plugins/newsletter/papyrus.plugin.ts +311 -0
- package/amplify/plugins/newsletter/schema.js +57 -0
- package/amplify/plugins/newsletter/schema.ts +62 -0
- package/amplify/plugins/newsletter/secure-string-parameter.js +107 -0
- package/amplify/plugins/newsletter/secure-string-parameter.ts +120 -0
- package/amplify/plugins/newsletter/sending.js +140 -0
- package/amplify/plugins/newsletter/sending.ts +186 -0
- package/amplify/plugins/reader-steering/config.js +182 -0
- package/amplify/plugins/reader-steering/config.ts +290 -0
- package/amplify/plugins/reader-steering/function/ai_moderation.py +246 -0
- package/amplify/plugins/reader-steering/function/handler.py +1589 -0
- package/amplify/plugins/reader-steering/function/moderation-prompt.md +27 -0
- package/amplify/plugins/reader-steering/function/moderation.py +316 -0
- package/amplify/plugins/reader-steering/function/proposals.py +487 -0
- package/amplify/plugins/reader-steering/function/resource.js +27 -0
- package/amplify/plugins/reader-steering/function/resource.ts +35 -0
- package/amplify/plugins/reader-steering/papyrus.plugin.js +187 -0
- package/amplify/plugins/reader-steering/papyrus.plugin.ts +213 -0
- package/amplify/plugins/reader-steering/schema.js +252 -0
- package/amplify/plugins/reader-steering/schema.ts +254 -0
- package/amplify/plugins/registry.js +4 -0
- package/amplify/plugins/registry.ts +4 -0
- package/amplify/plugins/signup/schema.js +2 -1
- package/amplify/plugins/signup/schema.ts +2 -1
- package/amplify/plugins/types.ts +5 -0
- package/amplify/plugins/wire.js +1 -0
- package/amplify/plugins/wire.ts +1 -0
- package/amplify/publication/projection-lease.js +30 -0
- package/amplify/publication/projection-lease.ts +39 -0
- package/amplify/publication/redirect-and-index.js +105 -0
- package/amplify/publication/redirect-rules.js +73 -0
- package/amplify/publication/redirect-rules.ts +93 -0
- package/amplify/publication/s3-projection.js +7 -1
- package/amplify/publication/s3-projection.ts +9 -1
- package/amplify/site-backend-config.js +19 -0
- package/amplify/site-backend-config.ts +45 -1
- package/amplify/site-backend-names.js +4 -2
- package/amplify/site-backend-names.ts +4 -2
- package/amplify/site-backend.js +42 -4
- package/amplify/site-backend.ts +45 -4
- package/app/.well-known/papyrus-cli.json/route.ts +9 -0
- package/app/api/auth/prospectus/sign-in/route.ts +4 -0
- package/app/api/newsletter/confirm/route.ts +13 -0
- package/app/api/newsletter/preferences/route.ts +13 -0
- package/app/api/newsletter/state/route.ts +9 -0
- package/app/api/newsletter/subscribe/route.ts +9 -0
- package/app/api/newsletter/unsubscribe/route.ts +13 -0
- package/app/api/newsroom/chat/[...path]/route.ts +17 -0
- package/app/api/newsroom/curation/context/route.ts +9 -0
- package/app/api/newsroom/curation/priming/route.ts +9 -0
- package/app/api/newsroom/curation/publication/route.ts +9 -0
- package/app/api/reader/comment/delete/route.ts +9 -0
- package/app/api/reader/comment/edit/route.ts +9 -0
- package/app/api/reader/comment/route.ts +9 -0
- package/app/api/reader/flag/route.ts +9 -0
- package/app/api/reader/handle/route.ts +9 -0
- package/app/api/reader/me/route.ts +9 -0
- package/app/api/reader/rating/route.ts +9 -0
- package/app/api/reader/report/route.ts +9 -0
- package/app/api/reader/retired/route.ts +9 -0
- package/app/api/reader/submit/route.ts +9 -0
- package/app/api/reader/vote/route.ts +9 -0
- package/app/layout.tsx +5 -1
- package/bin/papyrus-infra.mjs +20 -1
- package/components/automation-panel.tsx +49 -0
- package/components/automation-run-now.tsx +97 -0
- package/components/automation-view.tsx +195 -0
- package/components/editorial-checks-admin-panel.tsx +32 -17
- package/components/moderation-panel.tsx +248 -0
- package/components/monitoring-overview-view.tsx +422 -0
- package/components/monitoring-panel.tsx +60 -0
- package/components/news-desk-client-provider.tsx +18 -18
- package/components/news-desk-taxonomy-client.ts +28 -8
- package/components/newsletter-panel.tsx +474 -0
- package/components/newsletter-sections.tsx +163 -0
- package/components/newsroom-access-gate.tsx +16 -2
- package/components/newsroom-article-editor.tsx +9 -7
- package/components/newsroom-articles-view.tsx +6 -6
- package/components/newsroom-assignment-detail.tsx +6 -6
- package/components/newsroom-board-view.tsx +8 -3
- package/components/newsroom-curation-bootstrap.tsx +45 -0
- package/components/newsroom-ops-overview.tsx +26 -96
- package/components/newsroom-ops-shell.tsx +2 -2
- package/components/newsroom-organizer.css +33 -0
- package/components/newsroom-overview-skeleton.tsx +25 -0
- package/components/newsroom-references-view.tsx +192 -49
- package/components/notification-center.tsx +281 -0
- package/components/papyrus-console-conversation.css +14 -0
- package/components/papyrus-console-conversation.tsx +55 -0
- package/components/papyrus-console-shell.tsx +62 -420
- package/components/papyrus-private-chat-panel.tsx +134 -0
- package/components/publication-cyclotron-panel.tsx +15 -10
- package/components/reader-auth-state.ts +1 -1
- package/components/reference-add-entry.tsx +75 -0
- package/components/reference-curation-controls.tsx +31 -22
- package/components/reference-cyclotron-priming.tsx +58 -19
- package/components/reference-cyclotron-suggestion.tsx +10 -6
- package/components/reference-priming.tsx +14 -10
- package/components/reference-relevance-review.tsx +3 -3
- package/components/reference-section-suggestion.tsx +1 -1
- package/components/topic-steering-workspace.tsx +230 -144
- package/infra/amplify-app-shell.js +96 -16
- package/infra/amplify-app-shell.ts +108 -18
- package/infra/cloud-email-config.js +32 -0
- package/infra/cloud-email-config.ts +39 -0
- package/infra/cloud-email.js +57 -0
- package/infra/cloud-email.test.js +69 -0
- package/infra/cloud-email.test.ts +66 -0
- package/infra/cloud-email.ts +60 -0
- package/infra/index.js +1 -0
- package/infra/secure-string-parameter-handler.js +41 -0
- package/infra/secure-string-parameter-handler.ts +41 -0
- package/infra/secure-string-parameter.js +79 -0
- package/infra/secure-string-parameter.ts +91 -0
- package/infra/site-config.js +92 -6
- package/infra/site-config.ts +138 -6
- package/lib/automation-client.ts +54 -0
- package/lib/automation-fixture.ts +93 -0
- package/lib/automation-overview.ts +252 -0
- package/lib/category-repository.ts +14 -12
- package/lib/cli-login-discovery.ts +78 -0
- package/lib/console-web-context.ts +1 -1
- package/lib/curation-routes.ts +345 -0
- package/lib/cyclotron-suggestions.ts +4 -4
- package/lib/editorial-checks-client.ts +18 -0
- package/lib/editorial-checks-demo.ts +1 -1
- package/lib/editorial-checks.ts +8 -4
- package/lib/editorial-preview.ts +5 -5
- package/lib/monitoring-client.ts +83 -0
- package/lib/monitoring-overview-fixture.ts +51 -0
- package/lib/monitoring-overview.ts +191 -0
- package/lib/news-desk-session.ts +12 -21
- package/lib/newsletter-api.ts +418 -0
- package/lib/newsletter-client.ts +46 -0
- package/lib/newsletter-fixture.ts +184 -0
- package/lib/newsletter-model.ts +202 -0
- package/lib/newsroom-demo-dashboard.ts +1 -1
- package/lib/newsroom-nav.ts +1 -1
- package/lib/newsroom-organizer-nav.ts +2 -1
- package/lib/papyrus-chat-admission.ts +65 -0
- package/lib/papyrus-hosted-chat.ts +217 -0
- package/lib/papyrus-private-chat-client.ts +81 -0
- package/lib/prospectus-session.ts +30 -14
- package/lib/prospectus-sign-in-routes.ts +106 -31
- package/lib/prospectus-site-identity.ts +46 -6
- package/lib/reader-api.ts +145 -0
- package/lib/reader-moderation.ts +220 -0
- package/lib/reader-retirement-route.ts +89 -0
- package/lib/reader-subject.md +29 -0
- package/lib/reader-subject.ts +53 -0
- package/lib/reference-review-details.ts +71 -0
- package/lib/reference-review-request.ts +14 -0
- package/lib/relevance-decisions.ts +42 -10
- package/lib/relevance-status.ts +2 -2
- package/lib/review-client.ts +120 -0
- package/lib/review-fixture.ts +45 -0
- package/lib/review-requests.ts +246 -0
- package/lib/semantic-graph.ts +2 -0
- package/lib/shared-chat-admission.ts +182 -0
- package/lib/site-brand-tenant.ts +11 -6
- package/lib/site-brand.ts +6 -3
- package/lib/site-origin-value.ts +50 -0
- package/lib/site-origin.ts +8 -0
- package/lib/use-reference-review-data.ts +44 -0
- package/middleware.ts +14 -3
- package/package.json +67 -3
- package/routes.manifest.json +238 -2
- package/src/sync.mjs +11 -2
- package/src/with-papyrus.d.mts +1 -0
- package/src/with-papyrus.mjs +17 -1
|
@@ -6,7 +6,6 @@ import dynamic from "next/dynamic";
|
|
|
6
6
|
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
7
7
|
import { CheckIcon, UserIcon } from "lucide-react";
|
|
8
8
|
import { Md5 } from "@smithy/md5-js";
|
|
9
|
-
import { gsap } from "gsap";
|
|
10
9
|
import {
|
|
11
10
|
createConsoleMessage,
|
|
12
11
|
createConsoleThread,
|
|
@@ -35,10 +34,9 @@ import {
|
|
|
35
34
|
subscribeReaderSettingsChanges,
|
|
36
35
|
type ReaderMotionSetting,
|
|
37
36
|
} from "./reader-settings";
|
|
38
|
-
import {
|
|
37
|
+
import { PapyrusConsoleConversation, type ConsoleConversationMessage } from "./papyrus-console-conversation";
|
|
38
|
+
import { PapyrusPrivateChatPanel } from "./papyrus-private-chat-panel";
|
|
39
39
|
import { ModelSelector } from "./ai-elements/model-selector";
|
|
40
|
-
import { PromptInput, PromptInputBody, PromptInputFooter, type PromptInputMessage, PromptInputSubmit, PromptInputTextarea, PromptInputTools } from "./ai-elements/prompt-input";
|
|
41
|
-
import { Shimmer } from "./ai-elements/shimmer";
|
|
42
40
|
import { Suggestion, Suggestions } from "./ai-elements/suggestion";
|
|
43
41
|
import { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput } from "./ai-elements/tool";
|
|
44
42
|
import { CONNECTION_STATE_CHANGE, ConnectionState } from "aws-amplify/data";
|
|
@@ -54,7 +52,6 @@ import {
|
|
|
54
52
|
useRef,
|
|
55
53
|
useState,
|
|
56
54
|
} from "react";
|
|
57
|
-
import { useStickToBottomContext } from "use-stick-to-bottom";
|
|
58
55
|
|
|
59
56
|
const CONSOLE_STARTER_SUGGESTIONS = [
|
|
60
57
|
"Start a research assignment",
|
|
@@ -108,6 +105,7 @@ const CONSOLE_MODEL_OPTIONS = [
|
|
|
108
105
|
|
|
109
106
|
type PapyrusConsoleShellProps = {
|
|
110
107
|
children: ReactNode;
|
|
108
|
+
privateChatEnabled?: boolean;
|
|
111
109
|
};
|
|
112
110
|
|
|
113
111
|
type PapyrusConsoleContextValue = {
|
|
@@ -148,7 +146,7 @@ type ConsoleAuthorIdentity = {
|
|
|
148
146
|
|
|
149
147
|
const PapyrusConsoleContext = createContext<PapyrusConsoleContextValue | null>(null);
|
|
150
148
|
|
|
151
|
-
export function PapyrusConsoleShell({ children }: PapyrusConsoleShellProps) {
|
|
149
|
+
export function PapyrusConsoleShell({ children, privateChatEnabled = false }: PapyrusConsoleShellProps) {
|
|
152
150
|
const pathname = usePathname();
|
|
153
151
|
const [session, setSession] = useState<ReaderSessionSnapshot | null>(null);
|
|
154
152
|
const [open, setOpen] = useState(false);
|
|
@@ -367,7 +365,9 @@ export function PapyrusConsoleShell({ children }: PapyrusConsoleShellProps) {
|
|
|
367
365
|
<aside className={open ? "papyrus-console papyrus-console--open" : "papyrus-console"} aria-label="Papyrus console">
|
|
368
366
|
{open ? (
|
|
369
367
|
canUseConsole
|
|
370
|
-
?
|
|
368
|
+
? privateChatEnabled
|
|
369
|
+
? <PapyrusPrivateChatPanel onClose={toggleOpen} />
|
|
370
|
+
: <ConsolePanel actorEmail={session?.auth.status === "signedIn" ? (session.auth.email ?? null) : null} actorLabel={session?.auth.label ?? "Papyrus editor"} onClose={toggleOpen} />
|
|
371
371
|
: <ConsoleAccessPanel onClose={toggleOpen} session={session} />
|
|
372
372
|
) : null}
|
|
373
373
|
</aside>
|
|
@@ -421,7 +421,6 @@ function ConsolePanel({ actorEmail, actorLabel, onClose }: { actorEmail: string
|
|
|
421
421
|
const [thread, setThread] = useState<ConsoleChatThread | null>(null);
|
|
422
422
|
const [threads, setThreads] = useState<ConsoleChatThread[]>([]);
|
|
423
423
|
const [messages, setMessages] = useState<ConsoleChatMessage[]>([]);
|
|
424
|
-
const [draft, setDraft] = useState("");
|
|
425
424
|
const [loading, setLoading] = useState(false);
|
|
426
425
|
const [sending, setSending] = useState(false);
|
|
427
426
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -863,13 +862,6 @@ function ConsolePanel({ actorEmail, actorLabel, onClose }: { actorEmail: string
|
|
|
863
862
|
}
|
|
864
863
|
}
|
|
865
864
|
|
|
866
|
-
async function sendMessage(message: PromptInputMessage) {
|
|
867
|
-
const content = message.text.trim();
|
|
868
|
-
if (!content) return;
|
|
869
|
-
setDraft("");
|
|
870
|
-
await submitMessage(content);
|
|
871
|
-
}
|
|
872
|
-
|
|
873
865
|
function handleSuggestionClick(suggestion: string) {
|
|
874
866
|
if (sending) return;
|
|
875
867
|
void submitMessage(suggestion);
|
|
@@ -1045,18 +1037,18 @@ function ConsolePanel({ actorEmail, actorLabel, onClose }: { actorEmail: string
|
|
|
1045
1037
|
}
|
|
1046
1038
|
return latestAgentEntryId;
|
|
1047
1039
|
}, [isSupersededRunningAssistantMessage, timelineEntries]);
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
return [
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
(
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
}, [
|
|
1040
|
+
const presentationEntries = useMemo(() => new Map(timelineEntries.map((entry) => [entry.id, entry])), [timelineEntries]);
|
|
1041
|
+
const conversationMessages = useMemo<ConsoleConversationMessage[]>(() => timelineEntries.flatMap<ConsoleConversationMessage>((entry) => {
|
|
1042
|
+
if (entry.kind === "tool") return [{ id: entry.id, role: "TOOL", text: "", createdAt: entry.tool.firstCreatedAt }];
|
|
1043
|
+
const message = entry.message;
|
|
1044
|
+
const content = (message.content ?? "").trim();
|
|
1045
|
+
if (isSupersededRunningAssistantMessage(message, content)) return [];
|
|
1046
|
+
const awaiting = message.pendingPlaceholder || (message.role === "ASSISTANT" && message.responseStatus === "RUNNING");
|
|
1047
|
+
return [{ id: message.id, role: message.role,
|
|
1048
|
+
text: content || (awaiting ? "" : sanitizeConsoleSummary((message.summary ?? "").trim())),
|
|
1049
|
+
createdAt: message.createdAt, pending: awaiting, failed: message.responseStatus === "FAILED",
|
|
1050
|
+
error: message.responseError, context: message.messageKind === CONSOLE_WEB_CONTEXT_MESSAGE_KIND }];
|
|
1051
|
+
}), [isSupersededRunningAssistantMessage, timelineEntries]);
|
|
1060
1052
|
|
|
1061
1053
|
return (
|
|
1062
1054
|
<div className="papyrus-console__panel">
|
|
@@ -1089,141 +1081,49 @@ function ConsolePanel({ actorEmail, actorLabel, onClose }: { actorEmail: string
|
|
|
1089
1081
|
</button>
|
|
1090
1082
|
) : null}
|
|
1091
1083
|
</div>
|
|
1092
|
-
<
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
? resolveAssistantAvatarPresentation(message, motionSetting === "low", pending)
|
|
1136
|
-
: null;
|
|
1137
|
-
const userAvatarHash = message.role === "USER" ? readConsoleAuthorAvatarHash(message) : null;
|
|
1138
|
-
const userAuthorEmail = message.role === "USER" ? readConsoleAuthorEmail(message) : null;
|
|
1139
|
-
const shouldShowThinking = (
|
|
1140
|
-
message.pendingPlaceholder
|
|
1141
|
-
|| (message.role === "ASSISTANT" && message.responseStatus === "RUNNING" && !content && !isSupersededRunningAssistant)
|
|
1142
|
-
);
|
|
1143
|
-
if (isSupersededRunningAssistant) return null;
|
|
1144
|
-
|
|
1145
|
-
return (
|
|
1146
|
-
<ConsoleChatMessageRow
|
|
1147
|
-
key={message.id}
|
|
1148
|
-
role={isWebContextMessage ? "CONTEXT" : message.role}
|
|
1149
|
-
>
|
|
1150
|
-
<div className={message.role === "ASSISTANT" ? "papyrus-console-message__markdown" : undefined}>
|
|
1151
|
-
{message.role === "USER" ? <ConsoleUserAvatar authorEmail={userAuthorEmail} avatarHash={userAvatarHash} /> : null}
|
|
1152
|
-
{isWebContextMessage ? (
|
|
1153
|
-
<p className="papyrus-console-message__context-label">Page context</p>
|
|
1154
|
-
) : null}
|
|
1155
|
-
{shouldShowThinking ? (
|
|
1156
|
-
<Shimmer className="papyrus-console__thinking-shimmer">Thinking...</Shimmer>
|
|
1157
|
-
) : <ConsoleChatMessageContent role={message.role} text={content || sanitizedSummary} />}
|
|
1158
|
-
</div>
|
|
1159
|
-
{message.responseStatus === "FAILED" ? <span className="papyrus-console-message__error">{message.responseError ?? "Responder failed"}</span> : null}
|
|
1160
|
-
{assistantAvatar ? (
|
|
1161
|
-
<span className="papyrus-console-message__assistant-avatar" data-tone={assistantAvatar.tone}>
|
|
1162
|
-
<VultusBotAvatar
|
|
1163
|
-
ariaLabel={assistantAvatar.ariaLabel}
|
|
1164
|
-
lightColor={assistantAvatar.tone === "error" ? ASSISTANT_AVATAR_ERROR_LIGHT_COLOR : ASSISTANT_AVATAR_DEFAULT_LIGHT_COLOR}
|
|
1165
|
-
neutralIdleMode={assistantAvatar.neutralIdleMode}
|
|
1166
|
-
shadowColor={assistantAvatar.tone === "error" ? ASSISTANT_AVATAR_ERROR_SHADOW_COLOR : ASSISTANT_AVATAR_DEFAULT_SHADOW_COLOR}
|
|
1167
|
-
size={ASSISTANT_AVATAR_SIZE_PX}
|
|
1168
|
-
state={assistantAvatar.state}
|
|
1169
|
-
transitionDurationSeconds={0.35}
|
|
1170
|
-
/>
|
|
1171
|
-
</span>
|
|
1172
|
-
) : null}
|
|
1173
|
-
</ConsoleChatMessageRow>
|
|
1174
|
-
);
|
|
1175
|
-
})}
|
|
1176
|
-
</ConversationContent>
|
|
1177
|
-
<ConversationScrollButton
|
|
1178
|
-
aria-label="Scroll to latest message"
|
|
1179
|
-
className="papyrus-console__scroll-bottom"
|
|
1180
|
-
/>
|
|
1181
|
-
</Conversation>
|
|
1182
|
-
{!loading && !sortedMessages.length ? (
|
|
1183
|
-
<div className="papyrus-console__suggestions papyrus-console-ui">
|
|
1184
|
-
<Suggestions>
|
|
1185
|
-
{CONSOLE_STARTER_SUGGESTIONS.map((suggestion) => (
|
|
1186
|
-
<Suggestion
|
|
1187
|
-
disabled={sending}
|
|
1188
|
-
key={suggestion}
|
|
1189
|
-
onClick={handleSuggestionClick}
|
|
1190
|
-
suggestion={suggestion}
|
|
1191
|
-
/>
|
|
1192
|
-
))}
|
|
1193
|
-
</Suggestions>
|
|
1194
|
-
</div>
|
|
1195
|
-
) : null}
|
|
1196
|
-
{error ? <p className="papyrus-console__error">{error}</p> : null}
|
|
1197
|
-
<PromptInput className="papyrus-console__composer" onSubmit={(message) => void sendMessage(message)}>
|
|
1198
|
-
<PromptInputBody>
|
|
1199
|
-
<PromptInputTextarea
|
|
1200
|
-
aria-label="Console message"
|
|
1201
|
-
disabled={sending}
|
|
1202
|
-
onChange={(event) => setDraft(event.target.value)}
|
|
1203
|
-
placeholder="Write a message..."
|
|
1204
|
-
rows={3}
|
|
1205
|
-
value={draft}
|
|
1206
|
-
/>
|
|
1207
|
-
</PromptInputBody>
|
|
1208
|
-
<PromptInputFooter>
|
|
1209
|
-
<PromptInputTools>
|
|
1210
|
-
<ModelSelector
|
|
1211
|
-
aria-label="Console model"
|
|
1212
|
-
disabled={sending}
|
|
1213
|
-
onValueChange={handleModelChange}
|
|
1214
|
-
options={CONSOLE_MODEL_OPTIONS.map((option) => ({
|
|
1215
|
-
value: option.value,
|
|
1216
|
-
label: option.label,
|
|
1217
|
-
}))}
|
|
1218
|
-
value={selectedModel}
|
|
1219
|
-
/>
|
|
1220
|
-
</PromptInputTools>
|
|
1221
|
-
<PromptInputSubmit
|
|
1222
|
-
disabled={!draft.trim() || sending}
|
|
1223
|
-
status={pending ? "streaming" : "ready"}
|
|
1224
|
-
/>
|
|
1225
|
-
</PromptInputFooter>
|
|
1226
|
-
</PromptInput>
|
|
1084
|
+
<PapyrusConsoleConversation
|
|
1085
|
+
messages={conversationMessages}
|
|
1086
|
+
isSending={sending}
|
|
1087
|
+
isRunning={pending}
|
|
1088
|
+
onSendMessage={submitMessage}
|
|
1089
|
+
error={error}
|
|
1090
|
+
emptyState={loading ? <p className="papyrus-console__empty">Loading conversation…</p> : (
|
|
1091
|
+
<div className="papyrus-console__suggestions papyrus-console-ui">
|
|
1092
|
+
<Suggestions>{CONSOLE_STARTER_SUGGESTIONS.map((suggestion) => (
|
|
1093
|
+
<Suggestion disabled={sending} key={suggestion} onClick={handleSuggestionClick} suggestion={suggestion} />
|
|
1094
|
+
))}</Suggestions>
|
|
1095
|
+
</div>
|
|
1096
|
+
)}
|
|
1097
|
+
composerActions={<div className="papyrus-console-ui"><ModelSelector
|
|
1098
|
+
aria-label="Console model" disabled={sending} onValueChange={handleModelChange}
|
|
1099
|
+
options={CONSOLE_MODEL_OPTIONS.map((option) => ({ value: option.value, label: option.label }))}
|
|
1100
|
+
value={selectedModel}
|
|
1101
|
+
/></div>}
|
|
1102
|
+
presentation={{
|
|
1103
|
+
header: (id) => {
|
|
1104
|
+
const entry = presentationEntries.get(id);
|
|
1105
|
+
if (entry?.kind !== "message" || entry.message.role !== "USER") return null;
|
|
1106
|
+
return <ConsoleUserAvatar authorEmail={readConsoleAuthorEmail(entry.message)} avatarHash={readConsoleAuthorAvatarHash(entry.message)} />;
|
|
1107
|
+
},
|
|
1108
|
+
body: (id, body) => {
|
|
1109
|
+
const entry = presentationEntries.get(id);
|
|
1110
|
+
return entry?.kind === "tool" ? <ConsoleToolMessageContent tool={entry.tool} /> : body;
|
|
1111
|
+
},
|
|
1112
|
+
footer: (id) => {
|
|
1113
|
+
const entry = presentationEntries.get(id);
|
|
1114
|
+
if (!entry || id !== latestVisibleAvatarAnchorId) return null;
|
|
1115
|
+
const avatar = resolveAssistantAvatarPresentation(entry.kind === "tool" ? entry.tool : entry.message, motionSetting === "low", pending);
|
|
1116
|
+
if (!avatar) return null;
|
|
1117
|
+
return <span className="papyrus-console-message__assistant-avatar" data-tone={avatar.tone}>
|
|
1118
|
+
<VultusBotAvatar ariaLabel={avatar.ariaLabel}
|
|
1119
|
+
lightColor={avatar.tone === "error" ? ASSISTANT_AVATAR_ERROR_LIGHT_COLOR : ASSISTANT_AVATAR_DEFAULT_LIGHT_COLOR}
|
|
1120
|
+
shadowColor={avatar.tone === "error" ? ASSISTANT_AVATAR_ERROR_SHADOW_COLOR : ASSISTANT_AVATAR_DEFAULT_SHADOW_COLOR}
|
|
1121
|
+
neutralIdleMode={avatar.neutralIdleMode} size={ASSISTANT_AVATAR_SIZE_PX}
|
|
1122
|
+
state={avatar.state} transitionDurationSeconds={0.35} />
|
|
1123
|
+
</span>;
|
|
1124
|
+
},
|
|
1125
|
+
}}
|
|
1126
|
+
/>
|
|
1227
1127
|
</div>
|
|
1228
1128
|
);
|
|
1229
1129
|
}
|
|
@@ -1516,30 +1416,6 @@ function ConsoleToolMessageContent({ tool }: { tool: ConsoleToolTimelineEntry })
|
|
|
1516
1416
|
);
|
|
1517
1417
|
}
|
|
1518
1418
|
|
|
1519
|
-
function ConsoleToolTimelineRow({ children }: { children: ReactNode }) {
|
|
1520
|
-
const rowRef = useRef<HTMLElement | null>(null);
|
|
1521
|
-
|
|
1522
|
-
useEffect(() => {
|
|
1523
|
-
const row = rowRef.current;
|
|
1524
|
-
if (!row || prefersReducedMotion()) return;
|
|
1525
|
-
const tween = gsap.fromTo(
|
|
1526
|
-
row,
|
|
1527
|
-
{ autoAlpha: 0, y: 8 },
|
|
1528
|
-
{ autoAlpha: 1, y: 0, duration: 0.16, ease: "power2.out", overwrite: "auto" },
|
|
1529
|
-
);
|
|
1530
|
-
return () => {
|
|
1531
|
-
tween.kill();
|
|
1532
|
-
gsap.killTweensOf(row);
|
|
1533
|
-
};
|
|
1534
|
-
}, []);
|
|
1535
|
-
|
|
1536
|
-
return (
|
|
1537
|
-
<article className="papyrus-console-tool-row" ref={rowRef}>
|
|
1538
|
-
{children}
|
|
1539
|
-
</article>
|
|
1540
|
-
);
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
1419
|
function formatToolOutputText(value: unknown): string {
|
|
1544
1420
|
if (typeof value === "string") return value;
|
|
1545
1421
|
if (value === undefined || value === null) return "";
|
|
@@ -1550,240 +1426,6 @@ function formatToolOutputText(value: unknown): string {
|
|
|
1550
1426
|
}
|
|
1551
1427
|
}
|
|
1552
1428
|
|
|
1553
|
-
function ConsoleChatMessageContent({ role, text }: { role: string | null | undefined; text: string }) {
|
|
1554
|
-
const blocks = useMemo(() => parseConsoleMarkdownBlocks(text), [text]);
|
|
1555
|
-
if (role !== "ASSISTANT") return text;
|
|
1556
|
-
return (
|
|
1557
|
-
<>
|
|
1558
|
-
{blocks.map((block, index) => renderConsoleMarkdownBlock(block, index))}
|
|
1559
|
-
</>
|
|
1560
|
-
);
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
type ConsoleMarkdownBlock =
|
|
1564
|
-
| { type: "code"; text: string }
|
|
1565
|
-
| { type: "heading"; depth: number; text: string }
|
|
1566
|
-
| { type: "list"; ordered: boolean; items: string[] }
|
|
1567
|
-
| { type: "paragraph"; text: string };
|
|
1568
|
-
|
|
1569
|
-
function parseConsoleMarkdownBlocks(text: string): ConsoleMarkdownBlock[] {
|
|
1570
|
-
const blocks: ConsoleMarkdownBlock[] = [];
|
|
1571
|
-
const lines = text.split(/\r?\n/);
|
|
1572
|
-
let paragraph: string[] = [];
|
|
1573
|
-
let list: string[] = [];
|
|
1574
|
-
let listOrdered = false;
|
|
1575
|
-
let code: string[] = [];
|
|
1576
|
-
let inCode = false;
|
|
1577
|
-
|
|
1578
|
-
const flushParagraph = () => {
|
|
1579
|
-
if (!paragraph.length) return;
|
|
1580
|
-
blocks.push({ type: "paragraph", text: paragraph.join(" ").trim() });
|
|
1581
|
-
paragraph = [];
|
|
1582
|
-
};
|
|
1583
|
-
const flushList = () => {
|
|
1584
|
-
if (!list.length) return;
|
|
1585
|
-
blocks.push({ type: "list", ordered: listOrdered, items: list });
|
|
1586
|
-
list = [];
|
|
1587
|
-
};
|
|
1588
|
-
|
|
1589
|
-
for (const line of lines) {
|
|
1590
|
-
if (line.trim().startsWith("```")) {
|
|
1591
|
-
if (inCode) {
|
|
1592
|
-
blocks.push({ type: "code", text: code.join("\n") });
|
|
1593
|
-
code = [];
|
|
1594
|
-
inCode = false;
|
|
1595
|
-
} else {
|
|
1596
|
-
flushParagraph();
|
|
1597
|
-
flushList();
|
|
1598
|
-
inCode = true;
|
|
1599
|
-
}
|
|
1600
|
-
continue;
|
|
1601
|
-
}
|
|
1602
|
-
if (inCode) {
|
|
1603
|
-
code.push(line);
|
|
1604
|
-
continue;
|
|
1605
|
-
}
|
|
1606
|
-
const trimmed = line.trim();
|
|
1607
|
-
if (!trimmed) {
|
|
1608
|
-
flushParagraph();
|
|
1609
|
-
flushList();
|
|
1610
|
-
continue;
|
|
1611
|
-
}
|
|
1612
|
-
const heading = /^(#{1,4})\s+(.+)$/.exec(trimmed);
|
|
1613
|
-
if (heading) {
|
|
1614
|
-
flushParagraph();
|
|
1615
|
-
flushList();
|
|
1616
|
-
blocks.push({ type: "heading", depth: heading[1].length, text: heading[2].trim() });
|
|
1617
|
-
continue;
|
|
1618
|
-
}
|
|
1619
|
-
const bullet = /^([-*+])\s+(.+)$/.exec(trimmed);
|
|
1620
|
-
if (bullet) {
|
|
1621
|
-
flushParagraph();
|
|
1622
|
-
if (!list.length) listOrdered = false;
|
|
1623
|
-
list.push(bullet[2].trim());
|
|
1624
|
-
continue;
|
|
1625
|
-
}
|
|
1626
|
-
const ordered = /^\d+[.)]\s+(.+)$/.exec(trimmed);
|
|
1627
|
-
if (ordered) {
|
|
1628
|
-
flushParagraph();
|
|
1629
|
-
if (!list.length) listOrdered = true;
|
|
1630
|
-
list.push(ordered[1].trim());
|
|
1631
|
-
continue;
|
|
1632
|
-
}
|
|
1633
|
-
flushList();
|
|
1634
|
-
paragraph.push(trimmed);
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
flushParagraph();
|
|
1638
|
-
flushList();
|
|
1639
|
-
if (code.length) blocks.push({ type: "code", text: code.join("\n") });
|
|
1640
|
-
return blocks;
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
function renderConsoleMarkdownBlock(block: ConsoleMarkdownBlock, index: number): ReactNode {
|
|
1644
|
-
const key = `${block.type}-${index}`;
|
|
1645
|
-
if (block.type === "code") return <pre className="papyrus-console-message__code" key={key}>{block.text}</pre>;
|
|
1646
|
-
if (block.type === "list") {
|
|
1647
|
-
if (block.ordered) {
|
|
1648
|
-
return (
|
|
1649
|
-
<ol key={key}>
|
|
1650
|
-
{block.items.map((item, itemIndex) => <li key={`${key}-${itemIndex}`}>{renderConsoleInlineMarkdown(item)}</li>)}
|
|
1651
|
-
</ol>
|
|
1652
|
-
);
|
|
1653
|
-
}
|
|
1654
|
-
return (
|
|
1655
|
-
<ul key={key}>
|
|
1656
|
-
{block.items.map((item, itemIndex) => <li key={`${key}-${itemIndex}`}>{renderConsoleInlineMarkdown(item)}</li>)}
|
|
1657
|
-
</ul>
|
|
1658
|
-
);
|
|
1659
|
-
}
|
|
1660
|
-
if (block.type === "heading") {
|
|
1661
|
-
if (block.depth <= 1) return <h3 key={key}>{renderConsoleInlineMarkdown(block.text)}</h3>;
|
|
1662
|
-
if (block.depth === 2) return <h4 key={key}>{renderConsoleInlineMarkdown(block.text)}</h4>;
|
|
1663
|
-
return <h5 key={key}>{renderConsoleInlineMarkdown(block.text)}</h5>;
|
|
1664
|
-
}
|
|
1665
|
-
return <p key={key}>{renderConsoleInlineMarkdown(block.text)}</p>;
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
function renderConsoleInlineMarkdown(text: string): ReactNode[] {
|
|
1669
|
-
const nodes: ReactNode[] = [];
|
|
1670
|
-
const pattern = /(`[^`]+`|\*\*[^*]+\*\*|\[[^\]]+\]\([^)]+\))/g;
|
|
1671
|
-
let lastIndex = 0;
|
|
1672
|
-
let match: RegExpExecArray | null;
|
|
1673
|
-
while ((match = pattern.exec(text)) !== null) {
|
|
1674
|
-
if (match.index > lastIndex) nodes.push(text.slice(lastIndex, match.index));
|
|
1675
|
-
const token = match[0];
|
|
1676
|
-
if (token.startsWith("`")) {
|
|
1677
|
-
nodes.push(<code key={`${match.index}-code`}>{token.slice(1, -1)}</code>);
|
|
1678
|
-
} else if (token.startsWith("**")) {
|
|
1679
|
-
nodes.push(<strong key={`${match.index}-strong`}>{token.slice(2, -2)}</strong>);
|
|
1680
|
-
} else {
|
|
1681
|
-
const linkMatch = /^\[([^\]]+)\]\(([^)]+)\)$/.exec(token);
|
|
1682
|
-
if (linkMatch) {
|
|
1683
|
-
nodes.push(
|
|
1684
|
-
<a href={linkMatch[2]} key={`${match.index}-link`} rel="noreferrer noopener" target="_blank">
|
|
1685
|
-
{linkMatch[1]}
|
|
1686
|
-
</a>,
|
|
1687
|
-
);
|
|
1688
|
-
} else {
|
|
1689
|
-
nodes.push(token);
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
lastIndex = match.index + token.length;
|
|
1693
|
-
}
|
|
1694
|
-
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
|
|
1695
|
-
return nodes;
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
function ConsoleChatMessageRow({
|
|
1699
|
-
role,
|
|
1700
|
-
children,
|
|
1701
|
-
}: {
|
|
1702
|
-
role: string | null | undefined;
|
|
1703
|
-
children: ReactNode;
|
|
1704
|
-
}) {
|
|
1705
|
-
const rowRef = useRef<HTMLElement | null>(null);
|
|
1706
|
-
|
|
1707
|
-
useEffect(() => {
|
|
1708
|
-
const row = rowRef.current;
|
|
1709
|
-
if (!row || prefersReducedMotion()) return;
|
|
1710
|
-
const tween = gsap.fromTo(
|
|
1711
|
-
row,
|
|
1712
|
-
{ autoAlpha: 0, y: 8 },
|
|
1713
|
-
{ autoAlpha: 1, y: 0, duration: 0.16, ease: "power2.out", overwrite: "auto" },
|
|
1714
|
-
);
|
|
1715
|
-
return () => {
|
|
1716
|
-
tween.kill();
|
|
1717
|
-
gsap.killTweensOf(row);
|
|
1718
|
-
};
|
|
1719
|
-
}, []);
|
|
1720
|
-
|
|
1721
|
-
return (
|
|
1722
|
-
<article
|
|
1723
|
-
className={
|
|
1724
|
-
role === "USER"
|
|
1725
|
-
? "papyrus-console-message papyrus-console-message--user"
|
|
1726
|
-
: role === "CONTEXT"
|
|
1727
|
-
? "papyrus-console-message papyrus-console-message--context"
|
|
1728
|
-
: "papyrus-console-message papyrus-console-message--assistant"
|
|
1729
|
-
}
|
|
1730
|
-
ref={rowRef}
|
|
1731
|
-
>
|
|
1732
|
-
{children}
|
|
1733
|
-
</article>
|
|
1734
|
-
);
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
function ConsoleStreamingAutoStick({ active, streamSignal }: { active: boolean; streamSignal: string | null }) {
|
|
1738
|
-
const { scrollRef, scrollToBottom } = useStickToBottomContext();
|
|
1739
|
-
const previousSignalRef = useRef<string | null>(null);
|
|
1740
|
-
|
|
1741
|
-
const snapToBottom = useCallback(() => {
|
|
1742
|
-
void scrollToBottom({ animation: "instant" });
|
|
1743
|
-
const scrollElement = scrollRef.current;
|
|
1744
|
-
if (!scrollElement) return;
|
|
1745
|
-
scrollElement.scrollTop = Math.max(0, scrollElement.scrollHeight - scrollElement.clientHeight);
|
|
1746
|
-
}, [scrollRef, scrollToBottom]);
|
|
1747
|
-
|
|
1748
|
-
useEffect(() => {
|
|
1749
|
-
if (!active || !streamSignal) return;
|
|
1750
|
-
if (previousSignalRef.current === streamSignal) return;
|
|
1751
|
-
previousSignalRef.current = streamSignal;
|
|
1752
|
-
|
|
1753
|
-
snapToBottom();
|
|
1754
|
-
const frame = window.requestAnimationFrame(() => {
|
|
1755
|
-
snapToBottom();
|
|
1756
|
-
});
|
|
1757
|
-
return () => {
|
|
1758
|
-
window.cancelAnimationFrame(frame);
|
|
1759
|
-
};
|
|
1760
|
-
}, [active, snapToBottom, streamSignal]);
|
|
1761
|
-
|
|
1762
|
-
useEffect(() => {
|
|
1763
|
-
if (!active) return;
|
|
1764
|
-
let frame = 0;
|
|
1765
|
-
const pin = () => {
|
|
1766
|
-
snapToBottom();
|
|
1767
|
-
frame = window.requestAnimationFrame(pin);
|
|
1768
|
-
};
|
|
1769
|
-
pin();
|
|
1770
|
-
return () => {
|
|
1771
|
-
window.cancelAnimationFrame(frame);
|
|
1772
|
-
};
|
|
1773
|
-
}, [active, snapToBottom]);
|
|
1774
|
-
|
|
1775
|
-
useEffect(() => {
|
|
1776
|
-
if (!active) previousSignalRef.current = null;
|
|
1777
|
-
}, [active]);
|
|
1778
|
-
|
|
1779
|
-
return null;
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
function prefersReducedMotion(): boolean {
|
|
1783
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
1784
|
-
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
1429
|
function formatConsoleThreadOption(thread: ConsoleChatThread): string {
|
|
1788
1430
|
const stamp = thread.lastMessageAt ?? thread.updatedAt ?? thread.createdAt;
|
|
1789
1431
|
const date = stamp ? new Date(stamp) : null;
|