ai-workflow-contract 0.1.9 → 0.2.1
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 +2 -2
- package/src/agent-code.ts +46 -0
- package/src/index.ts +56 -3
- package/src/resource-kind-contracts.ts +413 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-workflow-contract",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared AI workflow contracts over WorkCtrlFlow and EagerDataFlow substrates",
|
|
6
6
|
"type": "module",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"eager-data-flow-contract": "0.1.4",
|
|
13
|
-
"halfcode-compiler.xnl": "0.
|
|
13
|
+
"halfcode-compiler.xnl": "0.3.1",
|
|
14
14
|
"task-manager-contract": "0.1.3",
|
|
15
15
|
"work-ctrl-flow-contract": "0.1.3"
|
|
16
16
|
},
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { CodeBinding, FrozenCodeClosure } from 'halfcode-compiler.xnl/application-assembly';
|
|
2
|
+
import type { ResourceRecord, Sha256Digest } from 'halfcode-compiler.xnl/resource-core';
|
|
3
|
+
import type { FlowClosedObject } from 'work-ctrl-flow-contract';
|
|
4
|
+
import type { AIWorkflowTypedResourceRef } from './index.js';
|
|
5
|
+
|
|
6
|
+
export type AIAgentCodeResourceKind = 'AgentContextPipeline' | 'AgentMessageSource';
|
|
7
|
+
/** Closed identity derived from the actual native resolver output. */
|
|
8
|
+
export interface AIAgentResolvedResourceIdentity {
|
|
9
|
+
readonly resourceId: string;
|
|
10
|
+
readonly readerId: string;
|
|
11
|
+
readonly readerSpecVersion: number;
|
|
12
|
+
readonly effectiveContentDigest: Sha256Digest;
|
|
13
|
+
readonly readerProfileId: string;
|
|
14
|
+
readonly resolutionReceiptDigest: Sha256Digest;
|
|
15
|
+
}
|
|
16
|
+
interface AIAgentCodeProjectionBase {
|
|
17
|
+
readonly kind: AIAgentCodeResourceKind;
|
|
18
|
+
readonly resource: ResourceRecord;
|
|
19
|
+
readonly config: FlowClosedObject;
|
|
20
|
+
readonly assets: Readonly<Record<string, 'text' | 'json'>>;
|
|
21
|
+
readonly dependencies: readonly AIWorkflowTypedResourceRef[];
|
|
22
|
+
}
|
|
23
|
+
export type AIAgentCodeResourceProjection = AIAgentCodeProjectionBase & (
|
|
24
|
+
| { readonly mode: 'native'; readonly binding: CodeBinding }
|
|
25
|
+
| { readonly mode: 'legacy'; readonly content: string }
|
|
26
|
+
);
|
|
27
|
+
export interface FrozenAIAgentCodeArtifact {
|
|
28
|
+
readonly resourceResolution?: AIAgentResolvedResourceIdentity;
|
|
29
|
+
readonly schemaVersion: 'ai-workflow.agent-code-artifact/v1';
|
|
30
|
+
readonly resourceId: string;
|
|
31
|
+
readonly resourceDigest: Sha256Digest;
|
|
32
|
+
readonly config: FlowClosedObject;
|
|
33
|
+
readonly closure: FrozenCodeClosure;
|
|
34
|
+
}
|
|
35
|
+
export interface AIAgentCodeExecutionIdentity {
|
|
36
|
+
readonly resourceId: string;
|
|
37
|
+
readonly resourceDigest: Sha256Digest;
|
|
38
|
+
readonly artifactDigest: Sha256Digest;
|
|
39
|
+
readonly closureDigest: string;
|
|
40
|
+
}
|
|
41
|
+
/** Runtime handle. Persist artifact/identity, never the callable. */
|
|
42
|
+
export interface CompiledAIAgentCodeExecution {
|
|
43
|
+
readonly artifact: FrozenAIAgentCodeArtifact;
|
|
44
|
+
readonly artifactDigest: Sha256Digest;
|
|
45
|
+
readonly execute: (runtime: unknown, input: unknown) => unknown;
|
|
46
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,8 @@ import type {
|
|
|
4
4
|
ResourceDependencySnapshot,
|
|
5
5
|
ResourceRecord,
|
|
6
6
|
ResourceRegistry,
|
|
7
|
-
|
|
7
|
+
AuthoredResourceTree,
|
|
8
|
+
ResolvedResourceTree,
|
|
8
9
|
ResourceValue,
|
|
9
10
|
Sha256Digest,
|
|
10
11
|
} from 'halfcode-compiler.xnl/resource-core';
|
|
@@ -22,6 +23,8 @@ import type {
|
|
|
22
23
|
} from 'work-ctrl-flow-contract';
|
|
23
24
|
|
|
24
25
|
export * from './holon-task.js';
|
|
26
|
+
export * from './resource-kind-contracts.js';
|
|
27
|
+
export * from './agent-code.js';
|
|
25
28
|
|
|
26
29
|
export {
|
|
27
30
|
FLOW_INSTANCE_SCHEMA_VERSION,
|
|
@@ -148,7 +151,8 @@ export interface ProjectAIWorkflowAppBundlesOptions {
|
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
export interface LoadedAIWorkflowResourcePackage {
|
|
151
|
-
readonly
|
|
154
|
+
readonly authoredTree: AuthoredResourceTree;
|
|
155
|
+
readonly tree: ResolvedResourceTree;
|
|
152
156
|
readonly appBundles: readonly AIWorkflowAppBundleProjection[];
|
|
153
157
|
}
|
|
154
158
|
|
|
@@ -257,6 +261,7 @@ export interface MaterialBindingProjection {
|
|
|
257
261
|
}
|
|
258
262
|
|
|
259
263
|
export interface AIWorkflowAgentResourceProjection {
|
|
264
|
+
readonly codeResources?: readonly import('./agent-code.js').AIAgentCodeResourceProjection[];
|
|
260
265
|
readonly agentDefinitions: readonly AIAgentDefinitionProjection[];
|
|
261
266
|
readonly materialPorts: readonly MaterialPortProjection[];
|
|
262
267
|
readonly materialBindings: readonly MaterialBindingProjection[];
|
|
@@ -264,6 +269,8 @@ export interface AIWorkflowAgentResourceProjection {
|
|
|
264
269
|
}
|
|
265
270
|
|
|
266
271
|
export interface AIWorkflowRunResourceFreezeReceipt {
|
|
272
|
+
readonly resourceResolutions?: readonly import('./agent-code.js').AIAgentResolvedResourceIdentity[];
|
|
273
|
+
readonly codeExecutions?: readonly import('./agent-code.js').AIAgentCodeExecutionIdentity[];
|
|
267
274
|
readonly schemaVersion: 'ai-workflow.run-resource-freeze/v1';
|
|
268
275
|
readonly task: AIWorkflowAgentTaskRef;
|
|
269
276
|
readonly bindingResourceIds: readonly string[];
|
|
@@ -273,6 +280,8 @@ export interface AIWorkflowRunResourceFreezeReceipt {
|
|
|
273
280
|
|
|
274
281
|
/** In-memory proof projected only from an admitted run-resource freeze receipt. */
|
|
275
282
|
export interface FrozenAIAgentTaskBinding {
|
|
283
|
+
readonly resourceResolutions?: readonly import('./agent-code.js').AIAgentResolvedResourceIdentity[];
|
|
284
|
+
readonly codeExecutions?: readonly import('./agent-code.js').AIAgentCodeExecutionIdentity[];
|
|
276
285
|
readonly schemaVersion: 'ai-workflow.frozen-agent-task-binding/v1';
|
|
277
286
|
readonly task: AIWorkflowAgentTaskRef;
|
|
278
287
|
readonly semanticFingerprint: Sha256Digest;
|
|
@@ -320,6 +329,13 @@ export interface AIAgentDefinitionAuthoringEvidence {
|
|
|
320
329
|
readonly planDigest: Sha256Digest;
|
|
321
330
|
readonly transactionId: string;
|
|
322
331
|
readonly receiptDigest: Sha256Digest;
|
|
332
|
+
readonly revision?: {
|
|
333
|
+
readonly candidateRef: `resource://${string}`;
|
|
334
|
+
readonly candidateDigest: Sha256Digest;
|
|
335
|
+
readonly contentDigest: Sha256Digest;
|
|
336
|
+
readonly registryRevision: Sha256Digest;
|
|
337
|
+
readonly feedback: AIAgentDefinitionRevisionFeedback;
|
|
338
|
+
};
|
|
323
339
|
}
|
|
324
340
|
|
|
325
341
|
export interface AIAgentDefinitionCandidate {
|
|
@@ -360,9 +376,30 @@ export interface AuthorNewAIAgentDefinitionDecision
|
|
|
360
376
|
readonly proposal: ResourceAuthoringProposal;
|
|
361
377
|
}
|
|
362
378
|
|
|
379
|
+
/** Upstream causal references, never diagnostic or publication authority. */
|
|
380
|
+
export interface AIAgentDefinitionRevisionFeedback {
|
|
381
|
+
readonly observationRef: string;
|
|
382
|
+
readonly attemptRef: string;
|
|
383
|
+
readonly verificationRef: string;
|
|
384
|
+
readonly requirementDigest: Sha256Digest;
|
|
385
|
+
readonly candidateSetDigest: Sha256Digest;
|
|
386
|
+
readonly candidateDigest: Sha256Digest;
|
|
387
|
+
readonly previousExecutionFingerprint: Sha256Digest;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export interface ReviseExistingAIAgentDefinitionDecision
|
|
391
|
+
extends AIAgentDefinitionSelectionDecisionBase {
|
|
392
|
+
readonly mode: 'revise-existing';
|
|
393
|
+
readonly candidateRef: `resource://${string}`;
|
|
394
|
+
readonly candidateDigest: Sha256Digest;
|
|
395
|
+
readonly feedback: AIAgentDefinitionRevisionFeedback;
|
|
396
|
+
readonly proposal: ResourceAuthoringProposal;
|
|
397
|
+
}
|
|
398
|
+
|
|
363
399
|
export type AIAgentDefinitionSelectionDecision =
|
|
364
400
|
| SelectExistingAIAgentDefinitionDecision
|
|
365
|
-
| AuthorNewAIAgentDefinitionDecision
|
|
401
|
+
| AuthorNewAIAgentDefinitionDecision
|
|
402
|
+
| ReviseExistingAIAgentDefinitionDecision;
|
|
366
403
|
|
|
367
404
|
export type AIAgentDefinitionSelectionRejectionCode =
|
|
368
405
|
| 'decision-invalid'
|
|
@@ -372,6 +409,9 @@ export type AIAgentDefinitionSelectionRejectionCode =
|
|
|
372
409
|
| 'candidate-digest-mismatch'
|
|
373
410
|
| 'candidate-incompatible'
|
|
374
411
|
| 'authoring-in-progress'
|
|
412
|
+
| 'selection-owner-required'
|
|
413
|
+
| 'selection-owner-mismatch'
|
|
414
|
+
| 'revision-feedback-invalid'
|
|
375
415
|
| 'authoring-proposal-invalid';
|
|
376
416
|
|
|
377
417
|
export interface SelectedAIAgentDefinitionAdmission {
|
|
@@ -393,6 +433,18 @@ export interface AuthorNewAIAgentDefinitionAdmission {
|
|
|
393
433
|
readonly reason: string;
|
|
394
434
|
}
|
|
395
435
|
|
|
436
|
+
export interface ReviseExistingAIAgentDefinitionAdmission {
|
|
437
|
+
readonly schemaVersion: typeof AI_AGENT_DEFINITION_SELECTION_SCHEMA_VERSION;
|
|
438
|
+
readonly status: 'revision-request';
|
|
439
|
+
readonly requirementDigest: Sha256Digest;
|
|
440
|
+
readonly candidateSetDigest: Sha256Digest;
|
|
441
|
+
readonly agentDefinitionRef: `resource://${string}`;
|
|
442
|
+
readonly candidate: AIAgentDefinitionCandidate;
|
|
443
|
+
readonly feedback: AIAgentDefinitionRevisionFeedback;
|
|
444
|
+
readonly proposal: ResourceAuthoringProposal;
|
|
445
|
+
readonly reason: string;
|
|
446
|
+
}
|
|
447
|
+
|
|
396
448
|
export interface RejectedAIAgentDefinitionAdmission {
|
|
397
449
|
readonly schemaVersion: typeof AI_AGENT_DEFINITION_SELECTION_SCHEMA_VERSION;
|
|
398
450
|
readonly status: 'rejected';
|
|
@@ -403,6 +455,7 @@ export interface RejectedAIAgentDefinitionAdmission {
|
|
|
403
455
|
export type AIAgentDefinitionSelectionAdmission =
|
|
404
456
|
| SelectedAIAgentDefinitionAdmission
|
|
405
457
|
| AuthorNewAIAgentDefinitionAdmission
|
|
458
|
+
| ReviseExistingAIAgentDefinitionAdmission
|
|
406
459
|
| RejectedAIAgentDefinitionAdmission;
|
|
407
460
|
|
|
408
461
|
export type AIWorkflowNodeType =
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import {
|
|
2
|
+
digestCanonical,
|
|
3
|
+
type JsonSchema,
|
|
4
|
+
type KindReaderRegistration,
|
|
5
|
+
type KindSemanticContract,
|
|
6
|
+
type KindSpecRevision,
|
|
7
|
+
type KindSubjectOwner,
|
|
8
|
+
type PortableSpec,
|
|
9
|
+
type SpecResolutionDefinition,
|
|
10
|
+
} from 'halfcode-compiler.xnl/resource-core';
|
|
11
|
+
import {
|
|
12
|
+
createKindSpecRevision,
|
|
13
|
+
createKindSubjectOwner,
|
|
14
|
+
} from 'halfcode-compiler.xnl/kind-definition';
|
|
15
|
+
|
|
16
|
+
export const DEPA_AI_RESOURCE_KINDS = Object.freeze([
|
|
17
|
+
'AIAgentDefinition',
|
|
18
|
+
'AgentMessageSource',
|
|
19
|
+
'AgentContextPipeline',
|
|
20
|
+
'Prompt',
|
|
21
|
+
'Tool',
|
|
22
|
+
'MessageSchema',
|
|
23
|
+
'EffectPolicy',
|
|
24
|
+
'MaterialPort',
|
|
25
|
+
'MaterialBinding',
|
|
26
|
+
'RequestMaterial',
|
|
27
|
+
'ContextMaterial',
|
|
28
|
+
'AIWorkflowAppBundle',
|
|
29
|
+
'AICtrlWorkflow',
|
|
30
|
+
'AIDataWorkflow',
|
|
31
|
+
] as const);
|
|
32
|
+
|
|
33
|
+
export type DepaAIResourceKind = (typeof DEPA_AI_RESOURCE_KINDS)[number];
|
|
34
|
+
|
|
35
|
+
export const DEPA_AI_RESOURCE_ENVELOPE_VERSION = 'halfcode.resource-envelope/v1' as const;
|
|
36
|
+
export const DEPA_AI_RESOURCE_SPEC_VERSION = 1 as const;
|
|
37
|
+
export const DEPA_AI_RESOURCE_KIND_OWNER_PACKAGE_ID = 'ai-workflow-contract' as const;
|
|
38
|
+
export const DEPA_AI_RESOURCE_SEMANTIC_API_VERSION = 'depa.flows/v1' as const;
|
|
39
|
+
|
|
40
|
+
const subjectFqnFor = (kind: DepaAIResourceKind): `Depa.Flows.KindDefinition.${DepaAIResourceKind}` =>
|
|
41
|
+
`Depa.Flows.KindDefinition.${kind}`;
|
|
42
|
+
|
|
43
|
+
export const DEPA_AI_RESOURCE_KIND_OWNER_PACKAGE_FINGERPRINT = digestCanonical(Object.freeze({
|
|
44
|
+
authority: 'ai-workflow-contract/depa-ai-resource-kinds/v1',
|
|
45
|
+
ownerPackageId: DEPA_AI_RESOURCE_KIND_OWNER_PACKAGE_ID,
|
|
46
|
+
subjects: Object.freeze(DEPA_AI_RESOURCE_KINDS.map(subjectFqnFor).sort(compareCodeUnits)),
|
|
47
|
+
semanticContractVersion: DEPA_AI_RESOURCE_SEMANTIC_API_VERSION,
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
interface KindSchemaShape {
|
|
51
|
+
readonly requiredProperties?: readonly string[];
|
|
52
|
+
readonly requiredSubdomains?: readonly string[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const KIND_SCHEMA_SHAPES: Readonly<Record<DepaAIResourceKind, KindSchemaShape>> = Object.freeze({
|
|
56
|
+
AIAgentDefinition: Object.freeze({ requiredSubdomains: Object.freeze(['ToolRefs', 'MaterialPortRefs']) }),
|
|
57
|
+
AgentMessageSource: Object.freeze({ requiredSubdomains: Object.freeze(['Content']) }),
|
|
58
|
+
AgentContextPipeline: Object.freeze({ requiredSubdomains: Object.freeze(['Content']) }),
|
|
59
|
+
Prompt: Object.freeze({ requiredProperties: Object.freeze(['template']) }),
|
|
60
|
+
Tool: Object.freeze({}),
|
|
61
|
+
MessageSchema: Object.freeze({ requiredProperties: Object.freeze(['schema']) }),
|
|
62
|
+
EffectPolicy: Object.freeze({ requiredProperties: Object.freeze(['toolMode']) }),
|
|
63
|
+
MaterialPort: Object.freeze({ requiredProperties: Object.freeze(['materialKind', 'required', 'cardinality']) }),
|
|
64
|
+
MaterialBinding: Object.freeze({ requiredSubdomains: Object.freeze(['AgentTaskRef', 'PortRef', 'MaterialRef']) }),
|
|
65
|
+
RequestMaterial: Object.freeze({ requiredProperties: Object.freeze(['value']) }),
|
|
66
|
+
ContextMaterial: Object.freeze({ requiredProperties: Object.freeze(['value']) }),
|
|
67
|
+
AIWorkflowAppBundle: Object.freeze({ requiredSubdomains: Object.freeze(['WorkflowBindings']) }),
|
|
68
|
+
AICtrlWorkflow: Object.freeze({}),
|
|
69
|
+
AIDataWorkflow: Object.freeze({}),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const KIND_SEMANTIC_OPERATIONS: Readonly<Record<DepaAIResourceKind, readonly string[]>> = Object.freeze({
|
|
73
|
+
AIAgentDefinition: Object.freeze(['projectAIWorkflowAgentResources', 'freezeAIAgentTaskResources']),
|
|
74
|
+
AgentMessageSource: Object.freeze(['projectAIWorkflowAgentResources', 'compileAgentMessageSource']),
|
|
75
|
+
AgentContextPipeline: Object.freeze(['projectAIWorkflowAgentResources', 'compileAgentContextPipeline']),
|
|
76
|
+
Prompt: Object.freeze(['projectAIWorkflowAgentResources', 'compileAgentMessagePrefix']),
|
|
77
|
+
Tool: Object.freeze(['projectAIWorkflowAgentResources', 'freezeAIAgentTaskResources']),
|
|
78
|
+
MessageSchema: Object.freeze(['projectAIWorkflowAgentResources', 'validateExecutableMessageSchema']),
|
|
79
|
+
EffectPolicy: Object.freeze(['projectAIWorkflowAgentResources', 'validateExecutableEffectPolicy']),
|
|
80
|
+
MaterialPort: Object.freeze(['projectAIWorkflowAgentResources', 'validateMaterialPortBindings']),
|
|
81
|
+
MaterialBinding: Object.freeze(['projectAIWorkflowAgentResources', 'freezeAIAgentTaskResources']),
|
|
82
|
+
RequestMaterial: Object.freeze(['projectAIWorkflowAgentResources', 'projectMaterialValue']),
|
|
83
|
+
ContextMaterial: Object.freeze(['projectAIWorkflowAgentResources', 'projectMaterialValue']),
|
|
84
|
+
AIWorkflowAppBundle: Object.freeze(['projectAIWorkflowAppBundles']),
|
|
85
|
+
AICtrlWorkflow: Object.freeze(['loadAICtrlWorkflowSources', 'projectAIWorkflowAppBundles']),
|
|
86
|
+
AIDataWorkflow: Object.freeze(['loadAIDataWorkflowSources', 'projectAIWorkflowAppBundles']),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const KIND_REFERENCE_ROLES: Readonly<Record<DepaAIResourceKind, readonly string[]>> = Object.freeze({
|
|
90
|
+
AIAgentDefinition: Object.freeze([
|
|
91
|
+
'message-prompt',
|
|
92
|
+
'message-schema',
|
|
93
|
+
'message-source',
|
|
94
|
+
'context-pipeline',
|
|
95
|
+
'input-schema',
|
|
96
|
+
'output-schema',
|
|
97
|
+
'tool',
|
|
98
|
+
'effect-policy',
|
|
99
|
+
'material-port',
|
|
100
|
+
]),
|
|
101
|
+
AgentMessageSource: Object.freeze([]),
|
|
102
|
+
AgentContextPipeline: Object.freeze([]),
|
|
103
|
+
Prompt: Object.freeze([]),
|
|
104
|
+
Tool: Object.freeze([]),
|
|
105
|
+
MessageSchema: Object.freeze([]),
|
|
106
|
+
EffectPolicy: Object.freeze([]),
|
|
107
|
+
MaterialPort: Object.freeze(['schema']),
|
|
108
|
+
MaterialBinding: Object.freeze(['agent-task', 'material-port', 'material-value']),
|
|
109
|
+
RequestMaterial: Object.freeze([]),
|
|
110
|
+
ContextMaterial: Object.freeze([]),
|
|
111
|
+
AIWorkflowAppBundle: Object.freeze(['workflow-binding']),
|
|
112
|
+
AICtrlWorkflow: Object.freeze([]),
|
|
113
|
+
AIDataWorkflow: Object.freeze([]),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export interface DepaAIResourceKindContract {
|
|
117
|
+
readonly kind: DepaAIResourceKind;
|
|
118
|
+
readonly subjectFqn: `Depa.Flows.KindDefinition.${DepaAIResourceKind}`;
|
|
119
|
+
readonly owner: KindSubjectOwner;
|
|
120
|
+
readonly revision: KindSpecRevision;
|
|
121
|
+
readonly reader: KindReaderRegistration<PortableSpec>;
|
|
122
|
+
readonly kindDefinitionSource: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function authoredSpecSchema(kind: DepaAIResourceKind): JsonSchema {
|
|
126
|
+
const shape = KIND_SCHEMA_SHAPES[kind];
|
|
127
|
+
const propertiesSchema = Object.freeze({
|
|
128
|
+
type: 'object',
|
|
129
|
+
...(shape.requiredProperties && shape.requiredProperties.length > 0
|
|
130
|
+
? { required: Object.freeze([...shape.requiredProperties]) }
|
|
131
|
+
: {}),
|
|
132
|
+
});
|
|
133
|
+
const subdomainsSchema = Object.freeze({
|
|
134
|
+
type: 'object',
|
|
135
|
+
...(shape.requiredSubdomains && shape.requiredSubdomains.length > 0
|
|
136
|
+
? { required: Object.freeze([...shape.requiredSubdomains]) }
|
|
137
|
+
: {}),
|
|
138
|
+
});
|
|
139
|
+
return Object.freeze({
|
|
140
|
+
type: 'object',
|
|
141
|
+
required: Object.freeze(['properties', 'body', 'subdomains']),
|
|
142
|
+
properties: Object.freeze({
|
|
143
|
+
properties: propertiesSchema,
|
|
144
|
+
body: Object.freeze({ type: 'array' }),
|
|
145
|
+
subdomains: subdomainsSchema,
|
|
146
|
+
text: Object.freeze({ type: 'string' }),
|
|
147
|
+
}),
|
|
148
|
+
additionalProperties: false,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Native code recipes have a separate writer contract; legacy Content stays spec 1. */
|
|
153
|
+
function nativeCodeSpecSchema(): JsonSchema {
|
|
154
|
+
const string = Object.freeze({ type: 'string', minLength: 1, pattern: '^\\S(?:[\\s\\S]*\\S)?$' });
|
|
155
|
+
const emptyObject = Object.freeze({ type: 'object', properties: Object.freeze({}), additionalProperties: false });
|
|
156
|
+
const emptyBody = Object.freeze({ type: 'array', maxItems: 0 });
|
|
157
|
+
const node = (tag: string, properties: JsonSchema, body: JsonSchema = emptyBody): JsonSchema => Object.freeze({
|
|
158
|
+
type: 'object',
|
|
159
|
+
required: Object.freeze(['tag', 'metadata', 'properties', 'body', 'subdomains']),
|
|
160
|
+
properties: Object.freeze({
|
|
161
|
+
tag: Object.freeze({ const: tag }),
|
|
162
|
+
resourceId: string,
|
|
163
|
+
metadata: emptyObject,
|
|
164
|
+
properties,
|
|
165
|
+
body,
|
|
166
|
+
subdomains: emptyObject,
|
|
167
|
+
}),
|
|
168
|
+
additionalProperties: false,
|
|
169
|
+
});
|
|
170
|
+
const closedProperties = (properties: Readonly<Record<string, JsonSchema>>, required = Object.keys(properties)): JsonSchema => Object.freeze({
|
|
171
|
+
type: 'object', required: Object.freeze(required), properties: Object.freeze(properties), additionalProperties: false,
|
|
172
|
+
});
|
|
173
|
+
return Object.freeze({
|
|
174
|
+
type: 'object',
|
|
175
|
+
required: Object.freeze(['properties', 'body', 'subdomains']),
|
|
176
|
+
properties: Object.freeze({
|
|
177
|
+
properties: closedProperties({ lifecycle: string, description: Object.freeze({ type: 'string' }) }, []),
|
|
178
|
+
body: emptyBody,
|
|
179
|
+
subdomains: closedProperties({
|
|
180
|
+
CodeBinding: node('CodeBinding', closedProperties({ packageName: string, module: string, exportName: string })),
|
|
181
|
+
Config: node('Config', closedProperties({ value: Object.freeze({ type: 'object' }) })),
|
|
182
|
+
Assets: node('Assets', emptyObject, Object.freeze({
|
|
183
|
+
type: 'array',
|
|
184
|
+
items: node('Asset', closedProperties({ path: string, format: Object.freeze({ enum: Object.freeze(['text', 'json']) }) })),
|
|
185
|
+
})),
|
|
186
|
+
DependencyRefs: node('DependencyRefs', emptyObject, Object.freeze({
|
|
187
|
+
type: 'array',
|
|
188
|
+
items: node('DependencyRef', closedProperties({
|
|
189
|
+
kind: string,
|
|
190
|
+
ref: Object.freeze({ type: 'string', pattern: '^resource://\\S+$' }),
|
|
191
|
+
})),
|
|
192
|
+
})),
|
|
193
|
+
}, ['CodeBinding']),
|
|
194
|
+
}),
|
|
195
|
+
additionalProperties: false,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function codeRecipeSpecSchema(kind: DepaAIResourceKind): JsonSchema {
|
|
200
|
+
const legacy = authoredSpecSchema(kind) as Readonly<Record<string, unknown>>;
|
|
201
|
+
return Object.freeze({
|
|
202
|
+
oneOf: Object.freeze([
|
|
203
|
+
nativeCodeSpecSchema(),
|
|
204
|
+
Object.freeze({
|
|
205
|
+
...legacy,
|
|
206
|
+
properties: Object.freeze({
|
|
207
|
+
...(legacy.properties as Readonly<Record<string, unknown>>),
|
|
208
|
+
subdomains: Object.freeze({
|
|
209
|
+
type: 'object', required: Object.freeze(['Content']),
|
|
210
|
+
properties: Object.freeze({ CodeBinding: false }),
|
|
211
|
+
}),
|
|
212
|
+
}),
|
|
213
|
+
}),
|
|
214
|
+
]),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function nativeCodeSemanticContract(kind: DepaAIResourceKind): KindSemanticContract {
|
|
219
|
+
const authority = 'ai-workflow-contract/depa-ai-resource-kinds/v2';
|
|
220
|
+
return Object.freeze({
|
|
221
|
+
semanticValidatorFingerprint: digestCanonical(Object.freeze({
|
|
222
|
+
authority, kind, role: 'closed-native-code-recipe-validation',
|
|
223
|
+
fields: Object.freeze(['CodeBinding', 'Config', 'Assets', 'DependencyRefs']),
|
|
224
|
+
config: 'closed-json-object', assets: 'source-root-relative-text-or-json',
|
|
225
|
+
legacy: 'exclusive-Content-requires-explicit-compatibility-resolver',
|
|
226
|
+
})),
|
|
227
|
+
referenceProjectionFingerprint: digestCanonical(Object.freeze({
|
|
228
|
+
authority, kind, role: 'typed-resource-reference-projection', relations: Object.freeze(['code-dependency']),
|
|
229
|
+
})),
|
|
230
|
+
compilerInputFingerprint: digestCanonical(Object.freeze({
|
|
231
|
+
authority, kind, role: 'native-code-binding-and-frozen-closure-projection',
|
|
232
|
+
fields: Object.freeze(['packageName', 'module', 'exportName', 'config', 'assets', 'dependencyRefs']),
|
|
233
|
+
})),
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function semanticContract(kind: DepaAIResourceKind): KindSemanticContract {
|
|
238
|
+
return Object.freeze({
|
|
239
|
+
semanticValidatorFingerprint: digestCanonical(Object.freeze({
|
|
240
|
+
authority: 'ai-workflow-contract/depa-ai-resource-kinds/v1',
|
|
241
|
+
kind,
|
|
242
|
+
role: 'closed-domain-semantic-validation',
|
|
243
|
+
operations: KIND_SEMANTIC_OPERATIONS[kind],
|
|
244
|
+
})),
|
|
245
|
+
referenceProjectionFingerprint: digestCanonical(Object.freeze({
|
|
246
|
+
authority: 'ai-workflow-contract/depa-ai-resource-kinds/v1',
|
|
247
|
+
kind,
|
|
248
|
+
role: 'typed-resource-reference-projection',
|
|
249
|
+
relations: KIND_REFERENCE_ROLES[kind],
|
|
250
|
+
})),
|
|
251
|
+
compilerInputFingerprint: digestCanonical(Object.freeze({
|
|
252
|
+
authority: 'ai-workflow-contract/depa-ai-resource-kinds/v1',
|
|
253
|
+
kind,
|
|
254
|
+
role: 'authored-spec-envelope-projection',
|
|
255
|
+
fields: Object.freeze(['properties', 'body', 'subdomains', 'text']),
|
|
256
|
+
})),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function readerRegistration(
|
|
261
|
+
kind: DepaAIResourceKind,
|
|
262
|
+
revision: KindSpecRevision,
|
|
263
|
+
): KindReaderRegistration<PortableSpec> {
|
|
264
|
+
const readerId = `ai-workflow-contract.${kind}.reader.v${revision.specVersion}`;
|
|
265
|
+
return Object.freeze({
|
|
266
|
+
readerId,
|
|
267
|
+
subjectFqn: revision.subjectFqn,
|
|
268
|
+
readerSpecVersion: revision.specVersion,
|
|
269
|
+
contractFingerprint: revision.contractFingerprint,
|
|
270
|
+
readerImplementationFingerprint: digestCanonical(Object.freeze({
|
|
271
|
+
authority: `ai-workflow-contract/depa-ai-resource-kinds/v${revision.specVersion}`,
|
|
272
|
+
readerId,
|
|
273
|
+
kind,
|
|
274
|
+
contractFingerprint: revision.contractFingerprint,
|
|
275
|
+
operations: KIND_SEMANTIC_OPERATIONS[kind],
|
|
276
|
+
implementation: 'identity-read-of-halfcode-validated-portable-spec',
|
|
277
|
+
})),
|
|
278
|
+
compatibilityPolicy: revision.specVersion === 1 ? 'exact' : 'backward-transitive',
|
|
279
|
+
read: (spec: PortableSpec): PortableSpec => spec,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function renderKindDefinition(owner: KindSubjectOwner, revisions: readonly KindSpecRevision[]): string {
|
|
284
|
+
return [
|
|
285
|
+
`<KindDefinition #${owner.subjectFqn} envelopeVersion="${DEPA_AI_RESOURCE_ENVELOPE_VERSION}" specVersion=1 {`,
|
|
286
|
+
' lifecycle = "Stable"',
|
|
287
|
+
` resourceKind = "${owner.kind}"`,
|
|
288
|
+
` subjectFqn = "${owner.subjectFqn}"`,
|
|
289
|
+
' sourceShapes = ["single-file"]',
|
|
290
|
+
` documentCardinality = "${owner.sourceContract.documentCardinality}"`,
|
|
291
|
+
` description = "Canonical depa-flows ${owner.kind} resource contract."`,
|
|
292
|
+
'} (',
|
|
293
|
+
' <SpecRevisions [',
|
|
294
|
+
...revisions.flatMap((revision) => [
|
|
295
|
+
` <SpecRevision #v${revision.specVersion} {`,
|
|
296
|
+
` specVersion = ${revision.specVersion}`,
|
|
297
|
+
` schemaRef = "vfs://./spec-v${revision.specVersion}.schema.json"`,
|
|
298
|
+
` schemaFingerprint = "${revision.schemaFingerprint}"`,
|
|
299
|
+
` contractFingerprint = "${revision.contractFingerprint}"`,
|
|
300
|
+
` semanticValidatorFingerprint = "${revision.semanticContract.semanticValidatorFingerprint}"`,
|
|
301
|
+
` referenceProjectionFingerprint = "${revision.semanticContract.referenceProjectionFingerprint}"`,
|
|
302
|
+
` compilerInputFingerprint = "${revision.semanticContract.compilerInputFingerprint}"`,
|
|
303
|
+
` stability = "${revision.stability}"`,
|
|
304
|
+
' }>',
|
|
305
|
+
]),
|
|
306
|
+
' ]>',
|
|
307
|
+
')>',
|
|
308
|
+
'',
|
|
309
|
+
].join('\n');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function createContract(kind: DepaAIResourceKind): DepaAIResourceKindContract {
|
|
313
|
+
const subjectFqn = subjectFqnFor(kind);
|
|
314
|
+
const owner = createKindSubjectOwner({
|
|
315
|
+
kind,
|
|
316
|
+
subjectFqn,
|
|
317
|
+
ownerPackageId: DEPA_AI_RESOURCE_KIND_OWNER_PACKAGE_ID,
|
|
318
|
+
ownerPackageFingerprint: DEPA_AI_RESOURCE_KIND_OWNER_PACKAGE_FINGERPRINT,
|
|
319
|
+
sourceShapes: Object.freeze(['single-file']),
|
|
320
|
+
documentCardinality: 'one',
|
|
321
|
+
});
|
|
322
|
+
const revision = createKindSpecRevision({
|
|
323
|
+
kind,
|
|
324
|
+
subjectFqn,
|
|
325
|
+
specVersion: DEPA_AI_RESOURCE_SPEC_VERSION,
|
|
326
|
+
specSchema: authoredSpecSchema(kind),
|
|
327
|
+
semanticContract: semanticContract(kind),
|
|
328
|
+
sourceContractFingerprint: owner.sourceContract.sourceContractFingerprint,
|
|
329
|
+
stability: 'stable',
|
|
330
|
+
});
|
|
331
|
+
return Object.freeze({
|
|
332
|
+
kind,
|
|
333
|
+
subjectFqn,
|
|
334
|
+
owner,
|
|
335
|
+
revision,
|
|
336
|
+
reader: readerRegistration(kind, revision),
|
|
337
|
+
kindDefinitionSource: renderKindDefinition(owner, [revision]),
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const LEGACY_KIND_CONTRACTS = DEPA_AI_RESOURCE_KINDS.map(createContract);
|
|
342
|
+
|
|
343
|
+
export const DEPA_AI_RESOURCE_KIND_CONTRACTS: readonly DepaAIResourceKindContract[] = Object.freeze([
|
|
344
|
+
...LEGACY_KIND_CONTRACTS,
|
|
345
|
+
...LEGACY_KIND_CONTRACTS.filter(({ kind }) => kind === 'AgentMessageSource' || kind === 'AgentContextPipeline').map((legacy) => {
|
|
346
|
+
const { kind } = legacy;
|
|
347
|
+
const revision = createKindSpecRevision({
|
|
348
|
+
kind,
|
|
349
|
+
subjectFqn: legacy.subjectFqn,
|
|
350
|
+
specVersion: 2,
|
|
351
|
+
specSchema: codeRecipeSpecSchema(kind),
|
|
352
|
+
semanticContract: nativeCodeSemanticContract(kind),
|
|
353
|
+
sourceContractFingerprint: legacy.owner.sourceContract.sourceContractFingerprint,
|
|
354
|
+
stability: 'stable',
|
|
355
|
+
});
|
|
356
|
+
return Object.freeze({
|
|
357
|
+
...legacy,
|
|
358
|
+
revision,
|
|
359
|
+
reader: readerRegistration(kind, revision),
|
|
360
|
+
kindDefinitionSource: renderKindDefinition(legacy.owner, [legacy.revision, revision]),
|
|
361
|
+
});
|
|
362
|
+
}),
|
|
363
|
+
]);
|
|
364
|
+
|
|
365
|
+
export const DEPA_AI_RESOURCE_KIND_OWNERS: readonly KindSubjectOwner[] = Object.freeze(
|
|
366
|
+
LEGACY_KIND_CONTRACTS.map((contract) => contract.owner),
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
export const DEPA_AI_RESOURCE_KIND_SPEC_REVISIONS: readonly KindSpecRevision[] = Object.freeze(
|
|
370
|
+
DEPA_AI_RESOURCE_KIND_CONTRACTS.map((contract) => contract.revision),
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
export const DEPA_AI_RESOURCE_KIND_READER_REGISTRATIONS: readonly KindReaderRegistration<PortableSpec>[] = Object.freeze(
|
|
374
|
+
DEPA_AI_RESOURCE_KIND_CONTRACTS.map((contract) => contract.reader),
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
const CONTRACTS_BY_KIND: ReadonlyMap<string, DepaAIResourceKindContract> = new Map(
|
|
378
|
+
DEPA_AI_RESOURCE_KIND_CONTRACTS.map((contract) => [`${contract.kind}@${contract.revision.specVersion}`, contract]),
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
export function depaAIResourceKindContract(kind: DepaAIResourceKind, specVersion: number = DEPA_AI_RESOURCE_SPEC_VERSION): DepaAIResourceKindContract {
|
|
382
|
+
const contract = CONTRACTS_BY_KIND.get(`${kind}@${specVersion}`);
|
|
383
|
+
if (!contract) throw new Error(`Unknown depa AI resource Kind '${kind}' spec version '${specVersion}'.`);
|
|
384
|
+
return contract;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export const DEPA_AI_RESOURCE_KIND_SPEC_RESOLUTIONS: readonly SpecResolutionDefinition[] = Object.freeze(
|
|
388
|
+
DEPA_AI_RESOURCE_KIND_CONTRACTS.filter((contract) => contract.revision.specVersion === 2).map((contract) => {
|
|
389
|
+
const writer = depaAIResourceKindContract(contract.kind, 1);
|
|
390
|
+
const identity = Object.freeze({
|
|
391
|
+
id: `ai-workflow-contract.${contract.kind}.v1-to-v2`,
|
|
392
|
+
subjectFqn: contract.subjectFqn,
|
|
393
|
+
ownerPackageId: contract.owner.ownerPackageId,
|
|
394
|
+
ownerPackageFingerprint: contract.owner.ownerPackageFingerprint,
|
|
395
|
+
writerSpecVersion: 1,
|
|
396
|
+
readerSpecVersion: 2,
|
|
397
|
+
writerContractFingerprint: writer.revision.contractFingerprint,
|
|
398
|
+
readerContractFingerprint: contract.revision.contractFingerprint,
|
|
399
|
+
});
|
|
400
|
+
return Object.freeze({
|
|
401
|
+
...identity,
|
|
402
|
+
resolutionFingerprint: digestCanonical(Object.freeze({
|
|
403
|
+
...identity,
|
|
404
|
+
implementation: 'identity-Content-preservation-requires-explicit-legacy-code-resolver',
|
|
405
|
+
})),
|
|
406
|
+
resolve: (spec: PortableSpec): PortableSpec => spec,
|
|
407
|
+
});
|
|
408
|
+
}),
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
function compareCodeUnits(left: string, right: string): number {
|
|
412
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
413
|
+
}
|