@cursor/july 0.1.23 → 0.1.25
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/package.json +8 -1
- package/src/artifacts.ts +78 -0
- package/src/channels/github/github.test.ts +6 -0
- package/src/evals/assertions.test.ts +37 -0
- package/src/evals/assertions.ts +37 -1
- package/src/evals.ts +1 -0
- package/src/index.ts +1 -0
- package/src/internal/artifacts-store.test.ts +283 -0
- package/src/internal/artifacts-store.ts +290 -0
- package/src/internal/builtin-tools/artifacts.test.ts +247 -0
- package/src/internal/builtin-tools/artifacts.ts +74 -0
- package/src/internal/builtin-tools/index.ts +21 -0
- package/src/internal/cli-slack.test.ts +8 -1
- package/src/internal/cli-slack.ts +13 -4
- package/src/internal/discovery.artifact-tool.test.ts +136 -0
- package/src/internal/discovery.artifacts.test.ts +119 -0
- package/src/internal/discovery.ts +102 -1
- package/src/internal/distribution.ts +1 -0
- package/src/internal/handleAgentServeTrigger.test.ts +6 -0
- package/src/internal/server.artifacts.test.ts +303 -0
- package/src/internal/server.ts +43 -0
- package/src/internal/session-engine.artifacts.test.ts +243 -0
- package/src/internal/session-engine.ts +125 -0
- package/src/internal/storage-coordinator.ts +62 -0
- package/src/storage.ts +6 -0
- package/src/types.ts +134 -1
|
@@ -491,6 +491,16 @@ interface ConnectWaitTarget {
|
|
|
491
491
|
notConnectedAt?: string;
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
+
/**
|
|
495
|
+
* Slack's team-scoped admin page listing pending app-install requests.
|
|
496
|
+
* Provisioned apps have no OAuth flow (no redirect URLs, install-from-Slack
|
|
497
|
+
* disabled), so the oauth_authorize_url Slack returns from
|
|
498
|
+
* apps.manifest.create dead-ends; approval happens here instead.
|
|
499
|
+
*/
|
|
500
|
+
function slackAppRequestsUrl(slackTeamId: string): string {
|
|
501
|
+
return `https://app.slack.com/apps-manage/${encodeURIComponent(slackTeamId)}/integrations/requests`;
|
|
502
|
+
}
|
|
503
|
+
|
|
494
504
|
/** "Name (T0123ABCD)", or the bare id when the backend has no name. */
|
|
495
505
|
function formatWorkspace(workspace: SlackConnectedWorkspace): string {
|
|
496
506
|
const name = workspace.slackTeamName ?? "";
|
|
@@ -719,6 +729,7 @@ export async function cmdSlackCreate(
|
|
|
719
729
|
const iconStatus = await applySlackCreateIcon(ctx, iconSource, result);
|
|
720
730
|
|
|
721
731
|
if (result.status === "pending_admin_approval") {
|
|
732
|
+
const approveUrl = slackAppRequestsUrl(workspace.slackTeamId);
|
|
722
733
|
if (ctx.json) {
|
|
723
734
|
ctx.out(
|
|
724
735
|
`${JSON.stringify(
|
|
@@ -729,7 +740,7 @@ export async function cmdSlackCreate(
|
|
|
729
740
|
slackTeamId: workspace.slackTeamId,
|
|
730
741
|
envKeys: target.envKeys,
|
|
731
742
|
wrote,
|
|
732
|
-
|
|
743
|
+
approveUrl,
|
|
733
744
|
iconStatus,
|
|
734
745
|
},
|
|
735
746
|
null,
|
|
@@ -739,9 +750,7 @@ export async function cmdSlackCreate(
|
|
|
739
750
|
} else {
|
|
740
751
|
ctx.err(
|
|
741
752
|
`The Slack app was created (app ${result.appId}), but installing it needs a Slack workspace-admin approval.\n` +
|
|
742
|
-
|
|
743
|
-
? ""
|
|
744
|
-
: `\n Approve it (or send this to a workspace admin):\n\n ${result.oauthAuthorizeUrl}\n\n`) +
|
|
753
|
+
`\n A workspace admin can approve it here:\n\n ${approveUrl}\n\n` +
|
|
745
754
|
`Once an admin approves the app, re-run \`${CLI} slack create\` to finish the install and mint the tokens.\n`
|
|
746
755
|
);
|
|
747
756
|
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { defineArtifacts } from "../artifacts.js";
|
|
6
|
+
import { defineAgent } from "../index.js";
|
|
7
|
+
import { defineTool } from "../tools.js";
|
|
8
|
+
import type { AgentConfig, ArtifactsConfig } from "../types.js";
|
|
9
|
+
import { loadAgentProject, projectInfo } from "./discovery.js";
|
|
10
|
+
|
|
11
|
+
const cleanups: Array<() => Promise<void>> = [];
|
|
12
|
+
|
|
13
|
+
afterEach(async () => {
|
|
14
|
+
while (cleanups.length > 0) {
|
|
15
|
+
await cleanups.pop()?.();
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const TAGGABLE: ArtifactsConfig = {
|
|
20
|
+
kinds: {
|
|
21
|
+
"reviewed-pr": { description: "A reviewed PR." },
|
|
22
|
+
report: { description: "A report." },
|
|
23
|
+
},
|
|
24
|
+
agentTool: true,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
async function scaffold(options: {
|
|
28
|
+
agent?: AgentConfig;
|
|
29
|
+
artifacts?: ArtifactsConfig;
|
|
30
|
+
authoredTools?: string[];
|
|
31
|
+
}) {
|
|
32
|
+
const rootDir = await mkdtemp(join(tmpdir(), "agent-serve-artifact-tool-"));
|
|
33
|
+
cleanups.push(() => rm(rootDir, { recursive: true, force: true }));
|
|
34
|
+
const agentDir = join(rootDir, "agent");
|
|
35
|
+
await mkdir(agentDir, { recursive: true });
|
|
36
|
+
await writeFile(join(agentDir, "agent.ts"), "export default {};\n");
|
|
37
|
+
await writeFile(join(agentDir, "instructions.md"), "Be brief.\n");
|
|
38
|
+
if (options.artifacts !== undefined) {
|
|
39
|
+
await writeFile(join(agentDir, "artifacts.ts"), "export default {};\n");
|
|
40
|
+
}
|
|
41
|
+
for (const name of options.authoredTools ?? []) {
|
|
42
|
+
await mkdir(join(agentDir, "tools"), { recursive: true });
|
|
43
|
+
await writeFile(
|
|
44
|
+
join(agentDir, "tools", `${name}.ts`),
|
|
45
|
+
"export default {};\n"
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return loadAgentProject(rootDir, {
|
|
49
|
+
importModule: async (path) => {
|
|
50
|
+
if (path.endsWith("artifacts.ts")) {
|
|
51
|
+
return { default: defineArtifacts(options.artifacts ?? {}) };
|
|
52
|
+
}
|
|
53
|
+
if (path.includes(join("agent", "tools"))) {
|
|
54
|
+
return {
|
|
55
|
+
default: defineTool({
|
|
56
|
+
description: "Authored tool.",
|
|
57
|
+
execute: () => "authored",
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return { default: defineAgent(options.agent ?? {}) };
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("loadAgentProject tag_artifact built-in", () => {
|
|
67
|
+
it("materializes tag_artifact when agentTool is true", async () => {
|
|
68
|
+
const project = await scaffold({ artifacts: TAGGABLE });
|
|
69
|
+
const tool = project.agent.tools.find((t) => t.name === "tag_artifact");
|
|
70
|
+
expect(tool).toMatchObject({
|
|
71
|
+
name: "tag_artifact",
|
|
72
|
+
execution: "server",
|
|
73
|
+
needsApproval: false,
|
|
74
|
+
});
|
|
75
|
+
expect(tool?.description).toContain("- reviewed-pr: A reviewed PR.");
|
|
76
|
+
expect(project.diagnostics).toEqual([]);
|
|
77
|
+
|
|
78
|
+
// Info/manifest surface it exactly like authored tools.
|
|
79
|
+
const info = projectInfo(project);
|
|
80
|
+
const infoTool = info.tools.find((t) => t.name === "tag_artifact");
|
|
81
|
+
expect(infoTool?.execution).toBe("server");
|
|
82
|
+
expect(infoTool?.inputSchema).toBeDefined();
|
|
83
|
+
expect(info.artifacts?.agentTool).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("stays absent without agentTool", async () => {
|
|
87
|
+
const withoutFlag = await scaffold({
|
|
88
|
+
artifacts: { kinds: { report: { description: "A report." } } },
|
|
89
|
+
});
|
|
90
|
+
expect(withoutFlag.agent.tools.some((t) => t.name === "tag_artifact")).toBe(
|
|
91
|
+
false
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const unauthored = await scaffold({});
|
|
95
|
+
expect(unauthored.agent.tools.some((t) => t.name === "tag_artifact")).toBe(
|
|
96
|
+
false
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("skips the tool on cloud runtime, with a warning", async () => {
|
|
101
|
+
const project = await scaffold({
|
|
102
|
+
agent: {
|
|
103
|
+
runtime: "cloud",
|
|
104
|
+
cloud: { repos: [{ url: "https://github.com/org/repo" }] },
|
|
105
|
+
},
|
|
106
|
+
artifacts: TAGGABLE,
|
|
107
|
+
});
|
|
108
|
+
expect(project.agent.tools.some((t) => t.name === "tag_artifact")).toBe(
|
|
109
|
+
false
|
|
110
|
+
);
|
|
111
|
+
expect(
|
|
112
|
+
project.diagnostics.some(
|
|
113
|
+
(d) =>
|
|
114
|
+
d.severity === "warning" &&
|
|
115
|
+
d.message.includes("tag_artifact") &&
|
|
116
|
+
d.message.includes("not available on cloud agents")
|
|
117
|
+
)
|
|
118
|
+
).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("lets an authored tag_artifact shadow the built-in, with a warning", async () => {
|
|
122
|
+
const project = await scaffold({
|
|
123
|
+
artifacts: TAGGABLE,
|
|
124
|
+
authoredTools: ["tag_artifact"],
|
|
125
|
+
});
|
|
126
|
+
const tools = project.agent.tools.filter((t) => t.name === "tag_artifact");
|
|
127
|
+
expect(tools).toHaveLength(1);
|
|
128
|
+
expect(tools[0]?.description).toBe("Authored tool.");
|
|
129
|
+
expect(project.diagnostics).toEqual([
|
|
130
|
+
expect.objectContaining({
|
|
131
|
+
severity: "warning",
|
|
132
|
+
message: expect.stringContaining("shadows the built-in tool"),
|
|
133
|
+
}),
|
|
134
|
+
]);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { defineArtifacts } from "../artifacts.js";
|
|
6
|
+
import { defineAgent } from "../index.js";
|
|
7
|
+
import type { ArtifactsConfig } from "../types.js";
|
|
8
|
+
import { loadAgentProject, projectInfo } from "./discovery.js";
|
|
9
|
+
|
|
10
|
+
const cleanups: Array<() => Promise<void>> = [];
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
while (cleanups.length > 0) {
|
|
14
|
+
await cleanups.pop()?.();
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
async function scaffold(options: {
|
|
19
|
+
artifacts?: ArtifactsConfig;
|
|
20
|
+
subagentArtifacts?: boolean;
|
|
21
|
+
}) {
|
|
22
|
+
const rootDir = await mkdtemp(join(tmpdir(), "agent-serve-artifacts-disc-"));
|
|
23
|
+
cleanups.push(() => rm(rootDir, { recursive: true, force: true }));
|
|
24
|
+
const agentDir = join(rootDir, "agent");
|
|
25
|
+
await mkdir(agentDir, { recursive: true });
|
|
26
|
+
await writeFile(join(agentDir, "agent.ts"), "export default {};\n");
|
|
27
|
+
await writeFile(join(agentDir, "instructions.md"), "Be brief.\n");
|
|
28
|
+
if (options.artifacts !== undefined) {
|
|
29
|
+
await writeFile(join(agentDir, "artifacts.ts"), "export default {};\n");
|
|
30
|
+
}
|
|
31
|
+
if (options.subagentArtifacts === true) {
|
|
32
|
+
const subagentDir = join(agentDir, "subagents", "helper");
|
|
33
|
+
await mkdir(subagentDir, { recursive: true });
|
|
34
|
+
await writeFile(join(subagentDir, "agent.ts"), "export default {};\n");
|
|
35
|
+
await writeFile(join(subagentDir, "instructions.md"), "Help.\n");
|
|
36
|
+
await writeFile(join(subagentDir, "artifacts.ts"), "export default {};\n");
|
|
37
|
+
}
|
|
38
|
+
return loadAgentProject(rootDir, {
|
|
39
|
+
importModule: async (path) => {
|
|
40
|
+
if (path.endsWith("artifacts.ts")) {
|
|
41
|
+
return { default: defineArtifacts(options.artifacts ?? {}) };
|
|
42
|
+
}
|
|
43
|
+
if (path.includes("subagents")) {
|
|
44
|
+
return { default: defineAgent({ description: "Helper." }) };
|
|
45
|
+
}
|
|
46
|
+
return { default: defineAgent({}) };
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe("loadAgentProject artifacts", () => {
|
|
52
|
+
it("discovers agent/artifacts.ts and surfaces it on the manifest and info", async () => {
|
|
53
|
+
const project = await scaffold({
|
|
54
|
+
artifacts: {
|
|
55
|
+
kinds: {
|
|
56
|
+
"reviewed-pr": { description: "A reviewed PR." },
|
|
57
|
+
report: { description: "A report." },
|
|
58
|
+
},
|
|
59
|
+
max: 50,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
expect(project.artifacts).toBeDefined();
|
|
63
|
+
expect(project.diagnostics).toEqual([]);
|
|
64
|
+
expect(projectInfo(project).artifacts).toEqual({
|
|
65
|
+
kinds: ["reviewed-pr", "report"],
|
|
66
|
+
agentTool: false,
|
|
67
|
+
max: 50,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("defaults max to 1000 on info and stays absent when unauthored", async () => {
|
|
72
|
+
const withDefaults = await scaffold({ artifacts: { kinds: {} } });
|
|
73
|
+
expect(projectInfo(withDefaults).artifacts).toEqual({
|
|
74
|
+
kinds: [],
|
|
75
|
+
agentTool: false,
|
|
76
|
+
max: 1000,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const without = await scaffold({});
|
|
80
|
+
expect(without.artifacts).toBeUndefined();
|
|
81
|
+
expect(projectInfo(without).artifacts).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("errors when agentTool is set without declared kinds", async () => {
|
|
85
|
+
const project = await scaffold({ artifacts: { agentTool: true } });
|
|
86
|
+
expect(project.diagnostics).toEqual([
|
|
87
|
+
expect.objectContaining({
|
|
88
|
+
severity: "error",
|
|
89
|
+
message: expect.stringContaining(
|
|
90
|
+
"the tag_artifact tool needs at least one declared kind"
|
|
91
|
+
),
|
|
92
|
+
}),
|
|
93
|
+
]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("accepts agentTool with at least one kind (schema optional)", async () => {
|
|
97
|
+
const project = await scaffold({
|
|
98
|
+
artifacts: {
|
|
99
|
+
kinds: { note: { description: "A note." } },
|
|
100
|
+
agentTool: true,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
expect(project.diagnostics).toEqual([]);
|
|
104
|
+
expect(projectInfo(project).artifacts?.agentTool).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("warns and ignores artifacts on subagents", async () => {
|
|
108
|
+
const project = await scaffold({ subagentArtifacts: true });
|
|
109
|
+
expect(project.diagnostics).toEqual([
|
|
110
|
+
expect.objectContaining({
|
|
111
|
+
severity: "warning",
|
|
112
|
+
message: expect.stringContaining(
|
|
113
|
+
"artifacts are not supported on subagents"
|
|
114
|
+
),
|
|
115
|
+
}),
|
|
116
|
+
]);
|
|
117
|
+
expect(project.artifacts).toBeUndefined();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "node:path";
|
|
19
19
|
import { pathToFileURL } from "node:url";
|
|
20
20
|
import { type ABConfigFile, resolveABMaxPlaygroundSessions } from "../ab.js";
|
|
21
|
+
import { resolveArtifactsMax } from "../artifacts.js";
|
|
21
22
|
import { httpChannel } from "../channels.js";
|
|
22
23
|
import type { StorageDefinition } from "../storage.js";
|
|
23
24
|
import {
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
type AgentProject,
|
|
28
29
|
type AgentProjectInfo,
|
|
29
30
|
type AgentRuntime,
|
|
31
|
+
type ArtifactsDefinition,
|
|
30
32
|
type BuiltinToolsConfig,
|
|
31
33
|
type ChannelDefinition,
|
|
32
34
|
type ConnectionDefinition,
|
|
@@ -58,7 +60,11 @@ import {
|
|
|
58
60
|
import { registerAuthoredModuleLoaders } from "./authored-loaders.js";
|
|
59
61
|
import { normalizePositiveInt } from "./bounded-int.js";
|
|
60
62
|
import { getDefinitionKind } from "./brand.js";
|
|
61
|
-
import {
|
|
63
|
+
import {
|
|
64
|
+
artifactBuiltinTool,
|
|
65
|
+
builtinTools,
|
|
66
|
+
TAG_ARTIFACT_TOOL_NAME,
|
|
67
|
+
} from "./builtin-tools/index.js";
|
|
62
68
|
import { isValidCron } from "./cron.js";
|
|
63
69
|
import { parseFrontmatter } from "./frontmatter.js";
|
|
64
70
|
import {
|
|
@@ -280,6 +286,8 @@ export async function loadAgentProject(
|
|
|
280
286
|
const abs = await loadABs(ctx, agentDir);
|
|
281
287
|
const abConfig = await loadABConfig(ctx, agentDir);
|
|
282
288
|
const storage = await loadStorage(ctx, agentDir);
|
|
289
|
+
const artifacts = await loadArtifacts(ctx, agentDir);
|
|
290
|
+
await appendArtifactTool(ctx, agentDir, agent, artifacts);
|
|
283
291
|
|
|
284
292
|
if (agentDir !== rootDir) {
|
|
285
293
|
await warnUnknownSlots(ctx, agentDir);
|
|
@@ -297,6 +305,7 @@ export async function loadAgentProject(
|
|
|
297
305
|
abs,
|
|
298
306
|
abConfig,
|
|
299
307
|
storage,
|
|
308
|
+
artifacts,
|
|
300
309
|
diagnostics,
|
|
301
310
|
};
|
|
302
311
|
}
|
|
@@ -351,6 +360,14 @@ async function loadAgent(
|
|
|
351
360
|
message: "builtinTools is not supported on subagents; ignoring.",
|
|
352
361
|
});
|
|
353
362
|
}
|
|
363
|
+
const artifactsPath = await findModuleFile(agentDir, "artifacts");
|
|
364
|
+
if (artifactsPath !== undefined) {
|
|
365
|
+
ctx.diagnostics.push({
|
|
366
|
+
severity: "warning",
|
|
367
|
+
path: displayPath(ctx, artifactsPath),
|
|
368
|
+
message: "artifacts are not supported on subagents; ignoring.",
|
|
369
|
+
});
|
|
370
|
+
}
|
|
354
371
|
return {
|
|
355
372
|
name,
|
|
356
373
|
...(config?.description === undefined
|
|
@@ -1579,6 +1596,81 @@ async function loadStorage(
|
|
|
1579
1596
|
return definition as StorageDefinition | undefined;
|
|
1580
1597
|
}
|
|
1581
1598
|
|
|
1599
|
+
// ============================================================================
|
|
1600
|
+
// Artifacts (agent/artifacts.ts)
|
|
1601
|
+
// ============================================================================
|
|
1602
|
+
|
|
1603
|
+
async function loadArtifacts(
|
|
1604
|
+
ctx: DiscoveryContext,
|
|
1605
|
+
agentDir: string
|
|
1606
|
+
): Promise<ArtifactsDefinition | undefined> {
|
|
1607
|
+
const path = await findModuleFile(agentDir, "artifacts");
|
|
1608
|
+
if (path === undefined) {
|
|
1609
|
+
return undefined;
|
|
1610
|
+
}
|
|
1611
|
+
const imported = await importDefault(ctx, path, "artifacts");
|
|
1612
|
+
if (imported === undefined) {
|
|
1613
|
+
return undefined;
|
|
1614
|
+
}
|
|
1615
|
+
const definition = imported as ArtifactsDefinition;
|
|
1616
|
+
if (
|
|
1617
|
+
definition.agentTool === true &&
|
|
1618
|
+
Object.keys(definition.kinds ?? {}).length === 0
|
|
1619
|
+
) {
|
|
1620
|
+
ctx.diagnostics.push({
|
|
1621
|
+
severity: "error",
|
|
1622
|
+
path: displayPath(ctx, path),
|
|
1623
|
+
message:
|
|
1624
|
+
"artifacts.agentTool requires kinds: the tag_artifact tool needs at least one declared kind; its descriptions are the tool prompt.",
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
return definition;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
/**
|
|
1631
|
+
* Materialize the `tag_artifact` built-in when `defineArtifacts` set
|
|
1632
|
+
* `agentTool: true`. Appended after `loadAgent` because the tool is
|
|
1633
|
+
* generated from the artifacts kinds registry, not `AgentConfig.builtinTools`.
|
|
1634
|
+
*/
|
|
1635
|
+
async function appendArtifactTool(
|
|
1636
|
+
ctx: DiscoveryContext,
|
|
1637
|
+
agentDir: string,
|
|
1638
|
+
agent: ResolvedAgent,
|
|
1639
|
+
artifacts: ArtifactsDefinition | undefined
|
|
1640
|
+
): Promise<void> {
|
|
1641
|
+
const tool = artifactBuiltinTool(artifacts);
|
|
1642
|
+
if (tool === undefined) {
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
const artifactsPath = displayPath(
|
|
1646
|
+
ctx,
|
|
1647
|
+
(await findModuleFile(agentDir, "artifacts")) ??
|
|
1648
|
+
join(agentDir, "artifacts.ts")
|
|
1649
|
+
);
|
|
1650
|
+
// TODO(artifacts-cloud): on cloud runtimes, ship tag_artifact as an
|
|
1651
|
+
// execution: "agent" script and have the host fold its action.result
|
|
1652
|
+
// stream events into artifact records — agent-tool calls already surface
|
|
1653
|
+
// durably in the session stream.
|
|
1654
|
+
if (agent.runtime === "cloud") {
|
|
1655
|
+
ctx.diagnostics.push({
|
|
1656
|
+
severity: "warning",
|
|
1657
|
+
path: artifactsPath,
|
|
1658
|
+
message:
|
|
1659
|
+
"tag_artifact is an in-process server tool (like builtinTools) and is not available on cloud agents; it is skipped.",
|
|
1660
|
+
});
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
if (agent.tools.some((t) => t.name === TAG_ARTIFACT_TOOL_NAME)) {
|
|
1664
|
+
ctx.diagnostics.push({
|
|
1665
|
+
severity: "warning",
|
|
1666
|
+
path: artifactsPath,
|
|
1667
|
+
message: `Authored tool "${TAG_ARTIFACT_TOOL_NAME}" shadows the built-in tool of the same name; the authored tool wins.`,
|
|
1668
|
+
});
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
agent.tools.push(tool);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1582
1674
|
function resolveABName(definition: unknown, fallback: string): string {
|
|
1583
1675
|
if (
|
|
1584
1676
|
typeof definition === "object" &&
|
|
@@ -1759,6 +1851,14 @@ export function projectInfo(project: AgentProject): AgentProjectInfo {
|
|
|
1759
1851
|
project.storage === undefined
|
|
1760
1852
|
? undefined
|
|
1761
1853
|
: { name: project.storage.name },
|
|
1854
|
+
artifacts:
|
|
1855
|
+
project.artifacts === undefined
|
|
1856
|
+
? undefined
|
|
1857
|
+
: {
|
|
1858
|
+
kinds: Object.keys(project.artifacts.kinds ?? {}),
|
|
1859
|
+
agentTool: project.artifacts.agentTool === true,
|
|
1860
|
+
max: resolveArtifactsMax(project.artifacts),
|
|
1861
|
+
},
|
|
1762
1862
|
diagnostics: project.diagnostics,
|
|
1763
1863
|
};
|
|
1764
1864
|
}
|
|
@@ -1779,6 +1879,7 @@ async function importDefault(
|
|
|
1779
1879
|
| "hook"
|
|
1780
1880
|
| "ab"
|
|
1781
1881
|
| "storage"
|
|
1882
|
+
| "artifacts"
|
|
1782
1883
|
): Promise<unknown> {
|
|
1783
1884
|
let mod: Record<string, unknown>;
|
|
1784
1885
|
try {
|
|
@@ -107,6 +107,7 @@ export const AUTHORING_ENTRY_FILES: ReadonlyArray<
|
|
|
107
107
|
["evals/reporters", "evals/reporters.js"],
|
|
108
108
|
["evals/loaders", "evals/loaders.js"],
|
|
109
109
|
["ab", "ab.js"],
|
|
110
|
+
["artifacts", "artifacts.js"],
|
|
110
111
|
["storage", "storage.js"],
|
|
111
112
|
["storage/file-kv", "storage-backends/file-kv.js"],
|
|
112
113
|
["storage/postgres-kv", "storage-backends/postgres-kv.js"],
|
|
@@ -115,6 +115,12 @@ function handlerArgs(send: SendMessageFn): ChannelHandlerArgs {
|
|
|
115
115
|
delete: async () => {},
|
|
116
116
|
},
|
|
117
117
|
},
|
|
118
|
+
artifacts: {
|
|
119
|
+
tag: async () => {
|
|
120
|
+
throw new Error("artifacts not configured in test");
|
|
121
|
+
},
|
|
122
|
+
list: async () => [],
|
|
123
|
+
},
|
|
118
124
|
send,
|
|
119
125
|
getSession: async () => null,
|
|
120
126
|
receive: async () => fakeSession("recv"),
|