agency-lang 0.7.0 → 0.7.2
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/lib/agents/agency-agent/agent.agency +169 -28
- package/dist/lib/agents/agency-agent/agent.js +579 -152
- package/dist/lib/agents/agency-agent/lib/attachments.js +2 -2
- package/dist/lib/agents/agency-agent/lib/config.agency +5 -0
- package/dist/lib/agents/agency-agent/lib/config.js +11 -0
- package/dist/lib/agents/agency-agent/lib/defaultPolicy.agency +106 -0
- package/dist/lib/agents/agency-agent/lib/defaultPolicy.js +764 -3
- package/dist/lib/agents/agency-agent/lib/modelFilters.js +1 -1
- package/dist/lib/agents/agency-agent/lib/resolution.js +4 -4
- package/dist/lib/agents/agency-agent/lib/search.js +3 -3
- package/dist/lib/agents/agency-agent/lib/settings.js +1 -1
- package/dist/lib/agents/agency-agent/lib/utils.js +1 -1
- package/dist/lib/agents/agency-agent/shared.agency +22 -0
- package/dist/lib/agents/agency-agent/shared.js +160 -3
- package/dist/lib/agents/agency-agent/subagents/code.js +1 -1
- package/dist/lib/agents/agency-agent/subagents/explorer.js +1 -1
- package/dist/lib/agents/agency-agent/subagents/oracle.js +1 -1
- package/dist/lib/agents/agency-agent/subagents/research.js +1 -1
- package/dist/lib/agents/agency-agent/subagents/review.js +4 -4
- package/dist/lib/agents/agency-agent/tests/agentTurn.js +1 -1
- package/dist/lib/agents/agency-agent/tests/attachments.js +1 -1
- package/dist/lib/agents/agency-agent/tests/attachmentsTurn.js +2 -2
- package/dist/lib/agents/agency-agent/tests/gitPolicy.js +3 -3
- package/dist/lib/agents/agency-agent/tests/oneShotRounds.agency +20 -0
- package/dist/lib/agents/agency-agent/tests/oneShotRounds.js +555 -0
- package/dist/lib/agents/agency-agent/tests/oneShotRounds.test.json +9 -0
- package/dist/lib/agents/review/agent.js +3 -3
- package/dist/lib/cli/doctor.d.ts +8 -0
- package/dist/lib/cli/doctor.js +21 -13
- package/dist/lib/cli/doctor.test.js +34 -0
- package/dist/lib/cli/help.js +1 -5
- package/dist/lib/cli/runBundledAgent.d.ts +17 -1
- package/dist/lib/cli/runBundledAgent.js +73 -2
- package/dist/lib/cli/runBundledAgent.test.d.ts +1 -0
- package/dist/lib/cli/runBundledAgent.test.js +96 -0
- package/dist/lib/config.d.ts +42 -33
- package/dist/lib/config.js +108 -6
- package/dist/lib/config.test.js +121 -2
- package/dist/lib/ir/prettyPrint.js +25 -4
- package/dist/lib/parseCache.js +8 -13
- package/dist/lib/parseCache.test.js +5 -8
- package/dist/lib/parser.js +5 -12
- package/dist/lib/preprocessors/typescriptPreprocessor.d.ts +0 -28
- package/dist/lib/preprocessors/typescriptPreprocessor.js +0 -245
- package/dist/lib/runtime/configOverrides.d.ts +17 -16
- package/dist/lib/runtime/configOverrides.js +27 -16
- package/dist/lib/runtime/configOverrides.test.js +116 -2
- package/dist/lib/runtime/prompt.js +7 -3
- package/dist/lib/runtime/runner.d.ts +2 -2
- package/dist/lib/runtime/runner.js +26 -7
- package/dist/lib/runtime/state/context.js +9 -2
- package/dist/lib/stdlib/llm.d.ts +1 -0
- package/dist/lib/stdlib/version.d.ts +1 -1
- package/dist/lib/stdlib/version.js +1 -1
- package/dist/scripts/agency.d.ts +2 -0
- package/dist/scripts/agency.js +55 -12
- package/dist/scripts/agency.test.js +73 -1
- package/package.json +1 -1
- package/stdlib/agency/eval.js +1 -1
- package/stdlib/agency.js +1 -1
- package/stdlib/agent.agency +9 -9
- package/stdlib/agent.js +5 -5
- package/stdlib/data/tech/hackernews.js +1 -1
- package/stdlib/index.js +15 -15
- package/stdlib/llm.agency +2 -1
- package/stdlib/llm.js +3 -3
- package/stdlib/markdown.js +1 -1
- package/stdlib/memory.js +2 -2
- package/stdlib/object.js +5 -5
- package/stdlib/policy.agency +21 -7
- package/stdlib/policy.js +45 -16
- package/stdlib/skills.js +1 -1
- package/stdlib/strategy.js +3 -3
- package/stdlib/thread.js +3 -3
- package/stdlib/ui/chart.js +2 -2
- package/stdlib/ui/cli.agency +1 -1
- package/stdlib/ui/cli.js +1 -1
- package/stdlib/ui/layout.js +3 -3
- package/stdlib/ui/table.js +2 -2
- package/stdlib/ui.js +3 -3
- package/dist/lib/agents/docs/cli/cli/agent.md +0 -16
- package/dist/lib/agents/docs/cli/cli/compile.md +0 -21
- package/dist/lib/agents/docs/cli/cli/coverage.md +0 -112
- package/dist/lib/agents/docs/cli/cli/debug.md +0 -41
- package/dist/lib/agents/docs/cli/cli/doc.md +0 -98
- package/dist/lib/agents/docs/cli/cli/editor-integration.md +0 -44
- package/dist/lib/agents/docs/cli/cli/eval-judge.md +0 -72
- package/dist/lib/agents/docs/cli/cli/eval.md +0 -194
- package/dist/lib/agents/docs/cli/cli/format.md +0 -23
- package/dist/lib/agents/docs/cli/cli/index.md +0 -7
- package/dist/lib/agents/docs/cli/cli/local.md +0 -92
- package/dist/lib/agents/docs/cli/cli/models.md +0 -63
- package/dist/lib/agents/docs/cli/cli/optimize.md +0 -338
- package/dist/lib/agents/docs/cli/cli/pack.md +0 -56
- package/dist/lib/agents/docs/cli/cli/policy.md +0 -23
- package/dist/lib/agents/docs/cli/cli/preprocess-and-ast.md +0 -21
- package/dist/lib/agents/docs/cli/cli/review.md +0 -16
- package/dist/lib/agents/docs/cli/cli/run.md +0 -79
- package/dist/lib/agents/docs/cli/cli/schedule.md +0 -89
- package/dist/lib/agents/docs/cli/cli/serve.md +0 -52
- package/dist/lib/agents/docs/cli/cli/test.md +0 -44
- package/dist/lib/agents/docs/cli/cli/trace-and-bundle.md +0 -75
- package/dist/lib/agents/docs/cli/cli/typecheck.md +0 -19
- package/dist/lib/agents/docs/guide/guide/advanced-types.md +0 -26
- package/dist/lib/agents/docs/guide/guide/agency-config-file.md +0 -212
- package/dist/lib/agents/docs/guide/guide/agency-packages.md +0 -54
- package/dist/lib/agents/docs/guide/guide/agency-stdlib.md +0 -14
- package/dist/lib/agents/docs/guide/guide/agency-vs-typescript.md +0 -33
- package/dist/lib/agents/docs/guide/guide/attachments.md +0 -56
- package/dist/lib/agents/docs/guide/guide/basic-syntax.md +0 -350
- package/dist/lib/agents/docs/guide/guide/blocks.md +0 -134
- package/dist/lib/agents/docs/guide/guide/build-integration.md +0 -99
- package/dist/lib/agents/docs/guide/guide/builtins.md +0 -84
- package/dist/lib/agents/docs/guide/guide/callbacks.md +0 -169
- package/dist/lib/agents/docs/guide/guide/checkpointing.md +0 -101
- package/dist/lib/agents/docs/guide/guide/cli-args.md +0 -190
- package/dist/lib/agents/docs/guide/guide/common-functions.md +0 -36
- package/dist/lib/agents/docs/guide/guide/compiling-and-running.md +0 -62
- package/dist/lib/agents/docs/guide/guide/concurrency.md +0 -108
- package/dist/lib/agents/docs/guide/guide/cross-thread-context.md +0 -226
- package/dist/lib/agents/docs/guide/guide/custom-providers.md +0 -195
- package/dist/lib/agents/docs/guide/guide/debugging.md +0 -67
- package/dist/lib/agents/docs/guide/guide/developer-tools.md +0 -90
- package/dist/lib/agents/docs/guide/guide/effects-and-raises.md +0 -137
- package/dist/lib/agents/docs/guide/guide/effects.md +0 -100
- package/dist/lib/agents/docs/guide/guide/error-handling.md +0 -161
- package/dist/lib/agents/docs/guide/guide/exercise-1.md +0 -32
- package/dist/lib/agents/docs/guide/guide/functions.md +0 -94
- package/dist/lib/agents/docs/guide/guide/getting-started.md +0 -35
- package/dist/lib/agents/docs/guide/guide/global-var-initialization.md +0 -70
- package/dist/lib/agents/docs/guide/guide/global-vs-static.md +0 -76
- package/dist/lib/agents/docs/guide/guide/guards.md +0 -134
- package/dist/lib/agents/docs/guide/guide/handlers.md +0 -196
- package/dist/lib/agents/docs/guide/guide/image-generation.md +0 -113
- package/dist/lib/agents/docs/guide/guide/imports-and-packages.md +0 -100
- package/dist/lib/agents/docs/guide/guide/interrupts-from-typescript.md +0 -84
- package/dist/lib/agents/docs/guide/guide/interrupts-part-2.md +0 -54
- package/dist/lib/agents/docs/guide/guide/interrupts.md +0 -139
- package/dist/lib/agents/docs/guide/guide/llm-part-2.md +0 -111
- package/dist/lib/agents/docs/guide/guide/llm.md +0 -109
- package/dist/lib/agents/docs/guide/guide/mcp.md +0 -295
- package/dist/lib/agents/docs/guide/guide/memory.md +0 -401
- package/dist/lib/agents/docs/guide/guide/message-threads.md +0 -111
- package/dist/lib/agents/docs/guide/guide/nodes.md +0 -50
- package/dist/lib/agents/docs/guide/guide/observability.md +0 -99
- package/dist/lib/agents/docs/guide/guide/partial-application.md +0 -96
- package/dist/lib/agents/docs/guide/guide/pattern-matching.md +0 -366
- package/dist/lib/agents/docs/guide/guide/policies.md +0 -67
- package/dist/lib/agents/docs/guide/guide/schema-parameter-injection.md +0 -101
- package/dist/lib/agents/docs/guide/guide/schemas.md +0 -99
- package/dist/lib/agents/docs/guide/guide/serving.md +0 -165
- package/dist/lib/agents/docs/guide/guide/state-isolation.md +0 -139
- package/dist/lib/agents/docs/guide/guide/streaming.md +0 -28
- package/dist/lib/agents/docs/guide/guide/tags.md +0 -72
- package/dist/lib/agents/docs/guide/guide/testing.md +0 -187
- package/dist/lib/agents/docs/guide/guide/troubleshooting.md +0 -86
- package/dist/lib/agents/docs/guide/guide/ts-helpers.md +0 -559
- package/dist/lib/agents/docs/guide/guide/ts-interop.md +0 -72
- package/dist/lib/agents/docs/guide/guide/type-validation.md +0 -187
- package/dist/lib/agents/docs/guide/guide/types.md +0 -181
- package/dist/lib/agents/docs/guide/guide/unused.md +0 -178
- package/dist/lib/agents/docs/guide/guide/unused2.md +0 -258
- package/dist/lib/agents/docs/guide/guide/value-parameterized-types.md +0 -186
- package/dist/lib/agents/docs/guide/guide/vscode-extension.md +0 -33
- package/dist/lib/agents/docs/guide/guide/why-agency.md +0 -323
- package/dist/lib/preprocessors/typescriptPreprocessor.config.test.js +0 -327
- package/stdlib/browser.js +0 -421
- package/stdlib/chart.js +0 -983
- package/stdlib/cli.js +0 -1116
- package/stdlib/email.js +0 -1104
- package/stdlib/imessage.js +0 -377
- package/stdlib/keyring.js +0 -803
- package/stdlib/layout.js +0 -3657
- package/stdlib/oauth.js +0 -862
- package/stdlib/schemas.js +0 -223
- package/stdlib/search.js +0 -629
- package/stdlib/sms.js +0 -419
- package/stdlib/table.js +0 -1419
- package/stdlib/threads.js +0 -1041
- package/stdlib/types.js +0 -222
- package/stdlib/validators.js +0 -1606
- /package/dist/lib/{preprocessors/typescriptPreprocessor.config.test.d.ts → cli/doctor.test.d.ts} +0 -0
package/dist/lib/cli/help.js
CHANGED
|
@@ -27,11 +27,7 @@ Flags:
|
|
|
27
27
|
Config File (agency.json):
|
|
28
28
|
{
|
|
29
29
|
"verbose": false, Enable verbose logging by default
|
|
30
|
-
"outDir": "./dist"
|
|
31
|
-
"excludeNodeTypes": ["comment"], Node types to exclude from code generation
|
|
32
|
-
"excludeBuiltinFunctions": ["write"], Builtin functions to exclude
|
|
33
|
-
"allowedFetchDomains": ["api.example.com"], Whitelist for fetch domains
|
|
34
|
-
"disallowedFetchDomains": ["blocked.com"] Blacklist for fetch domains
|
|
30
|
+
"outDir": "./dist" Default output directory for compiled files
|
|
35
31
|
}
|
|
36
32
|
|
|
37
33
|
Examples:
|
|
@@ -1,2 +1,18 @@
|
|
|
1
1
|
import { AgencyConfig } from "../config.js";
|
|
2
|
-
|
|
2
|
+
import { spawn as realSpawn } from "child_process";
|
|
3
|
+
/**
|
|
4
|
+
* Extract the debug flags (`--trace`, `--log-file`) from a bundled agent's
|
|
5
|
+
* forwarded argv, as a config override. This is the ONE place these flags are
|
|
6
|
+
* handled — every bundled agent gets them for free by going through
|
|
7
|
+
* `runBundledAgent`; no agent writes flag code.
|
|
8
|
+
*
|
|
9
|
+
* Tokenization is `node:util.parseArgs` (which `std::args` is itself built on)
|
|
10
|
+
* after the same bare-flag normalization std::args applies, so `--flag=value`,
|
|
11
|
+
* the `--` terminator, last-wins-on-repeat, AND a following-flag-is-not-a-value
|
|
12
|
+
* all match the agent's own parser. An empty/bare `--trace` maps to a per-run
|
|
13
|
+
* trace file in cwd; the flag→config meaning lives in `applyCliFlags`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function agentConfigOverride(args: string[]): Partial<AgencyConfig>;
|
|
16
|
+
export declare function runBundledAgent(config: AgencyConfig, agentName: string, args?: string[], deps?: {
|
|
17
|
+
spawn?: typeof realSpawn;
|
|
18
|
+
}): void;
|
|
@@ -1,9 +1,72 @@
|
|
|
1
|
+
import { applyCliFlags, CONFIG_OVERRIDES_ENV, serializeConfigOverrides, } from "../config.js";
|
|
1
2
|
import { compile, compiledOutputNodeArgs } from "./commands.js";
|
|
2
|
-
import { spawn } from "child_process";
|
|
3
|
+
import { spawn as realSpawn } from "child_process";
|
|
3
4
|
import * as fs from "fs";
|
|
4
5
|
import * as path from "path";
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
5
7
|
const currentDir = path.dirname(new URL(import.meta.url).pathname);
|
|
6
|
-
|
|
8
|
+
// The debug flags every bundled agent understands. Same names the agent
|
|
9
|
+
// declares (for --help) and that `agency run` accepts.
|
|
10
|
+
const AGENT_DEBUG_FLAGS = ["--trace", "--log-file"];
|
|
11
|
+
/**
|
|
12
|
+
* Rewrite a bare debug flag to its empty attached form (`--trace` → `--trace=`)
|
|
13
|
+
* when its next token is absent or starts with `-`. This replicates exactly
|
|
14
|
+
* what `std::args` does before parsing (lib/stdlib/args.ts:499-505), so the
|
|
15
|
+
* agent's own parser and this pre-scan agree: `--trace --print` and `--trace
|
|
16
|
+
* -p` are BOTH bare here, not a trace file named "--print" / "-p". Stops at the
|
|
17
|
+
* `--` terminator (everything after is positional).
|
|
18
|
+
*/
|
|
19
|
+
function normalizeBareDebugFlags(args) {
|
|
20
|
+
const out = [];
|
|
21
|
+
for (let i = 0; i < args.length; i++) {
|
|
22
|
+
const token = args[i];
|
|
23
|
+
if (token === "--") {
|
|
24
|
+
out.push(...args.slice(i));
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
if (AGENT_DEBUG_FLAGS.includes(token)) {
|
|
28
|
+
const next = args[i + 1];
|
|
29
|
+
const bare = next === undefined || next === "--" || next.startsWith("-");
|
|
30
|
+
out.push(bare ? `${token}=` : token);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
out.push(token);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract the debug flags (`--trace`, `--log-file`) from a bundled agent's
|
|
40
|
+
* forwarded argv, as a config override. This is the ONE place these flags are
|
|
41
|
+
* handled — every bundled agent gets them for free by going through
|
|
42
|
+
* `runBundledAgent`; no agent writes flag code.
|
|
43
|
+
*
|
|
44
|
+
* Tokenization is `node:util.parseArgs` (which `std::args` is itself built on)
|
|
45
|
+
* after the same bare-flag normalization std::args applies, so `--flag=value`,
|
|
46
|
+
* the `--` terminator, last-wins-on-repeat, AND a following-flag-is-not-a-value
|
|
47
|
+
* all match the agent's own parser. An empty/bare `--trace` maps to a per-run
|
|
48
|
+
* trace file in cwd; the flag→config meaning lives in `applyCliFlags`.
|
|
49
|
+
*/
|
|
50
|
+
export function agentConfigOverride(args) {
|
|
51
|
+
const { values } = parseArgs({
|
|
52
|
+
args: normalizeBareDebugFlags(args),
|
|
53
|
+
options: { trace: { type: "string" }, "log-file": { type: "string" } },
|
|
54
|
+
strict: false,
|
|
55
|
+
allowPositionals: true,
|
|
56
|
+
});
|
|
57
|
+
const flags = {};
|
|
58
|
+
// After normalization a present --trace is always a string ("" when bare).
|
|
59
|
+
if (typeof values.trace === "string") {
|
|
60
|
+
flags.trace = values.trace;
|
|
61
|
+
}
|
|
62
|
+
// --log-file requires a value; a bare (now "") --log-file is ignored.
|
|
63
|
+
if (typeof values["log-file"] === "string" && values["log-file"] !== "") {
|
|
64
|
+
flags.logFile = values["log-file"];
|
|
65
|
+
}
|
|
66
|
+
return applyCliFlags({}, flags);
|
|
67
|
+
}
|
|
68
|
+
export function runBundledAgent(config, agentName, args = [], deps = {}) {
|
|
69
|
+
const spawn = deps.spawn ?? realSpawn;
|
|
7
70
|
const agentDir = path.resolve(currentDir, `../agents/${agentName}`);
|
|
8
71
|
const agencyFile = path.join(agentDir, "agent.agency");
|
|
9
72
|
const precompiledFile = path.join(agentDir, "agent.js");
|
|
@@ -21,9 +84,17 @@ export function runBundledAgent(config, agentName, args = []) {
|
|
|
21
84
|
console.error(`Failed to compile agent ${agentName}.`);
|
|
22
85
|
process.exit(1);
|
|
23
86
|
}
|
|
87
|
+
const overrides = agentConfigOverride(args);
|
|
88
|
+
const env = Object.keys(overrides).length > 0
|
|
89
|
+
? {
|
|
90
|
+
...process.env,
|
|
91
|
+
[CONFIG_OVERRIDES_ENV]: serializeConfigOverrides(overrides),
|
|
92
|
+
}
|
|
93
|
+
: process.env;
|
|
24
94
|
const nodeProcess = spawn(process.execPath, [...compiledOutputNodeArgs(), runFile, ...args], {
|
|
25
95
|
stdio: "inherit",
|
|
26
96
|
shell: false,
|
|
97
|
+
env,
|
|
27
98
|
});
|
|
28
99
|
nodeProcess.on("error", (error) => {
|
|
29
100
|
console.error(`Failed to run ${agentName}:`, error);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
// Force the precompiled-file branch so runBundledAgent never invokes the
|
|
3
|
+
// compiler; spawn is injected, so nothing actually launches.
|
|
4
|
+
vi.mock("fs", async (importOriginal) => {
|
|
5
|
+
const actual = await importOriginal();
|
|
6
|
+
return { ...actual, existsSync: () => true };
|
|
7
|
+
});
|
|
8
|
+
import { CONFIG_OVERRIDES_ENV } from "../config.js";
|
|
9
|
+
import { agentConfigOverride, runBundledAgent } from "./runBundledAgent.js";
|
|
10
|
+
describe("agentConfigOverride", () => {
|
|
11
|
+
it("--trace <file> → traceFile", () => {
|
|
12
|
+
expect(agentConfigOverride(["--trace", "out.agencytrace"])).toEqual({
|
|
13
|
+
trace: true,
|
|
14
|
+
traceFile: "out.agencytrace",
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
it("--trace=<file> attached form", () => {
|
|
18
|
+
expect(agentConfigOverride(["--trace=x.trace"])).toEqual({
|
|
19
|
+
trace: true,
|
|
20
|
+
traceFile: "x.trace",
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
it("bare --trace → per-run dir (traceDir)", () => {
|
|
24
|
+
expect(agentConfigOverride(["--trace"])).toEqual({ trace: true, traceDir: "." });
|
|
25
|
+
});
|
|
26
|
+
it("empty --trace= behaves identically to bare --trace (the divergence bug)", () => {
|
|
27
|
+
expect(agentConfigOverride(["--trace="])).toEqual({ trace: true, traceDir: "." });
|
|
28
|
+
});
|
|
29
|
+
it("a following flag is NOT consumed as the --trace value (matches std::args)", () => {
|
|
30
|
+
// Both are bare on the agent side, so they must be bare here too — not a
|
|
31
|
+
// trace file named "--print" / "-p".
|
|
32
|
+
expect(agentConfigOverride(["--trace", "--print", "hi"])).toEqual({
|
|
33
|
+
trace: true,
|
|
34
|
+
traceDir: ".",
|
|
35
|
+
});
|
|
36
|
+
expect(agentConfigOverride(["--trace", "-p"])).toEqual({ trace: true, traceDir: "." });
|
|
37
|
+
});
|
|
38
|
+
it("bare --log-file (or one followed by a flag) is ignored, not a file named --print", () => {
|
|
39
|
+
expect(agentConfigOverride(["--log-file"])).toEqual({});
|
|
40
|
+
expect(agentConfigOverride(["--log-file", "--print"])).toEqual({});
|
|
41
|
+
});
|
|
42
|
+
it("--log-file <path> → log.logFile + observability, space and attached forms", () => {
|
|
43
|
+
const expected = { log: { logFile: "l.jsonl" }, observability: true };
|
|
44
|
+
expect(agentConfigOverride(["--log-file", "l.jsonl"])).toEqual(expected);
|
|
45
|
+
expect(agentConfigOverride(["--log-file=l.jsonl"])).toEqual(expected);
|
|
46
|
+
});
|
|
47
|
+
it("stops at the -- terminator", () => {
|
|
48
|
+
expect(agentConfigOverride(["--", "--trace", "x"])).toEqual({});
|
|
49
|
+
});
|
|
50
|
+
it("last --trace wins on repeats", () => {
|
|
51
|
+
expect(agentConfigOverride(["--trace", "a", "--trace", "b"])).toEqual({
|
|
52
|
+
trace: true,
|
|
53
|
+
traceFile: "b",
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
it("combines trace + log and ignores the agent's own flags/positionals", () => {
|
|
57
|
+
expect(agentConfigOverride(["--model", "gpt", "hi", "--trace", "t", "--log-file", "l"])).toEqual({
|
|
58
|
+
trace: true,
|
|
59
|
+
traceFile: "t",
|
|
60
|
+
log: { logFile: "l" },
|
|
61
|
+
observability: true,
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
it("empty when neither flag is present", () => {
|
|
65
|
+
expect(agentConfigOverride(["--print", "do it"])).toEqual({});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe("runBundledAgent passes config overrides to the child via env", () => {
|
|
69
|
+
afterEach(() => {
|
|
70
|
+
vi.restoreAllMocks();
|
|
71
|
+
});
|
|
72
|
+
it("serializes the override into AGENCY_CONFIG_OVERRIDES", () => {
|
|
73
|
+
const onMock = vi.fn();
|
|
74
|
+
const spawnMock = vi.fn((..._args) => ({ on: onMock }));
|
|
75
|
+
runBundledAgent({}, "agency-agent", ["--trace", "t.trace", "--log-file", "l.jsonl"], {
|
|
76
|
+
spawn: spawnMock,
|
|
77
|
+
});
|
|
78
|
+
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
79
|
+
const opts = spawnMock.mock.calls[0][2];
|
|
80
|
+
const overrides = JSON.parse(opts.env[CONFIG_OVERRIDES_ENV]);
|
|
81
|
+
expect(overrides).toEqual({
|
|
82
|
+
trace: true,
|
|
83
|
+
traceFile: "t.trace",
|
|
84
|
+
log: { logFile: "l.jsonl" },
|
|
85
|
+
observability: true,
|
|
86
|
+
});
|
|
87
|
+
// Parent env is preserved (spread), so PATH survives.
|
|
88
|
+
expect(opts.env.PATH).toBe(process.env.PATH);
|
|
89
|
+
});
|
|
90
|
+
it("does not set the env var when no debug flags are present", () => {
|
|
91
|
+
const spawnMock = vi.fn((..._args) => ({ on: vi.fn() }));
|
|
92
|
+
runBundledAgent({}, "agency-agent", ["--print", "hi"], { spawn: spawnMock });
|
|
93
|
+
const opts = spawnMock.mock.calls[0][2];
|
|
94
|
+
expect(opts.env[CONFIG_OVERRIDES_ENV]).toBeUndefined();
|
|
95
|
+
});
|
|
96
|
+
});
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -24,34 +24,6 @@ export interface AgencyConfig {
|
|
|
24
24
|
verbose?: boolean;
|
|
25
25
|
logLevel?: LogLevel;
|
|
26
26
|
outDir?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Array of AST node types to exclude from code generation
|
|
29
|
-
* Example: ["comment", "typeAlias"]
|
|
30
|
-
*/
|
|
31
|
-
excludeNodeTypes?: string[];
|
|
32
|
-
/**
|
|
33
|
-
* Array of built-in function names to exclude from code generation
|
|
34
|
-
* Example: ["fetch", "write"]
|
|
35
|
-
*/
|
|
36
|
-
excludeBuiltinFunctions?: string[];
|
|
37
|
-
/**
|
|
38
|
-
* Array of domains allowed for fetch operations
|
|
39
|
-
* If specified, only these domains can be fetched
|
|
40
|
-
* Example: ["api.example.com", "data.mysite.com"]
|
|
41
|
-
*/
|
|
42
|
-
allowedFetchDomains?: string[];
|
|
43
|
-
/**
|
|
44
|
-
* Array of domains disallowed for fetch operations
|
|
45
|
-
* These domains will throw an error if fetch is attempted
|
|
46
|
-
* If both allowed and disallowed are set, takes the intersection
|
|
47
|
-
* (only allowed domains, minus disallowed ones)
|
|
48
|
-
* Example: ["malicious.com", "blocked.site.com"]
|
|
49
|
-
*/
|
|
50
|
-
disallowedFetchDomains?: string[];
|
|
51
|
-
/**
|
|
52
|
-
* Optionally specify a custom host for tarsec trace collection
|
|
53
|
-
*/
|
|
54
|
-
tarsecTraceHost?: string;
|
|
55
27
|
/**
|
|
56
28
|
* Number of times the LLM can go back and forth between calling tools
|
|
57
29
|
* and responding to their outputs before halting execution to prevent infinite loops.
|
|
@@ -339,11 +311,6 @@ export declare const AgencyConfigSchema: z.ZodObject<{
|
|
|
339
311
|
error: "error";
|
|
340
312
|
}>>;
|
|
341
313
|
outDir: z.ZodOptional<z.ZodString>;
|
|
342
|
-
excludeNodeTypes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
343
|
-
excludeBuiltinFunctions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
344
|
-
allowedFetchDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
345
|
-
disallowedFetchDomains: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
346
|
-
tarsecTraceHost: z.ZodOptional<z.ZodString>;
|
|
347
314
|
maxToolCallRounds: z.ZodOptional<z.ZodNumber>;
|
|
348
315
|
observability: z.ZodOptional<z.ZodBoolean>;
|
|
349
316
|
log: z.ZodOptional<z.ZodObject<{
|
|
@@ -507,3 +474,45 @@ export declare function loadConfigSafe(configPath: string): {
|
|
|
507
474
|
* Returns the directory containing agency.json, or null if not found.
|
|
508
475
|
*/
|
|
509
476
|
export declare function findProjectRoot(startPath: string): string | null;
|
|
477
|
+
/** Per-invocation flags accepted by `agency run`/`compile` and forwarded to the
|
|
478
|
+
* bundled agents. Mapped onto AgencyConfig by applyCliFlags. `trace` is
|
|
479
|
+
* `string` for `--trace <file>`, `true` for a bare `--trace`. */
|
|
480
|
+
export type CliFlags = {
|
|
481
|
+
trace?: string | true;
|
|
482
|
+
logFile?: string;
|
|
483
|
+
observability?: boolean;
|
|
484
|
+
strict?: boolean;
|
|
485
|
+
maxToolCallRounds?: number;
|
|
486
|
+
maxToolResultChars?: number;
|
|
487
|
+
};
|
|
488
|
+
/**
|
|
489
|
+
* Fold per-invocation CLI flags onto a config COPY (never mutates the input).
|
|
490
|
+
* The single definition of what each debug flag means:
|
|
491
|
+
* --trace <file> → trace + traceFile=<file>
|
|
492
|
+
* --trace (bare) → trace + traceFile=<input>.trace when an input path is
|
|
493
|
+
* known (agency run), else traceDir="." (a bundled agent
|
|
494
|
+
* with no input file → a per-run file in cwd)
|
|
495
|
+
* --log-file <p> → log.logFile=<p> and observability=true
|
|
496
|
+
* --observability → observability=true
|
|
497
|
+
* --strict → typechecker.strict + strictTypes (the compile-path gate
|
|
498
|
+
* never runs the checker on strictTypes alone)
|
|
499
|
+
* --max-tool-call-rounds <n> → maxToolCallRounds=<n> (baked into runPrompt at
|
|
500
|
+
* compile time; overrides agency.json for this run)
|
|
501
|
+
* --max-tool-result-chars <n> → client.maxToolResultChars=<n> (0 disables the
|
|
502
|
+
* cap; overrides agency.json for this run)
|
|
503
|
+
*/
|
|
504
|
+
export declare function applyCliFlags(config: AgencyConfig, flags: CliFlags, input?: string): AgencyConfig;
|
|
505
|
+
/** The one env var carrying a JSON Partial<AgencyConfig> into an already-compiled
|
|
506
|
+
* process (see the source-of-truth note above, source 3). */
|
|
507
|
+
export declare const CONFIG_OVERRIDES_ENV = "AGENCY_CONFIG_OVERRIDES";
|
|
508
|
+
/** Serialize config overrides for a child process's AGENCY_CONFIG_OVERRIDES. */
|
|
509
|
+
export declare function serializeConfigOverrides(overrides: Partial<AgencyConfig>): string;
|
|
510
|
+
/** Read + validate AGENCY_CONFIG_OVERRIDES. Returns {} when the var is absent,
|
|
511
|
+
* unparseable, or fails schema validation, so a malformed value can never
|
|
512
|
+
* brick startup. */
|
|
513
|
+
export declare function readConfigOverrides(env?: NodeJS.ProcessEnv): Partial<AgencyConfig>;
|
|
514
|
+
/** Return a deep copy of `config` with secret-bearing fields masked, for
|
|
515
|
+
* human-facing output (`agency config show`). Masks every `apiKey` — the
|
|
516
|
+
* top-level `log.apiKey` string and each key under `client.apiKey` /
|
|
517
|
+
* `client.statelog.apiKey` — to `•••<last4>`. */
|
|
518
|
+
export declare function redactConfigSecrets(config: AgencyConfig): AgencyConfig;
|
package/dist/lib/config.js
CHANGED
|
@@ -29,12 +29,10 @@ export const AgencyConfigSchema = z
|
|
|
29
29
|
verbose: z.boolean(),
|
|
30
30
|
logLevel: z.enum(["debug", "info", "warn", "error"]),
|
|
31
31
|
outDir: z.string(),
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
tarsecTraceHost: z.string(),
|
|
37
|
-
maxToolCallRounds: z.number(),
|
|
32
|
+
// Positive integer: the generated code does `maxToolCallRounds || 10`, so a
|
|
33
|
+
// 0 would silently mean 10 and a float/negative is meaningless — reject at
|
|
34
|
+
// load rather than surprise. Mirrors maxCallDepth.
|
|
35
|
+
maxToolCallRounds: z.number().int().positive(),
|
|
38
36
|
observability: z.boolean(),
|
|
39
37
|
log: z
|
|
40
38
|
.object({
|
|
@@ -228,3 +226,107 @@ export function findProjectRoot(startPath) {
|
|
|
228
226
|
current = parent;
|
|
229
227
|
}
|
|
230
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Fold per-invocation CLI flags onto a config COPY (never mutates the input).
|
|
231
|
+
* The single definition of what each debug flag means:
|
|
232
|
+
* --trace <file> → trace + traceFile=<file>
|
|
233
|
+
* --trace (bare) → trace + traceFile=<input>.trace when an input path is
|
|
234
|
+
* known (agency run), else traceDir="." (a bundled agent
|
|
235
|
+
* with no input file → a per-run file in cwd)
|
|
236
|
+
* --log-file <p> → log.logFile=<p> and observability=true
|
|
237
|
+
* --observability → observability=true
|
|
238
|
+
* --strict → typechecker.strict + strictTypes (the compile-path gate
|
|
239
|
+
* never runs the checker on strictTypes alone)
|
|
240
|
+
* --max-tool-call-rounds <n> → maxToolCallRounds=<n> (baked into runPrompt at
|
|
241
|
+
* compile time; overrides agency.json for this run)
|
|
242
|
+
* --max-tool-result-chars <n> → client.maxToolResultChars=<n> (0 disables the
|
|
243
|
+
* cap; overrides agency.json for this run)
|
|
244
|
+
*/
|
|
245
|
+
export function applyCliFlags(config, flags, input) {
|
|
246
|
+
const next = { ...config };
|
|
247
|
+
if (flags.trace !== undefined) {
|
|
248
|
+
next.trace = true;
|
|
249
|
+
const explicitFile = typeof flags.trace === "string" && flags.trace !== "";
|
|
250
|
+
if (explicitFile) {
|
|
251
|
+
next.traceFile = flags.trace;
|
|
252
|
+
}
|
|
253
|
+
else if (input) {
|
|
254
|
+
next.traceFile = input.replace(/\.agency$/, ".trace");
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
next.traceDir = ".";
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (flags.logFile) {
|
|
261
|
+
next.log = { ...next.log, logFile: flags.logFile };
|
|
262
|
+
next.observability = true;
|
|
263
|
+
}
|
|
264
|
+
if (flags.observability) {
|
|
265
|
+
next.observability = true;
|
|
266
|
+
}
|
|
267
|
+
if (flags.strict) {
|
|
268
|
+
next.typechecker = { ...next.typechecker, strict: true, strictTypes: true };
|
|
269
|
+
}
|
|
270
|
+
if (flags.maxToolCallRounds !== undefined) {
|
|
271
|
+
next.maxToolCallRounds = flags.maxToolCallRounds;
|
|
272
|
+
}
|
|
273
|
+
if (flags.maxToolResultChars !== undefined) {
|
|
274
|
+
next.client = { ...next.client, maxToolResultChars: flags.maxToolResultChars };
|
|
275
|
+
}
|
|
276
|
+
return next;
|
|
277
|
+
}
|
|
278
|
+
/** The one env var carrying a JSON Partial<AgencyConfig> into an already-compiled
|
|
279
|
+
* process (see the source-of-truth note above, source 3). */
|
|
280
|
+
export const CONFIG_OVERRIDES_ENV = "AGENCY_CONFIG_OVERRIDES";
|
|
281
|
+
/** Serialize config overrides for a child process's AGENCY_CONFIG_OVERRIDES. */
|
|
282
|
+
export function serializeConfigOverrides(overrides) {
|
|
283
|
+
return JSON.stringify(overrides);
|
|
284
|
+
}
|
|
285
|
+
/** Read + validate AGENCY_CONFIG_OVERRIDES. Returns {} when the var is absent,
|
|
286
|
+
* unparseable, or fails schema validation, so a malformed value can never
|
|
287
|
+
* brick startup. */
|
|
288
|
+
export function readConfigOverrides(env = process.env) {
|
|
289
|
+
const raw = env[CONFIG_OVERRIDES_ENV];
|
|
290
|
+
if (!raw)
|
|
291
|
+
return {};
|
|
292
|
+
try {
|
|
293
|
+
const result = AgencyConfigSchema.safeParse(JSON.parse(raw));
|
|
294
|
+
if (!result.success) {
|
|
295
|
+
// Don't brick startup, but never fail silently — a typo'd override that
|
|
296
|
+
// makes --trace/--log-file quietly do nothing is the worst failure mode.
|
|
297
|
+
console.error(`Ignoring invalid ${CONFIG_OVERRIDES_ENV}: ${result.error.issues
|
|
298
|
+
.map((i) => i.path.join("."))
|
|
299
|
+
.join(", ")}`);
|
|
300
|
+
return {};
|
|
301
|
+
}
|
|
302
|
+
return result.data;
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
console.error(`Ignoring unparseable ${CONFIG_OVERRIDES_ENV}: ${err instanceof Error ? err.message : String(err)}`);
|
|
306
|
+
return {};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/** Return a deep copy of `config` with secret-bearing fields masked, for
|
|
310
|
+
* human-facing output (`agency config show`). Masks every `apiKey` — the
|
|
311
|
+
* top-level `log.apiKey` string and each key under `client.apiKey` /
|
|
312
|
+
* `client.statelog.apiKey` — to `•••<last4>`. */
|
|
313
|
+
export function redactConfigSecrets(config) {
|
|
314
|
+
const mask = (value) => value.length <= 4 ? "•••" : `•••${value.slice(-4)}`;
|
|
315
|
+
const clone = JSON.parse(JSON.stringify(config));
|
|
316
|
+
const redactKeyMap = (obj) => {
|
|
317
|
+
if (!obj)
|
|
318
|
+
return;
|
|
319
|
+
for (const key of Object.keys(obj)) {
|
|
320
|
+
if (typeof obj[key] === "string")
|
|
321
|
+
obj[key] = mask(obj[key]);
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
if (clone.log && typeof clone.log.apiKey === "string") {
|
|
325
|
+
clone.log.apiKey = mask(clone.log.apiKey);
|
|
326
|
+
}
|
|
327
|
+
redactKeyMap(clone.client?.apiKey);
|
|
328
|
+
if (clone.client?.statelog && typeof clone.client.statelog.apiKey === "string") {
|
|
329
|
+
clone.client.statelog.apiKey = mask(clone.client.statelog.apiKey);
|
|
330
|
+
}
|
|
331
|
+
return clone;
|
|
332
|
+
}
|
package/dist/lib/config.test.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as os from "os";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { AgencyConfigSchema, applyCliFlags, CONFIG_OVERRIDES_ENV, loadConfigSafe, readConfigOverrides, redactConfigSecrets, serializeConfigOverrides, } from "./config.js";
|
|
3
6
|
describe("AgencyConfigSchema", () => {
|
|
4
7
|
it("should accept an empty config", () => {
|
|
5
8
|
const result = AgencyConfigSchema.safeParse({});
|
|
@@ -79,6 +82,16 @@ describe("AgencyConfig maxCallDepth key", () => {
|
|
|
79
82
|
expect(AgencyConfigSchema.safeParse({ maxCallDepth: 12.5 }).success).toBe(false);
|
|
80
83
|
});
|
|
81
84
|
});
|
|
85
|
+
describe("AgencyConfig maxToolCallRounds key", () => {
|
|
86
|
+
it("accepts a positive integer", () => {
|
|
87
|
+
expect(AgencyConfigSchema.safeParse({ maxToolCallRounds: 20 }).success).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
it("rejects 0, negative, and non-integer (0 would silently mean 10 via `|| 10`)", () => {
|
|
90
|
+
expect(AgencyConfigSchema.safeParse({ maxToolCallRounds: 0 }).success).toBe(false);
|
|
91
|
+
expect(AgencyConfigSchema.safeParse({ maxToolCallRounds: -1 }).success).toBe(false);
|
|
92
|
+
expect(AgencyConfigSchema.safeParse({ maxToolCallRounds: 1.5 }).success).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
82
95
|
describe("AgencyConfig typechecker key", () => {
|
|
83
96
|
it("accepts the new typechecker object", () => {
|
|
84
97
|
const result = AgencyConfigSchema.safeParse({
|
|
@@ -102,3 +115,109 @@ describe("AgencyConfig typechecker key", () => {
|
|
|
102
115
|
expect(result.success).toBe(true);
|
|
103
116
|
});
|
|
104
117
|
});
|
|
118
|
+
describe("loadConfigSafe — removed options are ignored, not rejected", () => {
|
|
119
|
+
it("loads an agency.json with the removed keys, and a stale wrong-typed key, without error", () => {
|
|
120
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "cfg-removed-"));
|
|
121
|
+
const cfgPath = path.join(dir, "agency.json");
|
|
122
|
+
fs.writeFileSync(cfgPath, JSON.stringify({
|
|
123
|
+
excludeNodeTypes: "oops-was-string-array", // stale + wrong type: Zod error before removal, ignored after
|
|
124
|
+
excludeBuiltinFunctions: ["write"],
|
|
125
|
+
allowedFetchDomains: ["api.example.com"],
|
|
126
|
+
disallowedFetchDomains: ["blocked.com"],
|
|
127
|
+
outDir: "./dist",
|
|
128
|
+
}));
|
|
129
|
+
const { config, error } = loadConfigSafe(cfgPath);
|
|
130
|
+
expect(error).toBeUndefined();
|
|
131
|
+
expect(config.outDir).toBe("./dist");
|
|
132
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe("applyCliFlags", () => {
|
|
136
|
+
it("--trace <file> sets trace + traceFile", () => {
|
|
137
|
+
const out = applyCliFlags({}, { trace: "out.trace" });
|
|
138
|
+
expect(out.trace).toBe(true);
|
|
139
|
+
expect(out.traceFile).toBe("out.trace");
|
|
140
|
+
expect(out.traceDir).toBeUndefined();
|
|
141
|
+
});
|
|
142
|
+
it("bare --trace with an input derives <input>.trace (agency run)", () => {
|
|
143
|
+
const out = applyCliFlags({}, { trace: true }, "prog.agency");
|
|
144
|
+
expect(out.traceFile).toBe("prog.trace");
|
|
145
|
+
});
|
|
146
|
+
it("bare or empty --trace with no input uses traceDir='.' (bundled agent)", () => {
|
|
147
|
+
expect(applyCliFlags({}, { trace: true }).traceDir).toBe(".");
|
|
148
|
+
// Empty attached (--trace=) must behave like bare, not set traceFile="".
|
|
149
|
+
expect(applyCliFlags({}, { trace: "" }).traceDir).toBe(".");
|
|
150
|
+
expect(applyCliFlags({}, { trace: "" }).traceFile).toBeUndefined();
|
|
151
|
+
});
|
|
152
|
+
it("--log-file sets log.logFile and enables observability, preserving log.host", () => {
|
|
153
|
+
const out = applyCliFlags({ log: { host: "https://h" } }, { logFile: "x" });
|
|
154
|
+
expect(out.log).toEqual({ host: "https://h", logFile: "x" });
|
|
155
|
+
expect(out.observability).toBe(true);
|
|
156
|
+
});
|
|
157
|
+
it("--strict sets strict AND strictTypes", () => {
|
|
158
|
+
expect(applyCliFlags({}, { strict: true }).typechecker).toEqual({
|
|
159
|
+
strict: true,
|
|
160
|
+
strictTypes: true,
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
it("--max-tool-call-rounds sets the top-level maxToolCallRounds", () => {
|
|
164
|
+
expect(applyCliFlags({}, { maxToolCallRounds: 20 }).maxToolCallRounds).toBe(20);
|
|
165
|
+
});
|
|
166
|
+
it("--max-tool-result-chars sets client.maxToolResultChars, preserving other client fields", () => {
|
|
167
|
+
const out = applyCliFlags({ client: { defaultModel: "gpt" } }, { maxToolResultChars: 50000 });
|
|
168
|
+
expect(out.client).toEqual({ defaultModel: "gpt", maxToolResultChars: 50000 });
|
|
169
|
+
});
|
|
170
|
+
it("keeps maxToolResultChars=0 (0 disables the cap, not falsy-skipped)", () => {
|
|
171
|
+
expect(applyCliFlags({}, { maxToolResultChars: 0 }).client?.maxToolResultChars).toBe(0);
|
|
172
|
+
});
|
|
173
|
+
it("does not mutate the input config", () => {
|
|
174
|
+
const input = {};
|
|
175
|
+
applyCliFlags(input, { strict: true, logFile: "x", trace: "t", maxToolCallRounds: 5 });
|
|
176
|
+
expect(input).toEqual({});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe("config overrides env round-trip", () => {
|
|
180
|
+
it("serialize → read yields the same Partial<AgencyConfig>", () => {
|
|
181
|
+
const overrides = { trace: true, traceDir: ".", observability: true, log: { logFile: "l.jsonl" } };
|
|
182
|
+
const env = { [CONFIG_OVERRIDES_ENV]: serializeConfigOverrides(overrides) };
|
|
183
|
+
expect(readConfigOverrides(env)).toEqual(overrides);
|
|
184
|
+
});
|
|
185
|
+
it("returns {} for an absent value without warning", () => {
|
|
186
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
187
|
+
expect(readConfigOverrides({})).toEqual({});
|
|
188
|
+
expect(spy).not.toHaveBeenCalled();
|
|
189
|
+
spy.mockRestore();
|
|
190
|
+
});
|
|
191
|
+
it("returns {} and warns (never throws) for unparseable JSON", () => {
|
|
192
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
193
|
+
expect(readConfigOverrides({ [CONFIG_OVERRIDES_ENV]: "not json" })).toEqual({});
|
|
194
|
+
expect(spy).toHaveBeenCalledWith(expect.stringContaining("unparseable"));
|
|
195
|
+
spy.mockRestore();
|
|
196
|
+
});
|
|
197
|
+
it("returns {} and warns for a schema-violating field", () => {
|
|
198
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
199
|
+
expect(readConfigOverrides({ [CONFIG_OVERRIDES_ENV]: '{"maxCallDepth":-1}' })).toEqual({});
|
|
200
|
+
expect(spy).toHaveBeenCalledWith(expect.stringContaining("invalid"));
|
|
201
|
+
spy.mockRestore();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
describe("redactConfigSecrets", () => {
|
|
205
|
+
it("masks log.apiKey and client.apiKey.* while keeping other fields", () => {
|
|
206
|
+
const redacted = redactConfigSecrets({
|
|
207
|
+
outDir: "./dist",
|
|
208
|
+
log: { host: "https://h", apiKey: "sk-secret-1234" },
|
|
209
|
+
client: { defaultModel: "gpt", apiKey: { openAi: "sk-openai-abcd", anthropic: "x" } },
|
|
210
|
+
});
|
|
211
|
+
expect(redacted.outDir).toBe("./dist");
|
|
212
|
+
expect(redacted.log?.host).toBe("https://h");
|
|
213
|
+
expect(redacted.log?.apiKey).toBe("•••1234");
|
|
214
|
+
expect(redacted.client?.apiKey?.openAi).toBe("•••abcd");
|
|
215
|
+
expect(redacted.client?.apiKey?.anthropic).toBe("•••"); // <=4 chars fully masked
|
|
216
|
+
expect(redacted.client?.defaultModel).toBe("gpt");
|
|
217
|
+
});
|
|
218
|
+
it("does not mutate the input", () => {
|
|
219
|
+
const input = { log: { apiKey: "sk-secret-1234" } };
|
|
220
|
+
redactConfigSecrets(input);
|
|
221
|
+
expect(input.log.apiKey).toBe("sk-secret-1234");
|
|
222
|
+
});
|
|
223
|
+
});
|
|
@@ -326,8 +326,16 @@ export function printTs(node, indent = 0) {
|
|
|
326
326
|
// Always pass an opts object so Runner.thread has a single,
|
|
327
327
|
// uniform signature: legacy callers see `{}` and the
|
|
328
328
|
// hook-firing path can treat opts as required.
|
|
329
|
-
|
|
330
|
-
|
|
329
|
+
//
|
|
330
|
+
// The opts object is emitted as a thunk (`async () => (<opts>)`) so its
|
|
331
|
+
// value expressions (`label`, `session`, ...) are evaluated inside
|
|
332
|
+
// `runner.thread`, AFTER its halt/skip guards. An early `return` earlier
|
|
333
|
+
// in the function halts the runner and skips the steps that assign the
|
|
334
|
+
// locals those expressions reference; evaluating them eagerly here would
|
|
335
|
+
// then dereference an unset local and throw. Deferring lets the early
|
|
336
|
+
// return win — same fix as `runnerLoop`.
|
|
337
|
+
const optsObj = optsParts.length === 0 ? "{}" : `{ ${optsParts.join(", ")} }`;
|
|
338
|
+
return `await runner.thread(${node.id}, "${node.method}", async () => (${optsObj}), async (runner) => {\n${body}\n${ind(indent)}});`;
|
|
331
339
|
}
|
|
332
340
|
case "runnerHandle": {
|
|
333
341
|
const handler = printTs(node.handler, indent);
|
|
@@ -392,10 +400,23 @@ export function printTs(node, indent = 0) {
|
|
|
392
400
|
return `runner.exitMatch(${node.matchId}, ${value});\nreturn;`;
|
|
393
401
|
}
|
|
394
402
|
case "runnerLoop": {
|
|
395
|
-
|
|
403
|
+
// The iterable is emitted as a thunk (`async () => (<expr>)`) so its
|
|
404
|
+
// expression is evaluated inside `runner.loop`, AFTER the halt/skip
|
|
405
|
+
// guards. An early `return` earlier in the function halts the runner and
|
|
406
|
+
// skips the steps that assign the iterable's backing locals; evaluating
|
|
407
|
+
// the iterable eagerly here would then dereference an unset local and
|
|
408
|
+
// throw. Deferring it lets the early return win. Mirrors how
|
|
409
|
+
// `runnerWhileLoop` wraps its condition.
|
|
410
|
+
//
|
|
411
|
+
// The expression MUST be parenthesized: a bare `async () => ${items}`
|
|
412
|
+
// breaks when the iterable prints as an object literal (record
|
|
413
|
+
// iteration over a literal, e.g. `for (k in { a: 1 })`) — `async () => {
|
|
414
|
+
// a: 1 }` parses as a function BODY block, not an object. The parens
|
|
415
|
+
// force expression context. (Same reason `runnerThread` wraps its opts.)
|
|
416
|
+
const items = printTs(node.items, indent + 1);
|
|
396
417
|
const idxVar = node.indexVar ?? "_";
|
|
397
418
|
const body = node.body.map((n) => printTs(n, indent + 1)).join("\n");
|
|
398
|
-
return `await runner.loop(${node.id}, ${items}, async (${node.itemVar}, ${idxVar}, runner) => {\n${body}\n${ind(indent)}});`;
|
|
419
|
+
return `await runner.loop(${node.id}, async () => (${items}), async (${node.itemVar}, ${idxVar}, runner) => {\n${body}\n${ind(indent)}});`;
|
|
399
420
|
}
|
|
400
421
|
case "runnerWhileLoop": {
|
|
401
422
|
const cond = printTs(node.condition, indent + 1);
|
package/dist/lib/parseCache.js
CHANGED
|
@@ -27,23 +27,18 @@ export function parseAgencyFileCached(absPath, config = {}, applyTemplate = !isN
|
|
|
27
27
|
rest: "",
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
const bypass = !!config.tarsecTraceHost;
|
|
31
30
|
const key = `${applyTemplate ? "t" : "r"}:${absPath}`;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
41
|
-
}
|
|
31
|
+
const entry = cache[key];
|
|
32
|
+
if (entry && entry.mtimeMs === stat.mtimeMs && entry.size === stat.size) {
|
|
33
|
+
stats.hits++;
|
|
34
|
+
return {
|
|
35
|
+
success: true,
|
|
36
|
+
result: structuredClone(entry.program),
|
|
37
|
+
rest: "",
|
|
38
|
+
};
|
|
42
39
|
}
|
|
43
40
|
const contents = fs.readFileSync(absPath, "utf-8");
|
|
44
41
|
const result = parseAgency(contents, config, applyTemplate);
|
|
45
|
-
if (bypass)
|
|
46
|
-
return result;
|
|
47
42
|
stats.misses++;
|
|
48
43
|
if (result.success) {
|
|
49
44
|
cache[key] = {
|