@alexkroman1/aai-cli 13.3.0 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{_agent-Clbq3a4h.mjs → _agent-BzUeqOdj.mjs} +32 -9
- package/dist/_agent.d.ts +23 -0
- package/dist/{_bundler-DEkjO1d8.mjs → _bundler-DolUCMxu.mjs} +8 -7
- package/dist/_cli-common.d.ts +45 -6
- package/dist/{_config-ZY2euhdh.mjs → _config-DYzC6WMD.mjs} +2 -2
- package/dist/_dev-env.d.ts +1 -1
- package/dist/{_dev-server-C13xuGB1.mjs → _dev-server-CSMqF8PN.mjs} +20 -22
- package/dist/_dev-vite-config.d.ts +3 -3
- package/dist/{_init-Ci4QGjp8.mjs → _init-CQ8idAwo.mjs} +9 -23
- package/dist/{_output-DbJt9gsl.mjs → _output-DBP9Op_d.mjs} +22 -13
- package/dist/_output.d.ts +10 -9
- package/dist/{_preflight-B2laWPdU.mjs → _preflight-CcAR577V.mjs} +2 -2
- package/dist/_resource-commands.d.ts +9 -9
- package/dist/{_server-common-BV4_CrP3.mjs → _server-common-vILJp3it.mjs} +1 -1
- package/dist/{_slug-api-DlKpj91X.mjs → _slug-api-CGvc2YSQ.mjs} +18 -9
- package/dist/_slug-api.d.ts +19 -0
- package/dist/{_studio-DTi3EdXQ.mjs → _studio-DnR_BqFp.mjs} +7 -3
- package/dist/_studio-commands.d.ts +13 -13
- package/dist/{_templates-V4l5sZJy.mjs → _templates-CK4oKoeX.mjs} +3 -3
- package/dist/{_typecheck-gate-sV4N5F93.mjs → _typecheck-gate-U3jcil8L.mjs} +3 -2
- package/dist/_typecheck-gate.d.ts +3 -1
- package/dist/{_utils-Dhd058D9.mjs → _utils-D5JGcjiW.mjs} +43 -1
- package/dist/_utils.d.ts +35 -0
- package/dist/{_workflow-determinism-C9l8uC2R.mjs → _workflow-determinism-CvpqMpdh.mjs} +18 -26
- package/dist/_workflow-determinism.d.ts +3 -3
- package/dist/{build-CWyFGe-W.mjs → build-Mxk8gWvX.mjs} +46 -7
- package/dist/build.d.ts +5 -0
- package/dist/cli.d.ts +1 -2
- package/dist/cli.mjs +117 -135
- package/dist/{client-bundler-CT-_26c5.mjs → client-bundler-BJgREAh6.mjs} +1 -1
- package/dist/client-bundler.mjs +1 -1
- package/dist/{delete-RqlQZ6Y_.mjs → delete-BdFnbnVR.mjs} +4 -4
- package/dist/{deploy-B7f3CK7v.mjs → deploy-uAJ4NukN.mjs} +11 -11
- package/dist/{dev-DamXieW_.mjs → dev-DApPSaE_.mjs} +3 -3
- package/dist/{eval-h3DTVfRw.mjs → eval-BK47A_K5.mjs} +3 -3
- package/dist/{init-D-vFrV8h.mjs → init-DukDxECd.mjs} +11 -19
- package/dist/{login-CDiemNmC.mjs → login-DaLrPOFg.mjs} +4 -6
- package/dist/{logs-DGwPNCzB.mjs → logs-7BRwq0cG.mjs} +21 -14
- package/dist/project-config.mjs +1 -1
- package/dist/scaffold/CLAUDE.md +84 -56
- package/dist/scaffold/package.json +4 -4
- package/dist/scaffold/server.mjs +129 -25
- package/dist/{secret-V1w7y9-j.mjs → secret-Cjp912yF.mjs} +5 -5
- package/dist/secret.d.ts +8 -6
- package/dist/{studio-BpMCfUNx.mjs → studio-CpHlNHUZ.mjs} +13 -15
- package/dist/templates/call-audit/agent.test.ts +11 -6
- package/dist/templates/call-audit/agent.ts +2 -2
- package/dist/templates/call-audit/client.tsx +27 -42
- package/dist/templates/call-audit/workflows/audit.ts +7 -5
- package/dist/templates/call-audit/workflows/ingest.ts +7 -7
- package/dist/templates/call-audit/workflows/media.ts +12 -37
- package/dist/templates/call-audit/workflows/summarize.ts +8 -8
- package/dist/templates/call-audit/workflows/sync-api.ts +3 -3
- package/dist/templates/dispatch-center/agent.test.ts +7 -7
- package/dist/templates/dispatch-center/client.tsx +53 -61
- package/dist/templates/dispatch-center/shared.ts +0 -8
- package/dist/templates/dispatch-center/tools/ops_dashboard.ts +4 -1
- package/dist/templates/embedded-assets/agent.eval.test.ts +4 -2
- package/dist/templates/health-assistant/agent.eval.test.ts +4 -2
- package/dist/templates/infocom-adventure/client.tsx +40 -36
- package/dist/templates/link-digest/agent.test.ts +3 -3
- package/dist/templates/link-digest/agent.ts +2 -2
- package/dist/templates/link-digest/client.tsx +27 -17
- package/dist/templates/link-digest/workflows/digest.ts +13 -16
- package/dist/templates/night-owl/agent.eval.test.ts +4 -2
- package/dist/templates/night-owl/client.tsx +8 -2
- package/dist/templates/pipeline-simple/agent.test.ts +2 -2
- package/dist/templates/pizza-ordering/client.tsx +5 -4
- package/dist/templates/pizza-ordering/shared.ts +3 -8
- package/dist/templates/pizza-ordering/tools/add_pizza.ts +3 -2
- package/dist/templates/pizza-ordering/tools/place_order.ts +3 -2
- package/dist/templates/pizza-ordering/tools/remove_pizza.ts +3 -2
- package/dist/templates/pizza-ordering/tools/update_pizza.ts +3 -2
- package/dist/templates/pizza-ordering/tools/view_order.ts +3 -2
- package/dist/templates/plan-and-execute/agent.test.ts +10 -5
- package/dist/templates/plan-and-execute/client.tsx +2 -2
- package/dist/templates/podcast-digest/agent.test.ts +20 -11
- package/dist/templates/podcast-digest/client.tsx +16 -22
- package/dist/templates/podcast-digest/workflows/digest.ts +21 -19
- package/dist/templates/podcast-digest/workflows/feeds.ts +61 -33
- package/dist/templates/podcast-digest/workflows/slack.ts +7 -7
- package/dist/templates/recap-workflow/agent.eval.test.ts +21 -13
- package/dist/templates/recap-workflow/agent.test.ts +27 -23
- package/dist/templates/recap-workflow/workflows/recap.ts +24 -28
- package/dist/templates/redline/agent.eval.test.ts +5 -8
- package/dist/templates/redline/agent.test.ts +1 -1
- package/dist/templates/redline/client.tsx +36 -34
- package/dist/templates/redline/workflows/redline.ts +17 -17
- package/dist/templates/research-workflow/agent.eval.test.ts +5 -5
- package/dist/templates/research-workflow/agent.test.ts +10 -7
- package/dist/templates/research-workflow/workflows/research.ts +28 -30
- package/dist/templates/retail/agent.test.ts +17 -15
- package/dist/templates/retail/cancel.ts +7 -6
- package/dist/templates/retail/client.tsx +39 -58
- package/dist/templates/retail/payment.ts +4 -3
- package/dist/templates/retail/pending.test.ts +19 -19
- package/dist/templates/retail/store.test.ts +5 -5
- package/dist/templates/retail/swap.ts +8 -11
- package/dist/templates/solo-rpg/agent.eval.test.ts +2 -16
- package/dist/templates/solo-rpg/agent.test.ts +12 -12
- package/dist/templates/solo-rpg/client.tsx +3 -3
- package/dist/templates/spoken-summary/agent.test.ts +9 -9
- package/dist/templates/spoken-summary/agent.ts +3 -3
- package/dist/templates/spoken-summary/client.tsx +23 -30
- package/dist/templates/spoken-summary/workflows/summarize.ts +11 -11
- package/dist/templates/spoken-summary/workflows/transcribe.ts +14 -14
- package/dist/templates/support-line/client.tsx +4 -3
- package/dist/templates/support-line/prompts.ts +0 -2
- package/dist/templates/transcription-workflow/agent.test.ts +20 -12
- package/dist/templates/transcription-workflow/agent.ts +2 -2
- package/dist/templates/transcription-workflow/client.tsx +15 -14
- package/dist/templates/transcription-workflow/recover.ts +1 -1
- package/dist/templates/transcription-workflow/run-panel.tsx +34 -14
- package/dist/templates/transcription-workflow/total-latency.tsx +46 -20
- package/dist/templates/transcription-workflow/workflows/batch.ts +15 -15
- package/dist/templates/transcription-workflow/workflows/downsample.ts +1 -1
- package/dist/templates/transcription-workflow/workflows/normalize.ts +15 -19
- package/dist/templates/transcription-workflow/workflows/stream.ts +12 -12
- package/dist/templates/transcription-workflow/workflows/sync-api.ts +3 -3
- package/dist/templates/transcription-workflow/workflows/transcribe.ts +23 -34
- package/dist/templates/travel-concierge/agent.eval.test.ts +2 -2
- package/dist/templates/travel-concierge/agent.test.ts +15 -7
- package/dist/templates/travel-concierge/client.tsx +2 -2
- package/dist/templates/travel-concierge/shared.ts +10 -14
- package/dist/templates/travel-concierge/tools/lookup_booking.ts +4 -3
- package/dist/templates/travel-concierge/tools/search_car_rentals.ts +3 -2
- package/dist/templates/travel-concierge/tools/search_excursions.ts +3 -2
- package/dist/templates/travel-concierge/tools/search_flights.ts +3 -2
- package/dist/templates/travel-concierge/tools/search_hotels.ts +3 -2
- package/dist/{test-guenJUxi.mjs → test-D032V2cA.mjs} +4 -17
- package/dist/typecheck.mjs +1 -1
- package/dist/worker-bundler.mjs +1 -1
- package/dist/{workflow-BZyKLkhV.mjs → workflow-CVioyO3V.mjs} +6 -5
- package/package.json +4 -4
package/dist/_slug-api.d.ts
CHANGED
|
@@ -27,6 +27,25 @@ export declare function secretRequest<T = unknown>(cwd: string, resourcePath: st
|
|
|
27
27
|
data: T;
|
|
28
28
|
target: string;
|
|
29
29
|
}>;
|
|
30
|
+
/** An already-resolved deployed target — what {@link slugRequestOn} needs. */
|
|
31
|
+
export type SlugTarget = {
|
|
32
|
+
serverUrl: string;
|
|
33
|
+
slug: string;
|
|
34
|
+
apiKey: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* A slug-scoped request against an ALREADY-resolved target.
|
|
38
|
+
*
|
|
39
|
+
* {@link slugRequest} resolves per call, which is right for a command that
|
|
40
|
+
* issues one request and wrong for one that POLLS. `aai logs --follow` re-entered
|
|
41
|
+
* `getServerInfo` on every tick — a read and zod parse of `.aai/project.json`, a
|
|
42
|
+
* read of the global config, a `new URL` trust check, a slug-shape test, and with
|
|
43
|
+
* `--server` an `approveServer` that takes the CROSS-PROCESS config lock — once a
|
|
44
|
+
* second for the life of the follow, all of it re-deriving an immutable answer.
|
|
45
|
+
* `delete.ts` documents the same cost and answers it the same way, by hand; this
|
|
46
|
+
* is the general version, so the next polling command inherits it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function slugRequestOn<T = unknown>(target: SlugTarget, resourcePath: string, init: SlugRequestInit): Promise<T>;
|
|
30
49
|
export declare function slugRequest<T = unknown>(cwd: string, resourcePath: string, init: SlugRequestInit, server?: string): Promise<{
|
|
31
50
|
data: T;
|
|
32
51
|
slug: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as CliError } from "./_output-
|
|
2
|
+
import { t as CliError } from "./_output-DBP9Op_d.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { isRecord, omitUndefined } from "@alexkroman1/aai/utils";
|
|
5
5
|
import { MAX_SLUG_LENGTH, PREVIEW_SLUG_SUFFIX, VALID_SLUG_RE } from "@alexkroman1/aai/internal";
|
|
@@ -221,7 +221,11 @@ function fetchStudioProject(serverUrl, apiKey, project) {
|
|
|
221
221
|
apiKey,
|
|
222
222
|
action: "pull",
|
|
223
223
|
allow404: true
|
|
224
|
-
});
|
|
224
|
+
}).then((res) => res === null ? null : checkedResponse(res, isStudioProject, `the studio project ${project} at ${serverUrl}`));
|
|
225
|
+
}
|
|
226
|
+
/** A pulled workspace, as the wire must actually carry it. */
|
|
227
|
+
function isStudioProject(value) {
|
|
228
|
+
return isRecord(value) && isRecord(value.files) && Object.values(value.files).every((v) => typeof v === "string") && typeof value.sourceHash === "string";
|
|
225
229
|
}
|
|
226
230
|
/** `PUT /studio/projects/:project/source` — the atomic whole-tree push. */
|
|
227
231
|
function pushStudioSource(serverUrl, apiKey, project, body) {
|
|
@@ -231,7 +235,7 @@ function pushStudioSource(serverUrl, apiKey, project, body) {
|
|
|
231
235
|
method: "PUT",
|
|
232
236
|
body,
|
|
233
237
|
hints: { 409: "The studio has newer changes. Run `aai pull` to fetch them, or `aai push --force` to overwrite." }
|
|
234
|
-
});
|
|
238
|
+
}).then((res) => checkedResponse(res, (value) => isRecord(value) && typeof value.sourceHash === "string" && typeof value.created === "boolean", `the studio push route at ${serverUrl}`));
|
|
235
239
|
}
|
|
236
240
|
/** `POST /studio/projects/:project/deploy` — Publish, in the project's sandbox. */
|
|
237
241
|
function publishStudioProject(serverUrl, apiKey, project, opts = {}) {
|
|
@@ -10,6 +10,15 @@ export declare const list: import("citty").CommandDef<{
|
|
|
10
10
|
};
|
|
11
11
|
}>;
|
|
12
12
|
export declare const pull: import("citty").CommandDef<{
|
|
13
|
+
readonly server: {
|
|
14
|
+
readonly type: "string";
|
|
15
|
+
readonly alias: "s";
|
|
16
|
+
readonly description: "Platform server URL";
|
|
17
|
+
};
|
|
18
|
+
readonly json: {
|
|
19
|
+
readonly type: "boolean";
|
|
20
|
+
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
21
|
+
};
|
|
13
22
|
readonly project: {
|
|
14
23
|
readonly type: "positional";
|
|
15
24
|
readonly description: "Studio project name (see `aai list`)";
|
|
@@ -25,6 +34,8 @@ export declare const pull: import("citty").CommandDef<{
|
|
|
25
34
|
readonly alias: "f";
|
|
26
35
|
readonly description: "Overwrite files in a non-empty directory";
|
|
27
36
|
};
|
|
37
|
+
}>;
|
|
38
|
+
export declare const push: import("citty").CommandDef<{
|
|
28
39
|
readonly server: {
|
|
29
40
|
readonly type: "string";
|
|
30
41
|
readonly alias: "s";
|
|
@@ -34,13 +45,13 @@ export declare const pull: import("citty").CommandDef<{
|
|
|
34
45
|
readonly type: "boolean";
|
|
35
46
|
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
36
47
|
};
|
|
37
|
-
}>;
|
|
38
|
-
export declare const push: import("citty").CommandDef<{
|
|
39
48
|
readonly force: {
|
|
40
49
|
readonly type: "boolean";
|
|
41
50
|
readonly alias: "f";
|
|
42
51
|
readonly description: "Overwrite studio-side changes instead of failing the fast-forward check";
|
|
43
52
|
};
|
|
53
|
+
}>;
|
|
54
|
+
export declare const publish: import("citty").CommandDef<{
|
|
44
55
|
readonly server: {
|
|
45
56
|
readonly type: "string";
|
|
46
57
|
readonly alias: "s";
|
|
@@ -50,22 +61,11 @@ export declare const push: import("citty").CommandDef<{
|
|
|
50
61
|
readonly type: "boolean";
|
|
51
62
|
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
52
63
|
};
|
|
53
|
-
}>;
|
|
54
|
-
export declare const publish: import("citty").CommandDef<{
|
|
55
64
|
readonly force: {
|
|
56
65
|
readonly type: "boolean";
|
|
57
66
|
readonly alias: "f";
|
|
58
67
|
readonly description: "Overwrite studio-side changes instead of failing the fast-forward check";
|
|
59
68
|
};
|
|
60
|
-
readonly server: {
|
|
61
|
-
readonly type: "string";
|
|
62
|
-
readonly alias: "s";
|
|
63
|
-
readonly description: "Platform server URL";
|
|
64
|
-
};
|
|
65
|
-
readonly json: {
|
|
66
|
-
readonly type: "boolean";
|
|
67
|
-
readonly description: "Output JSON (auto-detected in non-TTY)";
|
|
68
|
-
};
|
|
69
69
|
readonly skipTypecheck: {
|
|
70
70
|
readonly type: "boolean";
|
|
71
71
|
readonly description: "Skip type checking before publishing";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { t as getMonorepoRoot } from "./_agent-
|
|
2
|
+
import { g as writeJson, s as errorMessage$1 } from "./_utils-D5JGcjiW.mjs";
|
|
3
|
+
import { t as getMonorepoRoot } from "./_agent-BzUeqOdj.mjs";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { isRecord } from "@alexkroman1/aai/utils";
|
|
@@ -106,7 +106,7 @@ async function layerScaffoldManifest(scaffoldDir, targetDir) {
|
|
|
106
106
|
const [mine, theirs] = await Promise.all([readJsonFile(target), readJsonFile(path.join(scaffoldDir, "package.json"))]);
|
|
107
107
|
if (!(mine && theirs)) return;
|
|
108
108
|
const merged = mergeScaffoldManifest(mine, theirs);
|
|
109
|
-
if (merged) await
|
|
109
|
+
if (merged) await writeJson(target, merged);
|
|
110
110
|
}
|
|
111
111
|
/** Parse a JSON file, or null when it is missing or unparseable. */
|
|
112
112
|
async function readJsonFile(file) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as CliError } from "./_output-
|
|
2
|
+
import { t as CliError } from "./_output-DBP9Op_d.mjs";
|
|
3
3
|
import { n as log } from "./_ui-DCt4qZrk.mjs";
|
|
4
4
|
import { typecheckProject } from "./typecheck.mjs";
|
|
5
5
|
//#region src/_typecheck-gate.ts
|
|
@@ -9,7 +9,8 @@ import { typecheckProject } from "./typecheck.mjs";
|
|
|
9
9
|
* bundlers strip types unchecked, so without this a type-broken agent
|
|
10
10
|
* ships and misbehaves at runtime instead of failing here.
|
|
11
11
|
*/
|
|
12
|
-
async function assertTypechecks(cwd) {
|
|
12
|
+
async function assertTypechecks(cwd, opts = {}) {
|
|
13
|
+
if (opts.skip) return;
|
|
13
14
|
log.step("Type checking…");
|
|
14
15
|
const result = await typecheckProject(cwd);
|
|
15
16
|
if (!result.ok) throw new CliError("typecheck_failed", result.output, "Fix the type errors, or pass --skipTypecheck to build anyway");
|
|
@@ -4,4 +4,6 @@
|
|
|
4
4
|
* bundlers strip types unchecked, so without this a type-broken agent
|
|
5
5
|
* ships and misbehaves at runtime instead of failing here.
|
|
6
6
|
*/
|
|
7
|
-
export declare function assertTypechecks(cwd: string
|
|
7
|
+
export declare function assertTypechecks(cwd: string, opts?: {
|
|
8
|
+
skip?: boolean | undefined;
|
|
9
|
+
}): Promise<void>;
|
|
@@ -115,5 +115,47 @@ async function writeJson(filePath, data, opts = {}) {
|
|
|
115
115
|
throw err;
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Code-unit ordering, never `localeCompare`.
|
|
120
|
+
*
|
|
121
|
+
* With no explicit locale `localeCompare` answers to the runtime's ICU default,
|
|
122
|
+
* so the same project would produce a differently-ordered artifact on a
|
|
123
|
+
* different machine — which is why the repo's own generated files sort this way.
|
|
124
|
+
* The reproducibility this protects is real in both callers: the scaffold's
|
|
125
|
+
* `pnpm-workspace.yaml` overrides, and the unrun-spec report.
|
|
126
|
+
*/
|
|
127
|
+
function compareCodeUnits(a, b) {
|
|
128
|
+
if (a === b) return 0;
|
|
129
|
+
return a < b ? -1 : 1;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Strip trailing slashes, so a `${base}/...` join cannot produce `//path`.
|
|
133
|
+
*
|
|
134
|
+
* Here rather than beside its first caller because BOTH callers need it and
|
|
135
|
+
* `_agent.ts` — where it lived — is a module `workflow.test.ts` mocks whole, so
|
|
136
|
+
* importing a pure helper from there breaks the mock. `workflow.ts`'s local copy
|
|
137
|
+
* took `/\/$/` — ONE slash — against this `/\/+$/`, so the two disagreed on the
|
|
138
|
+
* only input either was written for.
|
|
139
|
+
*/
|
|
140
|
+
function stripTrailingSlash(url) {
|
|
141
|
+
return url.replace(/\/+$/, "");
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* A list as one phrase — the first {@link MAX_NAMED_ITEMS} names, then a count.
|
|
145
|
+
*
|
|
146
|
+
* One definition because a user reads this wording from two different commands:
|
|
147
|
+
* the unrun-spec report (`aai test`) and the "no such project" hint
|
|
148
|
+
* (`aai pull`). Both open-coded the same slice, the same arithmetic and the same
|
|
149
|
+
* `, and N more`, so the cap could drift between two messages one CLI prints.
|
|
150
|
+
*
|
|
151
|
+
* Here rather than in `_ui.ts` because this is a pure formatter and that module
|
|
152
|
+
* is the OUTPUT seam specs mock — `studio.test.ts` replaces it with a bare
|
|
153
|
+
* factory, which drops every export the factory does not list.
|
|
154
|
+
*/
|
|
155
|
+
function formatCappedList(items, max = 10) {
|
|
156
|
+
const named = items.slice(0, max).join(", ");
|
|
157
|
+
const rest = items.length - max;
|
|
158
|
+
return rest > 0 ? `${named}, and ${rest} more` : named;
|
|
159
|
+
}
|
|
118
160
|
//#endregion
|
|
119
|
-
export {
|
|
161
|
+
export { errorCode as a, fileExists as c, readJson as d, readPackageJson as f, writeJson as g, validateAgentExport as h, compareCodeUnits as i, formatCappedList as l, stripTrailingSlash as m, binFromManifest as n, errorDetail as o, resolveCwd as p, binFromPackageJson as r, errorMessage$1 as s, AGENT_ENTRY as t, isEexist as u };
|
package/dist/_utils.d.ts
CHANGED
|
@@ -69,3 +69,38 @@ export declare function readJson(filePath: string): Promise<unknown>;
|
|
|
69
69
|
export declare function writeJson(filePath: string, data: unknown, opts?: {
|
|
70
70
|
mode?: number;
|
|
71
71
|
}): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Code-unit ordering, never `localeCompare`.
|
|
74
|
+
*
|
|
75
|
+
* With no explicit locale `localeCompare` answers to the runtime's ICU default,
|
|
76
|
+
* so the same project would produce a differently-ordered artifact on a
|
|
77
|
+
* different machine — which is why the repo's own generated files sort this way.
|
|
78
|
+
* The reproducibility this protects is real in both callers: the scaffold's
|
|
79
|
+
* `pnpm-workspace.yaml` overrides, and the unrun-spec report.
|
|
80
|
+
*/
|
|
81
|
+
export declare function compareCodeUnits(a: string, b: string): number;
|
|
82
|
+
/**
|
|
83
|
+
* Strip trailing slashes, so a `${base}/...` join cannot produce `//path`.
|
|
84
|
+
*
|
|
85
|
+
* Here rather than beside its first caller because BOTH callers need it and
|
|
86
|
+
* `_agent.ts` — where it lived — is a module `workflow.test.ts` mocks whole, so
|
|
87
|
+
* importing a pure helper from there breaks the mock. `workflow.ts`'s local copy
|
|
88
|
+
* took `/\/$/` — ONE slash — against this `/\/+$/`, so the two disagreed on the
|
|
89
|
+
* only input either was written for.
|
|
90
|
+
*/
|
|
91
|
+
export declare function stripTrailingSlash(url: string): string;
|
|
92
|
+
/** How many names a capped list prints before it starts counting. */
|
|
93
|
+
export declare const MAX_NAMED_ITEMS = 10;
|
|
94
|
+
/**
|
|
95
|
+
* A list as one phrase — the first {@link MAX_NAMED_ITEMS} names, then a count.
|
|
96
|
+
*
|
|
97
|
+
* One definition because a user reads this wording from two different commands:
|
|
98
|
+
* the unrun-spec report (`aai test`) and the "no such project" hint
|
|
99
|
+
* (`aai pull`). Both open-coded the same slice, the same arithmetic and the same
|
|
100
|
+
* `, and N more`, so the cap could drift between two messages one CLI prints.
|
|
101
|
+
*
|
|
102
|
+
* Here rather than in `_ui.ts` because this is a pure formatter and that module
|
|
103
|
+
* is the OUTPUT seam specs mock — `studio.test.ts` replaces it with a bare
|
|
104
|
+
* factory, which drops every export the factory does not list.
|
|
105
|
+
*/
|
|
106
|
+
export declare function formatCappedList(items: readonly string[], max?: number): string;
|
|
@@ -24,12 +24,12 @@ import { readFile, readdir } from "node:fs/promises";
|
|
|
24
24
|
* compiling every fence in this repo's shipped docs:
|
|
25
25
|
*
|
|
26
26
|
* ```ts
|
|
27
|
-
* import type {
|
|
27
|
+
* import type { WorkflowContext } from "@alexkroman1/aai";
|
|
28
28
|
*
|
|
29
29
|
* declare const charge: () => Promise<string>;
|
|
30
30
|
*
|
|
31
|
-
* export async function body(ctx:
|
|
32
|
-
* // No error. The banned shape, against the real `
|
|
31
|
+
* export async function body(ctx: WorkflowContext, coin: string): Promise<void> {
|
|
32
|
+
* // No error. The banned shape, against the real `WorkflowContext`.
|
|
33
33
|
* await ctx.step(`charge-${coin}`, charge);
|
|
34
34
|
* }
|
|
35
35
|
* ```
|
|
@@ -81,19 +81,6 @@ import { readFile, readdir } from "node:fs/promises";
|
|
|
81
81
|
* @module
|
|
82
82
|
*/
|
|
83
83
|
/**
|
|
84
|
-
* The three `ctx` methods whose first argument is a journal identity.
|
|
85
|
-
*
|
|
86
|
-
* All three key a journal row by it — `name#occurrence` for a step,
|
|
87
|
-
* `sleep!<label>#<n>` and `hook!<token>#<n>` for the waits — so a computed one
|
|
88
|
-
* mints a row no earlier walk reached in exactly the same way. Kept as a list
|
|
89
|
-
* so the message can name the method that was called.
|
|
90
|
-
*/
|
|
91
|
-
const IDENTITY_METHODS = [
|
|
92
|
-
"step",
|
|
93
|
-
"sleep",
|
|
94
|
-
"waitFor"
|
|
95
|
-
];
|
|
96
|
-
/**
|
|
97
84
|
* A `ctx.<method>(` immediately followed by a BACKTICK.
|
|
98
85
|
*
|
|
99
86
|
* The whole check, and its narrowness is the point. A quoted literal is
|
|
@@ -104,8 +91,17 @@ const IDENTITY_METHODS = [
|
|
|
104
91
|
* The receiver is not pinned to `ctx` — a body may destructure or rename it —
|
|
105
92
|
* and a `.step(` on anything else taking a template-literal first argument is
|
|
106
93
|
* not a shape worth excluding.
|
|
94
|
+
*
|
|
95
|
+
* Shared rather than built per call, which is safe ONLY because the single
|
|
96
|
+
* reader is `matchAll`: that method species-constructs its own clone and never
|
|
97
|
+
* advances this object's `lastIndex`. A `.exec` or `.test` added against a `/g`
|
|
98
|
+
* regex would carry state between lines — build a fresh one there.
|
|
107
99
|
*/
|
|
108
|
-
const
|
|
100
|
+
const IDENTITY_PATTERN = new RegExp(`\\.(${[
|
|
101
|
+
"step",
|
|
102
|
+
"sleep",
|
|
103
|
+
"waitFor"
|
|
104
|
+
].join("|")})\\(\``, "g");
|
|
109
105
|
/** Whether a template literal actually INTERPOLATES, or is merely quoted oddly. */
|
|
110
106
|
const INTERPOLATES = /\$\{/;
|
|
111
107
|
/**
|
|
@@ -132,7 +128,7 @@ function findComputedIdentities(source, file) {
|
|
|
132
128
|
const findings = [];
|
|
133
129
|
for (const [n, line] of source.split("\n").entries()) {
|
|
134
130
|
if (isCommentOnly(line)) continue;
|
|
135
|
-
for (const match of line.matchAll(
|
|
131
|
+
for (const match of line.matchAll(IDENTITY_PATTERN)) {
|
|
136
132
|
const method = match[1];
|
|
137
133
|
if (method === void 0) continue;
|
|
138
134
|
if (!INTERPOLATES.test(line.slice((match.index ?? 0) + match[0].length))) continue;
|
|
@@ -169,17 +165,13 @@ async function workflowFiles(cwd) {
|
|
|
169
165
|
* warning pass beside a typecheck that has already run on the same tree.
|
|
170
166
|
*/
|
|
171
167
|
async function scanWorkflowDeterminism(cwd) {
|
|
172
|
-
|
|
173
|
-
for (const rel of await workflowFiles(cwd)) {
|
|
174
|
-
let source;
|
|
168
|
+
return (await Promise.all((await workflowFiles(cwd)).map(async (rel) => {
|
|
175
169
|
try {
|
|
176
|
-
|
|
170
|
+
return findComputedIdentities(await readFile(path.join(cwd, rel), "utf-8"), rel);
|
|
177
171
|
} catch {
|
|
178
|
-
|
|
172
|
+
return [];
|
|
179
173
|
}
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
return findings;
|
|
174
|
+
}))).flat();
|
|
183
175
|
}
|
|
184
176
|
/**
|
|
185
177
|
* The remedy, once, however many findings there are.
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
* compiling every fence in this repo's shipped docs:
|
|
21
21
|
*
|
|
22
22
|
* ```ts
|
|
23
|
-
* import type {
|
|
23
|
+
* import type { WorkflowContext } from "@alexkroman1/aai";
|
|
24
24
|
*
|
|
25
25
|
* declare const charge: () => Promise<string>;
|
|
26
26
|
*
|
|
27
|
-
* export async function body(ctx:
|
|
28
|
-
* // No error. The banned shape, against the real `
|
|
27
|
+
* export async function body(ctx: WorkflowContext, coin: string): Promise<void> {
|
|
28
|
+
* // No error. The banned shape, against the real `WorkflowContext`.
|
|
29
29
|
* await ctx.step(`charge-${coin}`, charge);
|
|
30
30
|
* }
|
|
31
31
|
* ```
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as ok, t as CliError } from "./_output-
|
|
2
|
+
import { a as ok, t as CliError } from "./_output-DBP9Op_d.mjs";
|
|
3
3
|
import { n as log, r as notify } from "./_ui-DCt4qZrk.mjs";
|
|
4
|
-
import { r as evalWorkerBundle, t as buildAgentBundle } from "./_bundler-
|
|
5
|
-
import { assertTypechecks } from "./_typecheck-gate-
|
|
6
|
-
import { n as scanWorkflowDeterminism, t as determinismWarnings } from "./_workflow-determinism-
|
|
7
|
-
import { TEST_FILES, classifyVitestError, runVitest } from "./test-
|
|
4
|
+
import { r as evalWorkerBundle, t as buildAgentBundle } from "./_bundler-DolUCMxu.mjs";
|
|
5
|
+
import { assertTypechecks } from "./_typecheck-gate-U3jcil8L.mjs";
|
|
6
|
+
import { n as scanWorkflowDeterminism, t as determinismWarnings } from "./_workflow-determinism-CvpqMpdh.mjs";
|
|
7
|
+
import { TEST_FILES, classifyVitestError, runVitest } from "./test-D032V2cA.mjs";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import fs from "node:fs/promises";
|
|
10
10
|
import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
|
|
11
|
+
import { DEFAULT_SYSTEM_PROMPT } from "@alexkroman1/aai";
|
|
11
12
|
//#region src/build.ts
|
|
12
13
|
/**
|
|
13
14
|
* `aai build` — bundle the agent without deploying, behind the same gates
|
|
@@ -32,6 +33,41 @@ import { agentConfigWarnings } from "@alexkroman1/aai/manifest";
|
|
|
32
33
|
*/
|
|
33
34
|
const WORKER_ARTIFACT_REL = path.join(".aai", "worker.mjs");
|
|
34
35
|
/**
|
|
36
|
+
* The prose slot, named by convention. Mirrors `worker-bundler.ts`'s constant of
|
|
37
|
+
* the same name — the two cannot share one without an import cycle
|
|
38
|
+
* (`build.ts` → `_bundler.ts` → `worker-bundler.ts`), so `_build.test.ts` pins
|
|
39
|
+
* the pair end to end instead: a build whose prompt really came from the file
|
|
40
|
+
* has to report the file.
|
|
41
|
+
*/
|
|
42
|
+
const SYSTEM_PROMPT_FILE = "system-prompt.md";
|
|
43
|
+
/** What {@link systemPromptSource} reports when nothing declared a prompt. */
|
|
44
|
+
const FRAMEWORK_DEFAULT_PROMPT_SOURCE = "the framework default (DEFAULT_SYSTEM_PROMPT)";
|
|
45
|
+
/**
|
|
46
|
+
* Which of the three possible authors of this agent's system prompt actually
|
|
47
|
+
* wrote the one in the bundle.
|
|
48
|
+
*
|
|
49
|
+
* `aai build` reported `name` and `workerBytes` and nothing about the prompt,
|
|
50
|
+
* and the prompt is the one field that can change COMPLETELY without any
|
|
51
|
+
* config changing: deleting `system-prompt.md` swaps in
|
|
52
|
+
* `DEFAULT_SYSTEM_PROMPT` — a total personality change — with exit 0 and no
|
|
53
|
+
* line anywhere. `withSystemPrompt` refuses the case where a file exists and
|
|
54
|
+
* nothing reads it; it cannot refuse a file that is simply GONE, because that
|
|
55
|
+
* is what an agent with no file legitimately looks like. So this is a report
|
|
56
|
+
* rather than a check.
|
|
57
|
+
*
|
|
58
|
+
* Decided by comparing VALUES against the built agent's resolved prompt, which
|
|
59
|
+
* is the same method `withSystemPrompt` uses and for the same reason — the
|
|
60
|
+
* alternative asks the bundler's module graph a question, and the author may
|
|
61
|
+
* have imported the file and composed it, which is neither "the file" nor
|
|
62
|
+
* "agent.ts" alone.
|
|
63
|
+
*/
|
|
64
|
+
async function systemPromptSource(cwd, resolved) {
|
|
65
|
+
if (resolved === DEFAULT_SYSTEM_PROMPT) return FRAMEWORK_DEFAULT_PROMPT_SOURCE;
|
|
66
|
+
const trimmed = (await fs.readFile(path.join(cwd, SYSTEM_PROMPT_FILE), "utf-8").catch(() => void 0))?.trim();
|
|
67
|
+
if (trimmed === void 0 || trimmed === "" || !resolved.includes(trimmed)) return "agent.ts";
|
|
68
|
+
return resolved.trim() === trimmed ? SYSTEM_PROMPT_FILE : `agent.ts (composing ${SYSTEM_PROMPT_FILE})`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
35
71
|
* Map a {@link runVitest} failure to a CliError — distinguishing a real test
|
|
36
72
|
* failure (test_failed) from the runner not spawning (spawn_failed) instead
|
|
37
73
|
* of a generic command_failed.
|
|
@@ -50,7 +86,7 @@ async function executeBuild(opts) {
|
|
|
50
86
|
} catch (err) {
|
|
51
87
|
throw testGateError(err);
|
|
52
88
|
}
|
|
53
|
-
|
|
89
|
+
await assertTypechecks(cwd, { skip: opts.skipTypecheck });
|
|
54
90
|
const bundle = await buildAgentBundle(cwd, { minify: true });
|
|
55
91
|
const agentDef = await evalWorkerBundle(bundle.worker);
|
|
56
92
|
for (const warning of agentConfigWarnings(agentDef)) notify("warn", warning);
|
|
@@ -58,11 +94,14 @@ async function executeBuild(opts) {
|
|
|
58
94
|
const worker = path.join(cwd, WORKER_ARTIFACT_REL);
|
|
59
95
|
await fs.mkdir(path.dirname(worker), { recursive: true });
|
|
60
96
|
await fs.writeFile(worker, bundle.worker, "utf-8");
|
|
97
|
+
const systemPrompt = await systemPromptSource(cwd, agentDef.systemPrompt);
|
|
98
|
+
log.info(`System prompt: ${systemPrompt}`);
|
|
61
99
|
log.success("Build complete");
|
|
62
100
|
return ok({
|
|
63
101
|
name: agentDef.name,
|
|
64
102
|
workerBytes: bundle.worker.length,
|
|
65
|
-
worker
|
|
103
|
+
worker,
|
|
104
|
+
systemPrompt
|
|
66
105
|
});
|
|
67
106
|
}
|
|
68
107
|
//#endregion
|
package/dist/build.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ type BuildData = {
|
|
|
26
26
|
workerBytes: number;
|
|
27
27
|
/** Absolute path of the worker written — what `npm start` boots. */
|
|
28
28
|
worker: string;
|
|
29
|
+
/**
|
|
30
|
+
* WHERE the prompt this bundle carries came from — see
|
|
31
|
+
* {@link systemPromptSource}.
|
|
32
|
+
*/
|
|
33
|
+
systemPrompt: string;
|
|
29
34
|
};
|
|
30
35
|
export declare function executeBuild(opts: {
|
|
31
36
|
cwd: string;
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const mainCommand: CommandDef<ArgsDef>;
|
|
1
|
+
export declare const mainCommand: import("citty").CommandDef<import("citty").ArgsDef>;
|