@alexkroman1/aai-cli 13.3.0 → 15.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{_agent-Clbq3a4h.mjs → _agent-BzUeqOdj.mjs} +32 -9
- package/dist/_agent.d.ts +23 -0
- package/dist/_artifacts-BJOYGQPp.mjs +21 -0
- package/dist/_artifacts.d.ts +16 -0
- package/dist/_build-target.d.ts +172 -0
- package/dist/{_bundler-DEkjO1d8.mjs → _bundler-DM0d0M7m.mjs} +8 -7
- package/dist/_cli-common.d.ts +45 -6
- package/dist/{_config-ZY2euhdh.mjs → _config-DYzC6WMD.mjs} +2 -2
- package/dist/_dev-env.d.ts +1 -1
- package/dist/{_dev-server-C13xuGB1.mjs → _dev-server-BzWB6-4y.mjs} +25 -24
- package/dist/_dev-vite-config.d.ts +3 -3
- package/dist/_e2e-test-utils.d.ts +1 -1
- package/dist/{_init-Ci4QGjp8.mjs → _init-Bsi3DZNJ.mjs} +9 -23
- package/dist/{_output-DbJt9gsl.mjs → _output-DBP9Op_d.mjs} +22 -13
- package/dist/_output.d.ts +10 -9
- package/dist/{_preflight-B2laWPdU.mjs → _preflight-CcAR577V.mjs} +2 -2
- package/dist/_resource-commands.d.ts +9 -9
- package/dist/_server-common-De0haHr9.mjs +70 -0
- package/dist/_server-common.d.ts +20 -1
- package/dist/{_slug-api-DlKpj91X.mjs → _slug-api-CGvc2YSQ.mjs} +18 -9
- package/dist/_slug-api.d.ts +19 -0
- package/dist/{_studio-DTi3EdXQ.mjs → _studio-DnR_BqFp.mjs} +7 -3
- package/dist/_studio-commands.d.ts +13 -13
- package/dist/{_templates-V4l5sZJy.mjs → _templates-CIlJ3Vay.mjs} +4 -4
- package/dist/_templates.d.ts +1 -1
- package/dist/{_typecheck-gate-sV4N5F93.mjs → _typecheck-gate-U3jcil8L.mjs} +3 -2
- package/dist/_typecheck-gate.d.ts +3 -1
- package/dist/{_utils-Dhd058D9.mjs → _utils-D5JGcjiW.mjs} +43 -1
- package/dist/_utils.d.ts +35 -0
- package/dist/_vercel-output.d.ts +63 -0
- package/dist/{_workflow-determinism-C9l8uC2R.mjs → _workflow-determinism-CvpqMpdh.mjs} +18 -26
- package/dist/_workflow-determinism.d.ts +3 -3
- package/dist/build-BhEaxBPu.mjs +481 -0
- package/dist/build.d.ts +23 -10
- package/dist/cli.d.ts +1 -2
- package/dist/cli.mjs +155 -138
- package/dist/{client-bundler-CT-_26c5.mjs → client-bundler-6mTLs6ny.mjs} +5 -5
- package/dist/client-bundler.d.ts +1 -1
- package/dist/client-bundler.mjs +1 -1
- package/dist/{delete-RqlQZ6Y_.mjs → delete-BdFnbnVR.mjs} +4 -4
- package/dist/{deploy-B7f3CK7v.mjs → deploy-CGqPU5U-.mjs} +11 -11
- package/dist/{dev-DamXieW_.mjs → dev-Bx9gYBHM.mjs} +3 -3
- package/dist/{eval-h3DTVfRw.mjs → eval-B3I7FqN9.mjs} +3 -3
- package/dist/{init-D-vFrV8h.mjs → init-CFyusRbq.mjs} +48 -20
- package/dist/init.d.ts +14 -0
- package/dist/{login-CDiemNmC.mjs → login-DaLrPOFg.mjs} +4 -6
- package/dist/{logs-DGwPNCzB.mjs → logs-7BRwq0cG.mjs} +21 -14
- package/dist/project-config.mjs +1 -1
- package/dist/scaffold/CLAUDE.md +155 -76
- package/dist/scaffold/package.json +6 -6
- package/dist/{secret-V1w7y9-j.mjs → secret-Cjp912yF.mjs} +5 -5
- package/dist/secret.d.ts +8 -6
- package/dist/start.d.ts +112 -0
- package/dist/start.mjs +156 -0
- package/dist/{studio-BpMCfUNx.mjs → studio-C_zuRC_z.mjs} +13 -15
- package/dist/templates/briefing-desk/agent.eval.test.ts +156 -0
- package/dist/templates/call-audit/agent.test.ts +11 -6
- package/dist/templates/call-audit/agent.ts +2 -2
- package/dist/templates/call-audit/client.tsx +27 -42
- package/dist/templates/call-audit/workflows/audit.ts +7 -5
- package/dist/templates/call-audit/workflows/ingest.ts +7 -7
- package/dist/templates/call-audit/workflows/media.ts +12 -37
- package/dist/templates/call-audit/workflows/summarize.ts +8 -8
- package/dist/templates/call-audit/workflows/sync-api.ts +3 -3
- package/dist/templates/code-interpreter/agent.test.ts +103 -0
- package/dist/templates/dispatch-center/agent.test.ts +7 -7
- package/dist/templates/dispatch-center/client.tsx +53 -61
- package/dist/templates/dispatch-center/shared.ts +0 -8
- package/dist/templates/dispatch-center/tools/ops_dashboard.ts +4 -1
- package/dist/templates/embedded-assets/agent.eval.test.ts +4 -2
- package/dist/templates/health-assistant/agent.eval.test.ts +4 -2
- package/dist/templates/infocom-adventure/client.tsx +40 -36
- package/dist/templates/link-digest/agent.test.ts +3 -3
- package/dist/templates/link-digest/agent.ts +2 -2
- package/dist/templates/link-digest/client.tsx +81 -19
- package/dist/templates/link-digest/workflows/digest.ts +13 -16
- package/dist/templates/math-buddy/agent.test.ts +126 -0
- package/dist/templates/night-owl/agent.eval.test.ts +4 -2
- package/dist/templates/night-owl/client.tsx +8 -2
- package/dist/templates/personal-finance/agent.test.ts +127 -0
- package/dist/templates/pipeline-simple/agent.test.ts +2 -2
- package/dist/templates/pizza-ordering/client.tsx +5 -4
- package/dist/templates/pizza-ordering/shared.ts +3 -8
- package/dist/templates/pizza-ordering/tools/add_pizza.ts +3 -2
- package/dist/templates/pizza-ordering/tools/place_order.ts +3 -2
- package/dist/templates/pizza-ordering/tools/remove_pizza.ts +3 -2
- package/dist/templates/pizza-ordering/tools/update_pizza.ts +3 -2
- package/dist/templates/pizza-ordering/tools/view_order.ts +3 -2
- package/dist/templates/plan-and-execute/agent.test.ts +10 -5
- package/dist/templates/plan-and-execute/client.tsx +2 -2
- package/dist/templates/podcast-digest/agent.test.ts +20 -11
- package/dist/templates/podcast-digest/client.tsx +16 -22
- package/dist/templates/podcast-digest/workflows/digest.ts +21 -19
- package/dist/templates/podcast-digest/workflows/feeds.ts +61 -33
- package/dist/templates/podcast-digest/workflows/slack.ts +7 -7
- package/dist/templates/recap-workflow/agent.eval.test.ts +21 -13
- package/dist/templates/recap-workflow/agent.test.ts +27 -23
- package/dist/templates/recap-workflow/workflows/recap.ts +24 -28
- package/dist/templates/redline/agent.eval.test.ts +5 -8
- package/dist/templates/redline/agent.test.ts +1 -1
- package/dist/templates/redline/client.tsx +36 -34
- package/dist/templates/redline/workflows/redline.ts +17 -17
- package/dist/templates/research-workflow/agent.eval.test.ts +5 -5
- package/dist/templates/research-workflow/agent.test.ts +10 -7
- package/dist/templates/research-workflow/workflows/research.ts +28 -30
- package/dist/templates/retail/agent.test.ts +17 -15
- package/dist/templates/retail/cancel.ts +7 -6
- package/dist/templates/retail/client.tsx +39 -58
- package/dist/templates/retail/payment.ts +4 -3
- package/dist/templates/retail/pending.test.ts +19 -19
- package/dist/templates/retail/store.test.ts +5 -5
- package/dist/templates/retail/swap.ts +8 -11
- package/dist/templates/solo-rpg/agent.eval.test.ts +2 -16
- package/dist/templates/solo-rpg/agent.test.ts +12 -12
- package/dist/templates/solo-rpg/client.tsx +3 -3
- package/dist/templates/spoken-summary/agent.test.ts +9 -9
- package/dist/templates/spoken-summary/agent.ts +3 -3
- package/dist/templates/spoken-summary/client.tsx +23 -30
- package/dist/templates/spoken-summary/workflows/summarize.ts +11 -11
- package/dist/templates/spoken-summary/workflows/transcribe.ts +14 -14
- package/dist/templates/support-line/agent.ts +8 -0
- package/dist/templates/support-line/client.tsx +4 -3
- package/dist/templates/support-line/prompts.ts +0 -2
- package/dist/templates/transcription-workflow/agent.test.ts +20 -12
- package/dist/templates/transcription-workflow/agent.ts +2 -2
- package/dist/templates/transcription-workflow/client.tsx +15 -14
- package/dist/templates/transcription-workflow/recover.ts +1 -1
- package/dist/templates/transcription-workflow/run-panel.tsx +34 -14
- package/dist/templates/transcription-workflow/total-latency.tsx +46 -20
- package/dist/templates/transcription-workflow/workflows/batch.ts +15 -15
- package/dist/templates/transcription-workflow/workflows/downsample.ts +1 -1
- package/dist/templates/transcription-workflow/workflows/normalize.ts +15 -19
- package/dist/templates/transcription-workflow/workflows/stream.ts +12 -12
- package/dist/templates/transcription-workflow/workflows/sync-api.ts +3 -3
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +23 -34
- package/dist/templates/travel-concierge/agent.eval.test.ts +2 -2
- package/dist/templates/travel-concierge/agent.test.ts +15 -7
- package/dist/templates/travel-concierge/client.tsx +2 -2
- package/dist/templates/travel-concierge/routing.ts +64 -55
- package/dist/templates/travel-concierge/shared.ts +10 -14
- package/dist/templates/travel-concierge/tools/cancel_action.ts +3 -1
- package/dist/templates/travel-concierge/tools/complete_or_escalate.ts +3 -1
- package/dist/templates/travel-concierge/tools/confirm_action.ts +3 -1
- package/dist/templates/travel-concierge/tools/lookup_booking.ts +4 -3
- package/dist/templates/travel-concierge/tools/search_car_rentals.ts +3 -2
- package/dist/templates/travel-concierge/tools/search_excursions.ts +3 -2
- package/dist/templates/travel-concierge/tools/search_flights.ts +3 -2
- package/dist/templates/travel-concierge/tools/search_hotels.ts +3 -2
- package/dist/templates/web-researcher/agent.test.ts +130 -0
- package/dist/{test-guenJUxi.mjs → test-D032V2cA.mjs} +4 -17
- package/dist/typecheck.mjs +1 -1
- package/dist/worker-bundler.d.ts +1 -1
- package/dist/worker-bundler.mjs +8 -8
- package/dist/{workflow-BZyKLkhV.mjs → workflow-CVioyO3V.mjs} +6 -5
- package/package.json +9 -4
- package/dist/_server-common-BV4_CrP3.mjs +0 -43
- package/dist/build-CWyFGe-W.mjs +0 -69
- package/dist/scaffold/server.mjs +0 -204
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import { plural } from "@alexkroman1/aai/utils";
|
|
1
2
|
import "@alexkroman1/aai-ui/styles.css";
|
|
2
|
-
import type { AgentState, ConversationItem
|
|
3
|
+
import type { AgentState, ConversationItem } from "@alexkroman1/aai-ui";
|
|
3
4
|
import {
|
|
5
|
+
AGENT_STATE_LABELS,
|
|
4
6
|
AutoScroll,
|
|
5
|
-
|
|
7
|
+
mountClient,
|
|
8
|
+
SessionErrorBanner,
|
|
6
9
|
useAgentState,
|
|
7
10
|
useConversation,
|
|
8
|
-
|
|
11
|
+
useSessionActions,
|
|
9
12
|
useSessionSelector,
|
|
13
|
+
useSessionStatus,
|
|
10
14
|
} from "@alexkroman1/aai-ui";
|
|
11
15
|
import type { DispatchState, IncidentSummary, Severity, Status } from "./shared.ts";
|
|
12
16
|
import { dashboardProjection } from "./shared.ts";
|
|
@@ -77,6 +81,20 @@ const STATE_COLORS = {
|
|
|
77
81
|
error: "#6b7280",
|
|
78
82
|
} satisfies Record<AgentState, string>;
|
|
79
83
|
|
|
84
|
+
/*
|
|
85
|
+
* The board's own vocabulary, spread over the package's record rather than
|
|
86
|
+
* written as a ternary chain. Only three of the seven states get a dispatch
|
|
87
|
+
* word; the rest come from `AGENT_STATE_LABELS`, so a state added upstream
|
|
88
|
+
* reads as something rather than falling through to whichever arm the chain
|
|
89
|
+
* ended on.
|
|
90
|
+
*/
|
|
91
|
+
const STATE_LABELS: Record<AgentState, string> = {
|
|
92
|
+
...AGENT_STATE_LABELS,
|
|
93
|
+
listening: "LISTENING",
|
|
94
|
+
thinking: "PROCESSING",
|
|
95
|
+
speaking: "TRANSMITTING",
|
|
96
|
+
};
|
|
97
|
+
|
|
80
98
|
function Panel({ title, children }: { title: string; children: React.ReactNode }) {
|
|
81
99
|
return (
|
|
82
100
|
<div className="rounded-lg p-3" style={{ background: "#1a1a2e", border: "1px solid #1e293b" }}>
|
|
@@ -230,8 +248,16 @@ function Conversation() {
|
|
|
230
248
|
{streaming}
|
|
231
249
|
</div>
|
|
232
250
|
)}
|
|
251
|
+
{/* Same contract as the shipped `MessageList`'s indicator: three pulsing
|
|
252
|
+
dots are the only sign the desk is working, and to a screen reader
|
|
253
|
+
they are punctuation. */}
|
|
233
254
|
{thinking && (
|
|
234
|
-
<div
|
|
255
|
+
<div
|
|
256
|
+
role="status"
|
|
257
|
+
aria-label="Dispatch is thinking"
|
|
258
|
+
className="self-start text-[11px] px-3.5"
|
|
259
|
+
style={{ color: "#64748b" }}
|
|
260
|
+
>
|
|
235
261
|
<span style={{ animation: "dc-pulse 1.2s ease-in-out infinite" }}>· · ·</span>
|
|
236
262
|
</div>
|
|
237
263
|
)}
|
|
@@ -261,7 +287,7 @@ function Conversation() {
|
|
|
261
287
|
* it one level up dragged the incident board through every snapshot change.
|
|
262
288
|
*/
|
|
263
289
|
function StatusReadout() {
|
|
264
|
-
const state =
|
|
290
|
+
const state = useSessionStatus();
|
|
265
291
|
return (
|
|
266
292
|
<>
|
|
267
293
|
<span
|
|
@@ -278,70 +304,36 @@ function StatusReadout() {
|
|
|
278
304
|
title={state}
|
|
279
305
|
/>
|
|
280
306
|
<span className="text-[11px] font-normal normal-case" style={{ color: "#64748b" }}>
|
|
281
|
-
{state
|
|
282
|
-
? "LISTENING"
|
|
283
|
-
: state === "thinking"
|
|
284
|
-
? "PROCESSING"
|
|
285
|
-
: state === "speaking"
|
|
286
|
-
? "TRANSMITTING"
|
|
287
|
-
: state.toUpperCase()}
|
|
307
|
+
{STATE_LABELS[state]}
|
|
288
308
|
</span>
|
|
289
309
|
</>
|
|
290
310
|
);
|
|
291
311
|
}
|
|
292
312
|
|
|
293
|
-
|
|
294
|
-
* The
|
|
313
|
+
/*
|
|
314
|
+
* The shift controls.
|
|
295
315
|
*
|
|
296
|
-
* `
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
* board cannot use as its frame.
|
|
316
|
+
* `useSessionActions()` for the methods and two one-field selectors for the
|
|
317
|
+
* flags, rather than the whole-snapshot `useSession()` this used to hold. The
|
|
318
|
+
* methods are what the row is really after, and `useSession()` re-renders on
|
|
319
|
+
* every snapshot change — so four buttons re-rendered at STT-partial rate to
|
|
320
|
+
* read two booleans that flip once a shift.
|
|
302
321
|
*/
|
|
303
|
-
function ErrorBanner() {
|
|
304
|
-
const error = useSessionSelector((s) => s.error);
|
|
305
|
-
if (!error) return null;
|
|
306
|
-
return (
|
|
307
|
-
<div
|
|
308
|
-
role="alert"
|
|
309
|
-
className="px-4 py-2 text-xs"
|
|
310
|
-
style={{ background: "#450a0a", color: "#fca5a5", borderTop: "1px solid #991b1b" }}
|
|
311
|
-
>
|
|
312
|
-
ERROR: {error.message} ({error.code})
|
|
313
|
-
</div>
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Start a fresh conversation without leaving the console.
|
|
319
|
-
*
|
|
320
|
-
* Written out here rather than reached for on the session, because there is no
|
|
321
|
-
* one method that does it: `reset()` clears the CONVERSATION and keeps the
|
|
322
|
-
* session, which is wrong for any agent that also keeps session-scoped state —
|
|
323
|
-
* this one's incident board would come back with the next tool call.
|
|
324
|
-
*/
|
|
325
|
-
function newConversation(session: Session): void {
|
|
326
|
-
session.end();
|
|
327
|
-
session.start();
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/** The shift controls. The one place a whole-session read is what is wanted:
|
|
331
|
-
* it needs `started`, `running` and four methods, and it is four buttons. */
|
|
332
322
|
function ShiftControls({ logged }: { logged: number }) {
|
|
333
|
-
const
|
|
323
|
+
const { start, toggle, restart, end } = useSessionActions();
|
|
324
|
+
const started = useSessionSelector((s) => s.started);
|
|
325
|
+
const running = useSessionSelector((s) => s.running);
|
|
334
326
|
return (
|
|
335
327
|
<div
|
|
336
328
|
className="flex items-center gap-2.5 px-4 py-3"
|
|
337
329
|
style={{ background: "#111827", borderTop: "1px solid #1e293b" }}
|
|
338
330
|
>
|
|
339
|
-
{!
|
|
331
|
+
{!started ? (
|
|
340
332
|
<button
|
|
341
333
|
type="button"
|
|
342
334
|
className="px-4 py-2 border-none rounded-md font-mono text-xs font-semibold uppercase tracking-wider cursor-pointer text-white"
|
|
343
335
|
style={{ background: "#2563eb" }}
|
|
344
|
-
onClick={() =>
|
|
336
|
+
onClick={() => start()}
|
|
345
337
|
>
|
|
346
338
|
Start Dispatch
|
|
347
339
|
</button>
|
|
@@ -351,12 +343,12 @@ function ShiftControls({ logged }: { logged: number }) {
|
|
|
351
343
|
type="button"
|
|
352
344
|
className="px-4 py-2 border-none rounded-md font-mono text-xs font-semibold uppercase tracking-wider cursor-pointer"
|
|
353
345
|
style={{
|
|
354
|
-
background:
|
|
355
|
-
color:
|
|
346
|
+
background: running ? "#334155" : "#2563eb",
|
|
347
|
+
color: running ? "#e2e8f0" : "white",
|
|
356
348
|
}}
|
|
357
|
-
onClick={() =>
|
|
349
|
+
onClick={() => toggle()}
|
|
358
350
|
>
|
|
359
|
-
{
|
|
351
|
+
{running ? "Pause" : "Resume"}
|
|
360
352
|
</button>
|
|
361
353
|
{/* The one-click new conversation the default shell's
|
|
362
354
|
`<Controls>` gives every other template — a custom
|
|
@@ -375,7 +367,7 @@ function ShiftControls({ logged }: { logged: number }) {
|
|
|
375
367
|
type="button"
|
|
376
368
|
className="px-4 py-2 border-none rounded-md font-mono text-xs font-semibold uppercase tracking-wider cursor-pointer"
|
|
377
369
|
style={{ background: "#1e293b", color: "#e2e8f0" }}
|
|
378
|
-
onClick={
|
|
370
|
+
onClick={restart}
|
|
379
371
|
>
|
|
380
372
|
New Conversation
|
|
381
373
|
</button>
|
|
@@ -388,7 +380,7 @@ function ShiftControls({ logged }: { logged: number }) {
|
|
|
388
380
|
type="button"
|
|
389
381
|
className="px-4 py-2 border-none rounded-md font-mono text-xs font-semibold uppercase tracking-wider cursor-pointer text-white"
|
|
390
382
|
style={{ background: "#dc2626" }}
|
|
391
|
-
onClick={() =>
|
|
383
|
+
onClick={() => end()}
|
|
392
384
|
>
|
|
393
385
|
End Shift
|
|
394
386
|
</button>
|
|
@@ -396,7 +388,7 @@ function ShiftControls({ logged }: { logged: number }) {
|
|
|
396
388
|
)}
|
|
397
389
|
<div className="flex-1" />
|
|
398
390
|
<span className="text-[10px]" style={{ color: "#475569" }}>
|
|
399
|
-
{logged}
|
|
391
|
+
{logged} {plural(logged, "incident")} logged
|
|
400
392
|
</span>
|
|
401
393
|
</div>
|
|
402
394
|
);
|
|
@@ -471,7 +463,7 @@ function App() {
|
|
|
471
463
|
style={{ borderRight: "1px solid #1e293b" }}
|
|
472
464
|
>
|
|
473
465
|
<Conversation />
|
|
474
|
-
<
|
|
466
|
+
<SessionErrorBanner className="rounded-none border-x-0 border-b-0" />
|
|
475
467
|
<ShiftControls logged={incidentList.length} />
|
|
476
468
|
</div>
|
|
477
469
|
|
|
@@ -524,7 +516,7 @@ function App() {
|
|
|
524
516
|
);
|
|
525
517
|
}
|
|
526
518
|
|
|
527
|
-
|
|
519
|
+
mountClient({
|
|
528
520
|
component: App,
|
|
529
521
|
theme: {
|
|
530
522
|
bg: "#0a0a0f",
|
|
@@ -71,7 +71,6 @@ export interface Incident {
|
|
|
71
71
|
createdAt: number;
|
|
72
72
|
updatedAt: number;
|
|
73
73
|
escalationLevel: number;
|
|
74
|
-
protocolsActivated: string[];
|
|
75
74
|
casualties: { confirmed: number; estimated: number; treated: number };
|
|
76
75
|
hazards: string[];
|
|
77
76
|
}
|
|
@@ -82,7 +81,6 @@ export interface DispatchState {
|
|
|
82
81
|
incidentCounter: number;
|
|
83
82
|
mutualAidCounter: number;
|
|
84
83
|
alertLevel: "green" | "yellow" | "orange" | "red";
|
|
85
|
-
mutualAidRequested: boolean;
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
/**
|
|
@@ -157,7 +155,6 @@ export function createDefaultState(): DispatchState {
|
|
|
157
155
|
incidentCounter: 0,
|
|
158
156
|
mutualAidCounter: 0,
|
|
159
157
|
alertLevel: "green",
|
|
160
|
-
mutualAidRequested: false,
|
|
161
158
|
};
|
|
162
159
|
}
|
|
163
160
|
|
|
@@ -348,7 +345,6 @@ export function createIncident(state: DispatchState, overrides: Partial<Incident
|
|
|
348
345
|
createdAt: time,
|
|
349
346
|
updatedAt: time,
|
|
350
347
|
escalationLevel: 0,
|
|
351
|
-
protocolsActivated: [],
|
|
352
348
|
casualties: { confirmed: 0, estimated: 0, treated: 0 },
|
|
353
349
|
hazards: [],
|
|
354
350
|
...overrides,
|
|
@@ -730,8 +726,4 @@ export function recalculateAlertLevel(state: DispatchState): void {
|
|
|
730
726
|
} else {
|
|
731
727
|
state.alertLevel = "green";
|
|
732
728
|
}
|
|
733
|
-
|
|
734
|
-
// Mutual aid tracks system posture: requested at red alert, stood down
|
|
735
|
-
// when the alert level drops back below red.
|
|
736
|
-
state.mutualAidRequested = state.alertLevel === "red";
|
|
737
729
|
}
|
|
@@ -25,7 +25,10 @@ export default dispatchSlot.tool({
|
|
|
25
25
|
|
|
26
26
|
return {
|
|
27
27
|
systemAlertLevel: state.alertLevel,
|
|
28
|
-
|
|
28
|
+
// Derived, not stored: mutual aid is requested at red alert and stood
|
|
29
|
+
// down when the level drops back below it, so the alert level IS the
|
|
30
|
+
// fact and the two can never disagree.
|
|
31
|
+
mutualAidActive: state.alertLevel === "red",
|
|
29
32
|
resourceUtilization: `${utilization}%`,
|
|
30
33
|
resourceSummary,
|
|
31
34
|
activeIncidentCount: activeIncidents.length,
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
/**
|
|
14
14
|
* The def a DEPLOYED agent runs: authored, plus what `tools/` declares.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Taken from `virtual:aai/agent` rather than a hand-written glob: the plugin
|
|
17
|
+
* expands it against THIS file's own directory, so the spec needs no glob and
|
|
18
|
+
* no shared helper — which matters because this file SHIPS, and a scaffolded
|
|
19
|
+
* project has no repo helper to import. Without
|
|
18
20
|
* it this agent would have no tools and would answer every question from the
|
|
19
21
|
* model's own memory, which is exactly what the cases below forbid.
|
|
20
22
|
*
|
|
@@ -22,8 +22,10 @@
|
|
|
22
22
|
/**
|
|
23
23
|
* The def a DEPLOYED agent runs: authored, plus what `tools/` declares.
|
|
24
24
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* Taken from `virtual:aai/agent` rather than a hand-written glob: the plugin
|
|
26
|
+
* expands it against THIS file's own directory, so the spec needs no glob and
|
|
27
|
+
* no shared helper — which matters because this file SHIPS, and a scaffolded
|
|
28
|
+
* project has no repo helper to import. Without
|
|
27
29
|
* it the desk would have no tools and would answer every one of these from
|
|
28
30
|
* memory, which is the failure these cases exist to catch.
|
|
29
31
|
*
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import "@alexkroman1/aai-ui/styles.css";
|
|
2
|
-
import type { AgentState
|
|
2
|
+
import type { AgentState } from "@alexkroman1/aai-ui";
|
|
3
3
|
import {
|
|
4
4
|
AutoScroll,
|
|
5
|
-
|
|
5
|
+
mountClient,
|
|
6
6
|
useConversation,
|
|
7
|
-
|
|
7
|
+
useSessionActions,
|
|
8
|
+
useSessionError,
|
|
8
9
|
useSessionSelector,
|
|
9
10
|
useTheme,
|
|
10
11
|
} from "@alexkroman1/aai-ui";
|
|
@@ -98,7 +99,7 @@ function Transcript() {
|
|
|
98
99
|
// The one theme read left in this file, and the case `useTheme()` is still
|
|
99
100
|
// for: `scrollbar-color` takes TWO values and has no utility class, so it has
|
|
100
101
|
// to be a style — and reading it off the theme beats re-pinning the two hex
|
|
101
|
-
// codes the `
|
|
102
|
+
// codes the `mountClient({ theme })` block below already declares.
|
|
102
103
|
const theme = useTheme();
|
|
103
104
|
|
|
104
105
|
return (
|
|
@@ -146,8 +147,16 @@ function Transcript() {
|
|
|
146
147
|
{transcript.text}
|
|
147
148
|
</div>
|
|
148
149
|
)}
|
|
150
|
+
{/* Same contract as the shipped `MessageList`'s indicator: the blinking
|
|
151
|
+
block is the only sign the parser is working, and to a screen reader it
|
|
152
|
+
is one unpronounceable glyph. */}
|
|
149
153
|
{thinking && (
|
|
150
|
-
<div
|
|
154
|
+
<div
|
|
155
|
+
role="status"
|
|
156
|
+
aria-label="The parser is thinking"
|
|
157
|
+
className="animate-pulse"
|
|
158
|
+
style={{ color: GREEN_DIM }}
|
|
159
|
+
>
|
|
151
160
|
▌
|
|
152
161
|
</div>
|
|
153
162
|
)}
|
|
@@ -155,12 +164,18 @@ function Transcript() {
|
|
|
155
164
|
);
|
|
156
165
|
}
|
|
157
166
|
|
|
167
|
+
/** Module scope, so the selector has a STABLE identity: an inline arrow makes
|
|
168
|
+
* `useSyncExternalStoreWithSelector` rebuild its selection memo every render,
|
|
169
|
+
* and this runs on every snapshot push — each STT partial and streaming
|
|
170
|
+
* delta. Counting with `reduce` rather than `filter().length` for the same
|
|
171
|
+
* reason: the array copy is thrown away, and it grows for the whole adventure. */
|
|
172
|
+
const userTurns = (snapshot: { messages: { role: string }[] }): number =>
|
|
173
|
+
snapshot.messages.reduce((n, message) => (message.role === "user" ? n + 1 : n), 0);
|
|
174
|
+
|
|
158
175
|
/** The turn counter — a NUMBER out of the selector, so a new message array with
|
|
159
176
|
* the same user-message count re-renders nothing. */
|
|
160
177
|
function TurnCount() {
|
|
161
|
-
const turns = useSessionSelector(
|
|
162
|
-
(snapshot) => snapshot.messages.filter((message) => message.role === "user").length,
|
|
163
|
-
);
|
|
178
|
+
const turns = useSessionSelector(userTurns);
|
|
164
179
|
return <span>Turns: {turns}</span>;
|
|
165
180
|
}
|
|
166
181
|
|
|
@@ -207,7 +222,7 @@ function StatusDot() {
|
|
|
207
222
|
* never told an unannounced one appeared.
|
|
208
223
|
*/
|
|
209
224
|
function ErrorBanner() {
|
|
210
|
-
const error =
|
|
225
|
+
const error = useSessionError();
|
|
211
226
|
if (!error) return null;
|
|
212
227
|
return (
|
|
213
228
|
<div
|
|
@@ -215,33 +230,22 @@ function ErrorBanner() {
|
|
|
215
230
|
className="px-5 py-2 text-xs"
|
|
216
231
|
style={{ background: "#3a0000", color: "#ff4141" }}
|
|
217
232
|
>
|
|
218
|
-
ERROR: {error.message}
|
|
233
|
+
ERROR: {error.message} ({error.code})
|
|
219
234
|
</div>
|
|
220
235
|
);
|
|
221
236
|
}
|
|
222
237
|
|
|
223
|
-
|
|
224
|
-
*
|
|
238
|
+
/*
|
|
239
|
+
* Pause/resume, new game and hang-up.
|
|
225
240
|
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* resume identity, so the redial is a brand-new session (fresh world, opening
|
|
231
|
-
* scene included), and `start()` puts the player straight into it rather than
|
|
232
|
-
* back at the title screen.
|
|
241
|
+
* `useSessionActions()` is the narrow way `<Controls>` reaches the methods, and
|
|
242
|
+
* it is published now — so this row takes the three it presses and one
|
|
243
|
+
* selector for the flag it reads, instead of a whole-snapshot `useSession()`
|
|
244
|
+
* that re-rendered the footer on every partial transcript.
|
|
233
245
|
*/
|
|
234
|
-
function newConversation(session: Session): void {
|
|
235
|
-
session.end();
|
|
236
|
-
session.start();
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/** Pause/resume, new game and hang-up. The only place a whole-session
|
|
240
|
-
* subscription is still needed: the ACTIONS live on `useSession()`, and
|
|
241
|
-
* `useSessionCore` — the narrow way `<Controls>` reaches them — is not on the
|
|
242
|
-
* public surface. */
|
|
243
246
|
function Footer() {
|
|
244
|
-
const
|
|
247
|
+
const { toggle, restart, end } = useSessionActions();
|
|
248
|
+
const running = useSessionSelector((snapshot) => snapshot.running);
|
|
245
249
|
return (
|
|
246
250
|
<div
|
|
247
251
|
className="flex items-center justify-between px-5 py-2 shrink-0 gap-3"
|
|
@@ -253,9 +257,9 @@ function Footer() {
|
|
|
253
257
|
type="button"
|
|
254
258
|
className="px-4 py-1 bg-transparent cursor-pointer uppercase tracking-wider font-mono text-[11px]"
|
|
255
259
|
style={{ color: GREEN_DIM, border: `1px solid ${GREEN_DARK}` }}
|
|
256
|
-
onClick={
|
|
260
|
+
onClick={toggle}
|
|
257
261
|
>
|
|
258
|
-
{
|
|
262
|
+
{running ? "[P]ause" : "[R]esume"}
|
|
259
263
|
</button>
|
|
260
264
|
{/* The one-click new conversation the default shell's `<Controls>`
|
|
261
265
|
gives every other template — a custom `component:` renders no
|
|
@@ -266,7 +270,7 @@ function Footer() {
|
|
|
266
270
|
type="button"
|
|
267
271
|
className="px-4 py-1 bg-transparent cursor-pointer uppercase tracking-wider font-mono text-[11px]"
|
|
268
272
|
style={{ color: GREEN_DIM, border: `1px solid ${GREEN_DARK}` }}
|
|
269
|
-
onClick={
|
|
273
|
+
onClick={restart}
|
|
270
274
|
>
|
|
271
275
|
[N]ew Game
|
|
272
276
|
</button>
|
|
@@ -276,7 +280,7 @@ function Footer() {
|
|
|
276
280
|
type="button"
|
|
277
281
|
className="px-4 py-1 bg-transparent cursor-pointer uppercase tracking-wider font-mono text-[11px]"
|
|
278
282
|
style={{ color: GREEN_DIM, border: `1px solid ${GREEN_DARK}` }}
|
|
279
|
-
onClick={() =>
|
|
283
|
+
onClick={() => end()}
|
|
280
284
|
>
|
|
281
285
|
[Q]uit
|
|
282
286
|
</button>
|
|
@@ -315,7 +319,7 @@ function Crt({ children }: { children: ReactNode }) {
|
|
|
315
319
|
}
|
|
316
320
|
|
|
317
321
|
function TitleScreen() {
|
|
318
|
-
const
|
|
322
|
+
const { start } = useSessionActions();
|
|
319
323
|
return (
|
|
320
324
|
<Crt>
|
|
321
325
|
<div
|
|
@@ -345,7 +349,7 @@ function TitleScreen() {
|
|
|
345
349
|
border: `1px solid ${GREEN}`,
|
|
346
350
|
animation: "ic-pulse 2s ease-in-out infinite",
|
|
347
351
|
}}
|
|
348
|
-
onClick={
|
|
352
|
+
onClick={start}
|
|
349
353
|
>
|
|
350
354
|
Begin Adventure
|
|
351
355
|
</button>
|
|
@@ -381,7 +385,7 @@ function InfocomAdventure() {
|
|
|
381
385
|
);
|
|
382
386
|
}
|
|
383
387
|
|
|
384
|
-
|
|
388
|
+
mountClient({
|
|
385
389
|
component: InfocomAdventure,
|
|
386
390
|
theme: {
|
|
387
391
|
bg: CRT_BG,
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import {
|
|
29
|
-
|
|
29
|
+
createWorkflowContext,
|
|
30
30
|
schemaInputIssues,
|
|
31
31
|
stubGatewayRoute,
|
|
32
32
|
stubStepInfo,
|
|
@@ -170,7 +170,7 @@ describe("fetchArticle", () => {
|
|
|
170
170
|
});
|
|
171
171
|
|
|
172
172
|
test("fails FATALLY on a 404 and plainly on a 503", async () => {
|
|
173
|
-
// `
|
|
173
|
+
// `stepFetchOrFail` builds the message, so it is the SDK's one spelling for a
|
|
174
174
|
// bad response — the REQUEST, the status, and a preview of any body.
|
|
175
175
|
stubPage("", 404);
|
|
176
176
|
await expect(fetchArticle("https://example.com/gone")).rejects.toThrow(
|
|
@@ -264,7 +264,7 @@ describe("summarize", () => {
|
|
|
264
264
|
// which is the only place the policy is observable at all. `runSteps: false`
|
|
265
265
|
// because the subject is the declared policy rather than the work: the steps
|
|
266
266
|
// would otherwise need a page and a model.
|
|
267
|
-
const ctx =
|
|
267
|
+
const ctx = createWorkflowContext({ runSteps: false });
|
|
268
268
|
await digestFlow({ url: "https://example.com/a" }, ctx);
|
|
269
269
|
|
|
270
270
|
const summarizeStep = ctx.steps.find((step) => step.name === "summarize");
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* the entire product, and the declaration says so:
|
|
14
14
|
*
|
|
15
15
|
* - `workflowApp()` is `agent({ …, page: "static" })` with the discriminant
|
|
16
|
-
* already set. The declaration is not decoration: `
|
|
17
|
-
* `/websocket` with a reason (so a page mounted with `
|
|
16
|
+
* already set. The declaration is not decoration: `createRuntimeServer` declines
|
|
17
|
+
* `/websocket` with a reason (so a page mounted with `mountClient()` by mistake
|
|
18
18
|
* fails the same way in `aai dev` and in production rather than only after a
|
|
19
19
|
* deploy) and telephony defaults off.
|
|
20
20
|
* - There is no `stt`/`llm`/`tts`, no `tools` and no `systemPrompt` — and they
|