@alexkroman1/aai-cli 6.11.0 → 8.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/README.md +1 -0
- package/dist/{_agent-DpH2pBJd.mjs → _agent-BTwZJu4P.mjs} +2 -2
- package/dist/{_bundler-C2BDwU7r.mjs → _bundler-CDuYl5Gb.mjs} +2 -2
- package/dist/_dev-env.d.ts +22 -2
- package/dist/{_dev-server-LLLWnFBs.mjs → _dev-server-Me06wgeF.mjs} +38 -47
- package/dist/_dev-server.d.ts +0 -17
- package/dist/{_init-C57bWAQ8.mjs → _init-BhEfQ6Yi.mjs} +3 -2
- package/dist/{_preflight-BtfaYtbE.mjs → _preflight-Co1G9Jww.mjs} +1 -1
- package/dist/_preflight.d.ts +1 -1
- package/dist/_resource-commands.d.ts +23 -0
- package/dist/{_slug-api-BxrsJXVI.mjs → _slug-api-z-USxPtL.mjs} +16 -12
- package/dist/_slug-api.d.ts +5 -2
- package/dist/{_studio-U3jhKlA3.mjs → _studio-DR5yzZ1T.mjs} +2 -1
- package/dist/{_templates-Dxb_P2Wz.mjs → _templates-4WcKOjS5.mjs} +5 -6
- package/dist/_templates.d.ts +3 -4
- package/dist/_workflow-scan.d.ts +108 -0
- package/dist/{build-Dfza2pRI.mjs → build-BBxf8vom.mjs} +6 -3
- package/dist/cli.mjs +346 -349
- package/dist/{client-bundler-BEIqgOtd.mjs → client-bundler-CWnG42cU.mjs} +2 -1
- package/dist/client-bundler.mjs +1 -1
- package/dist/{delete-vbpjGzqs.mjs → delete-Dl_LSfvI.mjs} +3 -3
- package/dist/{deploy-B8lubiRT.mjs → deploy-DRC8jWr8.mjs} +5 -5
- package/dist/{dev-Uq5ujP8a.mjs → dev-BvcctCn2.mjs} +1 -1
- package/dist/eval-DfQ8cm5p.mjs +56 -0
- package/dist/eval.d.ts +43 -0
- package/dist/{init-l6xfU4xX.mjs → init-CUo87gcI.mjs} +3 -3
- package/dist/{login-C71-qz8F.mjs → login-BBuM1sxH.mjs} +3 -4
- package/dist/{logs-BWVGSjLR.mjs → logs-CQ2T46dr.mjs} +2 -2
- package/dist/project-config.mjs +1 -1
- package/dist/scaffold/CLAUDE.md +442 -80
- package/dist/scaffold/package.json +6 -3
- package/dist/scaffold/server.mjs +11 -4
- package/dist/scaffold/tsconfig.json +0 -1
- package/dist/{secret-BuMuFR4B.mjs → secret-Ct34kdgU.mjs} +2 -2
- package/dist/{storage-DzRZ-eCw.mjs → storage-i_79ZwAR.mjs} +2 -2
- package/dist/{studio-BCNUpDgP.mjs → studio-CzTzPqe1.mjs} +4 -4
- package/dist/templates/briefing-desk/agent.test.ts +350 -0
- package/dist/templates/briefing-desk/agent.ts +30 -0
- package/dist/templates/briefing-desk/shared.ts +200 -0
- package/dist/templates/briefing-desk/system-prompt.md +39 -0
- package/dist/templates/briefing-desk/tools/briefing_so_far.ts +35 -0
- package/dist/templates/briefing-desk/tools/research_topic.ts +88 -0
- package/dist/templates/briefing-desk/tools/verify_claim.ts +77 -0
- package/dist/templates/call-audit/agent.eval.test.ts +474 -0
- package/dist/templates/call-audit/agent.test.ts +57 -204
- package/dist/templates/call-audit/agent.ts +32 -19
- package/dist/templates/call-audit/client.tsx +17 -54
- package/dist/templates/call-audit/workflows/audit.ts +11 -18
- package/dist/templates/call-audit/workflows/ingest.ts +106 -114
- package/dist/templates/call-audit/workflows/media.ts +2 -12
- package/dist/templates/call-audit/workflows/summarize.ts +47 -52
- package/dist/templates/call-audit/workflows/sync-api.ts +7 -7
- package/dist/templates/code-interpreter/agent.eval.test.ts +165 -0
- package/dist/templates/dispatch-center/agent.eval.test.ts +349 -0
- package/dist/templates/dispatch-center/agent.test.ts +149 -25
- package/dist/templates/dispatch-center/client.tsx +239 -129
- package/dist/templates/dispatch-center/shared.ts +99 -1
- package/dist/templates/dispatch-center/system-prompt.md +3 -1
- package/dist/templates/dispatch-center/tools/incident_add_note.ts +16 -13
- package/dist/templates/dispatch-center/tools/incident_create.ts +19 -1
- package/dist/templates/dispatch-center/tools/incident_escalate.ts +68 -59
- package/dist/templates/dispatch-center/tools/incident_triage.ts +57 -43
- package/dist/templates/dispatch-center/tools/incident_update_status.ts +49 -40
- package/dist/templates/dispatch-center/tools/ops_run_scenario.ts +8 -2
- package/dist/templates/dispatch-center/tools/resources_dispatch.ts +69 -52
- package/dist/templates/dispatch-center/tools/resources_update_status.ts +45 -41
- package/dist/templates/embedded-assets/agent.eval.test.ts +138 -0
- package/dist/templates/embedded-assets/agent.test.ts +17 -14
- package/dist/templates/health-assistant/agent.eval.test.ts +167 -0
- package/dist/templates/health-assistant/agent.test.ts +22 -4
- package/dist/templates/infocom-adventure/agent.eval.test.ts +158 -0
- package/dist/templates/infocom-adventure/agent.test.ts +53 -24
- package/dist/templates/infocom-adventure/agent.ts +21 -0
- package/dist/templates/infocom-adventure/client.tsx +246 -164
- package/dist/templates/infocom-adventure/shared.ts +20 -9
- package/dist/templates/infocom-adventure/system-prompt.md +1 -1
- package/dist/templates/infocom-adventure/tools/game_state_move.ts +3 -2
- package/dist/templates/link-digest/agent.eval.test.ts +195 -0
- package/dist/templates/link-digest/agent.test.ts +24 -19
- package/dist/templates/link-digest/client.tsx +47 -61
- package/dist/templates/link-digest/workflows/digest.ts +19 -31
- package/dist/templates/math-buddy/agent.eval.test.ts +146 -0
- package/dist/templates/night-owl/agent.eval.test.ts +272 -0
- package/dist/templates/night-owl/agent.test.ts +70 -19
- package/dist/templates/night-owl/agent.ts +5 -0
- package/dist/templates/night-owl/client.tsx +56 -56
- package/dist/templates/night-owl/shared.ts +24 -0
- package/dist/templates/night-owl/tools/recommend.ts +18 -6
- package/dist/templates/personal-finance/agent.eval.test.ts +168 -0
- package/dist/templates/personal-finance/system-prompt.md +4 -0
- package/dist/templates/pipeline-simple/agent.eval.test.ts +78 -0
- package/dist/templates/pipeline-simple/agent.ts +2 -2
- package/dist/templates/pizza-ordering/agent.eval.test.ts +171 -0
- package/dist/templates/pizza-ordering/agent.test.ts +42 -27
- package/dist/templates/pizza-ordering/client.tsx +9 -26
- package/dist/templates/pizza-ordering/shared.ts +20 -1
- package/dist/templates/pizza-ordering/system-prompt.md +6 -0
- package/dist/templates/plan-and-execute/agent.eval.test.ts +209 -0
- package/dist/templates/plan-and-execute/agent.test.ts +99 -58
- package/dist/templates/plan-and-execute/agent.ts +1 -1
- package/dist/templates/plan-and-execute/client.tsx +12 -15
- package/dist/templates/plan-and-execute/shared.ts +71 -2
- package/dist/templates/plan-and-execute/tools/plan_status.ts +18 -6
- package/dist/templates/plan-and-execute/tools/revise_plan.ts +18 -10
- package/dist/templates/plan-and-execute/tools/start_plan.ts +13 -2
- package/dist/templates/plan-and-execute/tools/work_next_step.ts +70 -22
- package/dist/templates/podcast-digest/agent.eval.test.ts +411 -0
- package/dist/templates/podcast-digest/agent.test.ts +717 -0
- package/dist/templates/podcast-digest/agent.ts +139 -0
- package/dist/templates/podcast-digest/client.tsx +154 -0
- package/dist/templates/podcast-digest/workflows/digest.ts +444 -0
- package/dist/templates/podcast-digest/workflows/feeds.ts +507 -0
- package/dist/templates/podcast-digest/workflows/slack.ts +88 -0
- package/dist/templates/recap-workflow/agent.eval.test.ts +366 -0
- package/dist/templates/recap-workflow/agent.test.ts +97 -94
- package/dist/templates/recap-workflow/tools/recap_progress.ts +9 -12
- package/dist/templates/recap-workflow/tools/recap_status.ts +8 -9
- package/dist/templates/recap-workflow/tools/request_recap.ts +2 -1
- package/dist/templates/recap-workflow/workflows/recap.ts +36 -40
- package/dist/templates/redline/agent.eval.test.ts +209 -0
- package/dist/templates/redline/agent.test.ts +17 -15
- package/dist/templates/redline/client.tsx +12 -12
- package/dist/templates/redline/workflows/redline.ts +19 -31
- package/dist/templates/research-workflow/agent.eval.test.ts +329 -0
- package/dist/templates/research-workflow/agent.test.ts +60 -59
- package/dist/templates/research-workflow/tools/research_progress.ts +9 -12
- package/dist/templates/research-workflow/tools/research_status.ts +9 -11
- package/dist/templates/research-workflow/workflows/research.ts +44 -61
- package/dist/templates/retail/address.ts +87 -1
- package/dist/templates/retail/agent.eval.test.ts +314 -0
- package/dist/templates/retail/agent.test.ts +195 -112
- package/dist/templates/retail/agent.ts +35 -1
- package/dist/templates/retail/cancel.ts +116 -0
- package/dist/templates/retail/client.tsx +265 -117
- package/dist/templates/retail/payment.ts +107 -0
- package/dist/templates/retail/pending.test.ts +196 -0
- package/dist/templates/retail/pending.ts +137 -0
- package/dist/templates/retail/registry.test.ts +150 -7
- package/dist/templates/retail/returns.ts +102 -0
- package/dist/templates/retail/shared.test.ts +42 -0
- package/dist/templates/retail/shared.ts +35 -0
- package/dist/templates/retail/store.test.ts +84 -15
- package/dist/templates/retail/store.ts +231 -48
- package/dist/templates/retail/swap.test.ts +4 -4
- package/dist/templates/retail/swap.ts +248 -15
- package/dist/templates/retail/system-prompt.md +69 -28
- package/dist/templates/retail/tools/cancel_change.ts +33 -0
- package/dist/templates/retail/tools/cancel_pending_order.ts +16 -60
- package/dist/templates/retail/tools/confirm_change.ts +42 -0
- package/dist/templates/retail/tools/exchange_delivered_order_items.ts +22 -67
- package/dist/templates/retail/tools/find_user_id_by_email.ts +8 -5
- package/dist/templates/retail/tools/find_user_id_by_name_zip.ts +4 -5
- package/dist/templates/retail/tools/get_item_details.ts +3 -4
- package/dist/templates/retail/tools/get_order_details.ts +3 -4
- package/dist/templates/retail/tools/get_product_details.ts +3 -4
- package/dist/templates/retail/tools/get_user_details.ts +2 -2
- package/dist/templates/retail/tools/list_all_product_types.ts +5 -5
- package/dist/templates/retail/tools/modify_pending_order_address.ts +13 -25
- package/dist/templates/retail/tools/modify_pending_order_items.ts +23 -62
- package/dist/templates/retail/tools/modify_pending_order_payment.ts +16 -78
- package/dist/templates/retail/tools/modify_user_address.ts +13 -16
- package/dist/templates/retail/tools/return_delivered_order_items.ts +17 -72
- package/dist/templates/retail/tools/transfer_to_human_agents.ts +12 -6
- package/dist/templates/simple/agent.eval.test.ts +54 -0
- package/dist/templates/solo-rpg/agent.eval.test.ts +320 -0
- package/dist/templates/solo-rpg/agent.test.ts +227 -48
- package/dist/templates/solo-rpg/client.tsx +38 -37
- package/dist/templates/solo-rpg/shared.ts +145 -19
- package/dist/templates/solo-rpg/system-prompt.md +3 -2
- package/dist/templates/solo-rpg/tools/action_roll.ts +82 -63
- package/dist/templates/solo-rpg/tools/burn_momentum.ts +85 -50
- package/dist/templates/solo-rpg/tools/check_state.ts +24 -7
- package/dist/templates/solo-rpg/tools/load_game.ts +13 -1
- package/dist/templates/solo-rpg/tools/save_game.ts +16 -7
- package/dist/templates/solo-rpg/tools/setup_character.ts +22 -2
- package/dist/templates/solo-rpg/tools/update_state.ts +113 -100
- package/dist/templates/spoken-summary/agent.eval.test.ts +342 -0
- package/dist/templates/spoken-summary/agent.test.ts +68 -97
- package/dist/templates/spoken-summary/agent.ts +30 -17
- package/dist/templates/spoken-summary/client.tsx +10 -50
- package/dist/templates/spoken-summary/workflows/summarize.ts +17 -22
- package/dist/templates/spoken-summary/workflows/transcribe.ts +19 -26
- package/dist/templates/support-line/agent.eval.test.ts +273 -0
- package/dist/templates/support-line/agent.test.ts +11 -16
- package/dist/templates/support-line/agent.ts +1 -1
- package/dist/templates/support-line/client.tsx +9 -9
- package/dist/templates/support-line/nodes.ts +100 -0
- package/dist/templates/support-line/procedure.ts +407 -0
- package/dist/templates/support-line/system-prompt.md +2 -1
- package/dist/templates/support-line/tools/answer_question.ts +66 -4
- package/dist/templates/transcription-workflow/agent.eval.test.ts +234 -0
- package/dist/templates/transcription-workflow/agent.test.ts +94 -194
- package/dist/templates/transcription-workflow/agent.ts +1 -1
- package/dist/templates/transcription-workflow/client.tsx +17 -42
- package/dist/templates/transcription-workflow/workflows/batch.ts +19 -22
- package/dist/templates/transcription-workflow/workflows/normalize.ts +90 -186
- package/dist/templates/transcription-workflow/workflows/stitch.ts +0 -11
- package/dist/templates/transcription-workflow/workflows/stream.ts +4 -5
- package/dist/templates/transcription-workflow/workflows/sync-api.ts +6 -6
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +32 -26
- package/dist/templates/transcription-workflow/workflows/wav.ts +0 -31
- package/dist/templates/travel-concierge/agent.eval.test.ts +323 -0
- package/dist/templates/travel-concierge/agent.test.ts +174 -43
- package/dist/templates/travel-concierge/agent.ts +18 -9
- package/dist/templates/travel-concierge/client.tsx +11 -23
- package/dist/templates/travel-concierge/routing.ts +34 -15
- package/dist/templates/travel-concierge/shared.ts +180 -12
- package/dist/templates/travel-concierge/system-prompt.md +7 -1
- package/dist/templates/travel-concierge/tools/book_car_rental.ts +8 -5
- package/dist/templates/travel-concierge/tools/book_excursion.ts +8 -5
- package/dist/templates/travel-concierge/tools/book_hotel.ts +8 -5
- package/dist/templates/travel-concierge/tools/cancel_ticket.ts +8 -5
- package/dist/templates/travel-concierge/tools/search_car_rentals.ts +9 -5
- package/dist/templates/travel-concierge/tools/search_excursions.ts +9 -5
- package/dist/templates/travel-concierge/tools/search_flights.ts +9 -6
- package/dist/templates/travel-concierge/tools/search_hotels.ts +9 -5
- package/dist/templates/travel-concierge/tools/update_ticket.ts +8 -5
- package/dist/templates/web-researcher/agent.eval.test.ts +121 -0
- package/dist/templates/web-researcher/system-prompt.md +15 -4
- package/dist/{test-9kPKJI-w.mjs → test-BvyT6sAc.mjs} +24 -10
- package/dist/test.d.ts +29 -5
- package/dist/{worker-bundler-CGD4r8Kc.mjs → worker-bundler-CCVEDjm1.mjs} +241 -3
- package/dist/worker-bundler.mjs +1 -1
- package/dist/{workflow-CFpxOFfQ.mjs → workflow-BflATMmU.mjs} +47 -12
- package/dist/workflow-bundler.d.ts +6 -1
- package/dist/workflow.d.ts +1 -1
- package/package.json +5 -4
- package/dist/eject-31gjtaHF.mjs +0 -116
- package/dist/eject.d.ts +0 -60
- package/dist/templates/call-audit/workflows/temp-media.ts +0 -138
- package/dist/templates/infocom-adventure/tools/game_state_history.ts +0 -14
- package/dist/templates/support-line/graph.ts +0 -224
- package/dist/{_config-D_s09e7g.mjs → _config-CmJOFsAP.mjs} +1 -1
- /package/dist/templates/plan-and-execute/{graph.ts → procedure.ts} +0 -0
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import "@alexkroman1/aai-ui/styles.css";
|
|
2
|
-
import type { AgentState
|
|
3
|
-
import {
|
|
2
|
+
import type { AgentState } from "@alexkroman1/aai-ui";
|
|
3
|
+
import {
|
|
4
|
+
AutoScroll,
|
|
5
|
+
client,
|
|
6
|
+
useConversation,
|
|
7
|
+
useSession,
|
|
8
|
+
useSessionSelector,
|
|
9
|
+
useTheme,
|
|
10
|
+
} from "@alexkroman1/aai-ui";
|
|
11
|
+
import type { ReactNode } from "react";
|
|
4
12
|
|
|
5
13
|
const CSS = `
|
|
6
14
|
@keyframes ic-flicker {
|
|
@@ -71,89 +79,187 @@ const STATE_COLORS = {
|
|
|
71
79
|
error: GREEN_DARK,
|
|
72
80
|
} satisfies Record<AgentState, string>;
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
/**
|
|
83
|
+
* The exchange, in the CRT idiom.
|
|
84
|
+
*
|
|
85
|
+
* `useConversation()` rather than `session.messages`: the four decisions
|
|
86
|
+
* `<MessageList>` makes — the message/tool-call interleave, the streaming
|
|
87
|
+
* narrator line, the transcript's `null`-vs-`""` distinction and the thinking
|
|
88
|
+
* rule — are the hook's now, so this renders them instead of shipping a
|
|
89
|
+
* conversation missing all four. The old list read `messages` alone, so the
|
|
90
|
+
* eight `game_state_*` calls and every partial of the narrator's reply were
|
|
91
|
+
* invisible: the screen sat still until a whole utterance finalized.
|
|
92
|
+
*
|
|
93
|
+
* It also subscribes per FIELD, so a partial transcript no longer re-renders
|
|
94
|
+
* the status bar, the footer and the CRT overlays with it.
|
|
95
|
+
*/
|
|
96
|
+
function Transcript() {
|
|
97
|
+
const { items, streaming, transcript, thinking } = useConversation();
|
|
98
|
+
// The one theme read left in this file, and the case `useTheme()` is still
|
|
99
|
+
// for: `scrollbar-color` takes TWO values and has no utility class, so it has
|
|
100
|
+
// to be a style — and reading it off the theme beats re-pinning the two hex
|
|
101
|
+
// codes the `client({ theme })` block below already declares.
|
|
102
|
+
const theme = useTheme();
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<AutoScroll
|
|
106
|
+
scrollClassName="ic-messages overflow-y-auto"
|
|
107
|
+
contentClassName="p-5"
|
|
108
|
+
style={{ scrollbarWidth: "thin", scrollbarColor: `${theme.primary} ${theme.surface}` }}
|
|
109
|
+
>
|
|
110
|
+
{items.map((item) =>
|
|
111
|
+
item.kind === "message" ? (
|
|
112
|
+
<div
|
|
113
|
+
key={item.message.id}
|
|
114
|
+
className={`mb-4 ${item.message.role === "user" ? "ic-user-msg" : ""}`}
|
|
115
|
+
style={{
|
|
116
|
+
textShadow:
|
|
117
|
+
item.message.role === "user"
|
|
118
|
+
? "0 0 5px rgba(0,204,255,0.3)"
|
|
119
|
+
: "0 0 5px rgba(0,255,65,0.3)",
|
|
120
|
+
color: item.message.role === "user" ? CYAN : GREEN,
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
{item.message.content}
|
|
124
|
+
</div>
|
|
125
|
+
) : (
|
|
126
|
+
// The game engine's own bookkeeping, in the idiom the machine would
|
|
127
|
+
// have printed it in. Dim, because it is beneath the narration and
|
|
128
|
+
// not instead of it.
|
|
129
|
+
<div key={item.toolCall.callId} className="mb-4 text-[13px]" style={{ color: GREEN_DIM }}>
|
|
130
|
+
{`[ ${item.toolCall.name.replace(/^game_state_/, "").replace(/_/g, " ")}${
|
|
131
|
+
item.toolCall.status === "pending" ? " …" : ""
|
|
132
|
+
} ]`}
|
|
133
|
+
</div>
|
|
134
|
+
),
|
|
135
|
+
)}
|
|
136
|
+
{streaming !== null && (
|
|
137
|
+
<div className="mb-4" style={{ color: GREEN, textShadow: "0 0 5px rgba(0,255,65,0.3)" }}>
|
|
138
|
+
{streaming}
|
|
139
|
+
</div>
|
|
140
|
+
)}
|
|
141
|
+
{transcript.speaking && (
|
|
142
|
+
<div
|
|
143
|
+
className="ic-transcript italic"
|
|
144
|
+
style={{ color: "#007a1e", textShadow: "0 0 5px rgba(0,255,65,0.15)" }}
|
|
145
|
+
>
|
|
146
|
+
{transcript.text}
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
{thinking && (
|
|
150
|
+
<div className="animate-pulse" style={{ color: GREEN_DIM }}>
|
|
151
|
+
▌
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
</AutoScroll>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
80
157
|
|
|
158
|
+
/** The turn counter — a NUMBER out of the selector, so a new message array with
|
|
159
|
+
* the same user-message count re-renders nothing. */
|
|
160
|
+
function TurnCount() {
|
|
161
|
+
const turns = useSessionSelector(
|
|
162
|
+
(snapshot) => snapshot.messages.filter((message) => message.role === "user").length,
|
|
163
|
+
);
|
|
164
|
+
return <span>Turns: {turns}</span>;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** The live state dot and its label. */
|
|
168
|
+
function StatusDot() {
|
|
169
|
+
const state = useSessionSelector((snapshot) => snapshot.state);
|
|
170
|
+
const dotColor = STATE_COLORS[state];
|
|
81
171
|
const stateLabel =
|
|
82
|
-
|
|
172
|
+
state === "listening"
|
|
83
173
|
? "Listening"
|
|
84
|
-
:
|
|
174
|
+
: state === "speaking"
|
|
85
175
|
? "Narrating"
|
|
86
|
-
:
|
|
176
|
+
: state === "thinking"
|
|
87
177
|
? "Thinking"
|
|
88
|
-
:
|
|
178
|
+
: state === "connecting"
|
|
89
179
|
? "Connecting"
|
|
90
|
-
:
|
|
180
|
+
: state === "ready"
|
|
91
181
|
? "Ready"
|
|
92
182
|
: "Idle";
|
|
93
183
|
|
|
94
|
-
|
|
184
|
+
return (
|
|
185
|
+
<div
|
|
186
|
+
className="flex items-center gap-2.5 text-xs uppercase tracking-wider"
|
|
187
|
+
style={{ color: GREEN_DIM }}
|
|
188
|
+
>
|
|
189
|
+
<div
|
|
190
|
+
className="w-2 h-2 rounded-full"
|
|
191
|
+
style={{
|
|
192
|
+
background: dotColor,
|
|
193
|
+
boxShadow: dotColor !== GREEN_DARK ? `0 0 6px ${dotColor}` : "none",
|
|
194
|
+
}}
|
|
195
|
+
/>
|
|
196
|
+
<span>{stateLabel}</span>
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
95
200
|
|
|
96
|
-
|
|
201
|
+
/**
|
|
202
|
+
* The fault line.
|
|
203
|
+
*
|
|
204
|
+
* `role="alert"` for the reason `ConsoleShell` gives: once `session-core`
|
|
205
|
+
* latches a fatal error the state eyebrow goes back to reading like a live
|
|
206
|
+
* session, so this banner is the only remaining signal — and a screen reader is
|
|
207
|
+
* never told an unannounced one appeared.
|
|
208
|
+
*/
|
|
209
|
+
function ErrorBanner() {
|
|
210
|
+
const error = useSessionSelector((snapshot) => snapshot.error);
|
|
211
|
+
if (!error) return null;
|
|
212
|
+
return (
|
|
213
|
+
<div
|
|
214
|
+
role="alert"
|
|
215
|
+
className="px-5 py-2 text-xs"
|
|
216
|
+
style={{ background: "#3a0000", color: "#ff4141" }}
|
|
217
|
+
>
|
|
218
|
+
ERROR: {error.message}
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
97
222
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
223
|
+
/** Pause/resume and hang-up. The only place a whole-session subscription is
|
|
224
|
+
* still needed: the ACTIONS live on `useSession()`, and `useSessionCore` — the
|
|
225
|
+
* narrow way `<Controls>` reaches them — is not on the public surface. */
|
|
226
|
+
function Footer() {
|
|
227
|
+
const session = useSession();
|
|
228
|
+
return (
|
|
229
|
+
<div
|
|
230
|
+
className="flex items-center justify-between px-5 py-2 shrink-0 gap-3"
|
|
231
|
+
style={{ borderTop: `1px solid ${GREEN_DARK}`, background: "#001100" }}
|
|
232
|
+
>
|
|
233
|
+
<StatusDot />
|
|
234
|
+
<div className="flex gap-2">
|
|
235
|
+
<button
|
|
236
|
+
type="button"
|
|
237
|
+
className="px-4 py-1 bg-transparent cursor-pointer uppercase tracking-wider font-mono text-[11px]"
|
|
238
|
+
style={{ color: GREEN_DIM, border: `1px solid ${GREEN_DARK}` }}
|
|
239
|
+
onClick={session.toggle}
|
|
112
240
|
>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
</div>
|
|
132
|
-
<button
|
|
133
|
-
type="button"
|
|
134
|
-
className="mt-10 px-12 py-3.5 bg-transparent cursor-pointer uppercase tracking-[3px] font-mono text-base"
|
|
135
|
-
style={{
|
|
136
|
-
color: GREEN,
|
|
137
|
-
border: `1px solid ${GREEN}`,
|
|
138
|
-
animation: "ic-pulse 2s ease-in-out infinite",
|
|
139
|
-
}}
|
|
140
|
-
onClick={session.start}
|
|
141
|
-
>
|
|
142
|
-
Begin Adventure
|
|
143
|
-
</button>
|
|
144
|
-
</div>
|
|
145
|
-
<div
|
|
146
|
-
className="fixed inset-0 pointer-events-none z-12"
|
|
147
|
-
style={{
|
|
148
|
-
background:
|
|
149
|
-
"radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,0.4) 100%)",
|
|
150
|
-
}}
|
|
151
|
-
/>
|
|
152
|
-
</div>
|
|
153
|
-
</>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
241
|
+
{session.running ? "[P]ause" : "[R]esume"}
|
|
242
|
+
</button>
|
|
243
|
+
{/* end() hangs up and drops the sessionId, so the
|
|
244
|
+
session-scoped game state starts over and the title
|
|
245
|
+
screen returns. (session.reset() keeps the same
|
|
246
|
+
sessionId and would resume the old game.) */}
|
|
247
|
+
<button
|
|
248
|
+
type="button"
|
|
249
|
+
className="px-4 py-1 bg-transparent cursor-pointer uppercase tracking-wider font-mono text-[11px]"
|
|
250
|
+
style={{ color: GREEN_DIM, border: `1px solid ${GREEN_DARK}` }}
|
|
251
|
+
onClick={() => session.end()}
|
|
252
|
+
>
|
|
253
|
+
[N]ew Game
|
|
254
|
+
</button>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
156
259
|
|
|
260
|
+
/** The CRT itself: the flicker, the scanlines and the vignette every screen
|
|
261
|
+
* sits inside. */
|
|
262
|
+
function Crt({ children }: { children: ReactNode }) {
|
|
157
263
|
return (
|
|
158
264
|
<>
|
|
159
265
|
<style>{CSS}</style>
|
|
@@ -168,98 +274,7 @@ function InfocomAdventure() {
|
|
|
168
274
|
animation: "ic-flicker 4s infinite",
|
|
169
275
|
}}
|
|
170
276
|
>
|
|
171
|
-
|
|
172
|
-
{/* Status bar */}
|
|
173
|
-
<div
|
|
174
|
-
className="flex items-center justify-between px-5 py-2 text-[13px] font-bold tracking-wider shrink-0"
|
|
175
|
-
style={{ background: GREEN, color: CRT_BG }}
|
|
176
|
-
>
|
|
177
|
-
<div className="flex gap-6">
|
|
178
|
-
<span>CAVERN ADVENTURE</span>
|
|
179
|
-
<span>Turns: {msgCount}</span>
|
|
180
|
-
</div>
|
|
181
|
-
<span>Voice Adventure</span>
|
|
182
|
-
</div>
|
|
183
|
-
|
|
184
|
-
{session.error && (
|
|
185
|
-
<div className="px-5 py-2 text-xs" style={{ background: "#3a0000", color: "#ff4141" }}>
|
|
186
|
-
ERROR: {session.error.message}
|
|
187
|
-
</div>
|
|
188
|
-
)}
|
|
189
|
-
|
|
190
|
-
{/* Messages */}
|
|
191
|
-
<AutoScroll
|
|
192
|
-
scrollClassName="ic-messages overflow-y-auto"
|
|
193
|
-
contentClassName="p-5"
|
|
194
|
-
style={{ scrollbarWidth: "thin", scrollbarColor: `${GREEN} #001a00` }}
|
|
195
|
-
>
|
|
196
|
-
{session.messages.map((msg: ChatMessage, i: number) => (
|
|
197
|
-
<div
|
|
198
|
-
key={i}
|
|
199
|
-
className={`mb-4 ${msg.role === "user" ? "ic-user-msg" : ""}`}
|
|
200
|
-
style={{
|
|
201
|
-
textShadow:
|
|
202
|
-
msg.role === "user"
|
|
203
|
-
? "0 0 5px rgba(0,204,255,0.3)"
|
|
204
|
-
: "0 0 5px rgba(0,255,65,0.3)",
|
|
205
|
-
color: msg.role === "user" ? CYAN : GREEN,
|
|
206
|
-
}}
|
|
207
|
-
>
|
|
208
|
-
{msg.content}
|
|
209
|
-
</div>
|
|
210
|
-
))}
|
|
211
|
-
{speaking && (
|
|
212
|
-
<div
|
|
213
|
-
className="ic-transcript italic"
|
|
214
|
-
style={{ color: "#007a1e", textShadow: "0 0 5px rgba(0,255,65,0.15)" }}
|
|
215
|
-
>
|
|
216
|
-
{userTranscript}
|
|
217
|
-
</div>
|
|
218
|
-
)}
|
|
219
|
-
</AutoScroll>
|
|
220
|
-
|
|
221
|
-
{/* Footer controls */}
|
|
222
|
-
<div
|
|
223
|
-
className="flex items-center justify-between px-5 py-2 shrink-0 gap-3"
|
|
224
|
-
style={{ borderTop: `1px solid ${GREEN_DARK}`, background: "#001100" }}
|
|
225
|
-
>
|
|
226
|
-
<div
|
|
227
|
-
className="flex items-center gap-2.5 text-xs uppercase tracking-wider"
|
|
228
|
-
style={{ color: GREEN_DIM }}
|
|
229
|
-
>
|
|
230
|
-
<div
|
|
231
|
-
className="w-2 h-2 rounded-full"
|
|
232
|
-
style={{
|
|
233
|
-
background: dotColor,
|
|
234
|
-
boxShadow: dotColor !== GREEN_DARK ? `0 0 6px ${dotColor}` : "none",
|
|
235
|
-
}}
|
|
236
|
-
/>
|
|
237
|
-
<span>{stateLabel}</span>
|
|
238
|
-
</div>
|
|
239
|
-
<div className="flex gap-2">
|
|
240
|
-
<button
|
|
241
|
-
type="button"
|
|
242
|
-
className="px-4 py-1 bg-transparent cursor-pointer uppercase tracking-wider font-mono text-[11px]"
|
|
243
|
-
style={{ color: GREEN_DIM, border: `1px solid ${GREEN_DARK}` }}
|
|
244
|
-
onClick={session.toggle}
|
|
245
|
-
>
|
|
246
|
-
{session.running ? "[P]ause" : "[R]esume"}
|
|
247
|
-
</button>
|
|
248
|
-
{/* end() hangs up and drops the sessionId, so the
|
|
249
|
-
session-scoped game state starts over and the title
|
|
250
|
-
screen returns. (session.reset() keeps the same
|
|
251
|
-
sessionId and would resume the old game.) */}
|
|
252
|
-
<button
|
|
253
|
-
type="button"
|
|
254
|
-
className="px-4 py-1 bg-transparent cursor-pointer uppercase tracking-wider font-mono text-[11px]"
|
|
255
|
-
style={{ color: GREEN_DIM, border: `1px solid ${GREEN_DARK}` }}
|
|
256
|
-
onClick={() => session.end()}
|
|
257
|
-
>
|
|
258
|
-
[N]ew Game
|
|
259
|
-
</button>
|
|
260
|
-
</div>
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
277
|
+
{children}
|
|
263
278
|
<div
|
|
264
279
|
className="fixed inset-0 pointer-events-none z-12"
|
|
265
280
|
style={{
|
|
@@ -271,6 +286,73 @@ function InfocomAdventure() {
|
|
|
271
286
|
);
|
|
272
287
|
}
|
|
273
288
|
|
|
289
|
+
function TitleScreen() {
|
|
290
|
+
const session = useSession();
|
|
291
|
+
return (
|
|
292
|
+
<Crt>
|
|
293
|
+
<div
|
|
294
|
+
className="flex flex-col items-center justify-center h-full text-center p-10"
|
|
295
|
+
style={{ animation: "ic-boot 1.5s ease-out" }}
|
|
296
|
+
>
|
|
297
|
+
<div
|
|
298
|
+
className="text-[11px] whitespace-pre mb-8"
|
|
299
|
+
style={{ textShadow: "0 0 10px rgba(0,255,65,0.5)" }}
|
|
300
|
+
>
|
|
301
|
+
{ASCII_LOGO}
|
|
302
|
+
</div>
|
|
303
|
+
<div className="text-[13px] mb-2" style={{ color: GREEN_DIM }}>
|
|
304
|
+
AN INTERACTIVE FICTION
|
|
305
|
+
</div>
|
|
306
|
+
<div className="text-[13px] mb-2" style={{ color: GREEN_DIM }}>
|
|
307
|
+
In the style of the classic text adventures.
|
|
308
|
+
</div>
|
|
309
|
+
<div className="text-[13px] mt-4" style={{ color: GREEN }}>
|
|
310
|
+
VOICE-ENABLED EDITION
|
|
311
|
+
</div>
|
|
312
|
+
<button
|
|
313
|
+
type="button"
|
|
314
|
+
className="mt-10 px-12 py-3.5 bg-transparent cursor-pointer uppercase tracking-[3px] font-mono text-base"
|
|
315
|
+
style={{
|
|
316
|
+
color: GREEN,
|
|
317
|
+
border: `1px solid ${GREEN}`,
|
|
318
|
+
animation: "ic-pulse 2s ease-in-out infinite",
|
|
319
|
+
}}
|
|
320
|
+
onClick={session.start}
|
|
321
|
+
>
|
|
322
|
+
Begin Adventure
|
|
323
|
+
</button>
|
|
324
|
+
</div>
|
|
325
|
+
</Crt>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function InfocomAdventure() {
|
|
330
|
+
const started = useSessionSelector((snapshot) => snapshot.started);
|
|
331
|
+
if (!started) return <TitleScreen />;
|
|
332
|
+
|
|
333
|
+
return (
|
|
334
|
+
<Crt>
|
|
335
|
+
<div className="flex flex-col h-full">
|
|
336
|
+
{/* Status bar */}
|
|
337
|
+
<div
|
|
338
|
+
className="flex items-center justify-between px-5 py-2 text-[13px] font-bold tracking-wider shrink-0"
|
|
339
|
+
style={{ background: GREEN, color: CRT_BG }}
|
|
340
|
+
>
|
|
341
|
+
<div className="flex gap-6">
|
|
342
|
+
<span>CAVERN ADVENTURE</span>
|
|
343
|
+
<TurnCount />
|
|
344
|
+
</div>
|
|
345
|
+
<span>Voice Adventure</span>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<ErrorBanner />
|
|
349
|
+
<Transcript />
|
|
350
|
+
<Footer />
|
|
351
|
+
</div>
|
|
352
|
+
</Crt>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
274
356
|
client({
|
|
275
357
|
component: InfocomAdventure,
|
|
276
358
|
theme: {
|
|
@@ -26,13 +26,7 @@ export const DEFAULT_GAME_STATE: GameState = {
|
|
|
26
26
|
*/
|
|
27
27
|
export const MAX_HISTORY = 50;
|
|
28
28
|
|
|
29
|
-
/**
|
|
30
|
-
* How many of those commands a tool reports back to the model.
|
|
31
|
-
*
|
|
32
|
-
* Named because two tools answer with it (`game_state_get` and
|
|
33
|
-
* `game_state_history`), and they live in separate files now — an inline `-5`
|
|
34
|
-
* in each is a number that can disagree with itself.
|
|
35
|
-
*/
|
|
29
|
+
/** How many of those commands `game_state_get` reports back to the model. */
|
|
36
30
|
export const REPORTED_HISTORY = 5;
|
|
37
31
|
|
|
38
32
|
// The game lives in one `sessionSlot`, keyed per session — each session is its
|
|
@@ -52,7 +46,24 @@ export const REPORTED_HISTORY = 5;
|
|
|
52
46
|
// `tool()`.
|
|
53
47
|
export const gameSlot = sessionSlot("game", () => structuredClone(DEFAULT_GAME_STATE));
|
|
54
48
|
|
|
55
|
-
/**
|
|
56
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Log a player command and count the turn, holding {@link MAX_HISTORY}.
|
|
51
|
+
*
|
|
52
|
+
* **Nothing the MODEL can call runs this** — `agent.ts` declares it as a
|
|
53
|
+
* `user-transcript.committed` hook, so it runs once per thing the player says,
|
|
54
|
+
* whether or not the narrator cooperates. It replaced a `game_state_history`
|
|
55
|
+
* TOOL whose `value` argument was the player's own command: the framework
|
|
56
|
+
* already had the transcript, and the tool existed to hand it back. That cost a
|
|
57
|
+
* model call per turn and desynced `moves` and `history` from the game every
|
|
58
|
+
* time the model forgot the system prompt's instruction to call it.
|
|
59
|
+
*
|
|
60
|
+
* Which is also why `moves` is counted HERE and not in `game_state_move`. It
|
|
61
|
+
* used to be both, so a turn where the narrator moved the player AND logged the
|
|
62
|
+
* command counted twice, and a turn where it did neither counted nothing. A
|
|
63
|
+
* MOVE is a room change; a TURN is the player saying something, and only one of
|
|
64
|
+
* those is a thing the game can miscount.
|
|
65
|
+
*/
|
|
66
|
+
export function recordTurn(game: GameState, command: string): void {
|
|
57
67
|
pushCapped(game.history, command, MAX_HISTORY);
|
|
68
|
+
game.moves++;
|
|
58
69
|
}
|
|
@@ -33,7 +33,7 @@ COMMAND INTERPRETATION:
|
|
|
33
33
|
- "start over" / "new game" / "restart" = restart
|
|
34
34
|
- Accept natural conversational commands and map them to game actions
|
|
35
35
|
|
|
36
|
-
Use the game state tools to track inventory, location, score, and flags. Use game_state_get to read the current state, game_state_move to change rooms, game_state_take to pick up items, game_state_drop to drop items, game_state_score to add points, game_state_flag to set game flags
|
|
36
|
+
Use the game state tools to track inventory, location, score, and flags. Use game_state_get to read the current state, game_state_move to change rooms, game_state_take to pick up items, game_state_drop to drop items, game_state_score to add points, and game_state_flag to set game flags. You do not log commands or count moves — the game does that for you on every turn. When the player asks to restart, quit, or start a new game, call game_state_restart, then narrate the opening scene again. Always update state when the player takes an item, moves rooms, or triggers an event. Check state before responding to ensure consistency.
|
|
37
37
|
|
|
38
38
|
ATMOSPHERE:
|
|
39
39
|
- Underground areas should feel dark and foreboding with the lantern lit, and terrifying in pitch blackness
|
|
@@ -2,13 +2,14 @@ import { z } from "zod";
|
|
|
2
2
|
import { gameSlot } from "../shared.ts";
|
|
3
3
|
|
|
4
4
|
export default gameSlot.updateTool({
|
|
5
|
-
description: "Move the player to a new room
|
|
5
|
+
description: "Move the player to a new room.",
|
|
6
6
|
inputSchema: z.object({
|
|
7
7
|
value: z.string().describe("Room name to move to"),
|
|
8
8
|
}),
|
|
9
9
|
execute(args, game) {
|
|
10
|
+
// `moves` is NOT bumped here: a turn is counted once, by the
|
|
11
|
+
// `user-transcript.committed` hook in `agent.ts`. See `recordTurn`.
|
|
10
12
|
game.currentRoom = args.value;
|
|
11
|
-
game.moves++;
|
|
12
13
|
return { currentRoom: game.currentRoom, moves: game.moves };
|
|
13
14
|
},
|
|
14
15
|
});
|