ai-workflow-logic 0.2.0 → 0.2.2
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 +11 -9
- 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-invocation.ts +62 -2
- package/src/agent-resources.ts +3 -0
- package/src/agent-state.ts +3 -2
- package/src/browser.ts +1 -1
- package/src/checkpoint-filesystem.ts +23 -10
- package/src/checkpoint-profile.ts +3 -0
- package/src/checkpoint-sidecar.ts +77 -16
- package/src/checkpoint-store.ts +58 -8
- package/src/core.ts +544 -0
- package/src/definitions.ts +30 -0
- package/src/delegation.ts +387 -0
- package/src/index.ts +7 -563
- package/src/resource-resolution.ts +38 -14
- package/src/run-checkpoint.ts +30 -6
- package/src/run-freeze.ts +80 -1
package/src/run-checkpoint.ts
CHANGED
|
@@ -92,6 +92,15 @@ export function normalizeFlowRunCheckpoint(value: unknown): AIWorkflowFlowRunChe
|
|
|
92
92
|
? normalizeRunStepExtensions(dataField(record, 'stepExtensions', 'checkpoint.stepExtensions'))
|
|
93
93
|
: undefined;
|
|
94
94
|
const profile = normalizeProfile(dataField(record, 'profile', 'checkpoint.profile'), runId);
|
|
95
|
+
if (profile.kind !== 'WorkCtrlFlow') {
|
|
96
|
+
for (const [key, record] of Object.entries(profile.ai.delegationsByKey ?? {})) {
|
|
97
|
+
for (const attempt of record.attempts) {
|
|
98
|
+
if (attempt.request.lineage.instanceId !== instanceId || attempt.request.lineage.runId !== runId) {
|
|
99
|
+
invalid('DELEGATION_CHECKPOINT_SCOPE_MISMATCH', `checkpoint.profile.ai.delegationsByKey.${key}`, 'Delegation lineage must belong to the canonical instance and run.');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
95
104
|
validateProfileProjection(profile, { input, state, output, controllerSidecars, nodeSidecars });
|
|
96
105
|
return Object.freeze({
|
|
97
106
|
schemaVersion,
|
|
@@ -471,7 +480,7 @@ function normalizeWorkCtrlNestedFacts(snapshot: Record<string, unknown>, locatio
|
|
|
471
480
|
if (kind === 'Branch' && !hasBranchKey) invalid('WORK_CTRL_BRANCH_KEY_MISSING', `${factLocation}.branchKey`, 'Branch decisions require a branch key.');
|
|
472
481
|
if (hasBranchKey) {
|
|
473
482
|
canonicalIdentity(dataField(fact, 'branchKey', `${factLocation}.branchKey`), `${factLocation}.branchKey`);
|
|
474
|
-
if (kind
|
|
483
|
+
if (kind === 'NoMatch') invalid('WORK_CTRL_BRANCH_KEY_INVALID', `${factLocation}.branchKey`, 'NoMatch decisions cannot retain a branch key.');
|
|
475
484
|
}
|
|
476
485
|
}
|
|
477
486
|
|
|
@@ -501,7 +510,7 @@ function normalizeWorkCtrlNestedFacts(snapshot: Record<string, unknown>, locatio
|
|
|
501
510
|
normalizeWorkCtrlAttempts(dataField(orchestration, 'attempts', `${orchestrationLocation}.attempts`), `${orchestrationLocation}.attempts`);
|
|
502
511
|
normalizeWorkCtrlDeadlines(dataField(orchestration, 'deadlines', `${orchestrationLocation}.deadlines`), `${orchestrationLocation}.deadlines`);
|
|
503
512
|
const lanes = normalizeWorkCtrlLanes(dataField(orchestration, 'lanes', `${orchestrationLocation}.lanes`), `${orchestrationLocation}.lanes`);
|
|
504
|
-
normalizeWorkCtrlComposites(dataField(orchestration, 'composites', `${orchestrationLocation}.composites`), `${orchestrationLocation}.composites
|
|
513
|
+
normalizeWorkCtrlComposites(dataField(orchestration, 'composites', `${orchestrationLocation}.composites`), `${orchestrationLocation}.composites`, lanes);
|
|
505
514
|
normalizeWorkCtrlWinners(dataField(orchestration, 'winners', `${orchestrationLocation}.winners`), lanes, `${orchestrationLocation}.winners`);
|
|
506
515
|
normalizeWorkCtrlCancellations(dataField(orchestration, 'cancellations', `${orchestrationLocation}.cancellations`), `${orchestrationLocation}.cancellations`);
|
|
507
516
|
}
|
|
@@ -542,7 +551,8 @@ function normalizeWorkCtrlDeadlines(value: unknown, location: string): void {
|
|
|
542
551
|
function normalizeWorkCtrlLanes(value: unknown, location: string): Map<string, { invocationKey: string; laneId: string; status: string }> {
|
|
543
552
|
const output = new Map<string, { invocationKey: string; laneId: string; status: string }>();
|
|
544
553
|
for (const [key, item, itemLocation] of closedRecordEntries(value, location)) {
|
|
545
|
-
const fact = exactRecord(item, ['invocationKey', 'laneId', 'status', 'bodyId'], ['item', 'flowState', 'output'], itemLocation);
|
|
554
|
+
const fact = exactRecord(item, ['invocationKey', 'laneId', 'status', 'bodyId'], ['item', 'flowState', 'output', 'started'], itemLocation);
|
|
555
|
+
if (Object.hasOwn(fact, 'started') && typeof dataField(fact, 'started', `${itemLocation}.started`) !== 'boolean') invalid('WORK_CTRL_LANE_STARTED_INVALID', `${itemLocation}.started`, 'Lane slot claim must be boolean.');
|
|
546
556
|
const invocationKey = canonicalIdentity(dataField(fact, 'invocationKey', `${itemLocation}.invocationKey`), `${itemLocation}.invocationKey`);
|
|
547
557
|
const laneId = canonicalIdentity(dataField(fact, 'laneId', `${itemLocation}.laneId`), `${itemLocation}.laneId`);
|
|
548
558
|
if (key !== `${invocationKey}/${laneId}`) invalid('WORK_CTRL_LANE_IDENTITY_MISMATCH', itemLocation, 'Lane map key must equal invocationKey/laneId.');
|
|
@@ -583,12 +593,22 @@ function normalizeWorkCtrlCancellations(value: unknown, location: string): void
|
|
|
583
593
|
}
|
|
584
594
|
}
|
|
585
595
|
|
|
586
|
-
function normalizeWorkCtrlComposites(value: unknown, location: string): void {
|
|
596
|
+
function normalizeWorkCtrlComposites(value: unknown, location: string, lanes: Map<string, { invocationKey: string; laneId: string; status: string }>): void {
|
|
587
597
|
for (const [key, item, itemLocation] of closedRecordEntries(value, location)) {
|
|
588
|
-
const fact = exactRecord(item, ['invocationKey', 'primitive', 'bodyId', 'status', 'updatedAtMs'], ['input', 'cursor', 'childTreeId'], itemLocation);
|
|
598
|
+
const fact = exactRecord(item, ['invocationKey', 'primitive', 'bodyId', 'status', 'updatedAtMs'], ['input', 'cursor', 'childTreeId', 'expansion'], itemLocation);
|
|
589
599
|
assertFactKey(fact, key, itemLocation);
|
|
590
600
|
const primitive = dataField(fact, 'primitive', `${itemLocation}.primitive`);
|
|
591
601
|
if (!['Retry', 'Timeout', 'ForEach', 'Parallel', 'Race', 'CallFlow'].includes(String(primitive))) invalid('WORK_CTRL_COMPOSITE_PRIMITIVE_INVALID', `${itemLocation}.primitive`, 'Composite primitive is invalid.');
|
|
602
|
+
if (Object.hasOwn(fact, 'expansion')) {
|
|
603
|
+
const location = `${itemLocation}.expansion`;
|
|
604
|
+
const expansion = exactRecord(dataField(fact, 'expansion', location), ['itemCount', 'concurrency'], ['maxItems'], location);
|
|
605
|
+
const itemCount = nonNegativeSafeInteger(dataField(expansion, 'itemCount', `${location}.itemCount`), `${location}.itemCount`);
|
|
606
|
+
const concurrency = nonNegativeSafeInteger(dataField(expansion, 'concurrency', `${location}.concurrency`), `${location}.concurrency`);
|
|
607
|
+
const maxItems = Object.hasOwn(expansion, 'maxItems') ? nonNegativeSafeInteger(dataField(expansion, 'maxItems', `${location}.maxItems`), `${location}.maxItems`) : undefined;
|
|
608
|
+
if (primitive !== 'ForEach' || concurrency < 1 || concurrency > Math.min(32, maxItems ?? 1000) || (maxItems !== undefined && (maxItems < 1 || maxItems > 1000 || itemCount > maxItems))) invalid('WORK_CTRL_EXPANSION_INVALID', location, 'Frozen ForEach expansion exceeds its budget.');
|
|
609
|
+
const expanded = [...lanes.values()].filter(lane => lane.invocationKey === key);
|
|
610
|
+
if (expanded.length !== itemCount || Array.from({ length: itemCount }, (_, index) => `${key}/${index}`).some(id => !lanes.has(id))) invalid('WORK_CTRL_EXPANSION_INCOMPLETE', location, 'Frozen ForEach lanes must cover the exact input order.');
|
|
611
|
+
}
|
|
592
612
|
canonicalIdentity(dataField(fact, 'bodyId', `${itemLocation}.bodyId`), `${itemLocation}.bodyId`);
|
|
593
613
|
if (!['Open', 'Waiting', 'Completed', 'Failed', 'Cancelled'].includes(String(dataField(fact, 'status', `${itemLocation}.status`)))) invalid('WORK_CTRL_COMPOSITE_STATUS_INVALID', `${itemLocation}.status`, 'Composite status is invalid.');
|
|
594
614
|
nonNegativeSafeInteger(dataField(fact, 'updatedAtMs', `${itemLocation}.updatedAtMs`), `${itemLocation}.updatedAtMs`);
|
|
@@ -1412,7 +1432,7 @@ function normalizeAIDataNodeResult(
|
|
|
1412
1432
|
const result = exactRecord(
|
|
1413
1433
|
value,
|
|
1414
1434
|
['nodeId', 'generation', 'status'],
|
|
1415
|
-
['output', 'semanticFingerprint', 'reusedFrom'],
|
|
1435
|
+
['output', 'semanticFingerprint', 'reusedFrom', 'waitContext'],
|
|
1416
1436
|
location,
|
|
1417
1437
|
);
|
|
1418
1438
|
if (canonicalIdentity(dataField(result, 'nodeId', `${location}.nodeId`), `${location}.nodeId`) !== nodeId) {
|
|
@@ -1422,6 +1442,10 @@ function normalizeAIDataNodeResult(
|
|
|
1422
1442
|
invalid('AI_DATA_RESULT_GENERATION_INVALID', `${location}.generation`, 'Result generation must equal its run node generation.');
|
|
1423
1443
|
}
|
|
1424
1444
|
const resultStatus = String(dataField(result, 'status', `${location}.status`));
|
|
1445
|
+
if(Object.hasOwn(result,'waitContext')){
|
|
1446
|
+
const context=normalizeFlowClosedObject(dataField(result,'waitContext',`${location}.waitContext`),`${location}.waitContext`);
|
|
1447
|
+
if(new TextEncoder().encode(JSON.stringify(context)).length>16384)invalid('AI_DATA_WAIT_CONTEXT_BUDGET',`${location}.waitContext`,'Native wait context exceeds 16KiB.');
|
|
1448
|
+
}
|
|
1425
1449
|
if (!['Pending', 'Running', 'Waiting', 'Succeeded', 'Failed', 'Cancelled', 'Invalidated', 'Reused'].includes(resultStatus)) {
|
|
1426
1450
|
invalid('AI_DATA_RESULT_STATUS_INVALID', `${location}.status`, 'Result status is not part of the public contract.');
|
|
1427
1451
|
}
|
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)) {
|