@aglyn/plugins-ai 1.0.0-beta.226 → 1.0.0-beta.227
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 +2 -1
- package/package.json +17 -17
- package/src/lib/billing/ai-month-meter.js +5 -3
- package/src/lib/billing/ai-month-meter.js.map +1 -1
- package/src/lib/billing/ai-overage-ledger.js +3 -1
- package/src/lib/billing/ai-overage-ledger.js.map +1 -1
- package/src/lib/components/ai-brief-dialog.component.d.ts +6 -2
- package/src/lib/components/ai-brief-dialog.component.js +35 -17
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-describe-automation.component.js +17 -2
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-insight-dialog.component.js +24 -2
- package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-events.d.ts +14 -0
- package/src/lib/components/ai-job-events.js +76 -0
- package/src/lib/components/ai-job-events.js.map +1 -0
- package/src/lib/components/ai-job-follow.component.d.ts +49 -0
- package/src/lib/components/ai-job-follow.component.js +210 -0
- package/src/lib/components/ai-job-follow.component.js.map +1 -0
- package/src/lib/components/ai-job-links.d.ts +42 -0
- package/src/lib/components/ai-job-links.js +140 -0
- package/src/lib/components/ai-job-links.js.map +1 -0
- package/src/lib/components/ai-job-plan.component.d.ts +13 -6
- package/src/lib/components/ai-job-plan.component.js +29 -3
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +24 -0
- package/src/lib/components/ai-job-requests.js +76 -0
- package/src/lib/components/ai-job-requests.js.map +1 -0
- package/src/lib/components/ai-jobs-indicator.component.d.ts +25 -0
- package/src/lib/components/ai-jobs-indicator.component.js +80 -0
- package/src/lib/components/ai-jobs-indicator.component.js.map +1 -0
- package/src/lib/components/ai-jobs-store.d.ts +81 -0
- package/src/lib/components/ai-jobs-store.js +218 -0
- package/src/lib/components/ai-jobs-store.js.map +1 -0
- package/src/lib/components/ai-page-brief-dialog.component.d.ts +3 -3
- package/src/lib/components/ai-page-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.d.ts +23 -0
- package/src/lib/components/ai-site-build-page.component.js +377 -0
- package/src/lib/components/ai-site-build-page.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.d.ts +1 -1
- package/src/lib/components/ai-site-start-card.component.js +522 -130
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-site-starter-fallback.component.d.ts +24 -0
- package/src/lib/components/ai-site-starter-fallback.component.js +112 -0
- package/src/lib/components/ai-site-starter-fallback.component.js.map +1 -0
- package/src/lib/components/assist-jobs-drawer.component.d.ts +18 -20
- package/src/lib/components/assist-jobs-drawer.component.js +152 -172
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +62 -6
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/staff-ai-credit-return.component.d.ts +55 -0
- package/src/lib/components/staff-ai-credit-return.component.js +346 -0
- package/src/lib/components/staff-ai-credit-return.component.js.map +1 -0
- package/src/lib/components/staff-org-ai-card.component.js +364 -279
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/components/staff-user-ai-usage-card.component.d.ts +4 -0
- package/src/lib/components/staff-user-ai-usage-card.component.js +83 -28
- package/src/lib/components/staff-user-ai-usage-card.component.js.map +1 -1
- package/src/lib/components/use-ai-job-run.js +5 -18
- package/src/lib/components/use-ai-job-run.js.map +1 -1
- package/src/lib/jobs/ai-job-auto-confirm.d.ts +52 -0
- package/src/lib/jobs/ai-job-auto-confirm.js +62 -0
- package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -0
- package/src/lib/jobs/ai-job-plan-step.d.ts +33 -2
- package/src/lib/jobs/ai-job-plan-step.js +161 -17
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +7 -0
- package/src/lib/jobs/ai-job-site-step.js +34 -4
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +7 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +37 -0
- package/src/lib/jobs/ai-jobs-notify.js +51 -0
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -0
- package/src/lib/jobs/ai-jobs.d.ts +31 -4
- package/src/lib/jobs/ai-jobs.js +196 -32
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/model/ai-job-activity.d.ts +112 -0
- package/src/lib/model/ai-job-activity.js +144 -0
- package/src/lib/model/ai-job-activity.js.map +1 -0
- package/src/lib/model/ai-job-failure-copy.d.ts +69 -0
- package/src/lib/model/ai-job-failure-copy.js +78 -0
- package/src/lib/model/ai-job-failure-copy.js.map +1 -0
- package/src/lib/model/ai-job-notice.d.ts +61 -0
- package/src/lib/model/ai-job-notice.js +112 -0
- package/src/lib/model/ai-job-notice.js.map +1 -0
- package/src/lib/model/ai-jobs.types.d.ts +26 -0
- package/src/lib/model/ai-jobs.types.js +1 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-plan-capabilities.d.ts +7 -0
- package/src/lib/model/ai-plan-capabilities.js.map +1 -1
- package/src/lib/model/ai-site-build-progress.d.ts +45 -0
- package/src/lib/model/ai-site-build-progress.js +76 -0
- package/src/lib/model/ai-site-build-progress.js.map +1 -0
- package/src/lib/model/ai-site-inventory.d.ts +9 -0
- package/src/lib/model/ai-site-inventory.js +7 -2
- package/src/lib/model/ai-site-inventory.js.map +1 -1
- package/src/lib/model/ai-site-job.d.ts +93 -2
- package/src/lib/model/ai-site-job.js +88 -7
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +3 -1
- package/src/lib/model/ai-site-start.js +6 -4
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/plan-entitlements.d.ts +3 -3
- package/src/lib/plan-entitlements.js +3 -3
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/plugin.js +28 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/runtime/ai-abuse-guards.d.ts +9 -6
- package/src/lib/runtime/ai-abuse-guards.js +10 -7
- package/src/lib/runtime/ai-abuse-guards.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.js +31 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-gate.d.ts +3 -2
- package/src/lib/runtime/ai-gate.js +3 -2
- package/src/lib/runtime/ai-gate.js.map +1 -1
- package/src/lib/runtime/site-inventory.d.ts +16 -1
- package/src/lib/runtime/site-inventory.js +43 -3
- package/src/lib/runtime/site-inventory.js.map +1 -1
- package/src/lib/server/ai-admin-credits.d.ts +21 -0
- package/src/lib/server/ai-admin-credits.js +213 -0
- package/src/lib/server/ai-admin-credits.js.map +1 -0
- package/src/lib/server/ai-admin-org.js +12 -5
- package/src/lib/server/ai-admin-org.js.map +1 -1
- package/src/lib/server/ai-admin-signals.js +3 -1
- package/src/lib/server/ai-admin-signals.js.map +1 -1
- package/src/lib/server/ai-admin-user.js +12 -1
- package/src/lib/server/ai-admin-user.js.map +1 -1
- package/src/lib/server/ai-allotments.js +3 -2
- package/src/lib/server/ai-allotments.js.map +1 -1
- package/src/lib/server/ai-jobs-route.js +20 -2
- package/src/lib/server/ai-jobs-route.js.map +1 -1
- package/src/lib/server/assist-chat.js +3 -2
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/billing-credits.js +4 -3
- package/src/lib/server/billing-credits.js.map +1 -1
- package/src/lib/server/billing-overage.js +3 -2
- package/src/lib/server/billing-overage.js.map +1 -1
- package/src/lib/server.js +11 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/usage/assist-credit-returns-write.d.ts +75 -0
- package/src/lib/usage/assist-credit-returns-write.js +150 -0
- package/src/lib/usage/assist-credit-returns-write.js.map +1 -0
- package/src/lib/usage/assist-credit-returns.d.ts +68 -0
- package/src/lib/usage/assist-credit-returns.js +79 -0
- package/src/lib/usage/assist-credit-returns.js.map +1 -0
- package/src/lib/usage/assist-free-taste.js +6 -3
- package/src/lib/usage/assist-free-taste.js.map +1 -1
- package/src/lib/usage/assist-job-refund.d.ts +100 -0
- package/src/lib/usage/assist-job-refund.js +109 -0
- package/src/lib/usage/assist-job-refund.js.map +1 -0
- package/src/lib/usage/assist-usage.js +5 -2
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai.d.ts +31 -1
- package/src/lib/usage/staff-org-ai.js +19 -2
- package/src/lib/usage/staff-org-ai.js.map +1 -1
|
@@ -16,18 +16,24 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
16
16
|
* limitations under the License.
|
|
17
17
|
*/ import { checkEntitlement } from "@aglyn/aglyn";
|
|
18
18
|
import { trackEvent } from "@aglyn/aglyn/app-utils/analytics-events";
|
|
19
|
-
import { buildRoute, Route } from "@aglyn/aglyn/app-utils/console-routes";
|
|
20
19
|
import { formatBytes } from "@aglyn/aglyn/app-utils/measure-node-map";
|
|
21
|
-
import { pluginRecordHref, pluginRecordListHref } from "@aglyn/aglyn/plugin-manager/plugin-record-routes";
|
|
22
20
|
import { AI_JOB_TERMINAL_STATUSES } from "../model/ai-jobs.types.js";
|
|
23
21
|
import { mdiChevronDown, mdiChevronUp } from "@aglyn/shared-data-mdi";
|
|
24
22
|
import { AppLink, MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
25
23
|
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
26
24
|
import { Box, Button, Chip, CircularProgress, Collapse, IconButton, Stack, Typography } from "@mui/material";
|
|
27
25
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
26
|
+
import { AI_JOB_PHASE_LABELS, aiJobActivityCounts, aiJobPhase, aiJobsHeaderChips } from "../model/ai-job-activity.js";
|
|
27
|
+
import { cancelAiJobRequest, resumeAiJobRequest } from "./ai-job-requests.js";
|
|
28
|
+
import { publishAiJob } from "./ai-jobs-store.js";
|
|
28
29
|
import { AiPageBriefDialog } from "./ai-page-brief-dialog.component.js";
|
|
29
30
|
import { AiInsightDialog } from "./ai-insight-dialog.component.js";
|
|
30
31
|
import { AiJobPlan } from "./ai-job-plan.component.js";
|
|
32
|
+
import { aiJobOutputHref, aiJobPrimaryLink } from "./ai-job-links.js";
|
|
33
|
+
import { readEventFrames } from "./ai-job-events.js";
|
|
34
|
+
// Where an output opens, and the stream reader, live beside the dialogs that
|
|
35
|
+
// share them (AGL-3593); this module has always exported them.
|
|
36
|
+
export { aiJobOutputHref, aiJobPrimaryLink, readEventFrames };
|
|
31
37
|
/**
|
|
32
38
|
* The AI jobs drawer inside the Assist panel (AGL-2904): the workspace's
|
|
33
39
|
* running and recent generation jobs, each step's progress, an "open
|
|
@@ -40,97 +46,40 @@ import { AiJobPlan } from "./ai-job-plan.component.js";
|
|
|
40
46
|
* panel opens on every console page, and a list nobody asked for is a read
|
|
41
47
|
* on every page view.
|
|
42
48
|
*
|
|
49
|
+
* It opens EXPANDED while any of the workspace's jobs is moving or waiting
|
|
50
|
+
* for the person (AGL-3593) — the shared list the top-bar indicator reads
|
|
51
|
+
* says so — because a plan waiting behind a collapsed row is a site that
|
|
52
|
+
* never gets built. An "Open AI jobs" anywhere in the console expands it,
|
|
53
|
+
* scrolls to the job it names and highlights it for a moment.
|
|
54
|
+
*
|
|
43
55
|
* Hidden — not disabled — unless the flag shows it AND the plan carries
|
|
44
56
|
* `aiGenerative`: a released-off feature does not exist, and a plan without
|
|
45
57
|
* it is sold the add-on on the Billing page, not here.
|
|
46
58
|
*/ /** Jobs the drawer lists; the route caps higher, the drawer is a glance. */ const LIST_LIMIT = 10;
|
|
47
59
|
/** How much of a brief a row shows. */ const BRIEF_PREVIEW_CHARS = 90;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
done: 'Done',
|
|
54
|
-
failed: 'Failed',
|
|
55
|
-
canceled: 'Canceled'
|
|
56
|
-
};
|
|
57
|
-
const STATUS_COLOR = {
|
|
60
|
+
/**
|
|
61
|
+
* A row's chip says where the job stands in the words every AI surface uses
|
|
62
|
+
* (AGL-3593): a plan waiting to be confirmed is "Plan ready", never "Running"
|
|
63
|
+
* and never the machine's `needs_review`.
|
|
64
|
+
*/ const PHASE_COLOR = {
|
|
58
65
|
queued: 'default',
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
planning: 'primary',
|
|
67
|
+
'plan-ready': 'warning',
|
|
68
|
+
building: 'primary',
|
|
69
|
+
attention: 'warning',
|
|
62
70
|
done: 'success',
|
|
63
71
|
failed: 'error',
|
|
64
72
|
canceled: 'default'
|
|
65
73
|
};
|
|
66
|
-
/** The
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
template: 'templates',
|
|
71
|
-
// An email design is a screen: it opens in the screen besigner, as the
|
|
72
|
-
// Emails page's own Edit design does.
|
|
73
|
-
emailScreen: 'screens'
|
|
74
|
+
/** The header's count chips, colored as the rows they count. */ const STATE_COLOR = {
|
|
75
|
+
'needs-you': 'warning',
|
|
76
|
+
attention: 'warning',
|
|
77
|
+
running: 'primary'
|
|
74
78
|
};
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
* one the console lists without a detail page opens its list; a `theme`
|
|
80
|
-
* proposal opens the site's Theme section, where it is put in the editor.
|
|
81
|
-
*
|
|
82
|
-
* A console URL names a site by its SUBDOMAIN: the `[host]` segment resolves
|
|
83
|
-
* through the member's host projection by subdomain, never by document id.
|
|
84
|
-
* So an output that carries no `hostSubdomain` gets no link rather than one
|
|
85
|
-
* that opens no site.
|
|
86
|
-
*/ export function aiJobOutputHref(output, orgSlug) {
|
|
87
|
-
const host = output.hostSubdomain;
|
|
88
|
-
if (!orgSlug || !host) return null;
|
|
89
|
-
const segment = BESIGNER_SEGMENT[output.resource];
|
|
90
|
-
if (segment) {
|
|
91
|
-
const base = `/${orgSlug}/hosts/${host}/${segment}/${output.id}`;
|
|
92
|
-
return output.versionId ? `${base}/versions/${output.versionId}/besigner` : base;
|
|
93
|
-
}
|
|
94
|
-
if (output.resource === 'product') {
|
|
95
|
-
// The catalog a drafted product lands in: the commerce plugin's address
|
|
96
|
-
// for it (AGL-3080), or no link where commerce is not loaded.
|
|
97
|
-
return pluginRecordListHref('product', {
|
|
98
|
-
orgSlug,
|
|
99
|
-
host
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
if (output.resource === 'workflow') {
|
|
103
|
-
// A drafted automation is an action (AGL-2919), listed switched off on
|
|
104
|
-
// the workflows plugin's list of actions, or no link where it is not loaded.
|
|
105
|
-
return pluginRecordListHref('action', {
|
|
106
|
-
orgSlug,
|
|
107
|
-
host
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
if (output.resource === 'theme') {
|
|
111
|
-
return buildRoute(Route.HOST_SETUP_THEME, {
|
|
112
|
-
orgSlug,
|
|
113
|
-
host
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
if (output.resource === 'form') {
|
|
117
|
-
// A new form has no version for the besigner to open: its own page — the
|
|
118
|
-
// forms plugin's address for it — mints the first one, and holds the
|
|
119
|
-
// routing and consent it declares.
|
|
120
|
-
return pluginRecordHref('form', {
|
|
121
|
-
orgSlug,
|
|
122
|
-
host
|
|
123
|
-
}, output.id);
|
|
124
|
-
}
|
|
125
|
-
if (output.resource === 'campaign') {
|
|
126
|
-
// A campaign's page is the marketing plugin's, under the site: its
|
|
127
|
-
// address for the campaign (AGL-3080), or no link where it is not loaded.
|
|
128
|
-
return pluginRecordHref('campaign', {
|
|
129
|
-
orgSlug,
|
|
130
|
-
host
|
|
131
|
-
}, output.id);
|
|
132
|
-
}
|
|
133
|
-
return null;
|
|
79
|
+
/** How long a job AI jobs was opened on stays highlighted. */ const HIGHLIGHT_MS = 2500;
|
|
80
|
+
/** What a job cost the person: what it spent, less what we refunded. */ function netCredits(job) {
|
|
81
|
+
var _job_creditsSpent, _job_refundedCredits;
|
|
82
|
+
return Math.max(0, ((_job_creditsSpent = job.creditsSpent) != null ? _job_creditsSpent : 0) - ((_job_refundedCredits = job.refundedCredits) != null ? _job_refundedCredits : 0));
|
|
134
83
|
}
|
|
135
84
|
/**
|
|
136
85
|
* The navigation entry a page job proposes for the page it built (AGL-2907),
|
|
@@ -144,37 +93,17 @@ const STATUS_COLOR = {
|
|
|
144
93
|
const label = navigation['label'];
|
|
145
94
|
return typeof label === 'string' && label.trim() ? label.trim() : null;
|
|
146
95
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
* every panel that watches a job through the events route.
|
|
150
|
-
*/ export async function readEventFrames(body, onEvent) {
|
|
151
|
-
const reader = body.getReader();
|
|
152
|
-
const decoder = new TextDecoder();
|
|
153
|
-
let buffer = '';
|
|
154
|
-
for(;;){
|
|
155
|
-
var _frames_pop;
|
|
156
|
-
const { done, value } = await reader.read();
|
|
157
|
-
if (done) break;
|
|
158
|
-
buffer += decoder.decode(value, {
|
|
159
|
-
stream: true
|
|
160
|
-
});
|
|
161
|
-
const frames = buffer.split('\n\n');
|
|
162
|
-
buffer = (_frames_pop = frames.pop()) != null ? _frames_pop : '';
|
|
163
|
-
for (const frame of frames){
|
|
164
|
-
const line = frame.split('\n').find((part)=>part.startsWith('data: '));
|
|
165
|
-
if (!line) continue;
|
|
166
|
-
try {
|
|
167
|
-
onEvent(JSON.parse(line.slice('data: '.length)));
|
|
168
|
-
} catch (unused) {
|
|
169
|
-
// A torn frame is dropped; the next state event carries the whole job.
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible, isStaff = false, hostId, insight }) {
|
|
175
|
-
var _ref, _ref1, _ref2, _ref3;
|
|
96
|
+
export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible, isStaff = false, hostId, insight, inFlight, focus }) {
|
|
97
|
+
var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6;
|
|
176
98
|
const entitled = orgReady && checkEntitlement(org, 'aiGenerative');
|
|
177
99
|
const [expanded, setExpanded] = useState(false);
|
|
100
|
+
/**
|
|
101
|
+
* The person opened or closed the list themselves: from then on the
|
|
102
|
+
* expanded-by-default rule leaves their choice alone until the workspace
|
|
103
|
+
* changes.
|
|
104
|
+
*/ const toggledRef = useRef(false);
|
|
105
|
+
/** The job AI jobs was opened on, highlighted for a moment once it is listed. */ const [highlight, setHighlight] = useState(null);
|
|
106
|
+
const listRef = useRef(null);
|
|
178
107
|
const [jobs, setJobs] = useState([]);
|
|
179
108
|
const [loading, setLoading] = useState(false);
|
|
180
109
|
const [notice, setNotice] = useState(null);
|
|
@@ -186,6 +115,8 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
186
115
|
/** Job ids whose terminal event has been tracked, so a re-read does not count twice. */ const trackedRef = useRef(new Set());
|
|
187
116
|
/** The job whose stream is open, so a re-render does not open a second. */ const watchingRef = useRef(null);
|
|
188
117
|
const patchJob = useCallback((next)=>{
|
|
118
|
+
// Every counting surface moves with the row (AGL-3593).
|
|
119
|
+
publishAiJob(next);
|
|
189
120
|
setJobs((prior)=>{
|
|
190
121
|
const index = prior.findIndex((job)=>job.id === next.id);
|
|
191
122
|
if (index === -1) return [
|
|
@@ -229,6 +160,8 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
229
160
|
// watch finish, so its outcome is not this session's to count.
|
|
230
161
|
for (const job of payload.jobs){
|
|
231
162
|
if (AI_JOB_TERMINAL_STATUSES.includes(job.status)) trackedRef.current.add(job.id);
|
|
163
|
+
// A job the indicator still counts that has settled since leaves it.
|
|
164
|
+
publishAiJob(job);
|
|
232
165
|
}
|
|
233
166
|
setJobs(payload.jobs);
|
|
234
167
|
} catch (unused) {
|
|
@@ -251,15 +184,60 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
251
184
|
useEffect(()=>{
|
|
252
185
|
setJobs([]);
|
|
253
186
|
setExpanded(false);
|
|
187
|
+
toggledRef.current = false;
|
|
254
188
|
watchingRef.current = null;
|
|
255
189
|
}, [
|
|
256
190
|
orgId
|
|
257
191
|
]);
|
|
192
|
+
// Expanded by default while anything is moving or waiting (AGL-3593): a
|
|
193
|
+
// plan waiting to be confirmed behind a collapsed row is a site that never
|
|
194
|
+
// gets built. Otherwise collapsed, which reads nothing until asked.
|
|
195
|
+
const anyInFlight = ((_ref = inFlight == null ? void 0 : inFlight.length) != null ? _ref : 0) > 0;
|
|
196
|
+
useEffect(()=>{
|
|
197
|
+
if (anyInFlight && !toggledRef.current) setExpanded(true);
|
|
198
|
+
}, [
|
|
199
|
+
anyInFlight
|
|
200
|
+
]);
|
|
201
|
+
// A request to open AI jobs (AGL-3593): expand, read the list again so a
|
|
202
|
+
// job created a moment ago is in it, and highlight the job named.
|
|
203
|
+
const focusSeq = (_ref1 = focus == null ? void 0 : focus.seq) != null ? _ref1 : 0;
|
|
204
|
+
const focusJobId = (_ref2 = focus == null ? void 0 : focus.jobId) != null ? _ref2 : null;
|
|
205
|
+
const expandedRef = useRef(expanded);
|
|
206
|
+
expandedRef.current = expanded;
|
|
207
|
+
useEffect(()=>{
|
|
208
|
+
if (!focusSeq) return;
|
|
209
|
+
toggledRef.current = true;
|
|
210
|
+
setHighlight(focusJobId);
|
|
211
|
+
// An open list does not re-run the expand effect's read, so it reads here.
|
|
212
|
+
if (expandedRef.current) void load();
|
|
213
|
+
else setExpanded(true);
|
|
214
|
+
// Only a new request runs this; `load` and the job are read as they stand.
|
|
215
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
216
|
+
}, [
|
|
217
|
+
focusSeq
|
|
218
|
+
]);
|
|
219
|
+
// Scroll to the highlighted job once it is listed, then let it fade.
|
|
220
|
+
const highlightListed = highlight !== null && jobs.some((job)=>job.id === highlight);
|
|
221
|
+
useEffect(()=>{
|
|
222
|
+
var _ref;
|
|
223
|
+
var _listRef_current, _row_scrollIntoView;
|
|
224
|
+
if (!highlight || !highlightListed) return undefined;
|
|
225
|
+
const row = Array.from((_ref = (_listRef_current = listRef.current) == null ? void 0 : _listRef_current.querySelectorAll('[data-ai-job-id]')) != null ? _ref : []).find((element)=>element.dataset['aiJobId'] === highlight);
|
|
226
|
+
row == null ? void 0 : (_row_scrollIntoView = row.scrollIntoView) == null ? void 0 : _row_scrollIntoView.call(row, {
|
|
227
|
+
block: 'nearest',
|
|
228
|
+
behavior: 'smooth'
|
|
229
|
+
});
|
|
230
|
+
const timer = setTimeout(()=>setHighlight(null), HIGHLIGHT_MS);
|
|
231
|
+
return ()=>clearTimeout(timer);
|
|
232
|
+
}, [
|
|
233
|
+
highlight,
|
|
234
|
+
highlightListed
|
|
235
|
+
]);
|
|
258
236
|
// Watch the most recent job that is still moving. One stream at a time:
|
|
259
237
|
// the route re-reads a document every two seconds per open stream, and a
|
|
260
238
|
// drawer with three running jobs does not need three of them.
|
|
261
239
|
const watched = jobs.find((job)=>!AI_JOB_TERMINAL_STATUSES.includes(job.status));
|
|
262
|
-
const watchedId = (
|
|
240
|
+
const watchedId = (_ref3 = watched == null ? void 0 : watched.id) != null ? _ref3 : null;
|
|
263
241
|
useEffect(()=>{
|
|
264
242
|
if (!expanded || !orgId || !watchedId || watchingRef.current === watchedId) return;
|
|
265
243
|
watchingRef.current = watchedId;
|
|
@@ -303,28 +281,15 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
303
281
|
user,
|
|
304
282
|
patchJob
|
|
305
283
|
]);
|
|
284
|
+
// The same door the dialog that started the job cancels through (AGL-3593).
|
|
306
285
|
const cancel = useCallback(async (jobId)=>{
|
|
307
286
|
if (!orgId) return;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
'Content-Type': 'application/json'
|
|
313
|
-
},
|
|
314
|
-
body: JSON.stringify({
|
|
315
|
-
orgId
|
|
316
|
-
})
|
|
317
|
-
});
|
|
318
|
-
const payload = await response.json().catch(()=>null);
|
|
319
|
-
if (!response.ok) {
|
|
320
|
-
var _ref;
|
|
321
|
-
setNotice(String((_ref = payload == null ? void 0 : payload.error) != null ? _ref : 'The job could not be canceled — try again.'));
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
if (payload == null ? void 0 : payload.job) patchJob(payload.job);
|
|
325
|
-
} catch (unused) {
|
|
326
|
-
setNotice('The job could not be canceled — try again.');
|
|
287
|
+
const { job, error } = await cancelAiJobRequest(user, orgId, jobId);
|
|
288
|
+
if (error) {
|
|
289
|
+
setNotice(error);
|
|
290
|
+
return;
|
|
327
291
|
}
|
|
292
|
+
if (job) patchJob(job);
|
|
328
293
|
}, [
|
|
329
294
|
orgId,
|
|
330
295
|
user,
|
|
@@ -338,35 +303,20 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
338
303
|
if (!orgId) return;
|
|
339
304
|
setResuming(job.id);
|
|
340
305
|
setNotice(null);
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
},
|
|
347
|
-
body: JSON.stringify({
|
|
348
|
-
orgId,
|
|
349
|
-
hostId: job.hostId
|
|
350
|
-
})
|
|
351
|
-
});
|
|
352
|
-
const payload = await response.json().catch(()=>null);
|
|
353
|
-
if (payload == null ? void 0 : payload.job) patchJob(payload.job);
|
|
354
|
-
if (!response.ok) {
|
|
355
|
-
var _ref;
|
|
356
|
-
setNotice(String((_ref = payload == null ? void 0 : payload.error) != null ? _ref : 'The job could not be resumed — try again.'));
|
|
357
|
-
}
|
|
358
|
-
} catch (unused) {
|
|
359
|
-
setNotice('The job could not be resumed — try again.');
|
|
360
|
-
} finally{
|
|
361
|
-
setResuming(null);
|
|
362
|
-
}
|
|
306
|
+
// The same door the dialog that started the job confirms through (AGL-3593).
|
|
307
|
+
const { job: next, error } = await resumeAiJobRequest(user, orgId, job);
|
|
308
|
+
if (next) patchJob(next);
|
|
309
|
+
if (error) setNotice(error);
|
|
310
|
+
setResuming(null);
|
|
363
311
|
}, [
|
|
364
312
|
orgId,
|
|
365
313
|
user,
|
|
366
314
|
patchJob
|
|
367
315
|
]);
|
|
368
316
|
if (!visible || !orgId || !entitled) return null;
|
|
369
|
-
|
|
317
|
+
// Counted by state, never as one "running" figure (AGL-3593): a plan waiting
|
|
318
|
+
// to be confirmed needs the person, and a stopped job is not moving.
|
|
319
|
+
const chips = aiJobsHeaderChips(aiJobActivityCounts(inFlight != null ? inFlight : jobs));
|
|
370
320
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
371
321
|
sx: {
|
|
372
322
|
mb: 2,
|
|
@@ -389,14 +339,14 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
389
339
|
},
|
|
390
340
|
children: [
|
|
391
341
|
"AI jobs",
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
342
|
+
chips.map((chip)=>/*#__PURE__*/ _jsx(Chip, {
|
|
343
|
+
size: "small",
|
|
344
|
+
color: STATE_COLOR[chip.state],
|
|
345
|
+
label: chip.label,
|
|
346
|
+
sx: {
|
|
347
|
+
ml: 1
|
|
348
|
+
}
|
|
349
|
+
}, chip.state))
|
|
400
350
|
]
|
|
401
351
|
}),
|
|
402
352
|
insight && /*#__PURE__*/ _jsx(Button, {
|
|
@@ -413,7 +363,10 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
413
363
|
size: "small",
|
|
414
364
|
"aria-label": expanded ? 'Hide AI jobs' : 'Show AI jobs',
|
|
415
365
|
"aria-expanded": expanded,
|
|
416
|
-
onClick: ()=>
|
|
366
|
+
onClick: ()=>{
|
|
367
|
+
toggledRef.current = true;
|
|
368
|
+
setExpanded((prior)=>!prior);
|
|
369
|
+
},
|
|
417
370
|
children: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
418
371
|
path: expanded ? mdiChevronUp.path : mdiChevronDown.path,
|
|
419
372
|
sx: {
|
|
@@ -453,6 +406,7 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
453
406
|
children: "No AI jobs yet."
|
|
454
407
|
}),
|
|
455
408
|
/*#__PURE__*/ _jsx(Stack, {
|
|
409
|
+
ref: listRef,
|
|
456
410
|
spacing: 1,
|
|
457
411
|
sx: {
|
|
458
412
|
mt: 1
|
|
@@ -463,11 +417,24 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
463
417
|
const stepsDone = job.steps.filter((step)=>step.status === 'done').length;
|
|
464
418
|
const current = job.steps.find((step)=>step.status === 'running');
|
|
465
419
|
const terminal = AI_JOB_TERMINAL_STATUSES.includes(job.status);
|
|
420
|
+
const phase = aiJobPhase(job);
|
|
421
|
+
const primary = aiJobPrimaryLink(job, orgSlug);
|
|
422
|
+
const highlighted = highlight === job.id;
|
|
466
423
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
467
424
|
role: "listitem",
|
|
425
|
+
"data-ai-job-id": job.id,
|
|
426
|
+
"aria-current": highlighted ? 'true' : undefined,
|
|
468
427
|
sx: {
|
|
469
428
|
borderRadius: 1,
|
|
470
|
-
bgcolor: 'action.hover',
|
|
429
|
+
bgcolor: highlighted ? 'action.selected' : 'action.hover',
|
|
430
|
+
outline: 2,
|
|
431
|
+
outlineColor: highlighted ? 'primary.main' : 'transparent',
|
|
432
|
+
transition: (theme)=>theme.transitions.create([
|
|
433
|
+
'background-color',
|
|
434
|
+
'outline-color'
|
|
435
|
+
], {
|
|
436
|
+
duration: theme.transitions.duration.standard
|
|
437
|
+
}),
|
|
471
438
|
px: 1.5,
|
|
472
439
|
py: 1
|
|
473
440
|
},
|
|
@@ -486,8 +453,8 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
486
453
|
}),
|
|
487
454
|
/*#__PURE__*/ _jsx(Chip, {
|
|
488
455
|
size: "small",
|
|
489
|
-
color:
|
|
490
|
-
label:
|
|
456
|
+
color: PHASE_COLOR[phase],
|
|
457
|
+
label: AI_JOB_PHASE_LABELS[phase]
|
|
491
458
|
}),
|
|
492
459
|
/*#__PURE__*/ _jsxs(Typography, {
|
|
493
460
|
variant: "caption",
|
|
@@ -501,7 +468,7 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
501
468
|
job.steps.length,
|
|
502
469
|
" steps",
|
|
503
470
|
current ? ` — ${current.name}` : '',
|
|
504
|
-
job
|
|
471
|
+
netCredits(job) > 0 ? ` · ${netCredits(job)} credits` : ''
|
|
505
472
|
]
|
|
506
473
|
}),
|
|
507
474
|
!terminal && /*#__PURE__*/ _jsx(Button, {
|
|
@@ -525,6 +492,16 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
525
492
|
component: "div",
|
|
526
493
|
children: job.error
|
|
527
494
|
}),
|
|
495
|
+
primary ? /*#__PURE__*/ _jsx(Button, {
|
|
496
|
+
size: "small",
|
|
497
|
+
variant: "contained",
|
|
498
|
+
component: AppLink,
|
|
499
|
+
href: primary.href,
|
|
500
|
+
sx: {
|
|
501
|
+
mt: 0.5
|
|
502
|
+
},
|
|
503
|
+
children: primary.label
|
|
504
|
+
}) : null,
|
|
528
505
|
job.outputs.map((output, index)=>{
|
|
529
506
|
const href = aiJobOutputHref(output, orgSlug);
|
|
530
507
|
const navigation = aiJobNavigationProposal(output);
|
|
@@ -591,7 +568,8 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
591
568
|
job: job,
|
|
592
569
|
onResume: (target)=>void resume(target),
|
|
593
570
|
busy: resuming === job.id,
|
|
594
|
-
staff: isStaff
|
|
571
|
+
staff: isStaff,
|
|
572
|
+
user: user
|
|
595
573
|
})
|
|
596
574
|
]
|
|
597
575
|
}, job.id);
|
|
@@ -608,10 +586,10 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
608
586
|
orgId: orgId,
|
|
609
587
|
orgSlug: orgSlug,
|
|
610
588
|
hostId: (insight == null ? void 0 : insight.host) ? hostId != null ? hostId : null : null,
|
|
611
|
-
host: (
|
|
612
|
-
surface: (
|
|
589
|
+
host: (_ref4 = insight == null ? void 0 : insight.host) != null ? _ref4 : null,
|
|
590
|
+
surface: (_ref5 = insight == null ? void 0 : insight.surface) != null ? _ref5 : 'analytics',
|
|
613
591
|
user: user,
|
|
614
|
-
uid: (
|
|
592
|
+
uid: (_ref6 = user == null ? void 0 : user.uid) != null ? _ref6 : null,
|
|
615
593
|
jobId: insightOpen === 'ask' ? null : insightOpen
|
|
616
594
|
}),
|
|
617
595
|
hostId && /*#__PURE__*/ _jsx(AiPageBriefDialog, {
|
|
@@ -622,7 +600,9 @@ export function AssistJobsDrawer({ orgId, org, orgReady, orgSlug, user, visible,
|
|
|
622
600
|
},
|
|
623
601
|
orgId: orgId,
|
|
624
602
|
hostId: hostId,
|
|
625
|
-
user: user
|
|
603
|
+
user: user,
|
|
604
|
+
orgSlug: orgSlug,
|
|
605
|
+
isStaff: isStaff
|
|
626
606
|
})
|
|
627
607
|
]
|
|
628
608
|
});
|