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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-workflow-contract",
3
- "version": "0.1.9",
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.2.9",
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
- ResourceTree,
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 tree: ResourceTree;
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
+ }