@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
|
@@ -24,6 +24,10 @@ import {
|
|
|
24
24
|
import {
|
|
25
25
|
type AgentProject,
|
|
26
26
|
type ApprovalDecision,
|
|
27
|
+
type ArtifactListFilter,
|
|
28
|
+
type ArtifactRecord,
|
|
29
|
+
type ArtifactsApi,
|
|
30
|
+
type ArtifactTagInput,
|
|
27
31
|
type AuthContext,
|
|
28
32
|
type CallToolOptions,
|
|
29
33
|
type ChannelDefinition,
|
|
@@ -60,6 +64,7 @@ import { ABCollector } from "./ab-collector.js";
|
|
|
60
64
|
import { foldABStream } from "./ab-fold.js";
|
|
61
65
|
import { type ABSnapshot, buildABSnapshot } from "./ab-snapshot.js";
|
|
62
66
|
import { ApprovalGate } from "./approval-gate.js";
|
|
67
|
+
import { ArtifactsStore } from "./artifacts-store.js";
|
|
63
68
|
import { samePrincipal } from "./auth.js";
|
|
64
69
|
import { mergeCloudOptions, resolveSessionRuntime } from "./cloud-merge.js";
|
|
65
70
|
import { isDevMode } from "./dev-mode.js";
|
|
@@ -278,6 +283,8 @@ export class SessionEngine {
|
|
|
278
283
|
private reminderApi: HostContext["reminders"];
|
|
279
284
|
private evalsApi: HostContext["evals"];
|
|
280
285
|
private readonly kvApi: HostContext["kv"];
|
|
286
|
+
/** Engine-owned artifacts service behind every `ctx.artifacts` facade. */
|
|
287
|
+
private readonly artifactsStore: ArtifactsStore;
|
|
281
288
|
/** Serve-time resolved connections (see {@link setResolvedConnections}). */
|
|
282
289
|
private resolvedConnections: EngineResolvedConnection[] = [];
|
|
283
290
|
|
|
@@ -315,6 +322,11 @@ export class SessionEngine {
|
|
|
315
322
|
stateRoot: options.stateRoot,
|
|
316
323
|
...(this.storage === undefined ? {} : { storage: this.storage }),
|
|
317
324
|
});
|
|
325
|
+
this.artifactsStore = new ArtifactsStore({
|
|
326
|
+
stateRoot: options.stateRoot,
|
|
327
|
+
storage: this.storage,
|
|
328
|
+
config: options.project.artifacts,
|
|
329
|
+
});
|
|
318
330
|
this.abCollector = new ABCollector(
|
|
319
331
|
options.project.abs,
|
|
320
332
|
this.logger,
|
|
@@ -370,6 +382,78 @@ export class SessionEngine {
|
|
|
370
382
|
};
|
|
371
383
|
}
|
|
372
384
|
|
|
385
|
+
/** Unbound `ctx.artifacts` facade (channel handlers, scratch tool calls). */
|
|
386
|
+
get artifacts(): ArtifactsApi {
|
|
387
|
+
return this.artifactsApi();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Build one `ctx.artifacts` facade over the engine-owned artifacts
|
|
392
|
+
* service. With a `binding` (tools, hooks) `tag` auto-fills the bound
|
|
393
|
+
* session (and turn when known) and rejects a conflicting explicit
|
|
394
|
+
* `sessionId`; without one (channel handlers) callers may pass
|
|
395
|
+
* `sessionId` / `turnId` explicitly.
|
|
396
|
+
*/
|
|
397
|
+
artifactsApi(binding?: { sessionId: string; turnId?: string }): ArtifactsApi {
|
|
398
|
+
return {
|
|
399
|
+
tag: async (input) => {
|
|
400
|
+
if (binding === undefined) {
|
|
401
|
+
return this.tagArtifact(input);
|
|
402
|
+
}
|
|
403
|
+
if (
|
|
404
|
+
input.sessionId !== undefined &&
|
|
405
|
+
input.sessionId !== binding.sessionId
|
|
406
|
+
) {
|
|
407
|
+
throw new Error(
|
|
408
|
+
`artifacts.tag: this context is bound to session ${binding.sessionId}; cannot tag for ${input.sessionId}`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
return this.tagArtifact({
|
|
412
|
+
...input,
|
|
413
|
+
sessionId: binding.sessionId,
|
|
414
|
+
turnId: input.turnId ?? binding.turnId,
|
|
415
|
+
});
|
|
416
|
+
},
|
|
417
|
+
list: (filter) => this.artifactsStore.list(filter),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Single tag funnel: upsert the row, then — for session-bound tags —
|
|
423
|
+
* append `artifact.tagged` through {@link appendEvent} so persistence
|
|
424
|
+
* flush and hook fan-out are inherited. Session-less tags skip the event.
|
|
425
|
+
* An unknown `sessionId` rejects before the row is written (appending to
|
|
426
|
+
* a session that was never materialized here would mint an orphan local
|
|
427
|
+
* event log, breaking the local-over-sink restore invariant).
|
|
428
|
+
*/
|
|
429
|
+
private async tagArtifact(input: ArtifactTagInput): Promise<ArtifactRecord> {
|
|
430
|
+
if (
|
|
431
|
+
input.sessionId !== undefined &&
|
|
432
|
+
(await this.getOrRestoreSession(input.sessionId)) === undefined
|
|
433
|
+
) {
|
|
434
|
+
throw new UnknownSessionError(input.sessionId);
|
|
435
|
+
}
|
|
436
|
+
const record = await this.artifactsStore.tag(input);
|
|
437
|
+
if (record.sessionId !== undefined) {
|
|
438
|
+
await this.appendEvent(record.sessionId, {
|
|
439
|
+
type: "artifact.tagged",
|
|
440
|
+
turnId: record.turnId,
|
|
441
|
+
data: {
|
|
442
|
+
id: record.id,
|
|
443
|
+
kind: record.kind,
|
|
444
|
+
key: record.key,
|
|
445
|
+
title: record.title,
|
|
446
|
+
data: record.data,
|
|
447
|
+
source: record.source,
|
|
448
|
+
},
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
this.logger(
|
|
452
|
+
`[session] artifact tagged kind=${record.kind} id=${record.id} source=${record.source} session=${record.sessionId ?? "-"} key=${record.key ?? "-"}`
|
|
453
|
+
);
|
|
454
|
+
return record;
|
|
455
|
+
}
|
|
456
|
+
|
|
373
457
|
private slackNudgeHostApi(): SlackNudgeHostApi {
|
|
374
458
|
return {
|
|
375
459
|
append: (input) => this.nudgeStore.append(input),
|
|
@@ -1408,6 +1492,10 @@ export class SessionEngine {
|
|
|
1408
1492
|
workspaceDir: this.harnessCwd(record),
|
|
1409
1493
|
stateRoot: this.stateRoot,
|
|
1410
1494
|
host: this.host,
|
|
1495
|
+
artifacts: this.artifactsApi({
|
|
1496
|
+
sessionId: record.sessionId,
|
|
1497
|
+
turnId,
|
|
1498
|
+
}),
|
|
1411
1499
|
send: (channelId, message, sendOptions = {}) =>
|
|
1412
1500
|
this.send(channelId, message, {
|
|
1413
1501
|
...sendOptions,
|
|
@@ -1730,6 +1818,14 @@ export class SessionEngine {
|
|
|
1730
1818
|
workspaceDir,
|
|
1731
1819
|
stateRoot: this.stateRoot,
|
|
1732
1820
|
host: this.host,
|
|
1821
|
+
// Scratch calls have no real session; their facade stays unbound.
|
|
1822
|
+
artifacts:
|
|
1823
|
+
record === undefined
|
|
1824
|
+
? this.artifactsApi()
|
|
1825
|
+
: this.artifactsApi({
|
|
1826
|
+
sessionId: record.sessionId,
|
|
1827
|
+
turnId: callId,
|
|
1828
|
+
}),
|
|
1733
1829
|
send: (channelId, message, sendOptions = {}) =>
|
|
1734
1830
|
this.send(channelId, message, {
|
|
1735
1831
|
...sendOptions,
|
|
@@ -1882,6 +1978,10 @@ export class SessionEngine {
|
|
|
1882
1978
|
},
|
|
1883
1979
|
session: this.sessionInfo(record),
|
|
1884
1980
|
stateRoot: this.stateRoot,
|
|
1981
|
+
artifacts: this.artifactsApi({
|
|
1982
|
+
sessionId: record.sessionId,
|
|
1983
|
+
turnId: event.turnId,
|
|
1984
|
+
}),
|
|
1885
1985
|
});
|
|
1886
1986
|
} catch (error) {
|
|
1887
1987
|
this.logger(
|
|
@@ -1951,6 +2051,31 @@ export class SessionEngine {
|
|
|
1951
2051
|
return (await this.listSessionsWithSource(caller, options)).sessions;
|
|
1952
2052
|
}
|
|
1953
2053
|
|
|
2054
|
+
/**
|
|
2055
|
+
* Artifacts for `GET /v1/artifacts`, newest-updated first. Without
|
|
2056
|
+
* `includeAll`, the sessions-list ownership rule applies: only artifacts
|
|
2057
|
+
* tagged on a session whose principal matches the caller. Session-less
|
|
2058
|
+
* artifacts are agent-scoped and visible only with `includeAll`.
|
|
2059
|
+
*/
|
|
2060
|
+
async listArtifacts(
|
|
2061
|
+
caller: AuthContext | null,
|
|
2062
|
+
options?: { includeAll?: boolean; filter?: ArtifactListFilter }
|
|
2063
|
+
): Promise<ArtifactRecord[]> {
|
|
2064
|
+
const records = await this.artifactsStore.list(options?.filter);
|
|
2065
|
+
if (options?.includeAll === true) {
|
|
2066
|
+
return records;
|
|
2067
|
+
}
|
|
2068
|
+
const sessions = await this.sessions.list();
|
|
2069
|
+
const owned = new Set(
|
|
2070
|
+
sessions
|
|
2071
|
+
.filter((record) => samePrincipal(record.auth, caller))
|
|
2072
|
+
.map((record) => record.sessionId)
|
|
2073
|
+
);
|
|
2074
|
+
return records.filter(
|
|
2075
|
+
(record) => record.sessionId !== undefined && owned.has(record.sessionId)
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
|
|
1954
2079
|
/**
|
|
1955
2080
|
* Same as {@link listSessions}, plus which store answered
|
|
1956
2081
|
* (`cursor-hosted` = durable DB via the control-plane proxy; `local` =
|
|
@@ -495,6 +495,68 @@ export class StorageCoordinator {
|
|
|
495
495
|
await this.definition.delete?.(key, this.context("policy"));
|
|
496
496
|
}
|
|
497
497
|
|
|
498
|
+
// ==========================================================================
|
|
499
|
+
// Artifacts (`ctx.artifacts`)
|
|
500
|
+
// ==========================================================================
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Whether the sink can serve awaited artifact rows: `get` (upsert
|
|
504
|
+
* read-back), `list` (listing + eviction), and `delete` (`max` cap
|
|
505
|
+
* eviction) are all required — without `delete` the retention cap
|
|
506
|
+
* could never evict, so rows fall back to the filesystem store.
|
|
507
|
+
*/
|
|
508
|
+
get supportsArtifacts(): boolean {
|
|
509
|
+
return (
|
|
510
|
+
this.definition.get !== undefined &&
|
|
511
|
+
this.definition.list !== undefined &&
|
|
512
|
+
this.definition.delete !== undefined
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** Awaited artifact-row read. Errors propagate (read-after-write). */
|
|
517
|
+
async artifactGet(id: string): Promise<JsonValue | undefined> {
|
|
518
|
+
const get = this.definition.get;
|
|
519
|
+
if (get === undefined) {
|
|
520
|
+
throw new Error("storage get is not configured");
|
|
521
|
+
}
|
|
522
|
+
const value = await get(
|
|
523
|
+
storageKeys.artifact(this.agentName, id),
|
|
524
|
+
this.context("restore")
|
|
525
|
+
);
|
|
526
|
+
return value ?? undefined;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/** Awaited artifact-row write (straight to the sink, like author KV). */
|
|
530
|
+
async artifactPut(id: string, value: JsonValue): Promise<void> {
|
|
531
|
+
await this.definition.put(
|
|
532
|
+
storageKeys.artifact(this.agentName, id),
|
|
533
|
+
value,
|
|
534
|
+
this.context("policy")
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** Awaited artifact-row delete. Errors propagate (cap eviction). */
|
|
539
|
+
async artifactDelete(id: string): Promise<void> {
|
|
540
|
+
const del = this.definition.delete;
|
|
541
|
+
if (del === undefined) {
|
|
542
|
+
throw new Error("storage delete is not configured");
|
|
543
|
+
}
|
|
544
|
+
await del(storageKeys.artifact(this.agentName, id), this.context("policy"));
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/** All artifact-row values for this agent. Errors propagate. */
|
|
548
|
+
async artifactList(): Promise<JsonValue[]> {
|
|
549
|
+
const list = this.definition.list;
|
|
550
|
+
if (list === undefined) {
|
|
551
|
+
throw new Error("storage list is not configured");
|
|
552
|
+
}
|
|
553
|
+
const entries = await list(
|
|
554
|
+
storageKeys.artifactPrefix(this.agentName),
|
|
555
|
+
this.context("restore")
|
|
556
|
+
);
|
|
557
|
+
return Array.isArray(entries) ? entries.map((entry) => entry.value) : [];
|
|
558
|
+
}
|
|
559
|
+
|
|
498
560
|
// ==========================================================================
|
|
499
561
|
// Evals (dedicated `evals` table)
|
|
500
562
|
// ==========================================================================
|
package/src/storage.ts
CHANGED
|
@@ -277,6 +277,7 @@ export const STORAGE_KEY_ROOT = "agentkit/v1" as const;
|
|
|
277
277
|
* | `agentkit/v1/{agent}/continuation/{channelId}/{token}` | `{ sessionId }` |
|
|
278
278
|
* | `agentkit/v1/{agent}/reminder/{reminderId}` | `ReminderRecord` |
|
|
279
279
|
* | `agentkit/v1/{agent}/kv/{key}` | Author JSON via {@link HostContext.kv} |
|
|
280
|
+
* | `agentkit/v1/{agent}/artifacts/{id}` | `ArtifactRecord` via `ctx.artifacts` |
|
|
280
281
|
*
|
|
281
282
|
* Eval-run and A/B history do not flow through this KV scheme — they have
|
|
282
283
|
* dedicated tables ({@link StorageConfig.evals} / {@link StorageConfig.abs}).
|
|
@@ -311,6 +312,11 @@ export const storageKeys = {
|
|
|
311
312
|
kv: (agent: string, key: string): string =>
|
|
312
313
|
`${STORAGE_KEY_ROOT}/${agent}/kv/${keySegment(key)}`,
|
|
313
314
|
kvPrefix: (agent: string): string => `${STORAGE_KEY_ROOT}/${agent}/kv/`,
|
|
315
|
+
/** Tagged artifact rows (see `ctx.artifacts` / `ArtifactsApi`). */
|
|
316
|
+
artifact: (agent: string, id: string): string =>
|
|
317
|
+
`${STORAGE_KEY_ROOT}/${agent}/artifacts/${keySegment(id)}`,
|
|
318
|
+
artifactPrefix: (agent: string): string =>
|
|
319
|
+
`${STORAGE_KEY_ROOT}/${agent}/artifacts/`,
|
|
314
320
|
} as const;
|
|
315
321
|
|
|
316
322
|
/**
|
package/src/types.ts
CHANGED
|
@@ -45,7 +45,8 @@ export type DefinitionKind =
|
|
|
45
45
|
| "hook"
|
|
46
46
|
| "eval"
|
|
47
47
|
| "ab"
|
|
48
|
-
| "storage"
|
|
48
|
+
| "storage"
|
|
49
|
+
| "artifacts";
|
|
49
50
|
|
|
50
51
|
export interface BrandedDefinition<K extends DefinitionKind> {
|
|
51
52
|
readonly __agentServe: K;
|
|
@@ -516,6 +517,11 @@ export interface ToolContext {
|
|
|
516
517
|
channelId: string,
|
|
517
518
|
sessionId: string
|
|
518
519
|
): Promise<ChannelSession | null>;
|
|
520
|
+
/**
|
|
521
|
+
* Durable artifacts, bound to this tool call's session: `tag` auto-fills
|
|
522
|
+
* `sessionId` (and `turnId` when known).
|
|
523
|
+
*/
|
|
524
|
+
artifacts: ArtifactsApi;
|
|
519
525
|
}
|
|
520
526
|
|
|
521
527
|
/**
|
|
@@ -922,6 +928,18 @@ export type SessionEventPayload =
|
|
|
922
928
|
data: { callId: string; name?: string; description?: string };
|
|
923
929
|
}
|
|
924
930
|
| { type: "subagent.completed"; data: { callId: string; name?: string } }
|
|
931
|
+
| {
|
|
932
|
+
/** An artifact was tagged for this session (see {@link ArtifactsApi.tag}). */
|
|
933
|
+
type: "artifact.tagged";
|
|
934
|
+
data: {
|
|
935
|
+
id: string;
|
|
936
|
+
kind: string;
|
|
937
|
+
key?: string;
|
|
938
|
+
title?: string;
|
|
939
|
+
data: JsonValue;
|
|
940
|
+
source: ArtifactSource;
|
|
941
|
+
};
|
|
942
|
+
}
|
|
925
943
|
| {
|
|
926
944
|
type: "turn.completed";
|
|
927
945
|
data: { result?: string; usage?: TurnUsage };
|
|
@@ -1292,6 +1310,11 @@ export interface ChannelHandlerArgs {
|
|
|
1292
1310
|
request: Request,
|
|
1293
1311
|
sessionId: string
|
|
1294
1312
|
): { playgroundUrl: string; traceUrl: string };
|
|
1313
|
+
/**
|
|
1314
|
+
* Durable artifacts (unbound — channel handlers have no ambient session;
|
|
1315
|
+
* pass `sessionId` in `tag` input to attribute one).
|
|
1316
|
+
*/
|
|
1317
|
+
artifacts: ArtifactsApi;
|
|
1295
1318
|
}
|
|
1296
1319
|
|
|
1297
1320
|
export type ChannelRouteHandler = (
|
|
@@ -1575,6 +1598,11 @@ export interface HookContext {
|
|
|
1575
1598
|
* memory journal) write here.
|
|
1576
1599
|
*/
|
|
1577
1600
|
stateRoot: string;
|
|
1601
|
+
/**
|
|
1602
|
+
* Durable artifacts, bound to this hook's session: `tag` auto-fills
|
|
1603
|
+
* `sessionId` (and `turnId` when known).
|
|
1604
|
+
*/
|
|
1605
|
+
artifacts: ArtifactsApi;
|
|
1578
1606
|
}
|
|
1579
1607
|
|
|
1580
1608
|
export type HookHandler<TEvent extends SessionEvent = SessionEvent> = (
|
|
@@ -1595,6 +1623,107 @@ export interface HookConfig {
|
|
|
1595
1623
|
|
|
1596
1624
|
export type HookDefinition = HookConfig & BrandedDefinition<"hook">;
|
|
1597
1625
|
|
|
1626
|
+
// ============================================================================
|
|
1627
|
+
// Artifacts (agent/artifacts.ts)
|
|
1628
|
+
// ============================================================================
|
|
1629
|
+
|
|
1630
|
+
/** One declared artifact kind (see {@link ArtifactsConfig.kinds}). */
|
|
1631
|
+
export interface ArtifactKindConfig {
|
|
1632
|
+
/**
|
|
1633
|
+
* What this kind holds. Also the model-facing prompt for the
|
|
1634
|
+
* `tag_artifact` built-in tool when {@link ArtifactsConfig.agentTool}
|
|
1635
|
+
* is enabled.
|
|
1636
|
+
*/
|
|
1637
|
+
description: string;
|
|
1638
|
+
/**
|
|
1639
|
+
* Optional Zod schema; `tag` validates the payload against it and
|
|
1640
|
+
* persists the parsed output (defaults and coercions applied), so
|
|
1641
|
+
* the schema's output must stay JSON-serializable.
|
|
1642
|
+
*/
|
|
1643
|
+
schema?: z.ZodType;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
/** Input to `defineArtifacts` (authored at `agent/artifacts.ts`). */
|
|
1647
|
+
export interface ArtifactsConfig {
|
|
1648
|
+
/**
|
|
1649
|
+
* Declared artifact kinds. With kinds declared, `tag` only accepts
|
|
1650
|
+
* these; with none, any kind string is accepted freeform.
|
|
1651
|
+
*/
|
|
1652
|
+
kinds?: Record<string, ArtifactKindConfig>;
|
|
1653
|
+
/**
|
|
1654
|
+
* Expose the model-facing `tag_artifact` built-in tool generated from
|
|
1655
|
+
* {@link kinds}. Requires at least one declared kind.
|
|
1656
|
+
*/
|
|
1657
|
+
agentTool?: boolean;
|
|
1658
|
+
/**
|
|
1659
|
+
* Retention cap (default 1000). On insert past the cap, the
|
|
1660
|
+
* oldest-updated artifact is evicted.
|
|
1661
|
+
*/
|
|
1662
|
+
max?: number;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
export type ArtifactsDefinition = ArtifactsConfig &
|
|
1666
|
+
BrandedDefinition<"artifacts">;
|
|
1667
|
+
|
|
1668
|
+
/** Who tagged an artifact: host code, or the model via `tag_artifact`. */
|
|
1669
|
+
export type ArtifactSource = "host" | "model";
|
|
1670
|
+
|
|
1671
|
+
/** One durable tagged artifact. */
|
|
1672
|
+
export interface ArtifactRecord {
|
|
1673
|
+
/** Stable id: derived from {@link key} when present, else random. */
|
|
1674
|
+
id: string;
|
|
1675
|
+
kind: string;
|
|
1676
|
+
/** Author-chosen upsert key (same key ⇒ same id ⇒ replaces the row). */
|
|
1677
|
+
key?: string;
|
|
1678
|
+
title?: string;
|
|
1679
|
+
data: JsonValue;
|
|
1680
|
+
sessionId?: string;
|
|
1681
|
+
turnId?: string;
|
|
1682
|
+
source: ArtifactSource;
|
|
1683
|
+
createdAt: string;
|
|
1684
|
+
updatedAt: string;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
/** Input to {@link ArtifactsApi.tag}. */
|
|
1688
|
+
export interface ArtifactTagInput {
|
|
1689
|
+
/** Defaults to `"artifact"` when no kinds are declared. */
|
|
1690
|
+
kind?: string;
|
|
1691
|
+
data: JsonValue;
|
|
1692
|
+
title?: string;
|
|
1693
|
+
/** Upsert key: tagging the same key again replaces the row. */
|
|
1694
|
+
key?: string;
|
|
1695
|
+
/**
|
|
1696
|
+
* Session to attribute (and stream an `artifact.tagged` event to). On
|
|
1697
|
+
* session-bound facades ({@link ToolContext.artifacts},
|
|
1698
|
+
* {@link HookContext.artifacts}) this is auto-filled and a different
|
|
1699
|
+
* value is rejected; unbound facades
|
|
1700
|
+
* ({@link ChannelHandlerArgs.artifacts}) accept it explicitly.
|
|
1701
|
+
*/
|
|
1702
|
+
sessionId?: string;
|
|
1703
|
+
turnId?: string;
|
|
1704
|
+
/** Defaults to `"host"`. */
|
|
1705
|
+
source?: ArtifactSource;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
/** Filter for {@link ArtifactsApi.list}. */
|
|
1709
|
+
export interface ArtifactListFilter {
|
|
1710
|
+
kind?: string;
|
|
1711
|
+
sessionId?: string;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
/**
|
|
1715
|
+
* The artifacts API, attached as `ctx.artifacts` on tools
|
|
1716
|
+
* ({@link ToolContext}), hooks ({@link HookContext}), and channel handlers
|
|
1717
|
+
* ({@link ChannelHandlerArgs}). Tool and hook facades are session-bound:
|
|
1718
|
+
* `tag` auto-fills the session; the channel facade is unbound.
|
|
1719
|
+
*/
|
|
1720
|
+
export interface ArtifactsApi {
|
|
1721
|
+
/** Tag (upsert) one artifact. See {@link ArtifactTagInput}. */
|
|
1722
|
+
tag(input: ArtifactTagInput): Promise<ArtifactRecord>;
|
|
1723
|
+
/** Tagged artifacts, newest-updated first. */
|
|
1724
|
+
list(filter?: ArtifactListFilter): Promise<ArtifactRecord[]>;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1598
1727
|
// ============================================================================
|
|
1599
1728
|
// Schedules (agent/schedules/*)
|
|
1600
1729
|
// ============================================================================
|
|
@@ -1919,6 +2048,8 @@ export interface AgentProject {
|
|
|
1919
2048
|
abConfig?: ABConfigFile;
|
|
1920
2049
|
/** Optional `agent/storage.ts` (`defineStorage`). */
|
|
1921
2050
|
storage?: StorageDefinition;
|
|
2051
|
+
/** Optional `agent/artifacts.ts` (`defineArtifacts`). */
|
|
2052
|
+
artifacts?: ArtifactsDefinition;
|
|
1922
2053
|
diagnostics: Diagnostic[];
|
|
1923
2054
|
}
|
|
1924
2055
|
|
|
@@ -1988,6 +2119,8 @@ export interface AgentProjectInfo {
|
|
|
1988
2119
|
};
|
|
1989
2120
|
/** Project storage sink from `agent/storage.ts`, when authored. */
|
|
1990
2121
|
storage?: { name?: string };
|
|
2122
|
+
/** Declared artifact kinds from `agent/artifacts.ts`, when authored. */
|
|
2123
|
+
artifacts?: { kinds: string[]; agentTool: boolean; max: number };
|
|
1991
2124
|
diagnostics: Diagnostic[];
|
|
1992
2125
|
}
|
|
1993
2126
|
|