@agen-ai/agent-runtime 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -8
- package/dist/configurationValidation.js +12 -17
- package/dist/contractErrors.d.ts +2 -2
- package/dist/contractErrors.js +6 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interactionValidation.d.ts +23 -0
- package/dist/interactionValidation.js +238 -0
- package/dist/outputValidation.d.ts +4 -1
- package/dist/outputValidation.js +42 -4
- package/dist/sessionValidation.js +678 -18
- package/dist/sessions.d.ts +70 -4
- package/dist/testing/conformance.d.ts +5 -2
- package/dist/testing/conformance.js +161 -4
- package/dist/testing/fakeProvider.js +220 -17
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,12 +9,14 @@ its binding and conversation-local operations.
|
|
|
9
9
|
The runtime depends only on `@agen-ai/agent-protocol`. It has no concept of tenants, SaaS
|
|
10
10
|
workspaces, assigned users, database rows, persistence sequence, visibility, billing, host boots,
|
|
11
11
|
leases, or storage policy. A host must authorize and select an instance before calling this SPI.
|
|
12
|
-
|
|
12
|
+
This source package implements Agent Protocol V8. Agent Protocol V8, private host V16/catalog V10, and Workspaces event V10.
|
|
13
|
+
This is intentionally one coordinated tuple rather than a deployable mixed-version graph.
|
|
13
14
|
|
|
14
15
|
## Entrypoints
|
|
15
16
|
|
|
16
17
|
- `@agen-ai/agent-runtime` exports the public driver, instance, adapter, session, output,
|
|
17
|
-
readiness, bounded-evidence, artifact-candidate,
|
|
18
|
+
readiness, bounded-evidence, artifact-candidate, capability-bound interaction validation,
|
|
19
|
+
and registry APIs.
|
|
18
20
|
- `@agen-ai/agent-runtime/testing` exports the deterministic fake provider and reusable
|
|
19
21
|
conformance runner.
|
|
20
22
|
|
|
@@ -39,6 +41,13 @@ exactly once before returning the matching session. Capability-dependent operati
|
|
|
39
41
|
`supported`/`unsupported` discriminants, and the runtime rejects handlers that disagree with the
|
|
40
42
|
instance capability declaration.
|
|
41
43
|
|
|
44
|
+
V8 replaces interaction-extension booleans with cohesive capability-matched session ports:
|
|
45
|
+
configuration inventory and selection, typed operation inventory and invocation, managed-content
|
|
46
|
+
inventory, integration observation, collaboration spawn/control, and generated-resource access.
|
|
47
|
+
An unsupported declaration exposes exactly `{ kind: "unsupported" }`; a supported declaration must
|
|
48
|
+
expose exactly its typed handlers. Catalogs and results are parsed again at the runtime boundary,
|
|
49
|
+
bounded by the declaration, and correlated to the offered revision and caller-owned identity.
|
|
50
|
+
|
|
42
51
|
Approval continuations are refusal-first. Before delegating to the candidate adapter, the
|
|
43
52
|
validated session proves that the request is still pending and unexpired and that a selected
|
|
44
53
|
`optionId` was offered by that exact request. Provider-emitted approval requests must correlate to
|
|
@@ -87,6 +96,14 @@ provider-delegated steering failures throw `AgentProviderDelegatedOperationError
|
|
|
87
96
|
cause and explicit started-execution evidence so a host can retire the unusable session. Platform
|
|
88
97
|
scheduling and provider-native queue modes are intentionally outside this SPI.
|
|
89
98
|
|
|
99
|
+
Configuration selection, operation invocation, and collaboration mutation use the same execution
|
|
100
|
+
receipt rule: stale or malformed input fails before provider delegation, while any failure after
|
|
101
|
+
the candidate reports execution start is wrapped as `AgentProviderDelegatedOperationError` and
|
|
102
|
+
makes the session unusable. The start boundary commits only after the host observer returns
|
|
103
|
+
successfully; an observer failure remains a pre-delegation error and leaves the session reusable.
|
|
104
|
+
Operation, collaboration, and generated-resource observations also
|
|
105
|
+
enforce correlation, immutable identity, declared bounds, and forward-only lifecycle transitions.
|
|
106
|
+
|
|
90
107
|
## Minimal driver
|
|
91
108
|
|
|
92
109
|
```ts
|
|
@@ -146,13 +163,13 @@ turn/request ordering, request resolution, steering, interruption, configuration
|
|
|
146
163
|
close, and idempotent disposal. Unsupported operations must remain explicit discriminants and
|
|
147
164
|
must not expose handlers.
|
|
148
165
|
|
|
149
|
-
The package is at `0.2.
|
|
150
|
-
releases may include breaking changes during this beta period, and those changes
|
|
151
|
-
|
|
166
|
+
The package is at `0.2.3` while the public SPI is being proven with external adapters. Pre-1.0
|
|
167
|
+
releases may include breaking changes during this beta period, and those changes are called out in
|
|
168
|
+
the release notes.
|
|
152
169
|
|
|
153
|
-
The public runtime implements Agent Protocol
|
|
154
|
-
host, catalog, persistence, and member-projection versions.
|
|
155
|
-
|
|
170
|
+
The public runtime implements Agent Protocol V8. Its package version remains independent of private
|
|
171
|
+
host, catalog, persistence, and member-projection versions. V8 directly replaces V7; there is no
|
|
172
|
+
runtime shim or dual-protocol session surface.
|
|
156
173
|
|
|
157
174
|
Run the clean packed-consumer proof before any release:
|
|
158
175
|
|
|
@@ -1,31 +1,26 @@
|
|
|
1
1
|
import { throwAgentProviderContractError } from "./contractErrors.js";
|
|
2
2
|
function assertAgentSessionConfigurationSupported(capabilities, configuration) {
|
|
3
|
-
const entries = Object.entries(configuration.values);
|
|
4
3
|
if (capabilities.configuration.kind === "managed") {
|
|
5
|
-
if (
|
|
4
|
+
if (configuration.kind === "managed") return;
|
|
6
5
|
throwAgentProviderContractError(
|
|
7
6
|
capabilities.providerKey,
|
|
8
7
|
"configuration_key_unsupported",
|
|
9
|
-
"Managed
|
|
8
|
+
"Managed providers require a managed session configuration."
|
|
10
9
|
);
|
|
11
10
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"Provider configuration contains a key not declared by its capabilities."
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
if (typeof value !== "string" || !field.optionIds.includes(value)) {
|
|
11
|
+
if (configuration.kind !== "selected" || configuration.selections.length > capabilities.configuration.maxFields) {
|
|
12
|
+
throwAgentProviderContractError(
|
|
13
|
+
capabilities.providerKey,
|
|
14
|
+
"configuration_key_unsupported",
|
|
15
|
+
"Selectable providers require a bounded selected session configuration."
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
for (const selection of configuration.selections) {
|
|
19
|
+
if (!capabilities.configuration.fieldKinds.includes(selection.value.fieldKind)) {
|
|
25
20
|
throwAgentProviderContractError(
|
|
26
21
|
capabilities.providerKey,
|
|
27
22
|
"configuration_value_unsupported",
|
|
28
|
-
"Provider configuration contains
|
|
23
|
+
"Provider configuration contains a field kind outside its declared capability."
|
|
29
24
|
);
|
|
30
25
|
}
|
|
31
26
|
}
|
package/dist/contractErrors.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentProviderKey } from "@agen-ai/agent-protocol";
|
|
2
|
-
export declare const AGENT_PROVIDER_CONTRACT_ERROR_CODES: readonly ["capability_port_mismatch", "invalid_adapter", "invalid_session", "invalid_binding", "binding_callback_missing", "binding_callback_repeated", "binding_callback_mismatch", "resume_binding_mismatch", "branch_binding_reused", "session_closed", "session_unusable", "concurrent_turn", "active_turn_mismatch", "invalid_operation_result", "output_session_mismatch", "output_turn_mismatch", "output_authentication_attempt_mismatch", "output_capability_mismatch", "input_capability_mismatch", "invalid_turn_sequence", "request_resolution_mismatch", "configuration_key_unsupported", "configuration_value_unsupported", "request_pending"];
|
|
2
|
+
export declare const AGENT_PROVIDER_CONTRACT_ERROR_CODES: readonly ["capability_port_mismatch", "invalid_adapter", "invalid_session", "invalid_binding", "binding_callback_missing", "binding_callback_repeated", "binding_callback_mismatch", "resume_binding_mismatch", "branch_binding_reused", "session_closed", "session_unusable", "concurrent_turn", "active_turn_mismatch", "invalid_operation_result", "invalid_inventory", "invalid_collaboration_transition", "input_operation_mismatch", "output_session_mismatch", "output_turn_mismatch", "output_authentication_attempt_mismatch", "output_operation_mismatch", "output_collaboration_mismatch", "output_resource_mismatch", "output_capability_mismatch", "input_capability_mismatch", "invalid_turn_sequence", "request_resolution_mismatch", "configuration_key_unsupported", "configuration_value_unsupported", "request_pending"];
|
|
3
3
|
export type AgentProviderContractErrorCode = (typeof AGENT_PROVIDER_CONTRACT_ERROR_CODES)[number];
|
|
4
|
-
export type AgentProviderDelegatedOperation = "steer_turn";
|
|
4
|
+
export type AgentProviderDelegatedOperation = "steer_turn" | "invoke_operation" | "apply_configuration" | "spawn_collaboration" | "control_collaboration";
|
|
5
5
|
export declare class AgentProviderContractError extends Error {
|
|
6
6
|
readonly providerKey: AgentProviderKey;
|
|
7
7
|
readonly code: AgentProviderContractErrorCode;
|
package/dist/contractErrors.js
CHANGED
|
@@ -13,9 +13,15 @@ const AGENT_PROVIDER_CONTRACT_ERROR_CODES = [
|
|
|
13
13
|
"concurrent_turn",
|
|
14
14
|
"active_turn_mismatch",
|
|
15
15
|
"invalid_operation_result",
|
|
16
|
+
"invalid_inventory",
|
|
17
|
+
"invalid_collaboration_transition",
|
|
18
|
+
"input_operation_mismatch",
|
|
16
19
|
"output_session_mismatch",
|
|
17
20
|
"output_turn_mismatch",
|
|
18
21
|
"output_authentication_attempt_mismatch",
|
|
22
|
+
"output_operation_mismatch",
|
|
23
|
+
"output_collaboration_mismatch",
|
|
24
|
+
"output_resource_mismatch",
|
|
19
25
|
"output_capability_mismatch",
|
|
20
26
|
"input_capability_mismatch",
|
|
21
27
|
"invalid_turn_sequence",
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./artifacts.js";
|
|
|
3
3
|
export * from "./contractErrors.js";
|
|
4
4
|
export * from "./evidence.js";
|
|
5
5
|
export * from "./foundation.js";
|
|
6
|
+
export * from "./interactionValidation.js";
|
|
6
7
|
export * from "./outputs.js";
|
|
7
8
|
export * from "./providerCatalog.js";
|
|
8
9
|
export * from "./providerDriver.js";
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./artifacts.js";
|
|
|
3
3
|
export * from "./contractErrors.js";
|
|
4
4
|
export * from "./evidence.js";
|
|
5
5
|
export * from "./foundation.js";
|
|
6
|
+
export * from "./interactionValidation.js";
|
|
6
7
|
export * from "./outputs.js";
|
|
7
8
|
export * from "./providerCatalog.js";
|
|
8
9
|
export * from "./providerDriver.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type AgentCapabilities, type AgentCollaborationNode, type AgentConfigurationCatalog, type AgentGeneratedResourceDescriptor, type AgentIntegrationCatalog, type AgentManagedContentCatalog, type AgentOperationCatalog, type AgentOperationInvocation, type AgentOperationDescriptor, type AgentOperationResult, type AgentProviderKey } from "@agen-ai/agent-protocol";
|
|
2
|
+
export declare function validateAgentOperationCatalogForCapabilities(capabilities: AgentCapabilities, candidate: unknown): AgentOperationCatalog;
|
|
3
|
+
export declare function validateAgentManagedContentCatalogForCapabilities(capabilities: AgentCapabilities, candidate: unknown): AgentManagedContentCatalog;
|
|
4
|
+
export declare function validateAgentConfigurationCatalogForCapabilities(capabilities: AgentCapabilities, candidate: unknown): AgentConfigurationCatalog;
|
|
5
|
+
export declare function validateAgentIntegrationCatalogForCapabilities(capabilities: AgentCapabilities, candidate: unknown): AgentIntegrationCatalog;
|
|
6
|
+
export declare function validateAgentOperationResultForInvocation(providerKey: AgentProviderKey, descriptor: AgentOperationDescriptor, invocation: AgentOperationInvocation, candidate: unknown): AgentOperationResult;
|
|
7
|
+
export declare function validateAgentOperationResultTransition(input: {
|
|
8
|
+
readonly providerKey: AgentProviderKey;
|
|
9
|
+
readonly candidate: AgentOperationResult;
|
|
10
|
+
readonly previous?: AgentOperationResult;
|
|
11
|
+
}): AgentOperationResult;
|
|
12
|
+
export declare function validateAgentCollaborationNodeForCapabilities(input: {
|
|
13
|
+
readonly capabilities: AgentCapabilities;
|
|
14
|
+
readonly candidate: unknown;
|
|
15
|
+
readonly previous?: AgentCollaborationNode;
|
|
16
|
+
}): AgentCollaborationNode;
|
|
17
|
+
export declare function validateAgentGeneratedResourceForCapabilities(input: {
|
|
18
|
+
readonly capabilities: AgentCapabilities;
|
|
19
|
+
readonly candidate: unknown;
|
|
20
|
+
readonly expectedResourceId: string;
|
|
21
|
+
readonly previous?: AgentGeneratedResourceDescriptor;
|
|
22
|
+
}): AgentGeneratedResourceDescriptor;
|
|
23
|
+
//# sourceMappingURL=interactionValidation.d.ts.map
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import {
|
|
3
|
+
parseAgentCollaborationNode,
|
|
4
|
+
parseAgentConfigurationCatalog,
|
|
5
|
+
parseAgentGeneratedResourceDescriptor,
|
|
6
|
+
parseAgentIntegrationCatalog,
|
|
7
|
+
parseAgentManagedContentCatalog,
|
|
8
|
+
parseAgentOperationCatalog,
|
|
9
|
+
parseAgentOperationResultFor
|
|
10
|
+
} from "@agen-ai/agent-protocol";
|
|
11
|
+
import { throwAgentProviderContractError } from "./contractErrors.js";
|
|
12
|
+
function invalidInventory(providerKey, message) {
|
|
13
|
+
return throwAgentProviderContractError(providerKey, "invalid_inventory", message);
|
|
14
|
+
}
|
|
15
|
+
function validateAgentOperationCatalogForCapabilities(capabilities, candidate) {
|
|
16
|
+
let catalog;
|
|
17
|
+
try {
|
|
18
|
+
catalog = parseAgentOperationCatalog(candidate);
|
|
19
|
+
} catch {
|
|
20
|
+
return invalidInventory(capabilities.providerKey, "Provider returned an invalid operation catalog.");
|
|
21
|
+
}
|
|
22
|
+
const capability = capabilities.operations;
|
|
23
|
+
if (capability.kind !== "supported" || catalog.operations.length > capability.maxOperations || catalog.operations.some(
|
|
24
|
+
(operation) => !capability.operationKinds.includes(operation.kind) || !capability.executionModes.includes(operation.executionMode) || operation.fields.length > capability.maxFieldsPerOperation || operation.fields.some(
|
|
25
|
+
(field) => !capability.fieldKinds.includes(field.fieldKind)
|
|
26
|
+
)
|
|
27
|
+
)) {
|
|
28
|
+
return invalidInventory(
|
|
29
|
+
capabilities.providerKey,
|
|
30
|
+
"Operation catalog exceeds the provider's declared capability."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return catalog;
|
|
34
|
+
}
|
|
35
|
+
function validateAgentManagedContentCatalogForCapabilities(capabilities, candidate) {
|
|
36
|
+
let catalog;
|
|
37
|
+
try {
|
|
38
|
+
catalog = parseAgentManagedContentCatalog(candidate);
|
|
39
|
+
} catch {
|
|
40
|
+
return invalidInventory(capabilities.providerKey, "Provider returned an invalid managed-content catalog.");
|
|
41
|
+
}
|
|
42
|
+
const capability = capabilities.managedContent;
|
|
43
|
+
if (capability.kind !== "supported" || catalog.entries.length > capability.maxEntries || catalog.entries.some(
|
|
44
|
+
(entry) => !capability.contentKinds.includes(entry.kind)
|
|
45
|
+
)) {
|
|
46
|
+
return invalidInventory(
|
|
47
|
+
capabilities.providerKey,
|
|
48
|
+
"Managed-content catalog exceeds the provider's declared capability."
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return catalog;
|
|
52
|
+
}
|
|
53
|
+
function validateAgentConfigurationCatalogForCapabilities(capabilities, candidate) {
|
|
54
|
+
let catalog;
|
|
55
|
+
try {
|
|
56
|
+
catalog = parseAgentConfigurationCatalog(candidate);
|
|
57
|
+
} catch {
|
|
58
|
+
return invalidInventory(capabilities.providerKey, "Provider returned an invalid configuration catalog.");
|
|
59
|
+
}
|
|
60
|
+
const capability = capabilities.configuration;
|
|
61
|
+
if (capability.kind !== "selectable" || catalog.fields.length > capability.maxFields || catalog.fields.some(
|
|
62
|
+
(field) => !capability.fieldKinds.includes(field.fieldKind)
|
|
63
|
+
)) {
|
|
64
|
+
return invalidInventory(
|
|
65
|
+
capabilities.providerKey,
|
|
66
|
+
"Configuration catalog exceeds the provider's declared capability."
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return catalog;
|
|
70
|
+
}
|
|
71
|
+
function validateAgentIntegrationCatalogForCapabilities(capabilities, candidate) {
|
|
72
|
+
let catalog;
|
|
73
|
+
try {
|
|
74
|
+
catalog = parseAgentIntegrationCatalog(candidate);
|
|
75
|
+
} catch {
|
|
76
|
+
return invalidInventory(capabilities.providerKey, "Provider returned an invalid integration catalog.");
|
|
77
|
+
}
|
|
78
|
+
const capability = capabilities.integrations;
|
|
79
|
+
if (capability.kind !== "supported" || catalog.integrations.length > capability.maxIntegrations || catalog.integrations.some(
|
|
80
|
+
(integration) => !capability.integrationKinds.includes(integration.kind) || integration.servers.length > capability.maxServersPerIntegration || integration.servers.some(
|
|
81
|
+
(server) => server.tools.length > capability.maxToolsPerServer || server.resources.length > capability.maxResourcesPerServer
|
|
82
|
+
)
|
|
83
|
+
)) {
|
|
84
|
+
return invalidInventory(
|
|
85
|
+
capabilities.providerKey,
|
|
86
|
+
"Integration catalog exceeds the provider's declared capability."
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return catalog;
|
|
90
|
+
}
|
|
91
|
+
function validateAgentOperationResultForInvocation(providerKey, descriptor, invocation, candidate) {
|
|
92
|
+
try {
|
|
93
|
+
return parseAgentOperationResultFor(descriptor, invocation, candidate);
|
|
94
|
+
} catch {
|
|
95
|
+
return throwAgentProviderContractError(
|
|
96
|
+
providerKey,
|
|
97
|
+
"invalid_operation_result",
|
|
98
|
+
"Provider returned an invalid or uncorrelated operation result."
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const OPERATION_RESULT_TRANSITIONS = {
|
|
103
|
+
accepted: ["accepted", "waiting_for_request", "completed", "failed", "canceled"],
|
|
104
|
+
waiting_for_request: ["waiting_for_request", "accepted", "completed", "failed", "canceled"],
|
|
105
|
+
completed: ["completed"],
|
|
106
|
+
failed: ["failed"],
|
|
107
|
+
canceled: ["canceled"]
|
|
108
|
+
};
|
|
109
|
+
function validateAgentOperationResultTransition(input) {
|
|
110
|
+
if (input.previous !== void 0 && !OPERATION_RESULT_TRANSITIONS[input.previous.status].includes(
|
|
111
|
+
input.candidate.status
|
|
112
|
+
)) {
|
|
113
|
+
return throwAgentProviderContractError(
|
|
114
|
+
input.providerKey,
|
|
115
|
+
"invalid_operation_result",
|
|
116
|
+
"Provider returned an invalid operation lifecycle transition."
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
if (input.previous !== void 0 && ["completed", "failed", "canceled"].includes(input.previous.status) && JSON.stringify(input.previous) !== JSON.stringify(input.candidate)) {
|
|
120
|
+
return throwAgentProviderContractError(
|
|
121
|
+
input.providerKey,
|
|
122
|
+
"invalid_operation_result",
|
|
123
|
+
"Provider changed a terminal operation result."
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return input.candidate;
|
|
127
|
+
}
|
|
128
|
+
const COLLABORATION_TRANSITIONS = {
|
|
129
|
+
queued: ["queued", "starting", "running", "waiting", "completed", "failed", "canceled"],
|
|
130
|
+
starting: ["starting", "running", "waiting", "completed", "failed", "canceled"],
|
|
131
|
+
running: ["running", "waiting", "completed", "failed", "canceled"],
|
|
132
|
+
waiting: ["waiting", "running", "completed", "failed", "canceled"],
|
|
133
|
+
completed: ["completed"],
|
|
134
|
+
failed: ["failed"],
|
|
135
|
+
canceled: ["canceled"]
|
|
136
|
+
};
|
|
137
|
+
function collaborationUsageIsMonotonic(previous, next) {
|
|
138
|
+
if (previous.kind === "unavailable") return true;
|
|
139
|
+
if (next.kind === "unavailable") return false;
|
|
140
|
+
const fields = [
|
|
141
|
+
"inputTokens",
|
|
142
|
+
"outputTokens",
|
|
143
|
+
"reasoningTokens",
|
|
144
|
+
"totalTokens",
|
|
145
|
+
"modelCalls"
|
|
146
|
+
];
|
|
147
|
+
return fields.every(
|
|
148
|
+
(field) => previous[field] === void 0 || next[field] !== void 0 && next[field] >= previous[field]
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
function validateAgentCollaborationNodeForCapabilities(input) {
|
|
152
|
+
let node;
|
|
153
|
+
try {
|
|
154
|
+
node = parseAgentCollaborationNode(input.candidate);
|
|
155
|
+
} catch {
|
|
156
|
+
return throwAgentProviderContractError(
|
|
157
|
+
input.capabilities.providerKey,
|
|
158
|
+
"output_collaboration_mismatch",
|
|
159
|
+
"Provider returned an invalid collaboration node."
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
const capability = input.capabilities.collaboration;
|
|
163
|
+
if (capability.kind !== "supported" || !capability.roles.includes(node.role)) {
|
|
164
|
+
return throwAgentProviderContractError(
|
|
165
|
+
input.capabilities.providerKey,
|
|
166
|
+
"output_capability_mismatch",
|
|
167
|
+
"Provider returned collaboration state outside its declared capability."
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (input.previous !== void 0 && !COLLABORATION_TRANSITIONS[input.previous.status].includes(node.status)) {
|
|
171
|
+
return throwAgentProviderContractError(
|
|
172
|
+
input.capabilities.providerKey,
|
|
173
|
+
"invalid_collaboration_transition",
|
|
174
|
+
"Provider returned an invalid collaboration lifecycle transition."
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
if (input.previous !== void 0 && Date.parse(node.updatedAt) === Date.parse(input.previous.updatedAt) && !isDeepStrictEqual(node, input.previous)) {
|
|
178
|
+
return throwAgentProviderContractError(
|
|
179
|
+
input.capabilities.providerKey,
|
|
180
|
+
"invalid_collaboration_transition",
|
|
181
|
+
"Provider changed collaboration state without advancing its observation timestamp."
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
if (input.previous !== void 0 && (node.collaborationId !== input.previous.collaborationId || node.rootCollaborationId !== input.previous.rootCollaborationId || node.parentCollaborationId !== input.previous.parentCollaborationId || node.role !== input.previous.role || node.title !== input.previous.title || node.objective !== input.previous.objective || node.createdAt !== input.previous.createdAt || Date.parse(node.updatedAt) < Date.parse(input.previous.updatedAt) || !collaborationUsageIsMonotonic(input.previous.usage, node.usage) || input.previous.terminalAt !== void 0 && node.terminalAt !== input.previous.terminalAt || input.previous.closedAt !== void 0 && node.closedAt !== input.previous.closedAt || input.previous.outcome !== void 0 && !isDeepStrictEqual(node.outcome, input.previous.outcome) || input.previous.artifactIds !== void 0 && !isDeepStrictEqual(node.artifactIds, input.previous.artifactIds) || input.previous.resourceIds !== void 0 && !isDeepStrictEqual(node.resourceIds, input.previous.resourceIds))) {
|
|
185
|
+
return throwAgentProviderContractError(
|
|
186
|
+
input.capabilities.providerKey,
|
|
187
|
+
"output_collaboration_mismatch",
|
|
188
|
+
"Provider changed immutable collaboration identity."
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
return node;
|
|
192
|
+
}
|
|
193
|
+
function validateAgentGeneratedResourceForCapabilities(input) {
|
|
194
|
+
let resource;
|
|
195
|
+
try {
|
|
196
|
+
resource = parseAgentGeneratedResourceDescriptor(input.candidate);
|
|
197
|
+
} catch {
|
|
198
|
+
return throwAgentProviderContractError(
|
|
199
|
+
input.capabilities.providerKey,
|
|
200
|
+
"output_resource_mismatch",
|
|
201
|
+
"Provider returned an invalid generated resource."
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
const capability = input.capabilities.generatedResources;
|
|
205
|
+
if (capability.kind !== "supported" || resource.resourceId !== input.expectedResourceId || !capability.resourceKinds.includes(resource.kind) || resource.byteSize !== void 0 && resource.byteSize > capability.maxBytesPerResource) {
|
|
206
|
+
return throwAgentProviderContractError(
|
|
207
|
+
input.capabilities.providerKey,
|
|
208
|
+
"output_resource_mismatch",
|
|
209
|
+
"Provider returned a generated resource outside its declared capability or correlation."
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
if (input.previous !== void 0) {
|
|
213
|
+
const transitions = {
|
|
214
|
+
pending: ["pending", "available", "unavailable", "expired"],
|
|
215
|
+
available: ["available", "expired"],
|
|
216
|
+
unavailable: ["unavailable", "expired"],
|
|
217
|
+
expired: ["expired"]
|
|
218
|
+
};
|
|
219
|
+
if (resource.kind !== input.previous.kind || resource.displayName !== input.previous.displayName || !isDeepStrictEqual(resource.producer, input.previous.producer) || resource.createdAt !== input.previous.createdAt || !transitions[input.previous.status].includes(resource.status) || input.previous.status !== "pending" && input.previous.status === resource.status && !isDeepStrictEqual(resource, input.previous)) {
|
|
220
|
+
return throwAgentProviderContractError(
|
|
221
|
+
input.capabilities.providerKey,
|
|
222
|
+
"output_resource_mismatch",
|
|
223
|
+
"Provider changed immutable resource identity or returned an invalid lifecycle transition."
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return resource;
|
|
228
|
+
}
|
|
229
|
+
export {
|
|
230
|
+
validateAgentCollaborationNodeForCapabilities,
|
|
231
|
+
validateAgentConfigurationCatalogForCapabilities,
|
|
232
|
+
validateAgentGeneratedResourceForCapabilities,
|
|
233
|
+
validateAgentIntegrationCatalogForCapabilities,
|
|
234
|
+
validateAgentManagedContentCatalogForCapabilities,
|
|
235
|
+
validateAgentOperationCatalogForCapabilities,
|
|
236
|
+
validateAgentOperationResultForInvocation,
|
|
237
|
+
validateAgentOperationResultTransition
|
|
238
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentCapabilities, type AgentProviderKey, type AgentSessionId, type AgentTurnId } from "@agen-ai/agent-protocol";
|
|
1
|
+
import { type AgentCapabilities, type AgentCollaborationId, type AgentGeneratedResourceId, type AgentOperationInvocationId, type AgentProviderKey, type AgentSessionId, type AgentTurnId } from "@agen-ai/agent-protocol";
|
|
2
2
|
import { type AgentProviderOutput } from "./outputs.js";
|
|
3
3
|
import type { AgentProviderOperationResult } from "./sessions.js";
|
|
4
4
|
export interface AgentProviderOutputValidationContext {
|
|
@@ -7,6 +7,9 @@ export interface AgentProviderOutputValidationContext {
|
|
|
7
7
|
readonly sessionId?: AgentSessionId;
|
|
8
8
|
readonly turnId?: AgentTurnId;
|
|
9
9
|
readonly authenticationAttemptId?: string;
|
|
10
|
+
readonly operationInvocationId?: AgentOperationInvocationId;
|
|
11
|
+
readonly collaborationId?: AgentCollaborationId;
|
|
12
|
+
readonly generatedResourceId?: AgentGeneratedResourceId;
|
|
10
13
|
}
|
|
11
14
|
export declare function validateAgentProviderOutputForContext(candidate: AgentProviderOutput, context: AgentProviderOutputValidationContext): AgentProviderOutput;
|
|
12
15
|
export declare function validateAgentProviderOperationResult(candidate: AgentProviderOperationResult, context: AgentProviderOutputValidationContext): AgentProviderOperationResult;
|
package/dist/outputValidation.js
CHANGED
|
@@ -17,9 +17,9 @@ function isItemKindCapabilitySupported(capabilities, itemKind) {
|
|
|
17
17
|
case "file_change":
|
|
18
18
|
return capabilities.output.fileChanges === "structured";
|
|
19
19
|
case "mcp_tool_call":
|
|
20
|
-
return capabilities.
|
|
20
|
+
return capabilities.integrations.kind === "supported";
|
|
21
21
|
case "collaboration_tool_call":
|
|
22
|
-
return capabilities.
|
|
22
|
+
return capabilities.collaboration.kind === "supported";
|
|
23
23
|
case "user_message":
|
|
24
24
|
case "assistant_message":
|
|
25
25
|
case "reasoning":
|
|
@@ -102,9 +102,20 @@ function isEventCapabilitySupported(capabilities, event) {
|
|
|
102
102
|
return capabilities.output.artifactKinds.includes(
|
|
103
103
|
event.payload.artifact.kind
|
|
104
104
|
);
|
|
105
|
+
case "operation.updated":
|
|
106
|
+
return capabilities.operations.kind === "supported";
|
|
107
|
+
case "collaboration.updated":
|
|
108
|
+
return capabilities.collaboration.kind === "supported" && capabilities.collaboration.roles.includes(event.payload.node.role);
|
|
109
|
+
case "resource.updated":
|
|
110
|
+
return capabilities.generatedResources.kind === "supported" && capabilities.generatedResources.resourceKinds.includes(
|
|
111
|
+
event.payload.resource.kind
|
|
112
|
+
) && (event.payload.resource.byteSize === void 0 || event.payload.resource.byteSize <= capabilities.generatedResources.maxBytesPerResource);
|
|
105
113
|
case "request.opened":
|
|
106
|
-
|
|
107
|
-
(
|
|
114
|
+
if (event.payload.request.requestKind === "approval") {
|
|
115
|
+
return isApprovalRequestCapabilitySupported(capabilities, event);
|
|
116
|
+
}
|
|
117
|
+
return capabilities.requests.elicitation.kind === "supported" && event.payload.request.fields.length <= capabilities.requests.elicitation.maxFields && event.payload.request.fields.every(
|
|
118
|
+
(field) => capabilities.requests.elicitation.kind === "supported" && capabilities.requests.elicitation.fieldKinds.includes(field.kind) && (field.sensitivity === "ordinary" || capabilities.requests.elicitation.sensitiveFields)
|
|
108
119
|
);
|
|
109
120
|
case "context.usage.updated":
|
|
110
121
|
return isContextUsageCapabilitySupported(capabilities, event);
|
|
@@ -139,6 +150,33 @@ function assertOutputCapability(context, output) {
|
|
|
139
150
|
`Provider ${context.providerKey} emitted ${output.event.type} without advertising support.`
|
|
140
151
|
);
|
|
141
152
|
}
|
|
153
|
+
if (output.kind === "event" && output.event.type === "operation.updated") {
|
|
154
|
+
if (context.operationInvocationId !== void 0 && output.event.payload.result.invocationId !== context.operationInvocationId) {
|
|
155
|
+
throwAgentProviderContractError(
|
|
156
|
+
context.providerKey,
|
|
157
|
+
"output_operation_mismatch",
|
|
158
|
+
`Provider ${context.providerKey} emitted an update for another operation invocation.`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (output.kind === "event" && output.event.type === "collaboration.updated") {
|
|
163
|
+
if (context.collaborationId !== void 0 && output.event.payload.node.collaborationId !== context.collaborationId) {
|
|
164
|
+
throwAgentProviderContractError(
|
|
165
|
+
context.providerKey,
|
|
166
|
+
"output_collaboration_mismatch",
|
|
167
|
+
`Provider ${context.providerKey} emitted an update for another collaboration.`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (output.kind === "event" && output.event.type === "resource.updated") {
|
|
172
|
+
if (context.generatedResourceId !== void 0 && output.event.payload.resource.resourceId !== context.generatedResourceId) {
|
|
173
|
+
throwAgentProviderContractError(
|
|
174
|
+
context.providerKey,
|
|
175
|
+
"output_resource_mismatch",
|
|
176
|
+
`Provider ${context.providerKey} emitted an update for another generated resource.`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
142
180
|
}
|
|
143
181
|
function validateAgentProviderOutputForContext(candidate, context) {
|
|
144
182
|
const output = validateAgentProviderOutput(candidate);
|