ai-workflow-logic 0.2.0 → 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 +5 -4
- package/src/agent-code-brand.ts +14 -0
- package/src/agent-code-execution.ts +98 -0
- package/src/agent-code-projection.ts +98 -0
- package/src/agent-definition-selection.ts +256 -17
- package/src/agent-execution-resources.ts +160 -0
- package/src/agent-resources.ts +3 -0
- package/src/browser.ts +1 -1
- package/src/core.ts +563 -0
- package/src/index.ts +6 -563
- package/src/resource-resolution.ts +38 -14
- package/src/run-freeze.ts +80 -1
package/src/run-freeze.ts
CHANGED
|
@@ -7,6 +7,8 @@ import type {
|
|
|
7
7
|
AIWorkflowRunResourceFreezeReceipt,
|
|
8
8
|
MaterialBindingProjection,
|
|
9
9
|
MaterialPortProjection,
|
|
10
|
+
CompiledAIAgentCodeExecution,
|
|
11
|
+
AIAgentCodeExecutionIdentity,
|
|
10
12
|
} from 'ai-workflow-contract';
|
|
11
13
|
import {
|
|
12
14
|
normalizeHolonTaskTarget,
|
|
@@ -19,18 +21,24 @@ import {
|
|
|
19
21
|
composeLayeredResourceRegistry,
|
|
20
22
|
loadResourceTree,
|
|
21
23
|
sha256Digest,
|
|
24
|
+
digestCanonical,
|
|
22
25
|
type EffectiveResourceRegistry,
|
|
23
26
|
type ResourceContentIdentity,
|
|
24
27
|
type ResourceDependencyEdge,
|
|
28
|
+
type Sha256Digest,
|
|
25
29
|
} from 'halfcode-compiler.xnl/resource-core';
|
|
26
30
|
import { projectAIWorkflowAgentResources } from './agent-resources.js';
|
|
27
31
|
import { sourceRegistryForAIWorkflowAgentResourceProjection } from './agent-resource-brand.js';
|
|
32
|
+
import { authorityForAIAgentCodeExecution } from './agent-code-brand.js';
|
|
33
|
+
import { assertAIAgentExecutionResources, assertAIAgentFreezeRegistry, type AIAgentExecutionResourceObservation } from './agent-execution-resources.js';
|
|
28
34
|
import {
|
|
29
35
|
createResolvedAIWorkflowContentIdentityIndex,
|
|
30
36
|
resolveAIWorkflowResourceTree,
|
|
31
37
|
} from './resource-resolution.js';
|
|
32
38
|
|
|
33
39
|
export interface FreezeAIWorkflowRunResourcesInput {
|
|
40
|
+
readonly executionResources?: AIAgentExecutionResourceObservation;
|
|
41
|
+
readonly codeExecutions?: readonly CompiledAIAgentCodeExecution[];
|
|
34
42
|
readonly registry: EffectiveResourceRegistry;
|
|
35
43
|
readonly projection: AIWorkflowAgentResourceProjection;
|
|
36
44
|
readonly task: AIWorkflowAgentTaskRef;
|
|
@@ -63,6 +71,7 @@ export class AIWorkflowRunResourceFreezeError extends Error {
|
|
|
63
71
|
|
|
64
72
|
const authenticFreezeReceipts = new WeakSet<object>();
|
|
65
73
|
const authenticTaskBindings = new WeakSet<object>();
|
|
74
|
+
const receiptByTaskBinding = new WeakMap<object, AIWorkflowRunResourceFreezeReceipt>();
|
|
66
75
|
const authenticHolonTargetFreezeReceipts = new WeakSet<object>();
|
|
67
76
|
const authenticFrozenHolonTargets = new WeakSet<object>();
|
|
68
77
|
|
|
@@ -185,6 +194,7 @@ export function assertFrozenHolonTaskTarget(value: FrozenHolonTaskTarget): Froze
|
|
|
185
194
|
export function freezeAIWorkflowRunResources(
|
|
186
195
|
input: FreezeAIWorkflowRunResourcesInput,
|
|
187
196
|
): AIWorkflowRunResourceFreezeReceipt {
|
|
197
|
+
assertAIAgentFreezeRegistry(input.registry);
|
|
188
198
|
const diagnostics: AIWorkflowResourceProjectionDiagnostic[] = [];
|
|
189
199
|
if (sourceRegistryForAIWorkflowAgentResourceProjection(input.projection) !== input.registry) {
|
|
190
200
|
diagnostics.push(diagnostic(
|
|
@@ -194,6 +204,19 @@ export function freezeAIWorkflowRunResources(
|
|
|
194
204
|
));
|
|
195
205
|
throwFreezeError(diagnostics);
|
|
196
206
|
}
|
|
207
|
+
const executionResources = input.executionResources === undefined ? undefined
|
|
208
|
+
: assertAIAgentExecutionResources(input.executionResources, input.registry);
|
|
209
|
+
if (executionResources) {
|
|
210
|
+
if (input.contentIdentities.size !== executionResources.contentIdentities.size
|
|
211
|
+
|| [...executionResources.contentIdentities].some(([id, identity]) =>
|
|
212
|
+
digestCanonical(input.contentIdentities.get(id) ?? null) !== digestCanonical(identity))) {
|
|
213
|
+
throw new AIWorkflowRunResourceFreezeError(Object.freeze([
|
|
214
|
+
diagnostic('AI_RUN_EXECUTION_CONTENT_IDENTITY_MISMATCH', 'run-freeze:identities', 'Execution observation requires its exact authored content identity facts.'),
|
|
215
|
+
]));
|
|
216
|
+
}
|
|
217
|
+
input = { ...input, registry: executionResources.registry, projection: executionResources.projection,
|
|
218
|
+
contentIdentities: executionResources.contentIdentities };
|
|
219
|
+
}
|
|
197
220
|
const task = normalizeTask(input.task, diagnostics);
|
|
198
221
|
if (!task) throwFreezeError(diagnostics);
|
|
199
222
|
const workflowId = resourceIdFromRef(task.workflowRef)!;
|
|
@@ -230,19 +253,34 @@ export function freezeAIWorkflowRunResources(
|
|
|
230
253
|
agentDefinitionRef: task.agentDefinitionRef,
|
|
231
254
|
});
|
|
232
255
|
const bindingResourceIds = Object.freeze(orderedBindings.map((binding) => binding.resource.resourceId));
|
|
233
|
-
const
|
|
256
|
+
const resourceFingerprint = semanticResourceFingerprint(
|
|
234
257
|
frozenTask,
|
|
235
258
|
agent,
|
|
236
259
|
orderedBindings,
|
|
237
260
|
dependencySnapshot.closure,
|
|
238
261
|
dependencySnapshot.edges,
|
|
239
262
|
);
|
|
263
|
+
const codeExecutions = selectedCodeExecutions(input, dependencySnapshot.closure.map(item => item.resourceId));
|
|
264
|
+
const resourceResolutions = executionResources && Object.freeze(dependencySnapshot.closure
|
|
265
|
+
.map(item => {
|
|
266
|
+
const resolution = executionResources.resolutionIdentities.get(item.resourceId);
|
|
267
|
+
if (!resolution) throw new Error(`AI_RUN_RESOURCE_RESOLUTION_MISSING: ${item.resourceId}`);
|
|
268
|
+
return resolution;
|
|
269
|
+
}).sort((a, b) => a.resourceId < b.resourceId ? -1 : a.resourceId > b.resourceId ? 1 : 0));
|
|
270
|
+
const codeFingerprint = codeExecutions.length
|
|
271
|
+
? sha256Digest(JSON.stringify({ schemaVersion: 'ai-workflow.code-bound-semantic/v1', resourceFingerprint, codeExecutions }))
|
|
272
|
+
: resourceFingerprint;
|
|
273
|
+
const semanticFingerprint = resourceResolutions
|
|
274
|
+
? sha256Digest(JSON.stringify({ schemaVersion: 'ai-workflow.reader-bound-semantic/v1', resourceFingerprint: codeFingerprint, resourceResolutions }))
|
|
275
|
+
: codeFingerprint;
|
|
240
276
|
const receipt = Object.freeze({
|
|
241
277
|
schemaVersion: 'ai-workflow.run-resource-freeze/v1',
|
|
242
278
|
task: frozenTask,
|
|
243
279
|
bindingResourceIds,
|
|
244
280
|
dependencySnapshot,
|
|
245
281
|
semanticFingerprint,
|
|
282
|
+
...(codeExecutions.length ? { codeExecutions } : {}),
|
|
283
|
+
...(resourceResolutions ? { resourceResolutions } : {}),
|
|
246
284
|
});
|
|
247
285
|
authenticFreezeReceipts.add(receipt);
|
|
248
286
|
return receipt;
|
|
@@ -263,11 +301,52 @@ export function projectFrozenAIAgentTaskBinding(
|
|
|
263
301
|
semanticFingerprint: receipt.semanticFingerprint,
|
|
264
302
|
snapshotRevision: receipt.dependencySnapshot.snapshotRevision,
|
|
265
303
|
closureResourceIds: Object.freeze(receipt.dependencySnapshot.closure.map((item) => item.resourceId)),
|
|
304
|
+
...(receipt.codeExecutions ? { codeExecutions: receipt.codeExecutions } : {}),
|
|
305
|
+
...(receipt.resourceResolutions ? { resourceResolutions: receipt.resourceResolutions } : {}),
|
|
266
306
|
});
|
|
267
307
|
authenticTaskBindings.add(binding);
|
|
308
|
+
receiptByTaskBinding.set(binding, receipt);
|
|
268
309
|
return binding;
|
|
269
310
|
}
|
|
270
311
|
|
|
312
|
+
/** Retrieves the old Agent identity from the authentic freeze, not caller feedback. */
|
|
313
|
+
export function frozenAIAgentTaskAgentContentDigest(binding: FrozenAIAgentTaskBinding): Sha256Digest {
|
|
314
|
+
assertFrozenAIAgentTaskBinding(binding);
|
|
315
|
+
const receipt = receiptByTaskBinding.get(binding)!;
|
|
316
|
+
const agentId = resourceIdFromRef(receipt.task.agentDefinitionRef)!;
|
|
317
|
+
const identity = receipt.dependencySnapshot.closure.find(({ resourceId }) => resourceId === agentId);
|
|
318
|
+
if (!identity) throw new AIWorkflowRunResourceFreezeError(Object.freeze([
|
|
319
|
+
diagnostic('AI_RUN_AGENT_IDENTITY_MISSING', binding.task.agentDefinitionRef, 'Frozen Agent identity is absent.'),
|
|
320
|
+
]));
|
|
321
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(identity.contentDigest)) throw new Error('Invalid frozen Agent content digest');
|
|
322
|
+
return identity.contentDigest as Sha256Digest;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function selectedCodeExecutions(input: FreezeAIWorkflowRunResourcesInput, closureIds: readonly string[]): readonly AIAgentCodeExecutionIdentity[] {
|
|
326
|
+
const selected = new Set(closureIds);
|
|
327
|
+
const identities = new Map<string, AIAgentCodeExecutionIdentity>();
|
|
328
|
+
for (const execution of input.codeExecutions ?? []) {
|
|
329
|
+
const authority = authorityForAIAgentCodeExecution(execution);
|
|
330
|
+
if (!authority || !selected.has(authority.identity.resourceId)
|
|
331
|
+
|| input.registry.byId.get(authority.identity.resourceId)?.resource !== authority.projection.resource
|
|
332
|
+
|| !(input.projection.codeResources ?? []).includes(authority.projection)
|
|
333
|
+
|| identities.has(authority.identity.resourceId)) {
|
|
334
|
+
throw new AIWorkflowRunResourceFreezeError(Object.freeze([
|
|
335
|
+
diagnostic('AI_RUN_CODE_EXECUTION_UNTRUSTED', 'run-freeze:code', 'Code execution must be authentic, selected and belong to the exact registry projection.'),
|
|
336
|
+
]));
|
|
337
|
+
}
|
|
338
|
+
identities.set(authority.identity.resourceId, authority.identity);
|
|
339
|
+
}
|
|
340
|
+
for (const code of input.projection.codeResources ?? []) {
|
|
341
|
+
if (code.mode === 'native' && selected.has(code.resource.resourceId) && !identities.has(code.resource.resourceId)) {
|
|
342
|
+
throw new AIWorkflowRunResourceFreezeError(Object.freeze([
|
|
343
|
+
diagnostic('AI_RUN_CODE_EXECUTION_MISSING', `resource://${code.resource.resourceId}`, 'Native code must be compiled or restored before task freeze.'),
|
|
344
|
+
]));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return Object.freeze([...identities.values()].sort((a, b) => a.resourceId < b.resourceId ? -1 : a.resourceId > b.resourceId ? 1 : 0));
|
|
348
|
+
}
|
|
349
|
+
|
|
271
350
|
/** Runtime assertion; a structurally similar literal is not an admitted authority. */
|
|
272
351
|
export function assertFrozenAIAgentTaskBinding(value: FrozenAIAgentTaskBinding): FrozenAIAgentTaskBinding {
|
|
273
352
|
if (!authenticTaskBindings.has(value as object)) {
|