@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
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* guest's BAKED image instead of being inlined. Step bundle: 12 MB → ~7 KB.
|
|
30
30
|
* - **`bundleFinalOutput: false`** — skips wrapping the workflow-mode code in a
|
|
31
31
|
* runtime host, which is exactly what `workflowEntrypoint(code)` supplies
|
|
32
|
-
* itself at the other end.
|
|
32
|
+
* itself at the other end. Dialog bundle: 3.7 MB → ~69 KB.
|
|
33
33
|
*
|
|
34
34
|
* So `workflowCode` is passed to `workflowEntrypoint()` in the guest and
|
|
35
35
|
* `stepCode` is evaluated there to register its step functions.
|
|
@@ -108,6 +108,11 @@ export type WorkflowBundleOutput = {
|
|
|
108
108
|
* section for why it is this list and not a content sniff.
|
|
109
109
|
*/
|
|
110
110
|
inputFiles: readonly string[];
|
|
111
|
+
/**
|
|
112
|
+
* Replay-safety findings, as lines to print. See {@link replayWarnings} for
|
|
113
|
+
* why they are warnings rather than a failed build.
|
|
114
|
+
*/
|
|
115
|
+
warnings: readonly string[];
|
|
111
116
|
};
|
|
112
117
|
/**
|
|
113
118
|
* Attach the compiler's `workflowId`/`stepId` to the agent bundle's copy of
|
package/dist/workflow.d.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* slug, and PRINTING — which is most of why the verbs exist separately from the
|
|
26
26
|
* client's methods.
|
|
27
27
|
*/
|
|
28
|
-
import type { WorkflowRunSnapshot, WorkflowSummary } from "@alexkroman1/aai";
|
|
28
|
+
import type { WorkflowRunSnapshot, WorkflowSummary } from "@alexkroman1/aai/workflow-api";
|
|
29
29
|
import { type CommandResult } from "./_output.ts";
|
|
30
30
|
/**
|
|
31
31
|
* One run, as the API reports it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexkroman1/aai-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"aai": "bin.mjs"
|
|
@@ -44,8 +44,9 @@
|
|
|
44
44
|
"p-timeout": "^7.0.1",
|
|
45
45
|
"vite": "^8.2.1",
|
|
46
46
|
"zod": "^4.4.3",
|
|
47
|
-
"@alexkroman1/aai": "
|
|
48
|
-
"@alexkroman1/aai
|
|
47
|
+
"@alexkroman1/aai-runtime": "8.0.0",
|
|
48
|
+
"@alexkroman1/aai": "8.0.0",
|
|
49
|
+
"@alexkroman1/aai-ui": "8.0.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"playwright": "^1.62.1",
|
|
@@ -53,7 +54,7 @@
|
|
|
53
54
|
"verdaccio": "^6.9.2",
|
|
54
55
|
"vitest": "^4.1.10",
|
|
55
56
|
"workflow": "4.8.2",
|
|
56
|
-
"aai-templates": "0.3.
|
|
57
|
+
"aai-templates": "0.3.8"
|
|
57
58
|
},
|
|
58
59
|
"peerDependencies": {
|
|
59
60
|
"vitest": "^4.1.10"
|
package/dist/eject-31gjtaHF.mjs
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { a as ok, t as CliError } from "./_output-CKkmWs7i.mjs";
|
|
3
|
-
import { n as log } from "./_ui-u7T4YooX.mjs";
|
|
4
|
-
import { l as readJson, o as errorMessage, p as writeJson, s as fileExists } from "./_utils-B8QmtFhK.mjs";
|
|
5
|
-
import { scaffoldDir } from "./_templates-Dxb_P2Wz.mjs";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import fs from "node:fs/promises";
|
|
8
|
-
//#region eject.ts
|
|
9
|
-
/**
|
|
10
|
-
* `aai eject` — retrofit the self-hosted entrypoint into an existing project.
|
|
11
|
-
*
|
|
12
|
-
* Every project scaffolded from this CLI version onwards already has
|
|
13
|
-
* `server.mjs` and the `prestart`/`start` pair: the scaffold ships them, so
|
|
14
|
-
* self-hosting is the default rather than something to opt into. This command
|
|
15
|
-
* exists for the projects that predate that — `aai init` before it, or a
|
|
16
|
-
* workspace pulled from a studio that was created earlier — where the files
|
|
17
|
-
* are simply missing.
|
|
18
|
-
*
|
|
19
|
-
* It COPIES from the scaffold rather than writing its own contents. Two
|
|
20
|
-
* definitions of "the self-hosted entrypoint" would drift, and the one nobody
|
|
21
|
-
* runs locally is the one that would rot; this way an ejected project is
|
|
22
|
-
* byte-identical to a freshly scaffolded one.
|
|
23
|
-
*
|
|
24
|
-
* The SCRIPTS are the exception it cannot copy, since a project's package.json
|
|
25
|
-
* is its own — so `PRESTART_SCRIPT`/`START_SCRIPT` are written here and pinned
|
|
26
|
-
* against the scaffold's manifest by `eject.test.ts`. Both matter: the
|
|
27
|
-
* entrypoint boots the BUILT worker, so an ejected project with no `prestart`
|
|
28
|
-
* exits at once naming the missing artifact.
|
|
29
|
-
*/
|
|
30
|
-
/** The file `npm start` runs. Named once — the scaffold ships it under this name. */
|
|
31
|
-
const SERVER_ENTRY = "server.mjs";
|
|
32
|
-
/** The `scripts.start` value that runs {@link SERVER_ENTRY}. */
|
|
33
|
-
const START_SCRIPT = `node ${SERVER_ENTRY}`;
|
|
34
|
-
/**
|
|
35
|
-
* The `scripts.prestart` value, which npm runs before `start`.
|
|
36
|
-
*
|
|
37
|
-
* {@link SERVER_ENTRY} boots the BUILT worker (`.aai/worker.mjs`), because a
|
|
38
|
-
* tool is discovered by the bundler enumerating `tools/` and an un-bundled
|
|
39
|
-
* loader would serve an agent with no tools at all — so an entrypoint with no
|
|
40
|
-
* build in front of it is not a working project. `--skip-tests` because this
|
|
41
|
-
* runs on the way to serving traffic: `npm test` is where a suite belongs, and
|
|
42
|
-
* a failing test must not be what stops a container from starting. The
|
|
43
|
-
* typecheck stays, since the bundlers strip types unchecked and self-hosting
|
|
44
|
-
* has no other gate.
|
|
45
|
-
*
|
|
46
|
-
* Kept in step with the scaffold's own `package.json`, which the two together
|
|
47
|
-
* are the only definition of — `eject.test.ts` asserts they agree.
|
|
48
|
-
*/
|
|
49
|
-
const PRESTART_SCRIPT = "aai build --skip-tests";
|
|
50
|
-
/**
|
|
51
|
-
* Add `scripts.start` — and the `prestart` that builds what it boots — unless
|
|
52
|
-
* the project already declares a `start` of its own.
|
|
53
|
-
*
|
|
54
|
-
* An existing `start` is left alone even under `--force`: `--force` is about
|
|
55
|
-
* replacing the entrypoint file, and silently rewriting the command a project
|
|
56
|
-
* boots with is a different, larger act. `prestart` is part of the same act
|
|
57
|
-
* rather than a separate one, so it is written only alongside a `start` we
|
|
58
|
-
* wrote — bolting a build onto someone else's start command changes what that
|
|
59
|
-
* command does, which is precisely what the rule above refuses. The mismatch is
|
|
60
|
-
* reported instead, naming both halves, so the choice stays the author's.
|
|
61
|
-
*/
|
|
62
|
-
async function ensureStartScript(cwd) {
|
|
63
|
-
const manifestPath = path.join(cwd, "package.json");
|
|
64
|
-
const manifest = await readJson(manifestPath);
|
|
65
|
-
if (!manifest) {
|
|
66
|
-
log.warn(`No package.json here — add "prestart": "${PRESTART_SCRIPT}" and "start": "${START_SCRIPT}" yourself to get \`npm start\`.`);
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
const existing = manifest.scripts?.start;
|
|
70
|
-
if (existing === START_SCRIPT && manifest.scripts?.prestart === "aai build --skip-tests") return false;
|
|
71
|
-
if (existing !== void 0 && existing !== START_SCRIPT) {
|
|
72
|
-
log.warn(`Kept your existing "start" script (${existing}) — run \`${PRESTART_SCRIPT}\` then \`${START_SCRIPT}\`.`);
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
manifest.scripts = {
|
|
76
|
-
...manifest.scripts,
|
|
77
|
-
prestart: PRESTART_SCRIPT,
|
|
78
|
-
start: START_SCRIPT
|
|
79
|
-
};
|
|
80
|
-
await writeJson(manifestPath, manifest);
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* A missing scaffold source means a broken install (or an `AAI_TEMPLATES_DIR`
|
|
85
|
-
* pointed somewhere wrong), not anything the user did — so the message names
|
|
86
|
-
* the file and where it was looked for.
|
|
87
|
-
*
|
|
88
|
-
* Built here and thrown by the caller, the shape `build.ts` uses for the same
|
|
89
|
-
* reason: `useErrorCause` reads a `throw new Error` inside a `catch` as
|
|
90
|
-
* dropping the cause, and cannot see that `CliError` takes its options fourth.
|
|
91
|
-
*/
|
|
92
|
-
function scaffoldMissingError(source, err) {
|
|
93
|
-
return new CliError("scaffold_missing", `Could not read the scaffold's ${SERVER_ENTRY} at ${source}: ${errorMessage(err)}`, "Reinstall @alexkroman1/aai-cli.", { cause: err });
|
|
94
|
-
}
|
|
95
|
-
async function executeEject(opts) {
|
|
96
|
-
const { cwd, force } = opts;
|
|
97
|
-
const target = path.join(cwd, SERVER_ENTRY);
|
|
98
|
-
const overwritten = await fileExists(target);
|
|
99
|
-
if (overwritten && !force) throw new CliError("server_exists", `${SERVER_ENTRY} already exists — this project can already be self-hosted.`, `Run \`npm start\`, or re-run with --force to replace it with the current scaffold's copy.`);
|
|
100
|
-
const source = path.join(scaffoldDir(), SERVER_ENTRY);
|
|
101
|
-
try {
|
|
102
|
-
await fs.copyFile(source, target);
|
|
103
|
-
} catch (err) {
|
|
104
|
-
throw scaffoldMissingError(source, err);
|
|
105
|
-
}
|
|
106
|
-
const addedScripts = await ensureStartScript(cwd);
|
|
107
|
-
log.success(`Wrote ${SERVER_ENTRY}${overwritten ? " (replaced)" : ""}`);
|
|
108
|
-
log.info(`Next: npm start — it builds first (\`${PRESTART_SCRIPT}\`), then serves on 127.0.0.1:3000 (PORT and HOST override that)`);
|
|
109
|
-
return ok({
|
|
110
|
-
file: target,
|
|
111
|
-
overwritten,
|
|
112
|
-
addedScripts
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
//#endregion
|
|
116
|
-
export { executeEject };
|
package/dist/eject.d.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `aai eject` — retrofit the self-hosted entrypoint into an existing project.
|
|
3
|
-
*
|
|
4
|
-
* Every project scaffolded from this CLI version onwards already has
|
|
5
|
-
* `server.mjs` and the `prestart`/`start` pair: the scaffold ships them, so
|
|
6
|
-
* self-hosting is the default rather than something to opt into. This command
|
|
7
|
-
* exists for the projects that predate that — `aai init` before it, or a
|
|
8
|
-
* workspace pulled from a studio that was created earlier — where the files
|
|
9
|
-
* are simply missing.
|
|
10
|
-
*
|
|
11
|
-
* It COPIES from the scaffold rather than writing its own contents. Two
|
|
12
|
-
* definitions of "the self-hosted entrypoint" would drift, and the one nobody
|
|
13
|
-
* runs locally is the one that would rot; this way an ejected project is
|
|
14
|
-
* byte-identical to a freshly scaffolded one.
|
|
15
|
-
*
|
|
16
|
-
* The SCRIPTS are the exception it cannot copy, since a project's package.json
|
|
17
|
-
* is its own — so `PRESTART_SCRIPT`/`START_SCRIPT` are written here and pinned
|
|
18
|
-
* against the scaffold's manifest by `eject.test.ts`. Both matter: the
|
|
19
|
-
* entrypoint boots the BUILT worker, so an ejected project with no `prestart`
|
|
20
|
-
* exits at once naming the missing artifact.
|
|
21
|
-
*/
|
|
22
|
-
import { type CommandResult } from "./_output.ts";
|
|
23
|
-
/** The file `npm start` runs. Named once — the scaffold ships it under this name. */
|
|
24
|
-
export declare const SERVER_ENTRY = "server.mjs";
|
|
25
|
-
/** The `scripts.start` value that runs {@link SERVER_ENTRY}. */
|
|
26
|
-
export declare const START_SCRIPT = "node server.mjs";
|
|
27
|
-
/**
|
|
28
|
-
* The `scripts.prestart` value, which npm runs before `start`.
|
|
29
|
-
*
|
|
30
|
-
* {@link SERVER_ENTRY} boots the BUILT worker (`.aai/worker.mjs`), because a
|
|
31
|
-
* tool is discovered by the bundler enumerating `tools/` and an un-bundled
|
|
32
|
-
* loader would serve an agent with no tools at all — so an entrypoint with no
|
|
33
|
-
* build in front of it is not a working project. `--skip-tests` because this
|
|
34
|
-
* runs on the way to serving traffic: `npm test` is where a suite belongs, and
|
|
35
|
-
* a failing test must not be what stops a container from starting. The
|
|
36
|
-
* typecheck stays, since the bundlers strip types unchecked and self-hosting
|
|
37
|
-
* has no other gate.
|
|
38
|
-
*
|
|
39
|
-
* Kept in step with the scaffold's own `package.json`, which the two together
|
|
40
|
-
* are the only definition of — `eject.test.ts` asserts they agree.
|
|
41
|
-
*/
|
|
42
|
-
export declare const PRESTART_SCRIPT = "aai build --skip-tests";
|
|
43
|
-
type EjectData = {
|
|
44
|
-
/** Absolute path of the entrypoint written. */
|
|
45
|
-
file: string;
|
|
46
|
-
/** True when an existing `server.mjs` was replaced (`--force`). */
|
|
47
|
-
overwritten: boolean;
|
|
48
|
-
/**
|
|
49
|
-
* True when this run wrote `scripts.prestart`/`scripts.start` into
|
|
50
|
-
* package.json. Also true for a project ejected by an older CLI, whose
|
|
51
|
-
* `start` is already correct and whose `prestart` is missing — the entrypoint
|
|
52
|
-
* this run just wrote needs the build in front of it.
|
|
53
|
-
*/
|
|
54
|
-
addedScripts: boolean;
|
|
55
|
-
};
|
|
56
|
-
export declare function executeEject(opts: {
|
|
57
|
-
cwd: string;
|
|
58
|
-
force?: boolean | undefined;
|
|
59
|
-
}): Promise<CommandResult<EjectData>>;
|
|
60
|
-
export {};
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
// Copyright 2026 the AAI authors. MIT license.
|
|
2
|
-
/**
|
|
3
|
-
* Moving bytes between the upload store and a local file, which is what an
|
|
4
|
-
* ffmpeg step spends most of its lines on.
|
|
5
|
-
*
|
|
6
|
-
* No directive, so it sits under `workflows/` untransformed and is called FROM
|
|
7
|
-
* steps, inheriting their environment. It exists because both ffmpeg steps in
|
|
8
|
-
* this template need the same three things and the third one is the one that is
|
|
9
|
-
* easy to get wrong.
|
|
10
|
-
*
|
|
11
|
-
* ## Why a temp file at all
|
|
12
|
-
*
|
|
13
|
-
* `@alexkroman1/aai/ffmpeg` takes bytes as happily as a path, and for a short
|
|
14
|
-
* clip bytes are the better call. This desk uses paths, for two reasons that are
|
|
15
|
-
* both properties of real recordings rather than preferences:
|
|
16
|
-
*
|
|
17
|
-
* - **A pipe cannot seek.** An `.m4a` off a phone usually carries its `moov`
|
|
18
|
-
* index at the END of the file, so ffmpeg reading it from `pipe:0` fails with
|
|
19
|
-
* `moov atom not found`. That is the flagship input.
|
|
20
|
-
* - **Piped output is capped**, at `DEFAULT_MAX_FFMPEG_OUTPUT_BYTES` (64 MiB),
|
|
21
|
-
* which is about half an hour of this desk's 16 kHz mono PCM. The desk exists
|
|
22
|
-
* for the two-hour call.
|
|
23
|
-
*
|
|
24
|
-
* ## A temp file may not outlive its step
|
|
25
|
-
*
|
|
26
|
-
* A step is journaled by its RETURN VALUE and may be dispatched into a different
|
|
27
|
-
* process than its neighbours, so a path in a return value is a path that is
|
|
28
|
-
* replayed after the file behind it is gone — and the failure mode is a resumed
|
|
29
|
-
* run reading a directory that another run is using. {@link withTempDir} makes
|
|
30
|
-
* the lifetime a lexical scope: the directory is created on entry, removed on
|
|
31
|
-
* exit, and what crosses the step boundary is an upload id.
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
import { mkdtemp, open, rm } from "node:fs/promises";
|
|
35
|
-
import { tmpdir } from "node:os";
|
|
36
|
-
import { join } from "node:path";
|
|
37
|
-
import { readUpload } from "@alexkroman1/aai/utils";
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Bytes moved per `readUpload`, and per write.
|
|
41
|
-
*
|
|
42
|
-
* Large enough that a two-hour recording is a few hundred round trips rather
|
|
43
|
-
* than tens of thousands, and small enough that a step's resident set is a
|
|
44
|
-
* constant rather than a function of the recording. The number this must NOT be
|
|
45
|
-
* is "the whole file", which is the shape every first draft has.
|
|
46
|
-
*/
|
|
47
|
-
export const WINDOW_BYTES = 8 * 1024 * 1024;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Run `work` with a private temp directory, and remove it afterwards.
|
|
51
|
-
*
|
|
52
|
-
* `join(tmpdir(), …)` rather than a `/tmp` literal, which is this repo's rule
|
|
53
|
-
* (`guard-invariants` rule 11) and not merely portability theatre: on Windows a
|
|
54
|
-
* literal `/tmp/x` is DRIVE-RELATIVE, so it resolves somewhere that does not
|
|
55
|
-
* exist and every write fails with ENOENT. A step runs in a Linux guest when it
|
|
56
|
-
* is deployed and on the developer's own machine under `aai dev`, which is the
|
|
57
|
-
* half that makes it matter.
|
|
58
|
-
*
|
|
59
|
-
* The removal is in a `finally`, so it also runs on the failure paths — a guest's
|
|
60
|
-
* disk is small, and a step that leaves a copy of every recording it touched
|
|
61
|
-
* fills it. `force` so a run that never created its output does not fail HERE and
|
|
62
|
-
* replace the real error with this one.
|
|
63
|
-
*/
|
|
64
|
-
export async function withTempDir<T>(work: (dir: string) => Promise<T>): Promise<T> {
|
|
65
|
-
const dir = await mkdtemp(join(tmpdir(), "aai-call-audit-"));
|
|
66
|
-
try {
|
|
67
|
-
return await work(dir);
|
|
68
|
-
} finally {
|
|
69
|
-
await rm(dir, { recursive: true, force: true });
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Write an upload to a local path, a window at a time.
|
|
75
|
-
*
|
|
76
|
-
* A `for` loop rather than a fan-out deliberately: the bytes land in one file at
|
|
77
|
-
* one offset each, so concurrency buys nothing and costs exactly the memory the
|
|
78
|
-
* windows are here to bound.
|
|
79
|
-
*
|
|
80
|
-
* `windowBytes` defaults to {@link WINDOW_BYTES}; see {@link fileChunks} for why
|
|
81
|
-
* it is a parameter at all.
|
|
82
|
-
*/
|
|
83
|
-
export async function materializeUpload(
|
|
84
|
-
uploadId: string,
|
|
85
|
-
size: number,
|
|
86
|
-
path: string,
|
|
87
|
-
windowBytes: number = WINDOW_BYTES,
|
|
88
|
-
): Promise<void> {
|
|
89
|
-
const handle = await open(path, "w");
|
|
90
|
-
try {
|
|
91
|
-
for (let at = 0; at < size; at += windowBytes) {
|
|
92
|
-
const slice = await readUpload(uploadId, {
|
|
93
|
-
start: at,
|
|
94
|
-
end: Math.min(at + windowBytes, size),
|
|
95
|
-
});
|
|
96
|
-
await handle.write(slice.bytes);
|
|
97
|
-
}
|
|
98
|
-
} finally {
|
|
99
|
-
await handle.close();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* A local file as the stream `writeUpload` takes.
|
|
105
|
-
*
|
|
106
|
-
* A generator rather than `readFile`, for the same reason the windows above
|
|
107
|
-
* exist: the normalized PCM is the largest thing this desk touches, and handing
|
|
108
|
-
* the store an `AsyncIterable` is what keeps it off the heap.
|
|
109
|
-
*
|
|
110
|
-
* **The `.slice()` is load-bearing.** One buffer is reused across reads, so
|
|
111
|
-
* yielding a view of it hands the consumer memory the next read overwrites — a
|
|
112
|
-
* bug whose symptom is a stored file made of the LAST chunk repeated, and which
|
|
113
|
-
* does not reproduce whenever the consumer happens to copy before the next
|
|
114
|
-
* iteration.
|
|
115
|
-
*
|
|
116
|
-
* `windowBytes` is a parameter with a default for exactly that reason, and it is
|
|
117
|
-
* the one testability seam in this template. The aliasing bug above only manifests
|
|
118
|
-
* across MULTIPLE reads, so at the real 8 MiB window a spec would have to write a
|
|
119
|
-
* 16 MB file to reach it — and a first draft of that spec used 200 KB, passed with
|
|
120
|
-
* the `.slice()` deleted, and would have shipped a test proving nothing. A small
|
|
121
|
-
* window makes the multi-chunk path a few kilobytes instead.
|
|
122
|
-
*/
|
|
123
|
-
export async function* fileChunks(
|
|
124
|
-
path: string,
|
|
125
|
-
windowBytes: number = WINDOW_BYTES,
|
|
126
|
-
): AsyncIterable<Uint8Array> {
|
|
127
|
-
const handle = await open(path, "r");
|
|
128
|
-
try {
|
|
129
|
-
const buffer = new Uint8Array(windowBytes);
|
|
130
|
-
for (;;) {
|
|
131
|
-
const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
|
|
132
|
-
if (bytesRead === 0) return;
|
|
133
|
-
yield buffer.subarray(0, bytesRead).slice();
|
|
134
|
-
}
|
|
135
|
-
} finally {
|
|
136
|
-
await handle.close();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { gameSlot, REPORTED_HISTORY, recordCommand } from "../shared.ts";
|
|
3
|
-
|
|
4
|
-
export default gameSlot.updateTool({
|
|
5
|
-
description: "Log a player command to the history and increment the move counter.",
|
|
6
|
-
inputSchema: z.object({
|
|
7
|
-
value: z.string().describe("Command text to log"),
|
|
8
|
-
}),
|
|
9
|
-
execute(args, game) {
|
|
10
|
-
recordCommand(game, args.value);
|
|
11
|
-
game.moves++;
|
|
12
|
-
return { moves: game.moves, recentHistory: game.history.slice(-REPORTED_HISTORY) };
|
|
13
|
-
},
|
|
14
|
-
});
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The corrective-RAG loop, node by node.
|
|
3
|
-
*
|
|
4
|
-
* This is the port of the self-RAG / CRAG graph (see `prompts.ts` for the
|
|
5
|
-
* attribution). Their control flow is edges between nodes; here it is a `while`
|
|
6
|
-
* loop in one tool body, and the node names survive in {@link AnswerTrace.steps}
|
|
7
|
-
* so a run is still readable as the graph it came from:
|
|
8
|
-
*
|
|
9
|
-
* ```text
|
|
10
|
-
* retrieve → grade_documents → decide_to_generate
|
|
11
|
-
* ├─ no relevant docs → transform_query → retrieve
|
|
12
|
-
* └─ generate → grade_generation_v_documents
|
|
13
|
-
* ├─ not grounded → generate (once)
|
|
14
|
-
* └─ grade_generation_v_question
|
|
15
|
-
* ├─ not useful → transform_query
|
|
16
|
-
* └─ done
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* **The budget is the mechanism, not the prompt.** A graph with a
|
|
20
|
-
* `transform_query` edge can cycle, and their notebooks bound it with a recursion
|
|
21
|
-
* limit that raises on hit. A caller is holding the line, so the bound here is
|
|
22
|
-
* two attempts and one regeneration — {@link MAX_ATTEMPTS} — and running out is
|
|
23
|
-
* an ANSWER (`exhausted`, the caller is offered a ticket) rather than an error.
|
|
24
|
-
* An agent that cannot say "I don't have that documented" is the failure the
|
|
25
|
-
* whole grading apparatus exists to prevent, so it has to be a reachable state.
|
|
26
|
-
*
|
|
27
|
-
* **Documents are graded CONCURRENTLY.** Their loop is sequential because a
|
|
28
|
-
* notebook does not care; four serial grader calls is four round trips of dead
|
|
29
|
-
* air on a phone. The grades are independent, so the fan-out is free.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
import type { GenerateFn } from "@alexkroman1/aai";
|
|
33
|
-
import {
|
|
34
|
-
ANSWER_SYSTEM,
|
|
35
|
-
ANSWERS_SYSTEM,
|
|
36
|
-
binaryScore,
|
|
37
|
-
DOC_GRADER_SYSTEM,
|
|
38
|
-
GROUNDED_SYSTEM,
|
|
39
|
-
REWRITE_SYSTEM,
|
|
40
|
-
} from "./prompts.ts";
|
|
41
|
-
import type { AnswerTrace, Doc, GradedDoc, TraceStep } from "./shared.ts";
|
|
42
|
-
import { formatDoc, retrieve } from "./shared.ts";
|
|
43
|
-
|
|
44
|
-
/** Retrieve-and-answer attempts, i.e. one query rewrite. */
|
|
45
|
-
export const MAX_ATTEMPTS = 2;
|
|
46
|
-
/** Regenerations after a "not grounded" verdict, within one attempt. */
|
|
47
|
-
export const MAX_REGENERATIONS = 1;
|
|
48
|
-
|
|
49
|
-
function step(trace: AnswerTrace, node: string, detail: string): void {
|
|
50
|
-
const entry: TraceStep = { node, detail };
|
|
51
|
-
trace.steps.push(entry);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** `grade_documents` — one closed question per document, all at once. */
|
|
55
|
-
async function gradeDocuments(
|
|
56
|
-
generate: GenerateFn,
|
|
57
|
-
question: string,
|
|
58
|
-
docs: Doc[],
|
|
59
|
-
): Promise<GradedDoc[]> {
|
|
60
|
-
return await Promise.all(
|
|
61
|
-
docs.map(async (doc) => {
|
|
62
|
-
const { object } = await generate({
|
|
63
|
-
system: DOC_GRADER_SYSTEM,
|
|
64
|
-
prompt: `Caller's question: ${question}\n\nDocument:\n${formatDoc(doc)}`,
|
|
65
|
-
schema: binaryScore,
|
|
66
|
-
});
|
|
67
|
-
return {
|
|
68
|
-
id: doc.id,
|
|
69
|
-
title: doc.title,
|
|
70
|
-
relevant: object.score === "yes",
|
|
71
|
-
reason: object.reason,
|
|
72
|
-
};
|
|
73
|
-
}),
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** `transform_query` — their question re-writer, aimed at a keyword index. */
|
|
78
|
-
async function transformQuery(generate: GenerateFn, question: string): Promise<string> {
|
|
79
|
-
const { text } = await generate({
|
|
80
|
-
system: REWRITE_SYSTEM,
|
|
81
|
-
prompt: `Caller's question: ${question}`,
|
|
82
|
-
});
|
|
83
|
-
const rewritten = text.trim().replace(/^["']|["']$/g, "");
|
|
84
|
-
// A rewriter that returns nothing usable must not empty the query — that
|
|
85
|
-
// would retrieve zero documents and read as "we have nothing on this".
|
|
86
|
-
return rewritten.length > 0 ? rewritten : question;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** `generate` — the answer, from the graded-relevant documents only. */
|
|
90
|
-
async function generateAnswer(
|
|
91
|
-
generate: GenerateFn,
|
|
92
|
-
question: string,
|
|
93
|
-
docs: Doc[],
|
|
94
|
-
): Promise<string> {
|
|
95
|
-
const { text } = await generate({
|
|
96
|
-
system: ANSWER_SYSTEM,
|
|
97
|
-
prompt: `Documents:\n\n${docs.map(formatDoc).join("\n\n")}\n\nCaller's question: ${question}`,
|
|
98
|
-
});
|
|
99
|
-
return text.trim();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** The two generation graders — same closed question, different subject. */
|
|
103
|
-
async function grade(
|
|
104
|
-
generate: GenerateFn,
|
|
105
|
-
system: string,
|
|
106
|
-
prompt: string,
|
|
107
|
-
): Promise<{ pass: boolean; reason: string }> {
|
|
108
|
-
const { object } = await generate({ system, prompt, schema: binaryScore });
|
|
109
|
-
return { pass: object.score === "yes", reason: object.reason };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Run the graph for one caller question. Never throws for a bad ANSWER — only
|
|
114
|
-
* for a broken model call, which is the tool's to report.
|
|
115
|
-
*/
|
|
116
|
-
export async function runCorrectiveRag(
|
|
117
|
-
generate: GenerateFn,
|
|
118
|
-
question: string,
|
|
119
|
-
): Promise<AnswerTrace> {
|
|
120
|
-
const trace: AnswerTrace = {
|
|
121
|
-
question,
|
|
122
|
-
query: question,
|
|
123
|
-
rewrites: 0,
|
|
124
|
-
steps: [],
|
|
125
|
-
docs: [],
|
|
126
|
-
answer: null,
|
|
127
|
-
grounded: null,
|
|
128
|
-
useful: null,
|
|
129
|
-
exhausted: false,
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
133
|
-
const retrieved = retrieve(trace.query);
|
|
134
|
-
step(trace, "retrieve", `${retrieved.length} document(s) for "${trace.query}"`);
|
|
135
|
-
|
|
136
|
-
const graded = await gradeDocuments(
|
|
137
|
-
generate,
|
|
138
|
-
question,
|
|
139
|
-
retrieved.map((one) => one.doc),
|
|
140
|
-
);
|
|
141
|
-
trace.docs = graded;
|
|
142
|
-
const relevant = retrieved.filter((one) => graded.find((g) => g.id === one.doc.id)?.relevant);
|
|
143
|
-
step(trace, "grade_documents", `${relevant.length} of ${graded.length} relevant`);
|
|
144
|
-
|
|
145
|
-
// `decide_to_generate`: nothing relevant means the retrieval was wrong, not
|
|
146
|
-
// that the answer is unknown — so rewrite and try once more before giving up.
|
|
147
|
-
if (relevant.length === 0) {
|
|
148
|
-
if (attempt < MAX_ATTEMPTS) {
|
|
149
|
-
trace.query = await transformQuery(generate, question);
|
|
150
|
-
trace.rewrites++;
|
|
151
|
-
step(trace, "transform_query", `retrying as "${trace.query}"`);
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
trace.exhausted = true;
|
|
155
|
-
step(trace, "decide_to_generate", "no relevant documents, and no attempts left");
|
|
156
|
-
return trace;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const docs = relevant.map((one) => one.doc);
|
|
160
|
-
const facts = docs.map(formatDoc).join("\n\n");
|
|
161
|
-
let answer = await generateAnswer(generate, question, docs);
|
|
162
|
-
step(trace, "generate", `answered from ${docs.map((d) => d.id).join(", ")}`);
|
|
163
|
-
|
|
164
|
-
// `grade_generation_v_documents`: is it grounded? A regeneration is worth
|
|
165
|
-
// one shot — the same documents with the ungrounded attempt now visible.
|
|
166
|
-
let grounded = await grade(
|
|
167
|
-
generate,
|
|
168
|
-
GROUNDED_SYSTEM,
|
|
169
|
-
`Facts:\n\n${facts}\n\nAnswer: ${answer}`,
|
|
170
|
-
);
|
|
171
|
-
for (let redo = 0; !grounded.pass && redo < MAX_REGENERATIONS; redo++) {
|
|
172
|
-
step(trace, "grade_generation_v_documents", `not grounded: ${grounded.reason}`);
|
|
173
|
-
answer = await generateAnswer(generate, question, docs);
|
|
174
|
-
step(trace, "generate", "regenerated");
|
|
175
|
-
grounded = await grade(generate, GROUNDED_SYSTEM, `Facts:\n\n${facts}\n\nAnswer: ${answer}`);
|
|
176
|
-
}
|
|
177
|
-
trace.answer = answer;
|
|
178
|
-
trace.grounded = grounded.pass;
|
|
179
|
-
step(
|
|
180
|
-
trace,
|
|
181
|
-
"grade_generation_v_documents",
|
|
182
|
-
grounded.pass ? "grounded" : `still not grounded: ${grounded.reason}`,
|
|
183
|
-
);
|
|
184
|
-
if (!grounded.pass) {
|
|
185
|
-
// An ungrounded answer is never spoken. Two regenerations that both
|
|
186
|
-
// invented something is a signal about the documents, not the phrasing.
|
|
187
|
-
trace.answer = null;
|
|
188
|
-
trace.exhausted = true;
|
|
189
|
-
return trace;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// `grade_generation_v_question`: grounded is not the same as useful.
|
|
193
|
-
const useful = await grade(
|
|
194
|
-
generate,
|
|
195
|
-
ANSWERS_SYSTEM,
|
|
196
|
-
`Question: ${question}\n\nAnswer: ${answer}`,
|
|
197
|
-
);
|
|
198
|
-
trace.useful = useful.pass;
|
|
199
|
-
step(
|
|
200
|
-
trace,
|
|
201
|
-
"grade_generation_v_question",
|
|
202
|
-
useful.pass ? "useful" : `not useful: ${useful.reason}`,
|
|
203
|
-
);
|
|
204
|
-
if (useful.pass) return trace;
|
|
205
|
-
|
|
206
|
-
if (attempt < MAX_ATTEMPTS) {
|
|
207
|
-
trace.query = await transformQuery(generate, question);
|
|
208
|
-
trace.rewrites++;
|
|
209
|
-
step(trace, "transform_query", `retrying as "${trace.query}"`);
|
|
210
|
-
trace.answer = null;
|
|
211
|
-
trace.grounded = null;
|
|
212
|
-
trace.useful = null;
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
// Out of attempts holding an answer that is grounded but beside the point.
|
|
216
|
-
// It is still the best thing we have, so it is returned WITH its verdict —
|
|
217
|
-
// the tool tells the model to offer a ticket alongside it.
|
|
218
|
-
trace.exhausted = true;
|
|
219
|
-
return trace;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
trace.exhausted = true;
|
|
223
|
-
return trace;
|
|
224
|
-
}
|
|
@@ -4,8 +4,8 @@ import { c as isEexist, l as readJson, o as errorMessage, p as writeJson } from
|
|
|
4
4
|
import { mkdtempSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs/promises";
|
|
7
|
-
import { tmpdir } from "node:os";
|
|
8
7
|
import { sleep } from "@alexkroman1/aai/internal";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
9
|
import envPaths from "env-paths";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
//#region _config.ts
|
|
File without changes
|