@aglyn/plugins-ai 1.0.0-beta.293 → 1.0.0-beta.296
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/package.json +17 -17
- package/src/lib/components/ai-job-links.d.ts +15 -2
- package/src/lib/components/ai-job-links.js +24 -3
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +9 -0
- package/src/lib/components/ai-job-requests.js +34 -0
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +28 -2
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.d.ts +7 -1
- package/src/lib/components/assist-jobs-drawer.component.js +20 -6
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-page-change-card.component.d.ts +44 -0
- package/src/lib/components/assist-page-change-card.component.js +166 -0
- package/src/lib/components/assist-page-change-card.component.js.map +1 -0
- package/src/lib/components/assist-panel.component.d.ts +105 -1
- package/src/lib/components/assist-panel.component.js +224 -29
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/jobs/ai-job-edit-step.d.ts +62 -16
- package/src/lib/jobs/ai-job-edit-step.js +172 -61
- package/src/lib/jobs/ai-job-edit-step.js.map +1 -1
- package/src/lib/jobs/ai-job-navigation-step.js +4 -2
- package/src/lib/jobs/ai-job-navigation-step.js.map +1 -1
- package/src/lib/model/ai-edit-job.d.ts +33 -16
- package/src/lib/model/ai-edit-job.js +50 -34
- package/src/lib/model/ai-edit-job.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +6 -11
- package/src/lib/model/ai-job-activity.js +8 -15
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +6 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-site-inventory.d.ts +6 -0
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/assist-edit.d.ts +18 -1
- package/src/lib/model/assist-edit.js +7 -0
- package/src/lib/model/assist-edit.js.map +1 -1
- package/src/lib/model/assist-page-change.d.ts +86 -0
- package/src/lib/model/assist-page-change.js +86 -0
- package/src/lib/model/assist-page-change.js.map +1 -0
- package/src/lib/runtime/ai-eval.js +3 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-node-tree.d.ts +5 -0
- package/src/lib/runtime/ai-node-tree.js +18 -3
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/site-inventory.d.ts +8 -1
- package/src/lib/runtime/site-inventory.js +18 -0
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/assist-build.js +2 -0
- package/src/lib/server/assist-build.js.map +1 -1
- package/src/lib/server/assist-chat.js +51 -2
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit.d.ts +54 -2
- package/src/lib/server/assist-edit.js +219 -30
- package/src/lib/server/assist-edit.js.map +1 -1
- package/src/lib/server/assist-page-change.d.ts +62 -0
- package/src/lib/server/assist-page-change.js +140 -0
- package/src/lib/server/assist-page-change.js.map +1 -0
|
@@ -25,7 +25,7 @@ import { AppLink, MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
|
25
25
|
import { BetaChip } from "@aglyn/shared-ui-jsx/components/beta-chip.component";
|
|
26
26
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
27
27
|
import { Alert, Badge, Box, Button, Chip, CircularProgress, Collapse, Drawer, Fab, IconButton, Link, Paper, Stack, TextField, Tooltip, Typography } from "@mui/material";
|
|
28
|
-
import { usePathname } from "next/navigation";
|
|
28
|
+
import { usePathname, useRouter } from "next/navigation";
|
|
29
29
|
import { Fragment, memo, useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
30
30
|
import { useUser } from "@aglyn/tenant-feature-instance";
|
|
31
31
|
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
@@ -35,11 +35,14 @@ import { AssistEditCard } from "./assist-edit-card.component.js";
|
|
|
35
35
|
import { AssistBuildCard } from "./assist-build-card.component.js";
|
|
36
36
|
import { isAssistBuildProposal } from "../model/assist-build.js";
|
|
37
37
|
import { assistBuildDraftLinks, isAssistFollowUpDue, isLiveAssistFollowUp } from "../model/assist-follow-up.js";
|
|
38
|
-
import { startAssistBuildRequest } from "./ai-job-requests.js";
|
|
38
|
+
import { cancelAiJobRequest, startAssistBuildRequest, startAssistPageChangeRequest } from "./ai-job-requests.js";
|
|
39
|
+
import { assistPageChangeDraftVersion, isAssistPageChangeProposal } from "../model/assist-page-change.js";
|
|
40
|
+
import { AssistPageChangeCard } from "./assist-page-change-card.component.js";
|
|
39
41
|
import { aiInsightSurfaceForPath } from "../model/ai-insight.js";
|
|
40
42
|
import { AssistJobsDrawer } from "./assist-jobs-drawer.component.js";
|
|
41
43
|
import { AI_JOB_LINK_PARAM, openAiJobs, useAiJobsInFlight, useAiJobsOpenRequest } from "./ai-jobs-store.js";
|
|
42
|
-
import { aiJobsActivity, aiJobsLauncherLabel } from "../model/ai-job-activity.js";
|
|
44
|
+
import { aiJobPhase, aiJobsActivity, aiJobsLauncherLabel } from "../model/ai-job-activity.js";
|
|
45
|
+
import { AI_SITE_BUILD_HREF } from "../model/ai-job-notice.js";
|
|
43
46
|
import { AiModelSelector } from "./ai-model-selector.component.js";
|
|
44
47
|
import { AiUsageStrip } from "./ai-usage-strip.component.js";
|
|
45
48
|
import { useAiModelChoice } from "./use-ai-model-choice.js";
|
|
@@ -80,28 +83,69 @@ function saveFollowUp(orgId, followUp) {
|
|
|
80
83
|
] : []).reverse();
|
|
81
84
|
return assistBuildDraftLinks(jobs, orgSlug);
|
|
82
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* The builds in a thread whose plan is still waiting on the person
|
|
88
|
+
* (AGL-4116): queued, being planned, or planned and not yet confirmed. A new
|
|
89
|
+
* plan in the same thread replaces them, so a conversation that keeps
|
|
90
|
+
* changing a plan holds one plan, not one per message.
|
|
91
|
+
*/ export function assistUnconfirmedBuilds(messages) {
|
|
92
|
+
return messages.flatMap((message, index)=>{
|
|
93
|
+
const job = message.build && !message.buildReplaced ? message.buildJob : null;
|
|
94
|
+
if (!job) return [];
|
|
95
|
+
const phase = aiJobPhase(job);
|
|
96
|
+
return phase === 'queued' || phase === 'planning' || phase === 'plan-ready' ? [
|
|
97
|
+
{
|
|
98
|
+
index,
|
|
99
|
+
jobId: job.id
|
|
100
|
+
}
|
|
101
|
+
] : [];
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/** What a plan a later one replaced says in its card's place (AGL-4116). */ export const ASSIST_BUILD_REPLACED_COPY = 'Replaced by the updated plan below. Nothing from this one was built.';
|
|
83
105
|
/** What a stored build that never got its job says, rather than planning forever. */ const BUILD_NOT_STARTED = 'This build was not started. Ask again to plan it.';
|
|
106
|
+
/** What a stored page change that never got its job says (AGL-4116). */ const PAGE_CHANGE_NOT_STARTED = 'This change was not started. Ask again to make it.';
|
|
84
107
|
const HISTORY_TURNS_SENT = 12;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
108
|
+
/**
|
|
109
|
+
* The thread a question belongs to: one per site, and one for the
|
|
110
|
+
* workspace's own pages (AGL-4116). A workspace-wide thread carried one
|
|
111
|
+
* site's request into a build on another site, because the history that
|
|
112
|
+
* rides each question is the thread's last turns.
|
|
113
|
+
*/ export function assistThreadKey(orgId, hostId) {
|
|
114
|
+
return hostId ? `aglyn-assist:${orgId}:${hostId}` : `aglyn-assist:${orgId}`;
|
|
115
|
+
}
|
|
116
|
+
const NO_MESSAGES = [];
|
|
117
|
+
/**
|
|
118
|
+
* The AI jobs page of the site a console page is on (AGL-4116), or `null`
|
|
119
|
+
* off a site: `/{orgSlug}/hosts/{site}/ai-jobs`, the site named as the
|
|
120
|
+
* address names it.
|
|
121
|
+
*/ export function assistSiteAiJobsHref(pathname, orgSlug, hostId) {
|
|
122
|
+
if (!pathname || !orgSlug || !hostId) return null;
|
|
123
|
+
const parts = pathname.split('/').filter(Boolean);
|
|
124
|
+
const site = parts[0] === orgSlug && parts[1] === 'hosts' ? parts[2] : undefined;
|
|
125
|
+
return site ? `/${orgSlug}/hosts/${site}${AI_SITE_BUILD_HREF}` : null;
|
|
126
|
+
}
|
|
127
|
+
function loadThread(key) {
|
|
128
|
+
if (typeof sessionStorage === 'undefined') return [];
|
|
88
129
|
try {
|
|
89
|
-
const raw = sessionStorage.getItem(
|
|
130
|
+
const raw = sessionStorage.getItem(key);
|
|
90
131
|
const parsed = raw ? JSON.parse(raw) : [];
|
|
91
132
|
if (!Array.isArray(parsed)) return [];
|
|
92
133
|
// A build whose job was still being created when the page went away is
|
|
93
134
|
// not started again from a stored thread (AGL-3616): it says so instead.
|
|
135
|
+
// A page change likewise (AGL-4116).
|
|
94
136
|
return parsed.map((message)=>message.build && !message.buildJob && !message.buildNotice ? _extends({}, message, {
|
|
95
137
|
buildNotice: BUILD_NOT_STARTED
|
|
138
|
+
}) : message.pageChange && !message.pageChangeJob && !message.pageChangeNotice ? _extends({}, message, {
|
|
139
|
+
pageChangeNotice: PAGE_CHANGE_NOT_STARTED
|
|
96
140
|
}) : message);
|
|
97
141
|
} catch (unused) {
|
|
98
142
|
return [];
|
|
99
143
|
}
|
|
100
144
|
}
|
|
101
|
-
function saveThread(
|
|
102
|
-
if (
|
|
145
|
+
function saveThread(key, messages) {
|
|
146
|
+
if (typeof sessionStorage === 'undefined') return;
|
|
103
147
|
try {
|
|
104
|
-
sessionStorage.setItem(
|
|
148
|
+
sessionStorage.setItem(key, JSON.stringify(messages));
|
|
105
149
|
} catch (unused) {
|
|
106
150
|
// Quota/full storage never breaks chat.
|
|
107
151
|
}
|
|
@@ -523,7 +567,25 @@ export function AssistPanelComponent(props) {
|
|
|
523
567
|
const { enqueueSnackbar } = useSnackbar();
|
|
524
568
|
const [open, setOpen] = useState(false);
|
|
525
569
|
const [busy, setBusy] = useState(false);
|
|
526
|
-
|
|
570
|
+
// Every thread this panel has opened, by its key (AGL-4116): the one on
|
|
571
|
+
// screen is this site's, and an answer still streaming lands in the thread
|
|
572
|
+
// it was asked in, even after the page moved to another site.
|
|
573
|
+
const threadKey = scopedOrgId ? assistThreadKey(scopedOrgId, hostId) : null;
|
|
574
|
+
const [threads, setThreads] = useState({});
|
|
575
|
+
const messages = threadKey && threads[threadKey] || NO_MESSAGES;
|
|
576
|
+
const updateThread = useCallback((key, update)=>{
|
|
577
|
+
if (!key) return;
|
|
578
|
+
setThreads((prior)=>{
|
|
579
|
+
var _prior_key;
|
|
580
|
+
return _extends({}, prior, {
|
|
581
|
+
[key]: update((_prior_key = prior[key]) != null ? _prior_key : loadThread(key))
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
}, []);
|
|
585
|
+
const setMessages = useCallback((update)=>updateThread(threadKey, update), [
|
|
586
|
+
threadKey,
|
|
587
|
+
updateThread
|
|
588
|
+
]);
|
|
527
589
|
const [quota, setQuota] = useState(null);
|
|
528
590
|
const scrollRef = useRef(null);
|
|
529
591
|
// The model switch and the usage strip (AGL-2942): the pick rides the
|
|
@@ -540,22 +602,40 @@ export function AssistPanelComponent(props) {
|
|
|
540
602
|
// the one open.
|
|
541
603
|
const editorRevision = useSyncExternalStore(subscribeEditorSessions, editorSessionsRevision, editorSessionsRevision);
|
|
542
604
|
const editorSession = openEditorSession();
|
|
543
|
-
//
|
|
544
|
-
// Keyed on the SCOPED org (AGL-1934): gating only
|
|
545
|
-
// load — and, on the next keystroke, save — a
|
|
546
|
-
// org's key on every mount of a page that named
|
|
605
|
+
// A thread per site, session-persisted; moving to another site or org
|
|
606
|
+
// swaps threads (AGL-4116). Keyed on the SCOPED org (AGL-1934): gating only
|
|
607
|
+
// the render would still load — and, on the next keystroke, save — a
|
|
608
|
+
// thread under the fallback org's key on every mount of a page that named
|
|
609
|
+
// no workspace.
|
|
610
|
+
useEffect(()=>{
|
|
611
|
+
if (!threadKey) return;
|
|
612
|
+
setThreads((prior)=>threadKey in prior ? prior : _extends({}, prior, {
|
|
613
|
+
[threadKey]: loadThread(threadKey)
|
|
614
|
+
}));
|
|
615
|
+
}, [
|
|
616
|
+
threadKey
|
|
617
|
+
]);
|
|
547
618
|
useEffect(()=>{
|
|
548
|
-
setMessages(loadThread(scopedOrgId));
|
|
549
619
|
setQuota(null);
|
|
550
620
|
}, [
|
|
551
621
|
scopedOrgId
|
|
552
622
|
]);
|
|
623
|
+
// Each thread is written back when it changes, the one on screen or one an
|
|
624
|
+
// answer is still streaming into.
|
|
625
|
+
const savedThreads = useRef({});
|
|
626
|
+
useEffect(()=>{
|
|
627
|
+
for (const [key, list] of Object.entries(threads)){
|
|
628
|
+
if (savedThreads.current[key] === list) continue;
|
|
629
|
+
savedThreads.current[key] = list;
|
|
630
|
+
saveThread(key, list);
|
|
631
|
+
}
|
|
632
|
+
}, [
|
|
633
|
+
threads
|
|
634
|
+
]);
|
|
553
635
|
useEffect(()=>{
|
|
554
|
-
saveThread(scopedOrgId, messages);
|
|
555
636
|
const node = scrollRef.current;
|
|
556
637
|
if (node) node.scrollTop = node.scrollHeight;
|
|
557
638
|
}, [
|
|
558
|
-
scopedOrgId,
|
|
559
639
|
messages
|
|
560
640
|
]);
|
|
561
641
|
const entitled = orgReady && checkEntitlement(org, 'aiAssist');
|
|
@@ -574,10 +654,16 @@ export function AssistPanelComponent(props) {
|
|
|
574
654
|
// jobs that spend between chat messages move it (AGL-3722).
|
|
575
655
|
useEffect(()=>followAiJobsForUsageMeter(), []);
|
|
576
656
|
const jobsActivity = aiJobsActivity(inFlight);
|
|
657
|
+
// On a site, its jobs are read on its AI jobs page (AGL-4116): the list in
|
|
658
|
+
// the chat was too cramped to read, and opening it scrolled the chat to
|
|
659
|
+
// its top.
|
|
660
|
+
const siteJobsHref = assistSiteAiJobsHref(pathname, orgSlug, hostId);
|
|
661
|
+
const router = useRouter();
|
|
577
662
|
// Any surface asking to open AI jobs — an "Open AI jobs" button, the
|
|
578
|
-
// indicator, a notification's link —
|
|
579
|
-
//
|
|
580
|
-
//
|
|
663
|
+
// indicator, a notification's link — goes to the site's AI jobs page, on
|
|
664
|
+
// the job it names. Off a site, it opens the panel on its drawer, and the
|
|
665
|
+
// request is handed to the drawer until the panel closes, so opening the
|
|
666
|
+
// panel again later does not re-highlight a job asked for before.
|
|
581
667
|
const openRequest = useAiJobsOpenRequest();
|
|
582
668
|
const [jobsFocus, setJobsFocus] = useState(null);
|
|
583
669
|
// The request outlives the panel: it is module state, and a page in
|
|
@@ -587,6 +673,11 @@ export function AssistPanelComponent(props) {
|
|
|
587
673
|
useEffect(()=>{
|
|
588
674
|
if (openRequest.seq <= answeredSeq.current) return;
|
|
589
675
|
answeredSeq.current = openRequest.seq;
|
|
676
|
+
if (siteJobsHref) {
|
|
677
|
+
setOpen(false);
|
|
678
|
+
router.push(openRequest.jobId ? `${siteJobsHref}/${encodeURIComponent(openRequest.jobId)}` : siteJobsHref);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
590
681
|
setJobsFocus(openRequest);
|
|
591
682
|
setOpen(true);
|
|
592
683
|
// A new `seq` is a new request; the object is read as it stands.
|
|
@@ -620,11 +711,14 @@ export function AssistPanelComponent(props) {
|
|
|
620
711
|
// The drafts this thread's builds made (AGL-3616): the chat sees refs and
|
|
621
712
|
// labels; the addresses stay here, for the card a follow-up raises.
|
|
622
713
|
const draftLinks = hostId ? assistThreadDraftLinks(messages, orgSlug) : [];
|
|
714
|
+
// Only this site's turns ride the question (AGL-4116), and the answer
|
|
715
|
+
// lands in the thread it was asked in.
|
|
623
716
|
const history = messages.slice(-HISTORY_TURNS_SENT).map(({ role, text })=>({
|
|
624
717
|
role,
|
|
625
718
|
text
|
|
626
719
|
}));
|
|
627
|
-
|
|
720
|
+
const askedIn = threadKey;
|
|
721
|
+
updateThread(askedIn, (prior)=>[
|
|
628
722
|
...prior,
|
|
629
723
|
{
|
|
630
724
|
role: 'user',
|
|
@@ -636,7 +730,7 @@ export function AssistPanelComponent(props) {
|
|
|
636
730
|
}
|
|
637
731
|
]);
|
|
638
732
|
const patchAnswer = (patch)=>{
|
|
639
|
-
|
|
733
|
+
updateThread(askedIn, (prior)=>{
|
|
640
734
|
const next = [
|
|
641
735
|
...prior
|
|
642
736
|
];
|
|
@@ -655,6 +749,7 @@ export function AssistPanelComponent(props) {
|
|
|
655
749
|
}));
|
|
656
750
|
};
|
|
657
751
|
/** The build this turn proposed, started once its stream ends (AGL-3616). */ let proposedBuild = null;
|
|
752
|
+
/** The page change this turn asked for, started once its stream ends (AGL-4116). */ let proposedChange = null;
|
|
658
753
|
try {
|
|
659
754
|
var _ref;
|
|
660
755
|
// The canvas outline rides along from the besigner document the open
|
|
@@ -782,6 +877,9 @@ export function AssistPanelComponent(props) {
|
|
|
782
877
|
// The build proposal (AGL-3616), shape-checked before it can start a job.
|
|
783
878
|
const build = isAssistBuildProposal(event.build) ? event.build : null;
|
|
784
879
|
proposedBuild = build;
|
|
880
|
+
// The page change (AGL-4116), shape-checked before it can start a job.
|
|
881
|
+
const pageChange = isAssistPageChangeProposal(event.pageChange) ? event.pageChange : null;
|
|
882
|
+
proposedChange = pageChange;
|
|
785
883
|
patchAnswer((message)=>{
|
|
786
884
|
var _event_exchangeId;
|
|
787
885
|
return _extends({}, message, {
|
|
@@ -793,6 +891,10 @@ export function AssistPanelComponent(props) {
|
|
|
793
891
|
build,
|
|
794
892
|
buildJob: null,
|
|
795
893
|
buildNotice: null
|
|
894
|
+
} : {}, pageChange ? {
|
|
895
|
+
pageChange,
|
|
896
|
+
pageChangeJob: null,
|
|
897
|
+
pageChangeNotice: null
|
|
796
898
|
} : {});
|
|
797
899
|
});
|
|
798
900
|
if (event.quota) setQuota(event.quota);
|
|
@@ -816,6 +918,11 @@ export function AssistPanelComponent(props) {
|
|
|
816
918
|
action: build.id
|
|
817
919
|
});
|
|
818
920
|
}
|
|
921
|
+
if (pageChange) {
|
|
922
|
+
trackEvent('assistant_proposal_shown', {
|
|
923
|
+
action: pageChange.id
|
|
924
|
+
});
|
|
925
|
+
}
|
|
819
926
|
} else if (event.type === 'error') {
|
|
820
927
|
var _event_error;
|
|
821
928
|
failAnswer(String((_event_error = event.error) != null ? _event_error : 'The assistant stream failed.'));
|
|
@@ -825,6 +932,21 @@ export function AssistPanelComponent(props) {
|
|
|
825
932
|
// One request, one build (AGL-3616): the job is created here, once,
|
|
826
933
|
// through the job door, and plans inline; its card shows the plan.
|
|
827
934
|
if (proposedBuild) {
|
|
935
|
+
// One plan per conversation (AGL-4116): a plan this thread proposed
|
|
936
|
+
// and the person has not confirmed is replaced by the new one, which
|
|
937
|
+
// the chat wrote from the whole request as it now stands. Its job is
|
|
938
|
+
// canceled, so it never builds and never warns another door.
|
|
939
|
+
for (const replaced of assistUnconfirmedBuilds(messages)){
|
|
940
|
+
updateThread(askedIn, (prior)=>prior.map((entry, i)=>i === replaced.index ? _extends({}, entry, {
|
|
941
|
+
buildReplaced: true
|
|
942
|
+
}) : entry));
|
|
943
|
+
void cancelAiJobRequest(user, scopedOrgId, replaced.jobId).then(({ job })=>{
|
|
944
|
+
if (!job) return;
|
|
945
|
+
updateThread(askedIn, (prior)=>prior.map((entry, i)=>i === replaced.index ? _extends({}, entry, {
|
|
946
|
+
buildJob: job
|
|
947
|
+
}) : entry));
|
|
948
|
+
});
|
|
949
|
+
}
|
|
828
950
|
const decision = await startAssistBuildRequest(user, scopedOrgId, proposedBuild);
|
|
829
951
|
// The plan the door ran inline has just spent (AGL-3722), so the strip
|
|
830
952
|
// moves with it, to the figure the plan card quotes. Left to the jobs
|
|
@@ -843,6 +965,17 @@ export function AssistPanelComponent(props) {
|
|
|
843
965
|
buildNotice: decision.error
|
|
844
966
|
}));
|
|
845
967
|
}
|
|
968
|
+
// A change to a page (AGL-4116): its edit job starts here, once, with
|
|
969
|
+
// no plan to confirm. A follow-up to a page this conversation already
|
|
970
|
+
// changed starts from the draft that change made.
|
|
971
|
+
if (proposedChange) {
|
|
972
|
+
const decision = await startAssistPageChangeRequest(user, scopedOrgId, proposedChange, assistPageChangeDraftVersion(messages, proposedChange.pageId));
|
|
973
|
+
publishMeter(decision.meter);
|
|
974
|
+
patchAnswer((message)=>_extends({}, message, {
|
|
975
|
+
pageChangeJob: decision.job,
|
|
976
|
+
pageChangeNotice: decision.error
|
|
977
|
+
}));
|
|
978
|
+
}
|
|
846
979
|
} catch (error) {
|
|
847
980
|
console.error(error);
|
|
848
981
|
failAnswer('The assistant request failed — check your connection and try again.');
|
|
@@ -861,6 +994,8 @@ export function AssistPanelComponent(props) {
|
|
|
861
994
|
scopedOrgId,
|
|
862
995
|
orgSlug,
|
|
863
996
|
pathname,
|
|
997
|
+
threadKey,
|
|
998
|
+
updateThread,
|
|
864
999
|
user
|
|
865
1000
|
]);
|
|
866
1001
|
// A follow-up waiting for its draft's canvas (AGL-3616): kept per org, so a
|
|
@@ -924,7 +1059,9 @@ export function AssistPanelComponent(props) {
|
|
|
924
1059
|
setMessages((prior)=>prior.map((entry, i)=>i === index ? _extends({}, entry, {
|
|
925
1060
|
proposalResolved: true
|
|
926
1061
|
}) : entry));
|
|
927
|
-
}, [
|
|
1062
|
+
}, [
|
|
1063
|
+
setMessages
|
|
1064
|
+
]);
|
|
928
1065
|
/**
|
|
929
1066
|
* Land a confirmed edit on the open editor (AGL-2906): through the canvas's
|
|
930
1067
|
* own mutators, as one undo step, unsaved. The only request that follows is
|
|
@@ -979,18 +1116,52 @@ export function AssistPanelComponent(props) {
|
|
|
979
1116
|
enqueueSnackbar,
|
|
980
1117
|
messages,
|
|
981
1118
|
scopedOrgId,
|
|
1119
|
+
setMessages,
|
|
982
1120
|
user
|
|
983
1121
|
]);
|
|
984
1122
|
/** Keep what a build card learned about its job on its own message (AGL-3616). */ const patchBuild = useCallback((index, patch)=>{
|
|
985
1123
|
setMessages((prior)=>prior.map((entry, i)=>i === index ? _extends({}, entry, patch) : entry));
|
|
986
|
-
}, [
|
|
1124
|
+
}, [
|
|
1125
|
+
setMessages
|
|
1126
|
+
]);
|
|
1127
|
+
/** Keep what a page change card learned about its job on its own message (AGL-4116). */ const patchPageChange = useCallback((index, patch)=>{
|
|
1128
|
+
setMessages((prior)=>prior.map((entry, i)=>i === index ? _extends({}, entry, patch) : entry));
|
|
1129
|
+
}, [
|
|
1130
|
+
setMessages
|
|
1131
|
+
]);
|
|
1132
|
+
/** Start a page change again after it failed: a new edit job, from the same draft. */ const [retryingChange, setRetryingChange] = useState(null);
|
|
1133
|
+
const retryPageChange = useCallback(async (index)=>{
|
|
1134
|
+
var _messages_index;
|
|
1135
|
+
const proposal = (_messages_index = messages[index]) == null ? void 0 : _messages_index.pageChange;
|
|
1136
|
+
if (!proposal || !scopedOrgId) return;
|
|
1137
|
+
setRetryingChange(index);
|
|
1138
|
+
patchPageChange(index, {
|
|
1139
|
+
pageChangeJob: null,
|
|
1140
|
+
pageChangeNotice: null
|
|
1141
|
+
});
|
|
1142
|
+
const decision = await startAssistPageChangeRequest(user, scopedOrgId, proposal, assistPageChangeDraftVersion(messages.slice(0, index), proposal.pageId));
|
|
1143
|
+
publishMeter(decision.meter);
|
|
1144
|
+
patchPageChange(index, {
|
|
1145
|
+
pageChangeJob: decision.job,
|
|
1146
|
+
pageChangeNotice: decision.error
|
|
1147
|
+
});
|
|
1148
|
+
setRetryingChange(null);
|
|
1149
|
+
}, [
|
|
1150
|
+
messages,
|
|
1151
|
+
patchPageChange,
|
|
1152
|
+
publishMeter,
|
|
1153
|
+
scopedOrgId,
|
|
1154
|
+
user
|
|
1155
|
+
]);
|
|
987
1156
|
/** Wave an edit card away. Nothing to tell the server: nothing happened. */ const dismissEdit = useCallback((index)=>{
|
|
988
1157
|
setMessages((prior)=>prior.map((entry, i)=>i === index ? _extends({}, entry, {
|
|
989
1158
|
editOutcome: {
|
|
990
1159
|
kind: 'dismissed'
|
|
991
1160
|
}
|
|
992
1161
|
}) : entry));
|
|
993
|
-
}, [
|
|
1162
|
+
}, [
|
|
1163
|
+
setMessages
|
|
1164
|
+
]);
|
|
994
1165
|
const sendFeedback = useCallback(async (index, feedback)=>{
|
|
995
1166
|
const message = messages[index];
|
|
996
1167
|
if (!(message == null ? void 0 : message.exchangeId) || !scopedOrgId) return;
|
|
@@ -1022,6 +1193,7 @@ export function AssistPanelComponent(props) {
|
|
|
1022
1193
|
enqueueSnackbar,
|
|
1023
1194
|
messages,
|
|
1024
1195
|
scopedOrgId,
|
|
1196
|
+
setMessages,
|
|
1025
1197
|
user
|
|
1026
1198
|
]);
|
|
1027
1199
|
// A released-off feature does not exist (staff preview excepted) — and
|
|
@@ -1163,7 +1335,8 @@ export function AssistPanelComponent(props) {
|
|
|
1163
1335
|
// the jobs door where the member may not generate.
|
|
1164
1336
|
insight: ai.generate ? aiInsightSurfaceForPath(pathname) : null,
|
|
1165
1337
|
inFlight: jobsVisible ? inFlight : undefined,
|
|
1166
|
-
focus: jobsFocus
|
|
1338
|
+
focus: jobsFocus,
|
|
1339
|
+
pageHref: siteJobsHref
|
|
1167
1340
|
}),
|
|
1168
1341
|
!messages.length && /*#__PURE__*/ _jsxs(Alert, {
|
|
1169
1342
|
severity: "info",
|
|
@@ -1189,7 +1362,7 @@ export function AssistPanelComponent(props) {
|
|
|
1189
1362
|
/*#__PURE__*/ _jsx(Stack, {
|
|
1190
1363
|
spacing: 1.5,
|
|
1191
1364
|
children: messages.map((message, index)=>{
|
|
1192
|
-
var _message_buildJob, _message_buildNotice;
|
|
1365
|
+
var _message_pageChangeJob, _message_pageChangeNotice, _message_buildJob, _message_buildNotice;
|
|
1193
1366
|
var _message_editOutcome, _message_editOutcome1, _message_editOutcome2, _message_editOutcome3;
|
|
1194
1367
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
1195
1368
|
sx: {
|
|
@@ -1249,7 +1422,29 @@ export function AssistPanelComponent(props) {
|
|
|
1249
1422
|
},
|
|
1250
1423
|
onDismiss: ()=>dismissEdit(index)
|
|
1251
1424
|
}),
|
|
1252
|
-
message.role === 'assistant' && message.
|
|
1425
|
+
message.role === 'assistant' && message.pageChange && scopedOrgId && /*#__PURE__*/ _jsx(AssistPageChangeCard, {
|
|
1426
|
+
proposal: message.pageChange,
|
|
1427
|
+
job: (_message_pageChangeJob = message.pageChangeJob) != null ? _message_pageChangeJob : null,
|
|
1428
|
+
notice: (_message_pageChangeNotice = message.pageChangeNotice) != null ? _message_pageChangeNotice : null,
|
|
1429
|
+
orgId: scopedOrgId,
|
|
1430
|
+
orgSlug: orgSlug,
|
|
1431
|
+
user: user,
|
|
1432
|
+
busy: retryingChange === index,
|
|
1433
|
+
onJob: (job)=>patchPageChange(index, {
|
|
1434
|
+
pageChangeJob: job
|
|
1435
|
+
}),
|
|
1436
|
+
onRetry: ()=>void retryPageChange(index)
|
|
1437
|
+
}),
|
|
1438
|
+
message.role === 'assistant' && message.build && message.buildReplaced && /*#__PURE__*/ _jsx(Typography, {
|
|
1439
|
+
variant: "caption",
|
|
1440
|
+
color: "text.secondary",
|
|
1441
|
+
component: "div",
|
|
1442
|
+
sx: {
|
|
1443
|
+
mt: 0.5
|
|
1444
|
+
},
|
|
1445
|
+
children: ASSIST_BUILD_REPLACED_COPY
|
|
1446
|
+
}),
|
|
1447
|
+
message.role === 'assistant' && message.build && !message.buildReplaced && scopedOrgId && /*#__PURE__*/ _jsx(AssistBuildCard, {
|
|
1253
1448
|
proposal: message.build,
|
|
1254
1449
|
job: (_message_buildJob = message.buildJob) != null ? _message_buildJob : null,
|
|
1255
1450
|
notice: (_message_buildNotice = message.buildNotice) != null ? _message_buildNotice : null,
|