@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
|
@@ -10,20 +10,23 @@
|
|
|
10
10
|
"start": "node server.mjs",
|
|
11
11
|
"build": "aai build",
|
|
12
12
|
"test": "aai test",
|
|
13
|
+
"eval": "aai eval",
|
|
13
14
|
"publish:agent": "aai publish"
|
|
14
15
|
},
|
|
15
16
|
"dependencies": {
|
|
16
|
-
"@alexkroman1/aai": "^
|
|
17
|
-
"@alexkroman1/aai-
|
|
17
|
+
"@alexkroman1/aai": "^8.0.0",
|
|
18
|
+
"@alexkroman1/aai-runtime": "^8.0.0",
|
|
19
|
+
"@alexkroman1/aai-ui": "^8.0.0",
|
|
18
20
|
"@workflow/world-postgres": "4.3.3",
|
|
19
21
|
"react": "^19.2.8",
|
|
20
22
|
"react-dom": "^19.2.8",
|
|
21
23
|
"tailwindcss": "^4.0.0",
|
|
22
24
|
"workflow": "4.8.2",
|
|
25
|
+
"xstate": "^5.32.5",
|
|
23
26
|
"zod": "^4.4.3"
|
|
24
27
|
},
|
|
25
28
|
"devDependencies": {
|
|
26
|
-
"@alexkroman1/aai-cli": "^
|
|
29
|
+
"@alexkroman1/aai-cli": "^8.0.0",
|
|
27
30
|
"@tailwindcss/vite": "^4.3.3",
|
|
28
31
|
"@types/node": "^26.2.0",
|
|
29
32
|
"@types/react": "^19.2.18",
|
package/dist/scaffold/server.mjs
CHANGED
|
@@ -17,7 +17,7 @@ import { readFile } from "node:fs/promises";
|
|
|
17
17
|
import path from "node:path";
|
|
18
18
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
19
19
|
import { parseEnv } from "node:util";
|
|
20
|
-
import { createAgentServer, withHostCredentialFallback } from "@alexkroman1/aai
|
|
20
|
+
import { createAgentServer, withHostCredentialFallback } from "@alexkroman1/aai-runtime";
|
|
21
21
|
import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
|
|
22
22
|
|
|
23
23
|
const root = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -56,7 +56,12 @@ if (!existsSync(workerPath)) {
|
|
|
56
56
|
// Windows, where a bare POSIX-looking path is not a valid module specifier.
|
|
57
57
|
const { default: agent } = await import(pathToFileURL(workerPath).href);
|
|
58
58
|
|
|
59
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Parse a dotenv-syntax file into a record; `{}` when it does not exist.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} file - Path relative to the project root.
|
|
63
|
+
* @returns {Promise<Record<string, string | undefined>>}
|
|
64
|
+
*/
|
|
60
65
|
async function readEnvFile(file) {
|
|
61
66
|
try {
|
|
62
67
|
return parseEnv(await readFile(path.join(root, file), "utf-8"));
|
|
@@ -84,13 +89,15 @@ async function readEnvFile(file) {
|
|
|
84
89
|
*/
|
|
85
90
|
async function resolveAgentEnv() {
|
|
86
91
|
const declared = { ...(await readEnvFile(".env.example")), ...(await readEnvFile(".env")) };
|
|
92
|
+
/** @type {Record<string, string>} */
|
|
87
93
|
const env = {};
|
|
88
94
|
for (const [key, fileValue] of Object.entries(declared)) {
|
|
89
95
|
const value = process.env[key] ?? fileValue;
|
|
90
96
|
// An empty value is worse than a missing one: a provider would try to
|
|
91
97
|
// authenticate with "" rather than report the credential as absent. The
|
|
92
|
-
// example file is full of them by design (`BRAVE_API_KEY=`).
|
|
93
|
-
|
|
98
|
+
// example file is full of them by design (`BRAVE_API_KEY=`). A key the
|
|
99
|
+
// parser saw with no value at all is the same case.
|
|
100
|
+
if (value !== undefined && value !== "") env[key] = value;
|
|
94
101
|
}
|
|
95
102
|
return env;
|
|
96
103
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
|
|
3
3
|
import { n as log, s as unwrapCancel } from "./_ui-u7T4YooX.mjs";
|
|
4
|
-
import { f as checkedResponse, p as isStringArray } from "./_studio-
|
|
5
|
-
import { t as secretRequest } from "./_slug-api-
|
|
4
|
+
import { f as checkedResponse, p as isStringArray } from "./_studio-DR5yzZ1T.mjs";
|
|
5
|
+
import { t as secretRequest } from "./_slug-api-z-USxPtL.mjs";
|
|
6
6
|
import { isRecord } from "@alexkroman1/aai/utils";
|
|
7
7
|
import * as p from "@clack/prompts";
|
|
8
8
|
import { text } from "node:stream/consumers";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as ok, n as fail } from "./_output-CKkmWs7i.mjs";
|
|
3
3
|
import { n as log } from "./_ui-u7T4YooX.mjs";
|
|
4
|
-
import { f as checkedResponse } from "./_studio-
|
|
5
|
-
import { n as slugRequest } from "./_slug-api-
|
|
4
|
+
import { f as checkedResponse } from "./_studio-DR5yzZ1T.mjs";
|
|
5
|
+
import { n as slugRequest } from "./_slug-api-z-USxPtL.mjs";
|
|
6
6
|
import { isRecord } from "@alexkroman1/aai/utils";
|
|
7
7
|
import * as p from "@clack/prompts";
|
|
8
8
|
//#region storage.ts
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as ok, t as CliError } from "./_output-CKkmWs7i.mjs";
|
|
3
3
|
import { n as log, t as fmtUrl } from "./_ui-u7T4YooX.mjs";
|
|
4
|
-
import { s as updateProjectConfig } from "./_config-
|
|
4
|
+
import { s as updateProjectConfig } from "./_config-CmJOFsAP.mjs";
|
|
5
5
|
import { t as resolveServerEnv } from "./_server-common-6e0QI4mq.mjs";
|
|
6
|
-
import { a as resolveDeployTarget } from "./_agent-
|
|
7
|
-
import { layerScaffold } from "./_templates-
|
|
8
|
-
import { a as publishStudioProject, c as studioProjectUrl, f as checkedResponse, i as projectNameFromDir, n as fetchStudioProject, o as pushStudioSource, r as listStudioProjects, s as studioProjectApiUrl, t as collectSourceFiles, u as apiRequest } from "./_studio-
|
|
6
|
+
import { a as resolveDeployTarget } from "./_agent-BTwZJu4P.mjs";
|
|
7
|
+
import { layerScaffold } from "./_templates-4WcKOjS5.mjs";
|
|
8
|
+
import { a as publishStudioProject, c as studioProjectUrl, f as checkedResponse, i as projectNameFromDir, n as fetchStudioProject, o as pushStudioSource, r as listStudioProjects, s as studioProjectApiUrl, t as collectSourceFiles, u as apiRequest } from "./_studio-DR5yzZ1T.mjs";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { isRecord } from "@alexkroman1/aai/utils";
|
|
11
11
|
import { mkdir, readdir, writeFile } from "node:fs/promises";
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createToolContext,
|
|
5
|
+
type StubDelegateCall,
|
|
6
|
+
stubDelegate,
|
|
7
|
+
toolRunner,
|
|
8
|
+
withDiscoveredTools,
|
|
9
|
+
} from "@alexkroman1/aai/testing";
|
|
10
|
+
import { describe, expect, test } from "vitest";
|
|
11
|
+
import authoredAgent from "./agent.ts";
|
|
12
|
+
import type { AngleWork, Finding } from "./shared.ts";
|
|
13
|
+
import {
|
|
14
|
+
angleBrief,
|
|
15
|
+
briefingSlot,
|
|
16
|
+
countWork,
|
|
17
|
+
factChecker,
|
|
18
|
+
findByAngle,
|
|
19
|
+
MAX_ANGLES,
|
|
20
|
+
MAX_FINDINGS,
|
|
21
|
+
MAX_RESEARCH_STEPS,
|
|
22
|
+
recordFinding,
|
|
23
|
+
researcher,
|
|
24
|
+
} from "./shared.ts";
|
|
25
|
+
|
|
26
|
+
/** A finding whose cost is irrelevant to the case at hand. */
|
|
27
|
+
const NO_WORK: AngleWork = { searches: 0, reads: 0 };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The def a DEPLOYED agent runs: authored, plus what `tools/` declares.
|
|
31
|
+
*
|
|
32
|
+
* The glob is written HERE rather than reached for from a shared helper because
|
|
33
|
+
* this file SHIPS — it is what a scaffolded project runs, so it may not import
|
|
34
|
+
* anything outside its own template.
|
|
35
|
+
*/
|
|
36
|
+
const agentDef = withDiscoveredTools(
|
|
37
|
+
authoredAgent,
|
|
38
|
+
import.meta.glob("./tools/*.ts", { eager: true }),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const run = toolRunner(agentDef);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The desk's two subagents, faked.
|
|
45
|
+
*
|
|
46
|
+
* `stubDelegate` routes by SUBAGENT NAME, which is what tells a research run
|
|
47
|
+
* from a check — the two things this desk actually does. Nothing here runs a
|
|
48
|
+
* model or touches the network: a subagent is a model loop, and a spec that
|
|
49
|
+
* asserted on its steps would be asserting on a provider's choices. What is
|
|
50
|
+
* worth asserting is what the desk ASKS for and what it does with what comes
|
|
51
|
+
* back.
|
|
52
|
+
*/
|
|
53
|
+
function desk(
|
|
54
|
+
options: {
|
|
55
|
+
research?: (call: StubDelegateCall) => string | { text: string; searches?: number };
|
|
56
|
+
check?: string;
|
|
57
|
+
} = {},
|
|
58
|
+
) {
|
|
59
|
+
const research = options.research ?? ((call) => `Findings for ${call.task}.`);
|
|
60
|
+
return stubDelegate({
|
|
61
|
+
researcher: (call) => {
|
|
62
|
+
const reply = research(call);
|
|
63
|
+
if (typeof reply === "string") return { text: reply };
|
|
64
|
+
return {
|
|
65
|
+
text: reply.text,
|
|
66
|
+
toolCalls: Array.from({ length: reply.searches ?? 0 }, (_unused, index) => ({
|
|
67
|
+
name: "web_search",
|
|
68
|
+
input: { query: `q${index}` },
|
|
69
|
+
})),
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
"fact-checker": options.check ?? "Confirmed: two sources say so.",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
describe("the desk itself", () => {
|
|
77
|
+
test("has no web tools of its own — everything goes through a subagent", () => {
|
|
78
|
+
expect(authoredAgent.builtinTools ?? []).toEqual([]);
|
|
79
|
+
// And the researcher does, which is the split the template exists to show.
|
|
80
|
+
expect(researcher.builtinTools).toContain("web_search");
|
|
81
|
+
expect(factChecker.builtinTools).toEqual(["web_search"]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("gives the checker a tighter budget than the researcher", () => {
|
|
85
|
+
expect(factChecker.maxSteps).toBeLessThan(MAX_RESEARCH_STEPS);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("tells each subagent that its final message is all the desk sees", () => {
|
|
89
|
+
expect(researcher.instructions).toMatch(/FINAL message/);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("research_topic", () => {
|
|
94
|
+
test("fans every angle out as its own run, each with a self-contained task", async () => {
|
|
95
|
+
const model = desk();
|
|
96
|
+
const ctx = createToolContext({ delegate: model.delegate });
|
|
97
|
+
|
|
98
|
+
await run(
|
|
99
|
+
"research_topic",
|
|
100
|
+
{ topic: "home batteries", angles: ["price trend", "install lead times"] },
|
|
101
|
+
ctx,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
expect(model.calls).toHaveLength(2);
|
|
105
|
+
expect(model.calls.map((call) => call.subagent.name)).toEqual(["researcher", "researcher"]);
|
|
106
|
+
expect(model.calls.map((call) => call.task)).toEqual(["price trend", "install lead times"]);
|
|
107
|
+
// The subagent has not heard the call, so the topic rides in `context`.
|
|
108
|
+
expect(model.calls[0]?.options.context).toContain("home batteries");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("starts the runs concurrently rather than one after another", async () => {
|
|
112
|
+
let inFlight = 0;
|
|
113
|
+
let peak = 0;
|
|
114
|
+
const model = stubDelegate({
|
|
115
|
+
researcher: () => {
|
|
116
|
+
inFlight += 1;
|
|
117
|
+
peak = Math.max(peak, inFlight);
|
|
118
|
+
return "found";
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
// The fake answers synchronously, so `inFlight` is only ever 1 unless the
|
|
122
|
+
// tool really did start every run before awaiting any — which is the claim.
|
|
123
|
+
const delegate = ((sub, options) =>
|
|
124
|
+
model.delegate(sub, options).finally(() => {
|
|
125
|
+
inFlight -= 1;
|
|
126
|
+
})) as typeof model.delegate;
|
|
127
|
+
|
|
128
|
+
await run(
|
|
129
|
+
"research_topic",
|
|
130
|
+
{ topic: "t", angles: ["a", "b", "c"] },
|
|
131
|
+
createToolContext({ delegate }),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect(peak).toBe(3);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("records what each angle concluded, and only that", async () => {
|
|
138
|
+
const model = desk({
|
|
139
|
+
research: (call) => ({ text: `Answer to ${call.task}.`, searches: 3 }),
|
|
140
|
+
});
|
|
141
|
+
const ctx = createToolContext({ delegate: model.delegate });
|
|
142
|
+
|
|
143
|
+
const result = (await run("research_topic", { topic: "t", angles: ["a"] }, ctx)) as {
|
|
144
|
+
findings: Finding[];
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
expect(result.findings).toEqual([
|
|
148
|
+
{ angle: "a", summary: "Answer to a.", work: { searches: 3, reads: 0 } },
|
|
149
|
+
]);
|
|
150
|
+
const board = briefingSlot.get(ctx);
|
|
151
|
+
expect(board.topic).toBe("t");
|
|
152
|
+
expect(board.findings).toEqual(result.findings);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("one failed angle does not sink the briefing", async () => {
|
|
156
|
+
const model = stubDelegate({
|
|
157
|
+
researcher: (call) => {
|
|
158
|
+
if (call.task === "b") throw new Error("provider is having a day");
|
|
159
|
+
return `Answer to ${call.task}.`;
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
const ctx = createToolContext({ delegate: model.delegate });
|
|
163
|
+
|
|
164
|
+
const result = (await run("research_topic", { topic: "t", angles: ["a", "b", "c"] }, ctx)) as {
|
|
165
|
+
findings: { angle: string }[];
|
|
166
|
+
failed: { angle: string; error: string }[];
|
|
167
|
+
message: string;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
expect(result.findings.map((one) => one.angle)).toEqual(["a", "c"]);
|
|
171
|
+
expect(result.failed).toEqual([{ angle: "b", error: "provider is having a day" }]);
|
|
172
|
+
// And the desk is told to say so rather than quietly reporting two angles.
|
|
173
|
+
expect(result.message).toMatch(/could not get to/);
|
|
174
|
+
expect(briefingSlot.get(ctx).findings).toHaveLength(2);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("fails as a tool when every angle fails, quoting the first reason", async () => {
|
|
178
|
+
const model = stubDelegate({
|
|
179
|
+
researcher: () => {
|
|
180
|
+
throw new Error("gateway said no");
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const result = await run(
|
|
185
|
+
"research_topic",
|
|
186
|
+
{ topic: "t", angles: ["a", "b"] },
|
|
187
|
+
createToolContext({ delegate: model.delegate }),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
expect(result).toEqual({ error: expect.stringContaining("gateway said no") });
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("refuses a call whose angles are all blank, without spending a subagent", async () => {
|
|
194
|
+
const model = desk();
|
|
195
|
+
|
|
196
|
+
const result = await run(
|
|
197
|
+
"research_topic",
|
|
198
|
+
{ topic: "t", angles: [" "] },
|
|
199
|
+
createToolContext({ delegate: model.delegate }),
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
expect(result).toEqual({ error: expect.stringContaining("No angles") });
|
|
203
|
+
expect(model.calls).toEqual([]);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("accepts at most MAX_ANGLES angles", () => {
|
|
207
|
+
const schema = agentDef.tools.research_topic?.inputSchema;
|
|
208
|
+
const tooMany = { topic: "t", angles: Array.from({ length: MAX_ANGLES + 1 }, () => "a") };
|
|
209
|
+
expect(schema?.["~standard"].validate(tooMany)).toMatchObject({ issues: expect.anything() });
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("verify_claim", () => {
|
|
214
|
+
test("asks the fact-checker, not the researcher", async () => {
|
|
215
|
+
const model = desk({ check: "Contradicted: the figure is 12%." });
|
|
216
|
+
const ctx = createToolContext({ delegate: model.delegate });
|
|
217
|
+
|
|
218
|
+
const result = (await run("verify_claim", { claim: "The figure is 40%." }, ctx)) as {
|
|
219
|
+
verdict: string;
|
|
220
|
+
checkedAgainst: string | null;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
expect(model.calls.map((call) => call.subagent.name)).toEqual(["fact-checker"]);
|
|
224
|
+
expect(result.verdict).toBe("Contradicted: the figure is 12%.");
|
|
225
|
+
expect(result.checkedAgainst).toBeNull();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("quotes the finding a claim came from, so the checker can see the source", async () => {
|
|
229
|
+
const model = desk();
|
|
230
|
+
const ctx = createToolContext({ delegate: model.delegate });
|
|
231
|
+
briefingSlot.update(ctx, (board) => {
|
|
232
|
+
recordFinding(board, {
|
|
233
|
+
angle: "install lead times",
|
|
234
|
+
summary: "Installers quote eight weeks.",
|
|
235
|
+
work: { searches: 2, reads: 1 },
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const result = (await run(
|
|
240
|
+
"verify_claim",
|
|
241
|
+
{ claim: "Installs take eight weeks.", about: "lead times" },
|
|
242
|
+
ctx,
|
|
243
|
+
)) as { checkedAgainst: string | null };
|
|
244
|
+
|
|
245
|
+
expect(model.calls[0]?.options.context).toContain("Installers quote eight weeks.");
|
|
246
|
+
expect(result.checkedAgainst).toBe("install lead times");
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("reports a failed check as a tool failure the model can recover from", async () => {
|
|
250
|
+
const model = stubDelegate({
|
|
251
|
+
"fact-checker": () => {
|
|
252
|
+
throw new Error("checker timed out");
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const result = await run(
|
|
257
|
+
"verify_claim",
|
|
258
|
+
{ claim: "Something." },
|
|
259
|
+
createToolContext({ delegate: model.delegate }),
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
expect(result).toEqual({ error: expect.stringContaining("checker timed out") });
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("refuses a blank claim without spending a subagent", async () => {
|
|
266
|
+
const model = desk();
|
|
267
|
+
const result = await run(
|
|
268
|
+
"verify_claim",
|
|
269
|
+
{ claim: " " },
|
|
270
|
+
createToolContext({ delegate: model.delegate }),
|
|
271
|
+
);
|
|
272
|
+
expect(result).toEqual({ error: expect.stringContaining("Nothing to check") });
|
|
273
|
+
expect(model.calls).toEqual([]);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe("briefing_so_far", () => {
|
|
278
|
+
test("says so when there is nothing yet, and spends no subagent", async () => {
|
|
279
|
+
const ctx = createToolContext();
|
|
280
|
+
const result = (await run("briefing_so_far", {}, ctx)) as { findings: unknown[] };
|
|
281
|
+
expect(result.findings).toEqual([]);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("adds the lookups up across every angle", async () => {
|
|
285
|
+
const ctx = createToolContext();
|
|
286
|
+
briefingSlot.update(ctx, (board) => {
|
|
287
|
+
board.topic = "t";
|
|
288
|
+
recordFinding(board, { angle: "a", summary: "A.", work: { searches: 2, reads: 1 } });
|
|
289
|
+
recordFinding(board, { angle: "b", summary: "B.", work: { searches: 3, reads: 0 } });
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
const result = (await run("briefing_so_far", {}, ctx)) as {
|
|
293
|
+
totalSearches: number;
|
|
294
|
+
totalReads: number;
|
|
295
|
+
};
|
|
296
|
+
expect(result.totalSearches).toBe(5);
|
|
297
|
+
expect(result.totalReads).toBe(1);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
describe("the brief a subagent is sent", () => {
|
|
302
|
+
test("carries the topic, because the subagent has not heard the call", () => {
|
|
303
|
+
expect(angleBrief("home batteries", "price trend")).toEqual({
|
|
304
|
+
task: "price trend",
|
|
305
|
+
context: expect.stringContaining("home batteries"),
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
describe("countWork", () => {
|
|
311
|
+
test("tells searches from page reads", () => {
|
|
312
|
+
expect(
|
|
313
|
+
countWork([
|
|
314
|
+
{ name: "web_search", input: {} },
|
|
315
|
+
{ name: "visit_webpage", input: {} },
|
|
316
|
+
{ name: "web_search", input: {} },
|
|
317
|
+
]),
|
|
318
|
+
).toEqual({ searches: 2, reads: 1 });
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("counts a tool it does not recognise as neither", () => {
|
|
322
|
+
// A researcher that gains a third tool must not silently inflate "searches".
|
|
323
|
+
expect(countWork([{ name: "think", input: {} }])).toEqual({ searches: 0, reads: 0 });
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe("the board", () => {
|
|
328
|
+
test("holds MAX_FINDINGS, dropping the oldest", () => {
|
|
329
|
+
const ctx = createToolContext();
|
|
330
|
+
briefingSlot.update(ctx, (board) => {
|
|
331
|
+
for (let index = 0; index < MAX_FINDINGS + 3; index++) {
|
|
332
|
+
recordFinding(board, { angle: `angle ${index}`, summary: "s", work: NO_WORK });
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
const board = briefingSlot.get(ctx);
|
|
336
|
+
expect(board.findings).toHaveLength(MAX_FINDINGS);
|
|
337
|
+
expect(board.findings[0]?.angle).toBe("angle 3");
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("finds an angle from a loose mention, and nothing from a blank one", () => {
|
|
341
|
+
const ctx = createToolContext();
|
|
342
|
+
briefingSlot.update(ctx, (board) => {
|
|
343
|
+
recordFinding(board, { angle: "install lead times", summary: "s", work: NO_WORK });
|
|
344
|
+
});
|
|
345
|
+
const board = briefingSlot.get(ctx);
|
|
346
|
+
expect(findByAngle(board, "lead times")?.angle).toBe("install lead times");
|
|
347
|
+
expect(findByAngle(board, " ")).toBeUndefined();
|
|
348
|
+
expect(findByAngle(board, "battery chemistry")).toBeUndefined();
|
|
349
|
+
});
|
|
350
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { agent } from "@alexkroman1/aai";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A briefing desk you can phone: you name a subject, it puts several
|
|
5
|
+
* researchers on it at once and reads you back what they found.
|
|
6
|
+
*
|
|
7
|
+
* **It is the worked example for `ctx.delegate` — subagents.** The desk itself
|
|
8
|
+
* has NO web tools. Everything it knows comes back from a subagent run started
|
|
9
|
+
* inside a tool call: `researcher` (search + page reads, six steps) for each
|
|
10
|
+
* angle of a topic, `fact-checker` (search only, two steps, a cheaper model)
|
|
11
|
+
* for one claim at a time. `shared.ts` declares both and argues the split.
|
|
12
|
+
*
|
|
13
|
+
* **What that buys, on a phone call specifically.** A researcher may read tens
|
|
14
|
+
* of thousands of tokens of web pages; what crosses back into the conversation
|
|
15
|
+
* is its final paragraph. So the desk stays coherent over a long call — its
|
|
16
|
+
* context grows by a summary per angle rather than by everything four
|
|
17
|
+
* researchers read — and the four angles run at once, so the caller waits for
|
|
18
|
+
* the slowest rather than the sum. `tools/research_topic.ts` is where both of
|
|
19
|
+
* those actually happen.
|
|
20
|
+
*
|
|
21
|
+
* Compare `web-researcher`, which gives the search builtins to the agent
|
|
22
|
+
* itself: that is the right shape for one lookup, and the wrong one the moment
|
|
23
|
+
* a question has four sides.
|
|
24
|
+
*/
|
|
25
|
+
export default agent({
|
|
26
|
+
name: "Briefing Desk",
|
|
27
|
+
greeting:
|
|
28
|
+
"Briefing desk. Tell me a subject and I'll put a few researchers on it — " +
|
|
29
|
+
"try something like, what's going on with home battery prices.",
|
|
30
|
+
});
|