@tomflow/proflow-platform-host 0.1.22 → 0.1.23
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/CHANGELOG.md +7 -0
- package/DOCS.md +3 -2
- package/dist/deployment/adapter.d.ts +10 -17
- package/dist/deployment/adapter.js +30 -43
- package/dist/deployment/descriptor.d.ts +4 -7
- package/dist/deployment/descriptor.js +4 -8
- package/dist/src/cli.js +0 -4
- package/dist/src/index.d.ts +18 -8
- package/dist/src/index.js +269 -271
- package/dist/src/reconciliation-coordinator.d.ts +37 -0
- package/dist/src/reconciliation-coordinator.js +336 -0
- package/dist/src/role-operations.d.ts +4 -6
- package/dist/src/role-operations.js +37 -12
- package/dist/src/task-observer.d.ts +49 -0
- package/dist/src/task-observer.js +90 -0
- package/package.json +7 -7
- package/proflow.module.json +4 -8
package/dist/src/index.js
CHANGED
|
@@ -4,14 +4,15 @@ import { appendFile, chmod, mkdir, readFile, stat, writeFile, } from "node:fs/pr
|
|
|
4
4
|
import { createServer } from "node:http";
|
|
5
5
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
6
6
|
import { createAgentRuntime } from "@tomflow/proflow-agent-runtime";
|
|
7
|
-
import {
|
|
7
|
+
import { createLocalToolBridgeHostClient, LocalToolBridgeError, } from "@tomflow/proflow-execution-browser-extension/local-tool-bridge";
|
|
8
8
|
import { applyMigrations } from "@tomflow/proflow-task-migration-runner";
|
|
9
9
|
import { createTaskServices, publicOperationNames, } from "@tomflow/proflow-task-orchestration";
|
|
10
10
|
import { SqliteTaskStore } from "@tomflow/proflow-task-store-sqlite";
|
|
11
11
|
import { taskMigrations } from "@tomflow/proflow-task-store-sqlite/migrations";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { classifyBrowserPermission, } from "./browser-permission-policy.js";
|
|
14
|
-
import {
|
|
14
|
+
import { createReconciliationCoordinator } from "./reconciliation-coordinator.js";
|
|
15
|
+
import { directToolActionIds, roleAllowsDirectToolOperation, roleOperations, rolePackageRefs, } from "./role-operations.js";
|
|
15
16
|
const loopbackHosts = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
16
17
|
// Business owner calls are Promise-driven. This is only a last-resort transport
|
|
17
18
|
// watchdog for a hung local call, not a normal workflow deadline.
|
|
@@ -67,16 +68,6 @@ const browserStructuredLogSchema = z
|
|
|
67
68
|
tabId: z.number().int().nonnegative().optional(),
|
|
68
69
|
})
|
|
69
70
|
.strict();
|
|
70
|
-
const taskDiagnosticReasonResultSchema = z
|
|
71
|
-
.object({
|
|
72
|
-
finding: z.string().min(1).max(1_000),
|
|
73
|
-
probableCause: z.string().min(1).max(1_000),
|
|
74
|
-
confidence: z.number().min(0).max(1),
|
|
75
|
-
recommendedNextObservation: z.string().min(1).max(1_000),
|
|
76
|
-
recommendedRecoveryAction: z.string().min(1).max(1_000),
|
|
77
|
-
needsHumanAttention: z.boolean(),
|
|
78
|
-
})
|
|
79
|
-
.strict();
|
|
80
71
|
const taskDocumentFileSchema = z.object({
|
|
81
72
|
taskId: z.string().min(1),
|
|
82
73
|
documentType: z.string().min(1),
|
|
@@ -102,6 +93,40 @@ function fileBridgeOutputForTaskResult(result) {
|
|
|
102
93
|
};
|
|
103
94
|
return output;
|
|
104
95
|
}
|
|
96
|
+
const directToolPlatformIdentityFields = new Set([
|
|
97
|
+
"actorRef",
|
|
98
|
+
"authenticatedRoleRef",
|
|
99
|
+
"callerRef",
|
|
100
|
+
"roleRef",
|
|
101
|
+
"taskId",
|
|
102
|
+
"nodeId",
|
|
103
|
+
"runNo",
|
|
104
|
+
"workerRef",
|
|
105
|
+
"executionRef",
|
|
106
|
+
"workspaceRoot",
|
|
107
|
+
"deadlineAt",
|
|
108
|
+
"commandId",
|
|
109
|
+
"commandDigest",
|
|
110
|
+
"generation",
|
|
111
|
+
"extensionId",
|
|
112
|
+
"extensionInstanceId",
|
|
113
|
+
]);
|
|
114
|
+
function assertNoDirectToolPlatformIdentity(value) {
|
|
115
|
+
if (Array.isArray(value)) {
|
|
116
|
+
for (const item of value)
|
|
117
|
+
assertNoDirectToolPlatformIdentity(item);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (typeof value !== "object" || value === null)
|
|
121
|
+
return;
|
|
122
|
+
for (const [key, item] of Object.entries(value)) {
|
|
123
|
+
if (directToolPlatformIdentityFields.has(key))
|
|
124
|
+
throw Object.assign(new Error("DIRECT_TOOL_IDENTITY_FIELD_DENIED"), {
|
|
125
|
+
httpStatus: 400,
|
|
126
|
+
});
|
|
127
|
+
assertNoDirectToolPlatformIdentity(item);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
105
130
|
const loopbackUrl = z
|
|
106
131
|
.url()
|
|
107
132
|
.transform((value) => new URL(value))
|
|
@@ -113,9 +138,9 @@ const configSchema = z
|
|
|
113
138
|
workspaceRoot: z.string().min(1),
|
|
114
139
|
host: z.string().min(1).default("127.0.0.1"),
|
|
115
140
|
port: z.number().int().min(0).max(65_535).default(0),
|
|
116
|
-
executionBaseUrl: loopbackUrl,
|
|
141
|
+
executionBaseUrl: loopbackUrl.optional(),
|
|
117
142
|
executionTransportCredentialFile: z.string().min(1).optional(),
|
|
118
|
-
modelBaseUrl: loopbackUrl,
|
|
143
|
+
modelBaseUrl: loopbackUrl.optional(),
|
|
119
144
|
modelTransportCredentialFile: z.string().min(1).optional(),
|
|
120
145
|
gatewayTransportCredentialFile: z.string().min(1).optional(),
|
|
121
146
|
roles: z
|
|
@@ -208,10 +233,21 @@ async function responseJson(response) {
|
|
|
208
233
|
});
|
|
209
234
|
return value;
|
|
210
235
|
}
|
|
211
|
-
function createOwnerHttpClient(owner, baseUrl, credential) {
|
|
236
|
+
function createOwnerHttpClient(owner, baseUrl, credential, resolveConnection) {
|
|
237
|
+
const connection = async () => {
|
|
238
|
+
const resolved = resolveConnection
|
|
239
|
+
? await resolveConnection(owner)
|
|
240
|
+
: baseUrl
|
|
241
|
+
? { baseUrl, credential }
|
|
242
|
+
: undefined;
|
|
243
|
+
if (!resolved)
|
|
244
|
+
throw new Error(`${owner.toUpperCase()}_SERVICE_UNAVAILABLE`);
|
|
245
|
+
return { ...resolved, baseUrl: loopbackUrl.parse(resolved.baseUrl) };
|
|
246
|
+
};
|
|
212
247
|
return Object.freeze({
|
|
213
248
|
async readiness() {
|
|
214
249
|
try {
|
|
250
|
+
const { baseUrl, credential } = await connection();
|
|
215
251
|
const response = await fetch(`${baseUrl}/ready`, {
|
|
216
252
|
headers: credential ? { authorization: `Bearer ${credential}` } : {},
|
|
217
253
|
signal: AbortSignal.timeout(2_000),
|
|
@@ -235,6 +271,7 @@ function createOwnerHttpClient(owner, baseUrl, credential) {
|
|
|
235
271
|
}
|
|
236
272
|
},
|
|
237
273
|
async invoke(operationId, input) {
|
|
274
|
+
const { baseUrl, credential } = await connection();
|
|
238
275
|
let path;
|
|
239
276
|
let method = "POST";
|
|
240
277
|
let callerContext;
|
|
@@ -377,7 +414,7 @@ async function readDeploymentOwnerSummary(stateRoot) {
|
|
|
377
414
|
return undefined;
|
|
378
415
|
}
|
|
379
416
|
}
|
|
380
|
-
async function constructGraph(config, executionCredential, modelCredential) {
|
|
417
|
+
async function constructGraph(config, executionCredential, modelCredential, resolveConnection, resolveLocalToolConnection) {
|
|
381
418
|
const databasePath = join(config.stateRoot, "state", "task.sqlite");
|
|
382
419
|
const migration = applyMigrations({
|
|
383
420
|
databasePath,
|
|
@@ -444,8 +481,9 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
444
481
|
taskStore.close();
|
|
445
482
|
throw error;
|
|
446
483
|
}
|
|
447
|
-
const execution = createOwnerHttpClient("execution", config.executionBaseUrl, executionCredential);
|
|
448
|
-
const model = createOwnerHttpClient("model", config.modelBaseUrl, modelCredential);
|
|
484
|
+
const execution = createOwnerHttpClient("execution", config.executionBaseUrl, executionCredential, resolveConnection);
|
|
485
|
+
const model = createOwnerHttpClient("model", config.modelBaseUrl, modelCredential, resolveConnection);
|
|
486
|
+
let reconciliationCoordinator;
|
|
449
487
|
const boundedSystemView = async (view) => {
|
|
450
488
|
if (view === "task") {
|
|
451
489
|
const tasks = unwrap(task.queries.listTasks({})).tasks;
|
|
@@ -615,56 +653,6 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
615
653
|
});
|
|
616
654
|
return binding.workerRef;
|
|
617
655
|
};
|
|
618
|
-
const admitExecutionRead = async (authenticatedRoleRef, rawRecord) => {
|
|
619
|
-
const record = object(rawRecord, "execution read record");
|
|
620
|
-
if (record.callerRef !== authenticatedRoleRef)
|
|
621
|
-
throw Object.assign(new Error("EXECUTION_CALLER_MISMATCH"), {
|
|
622
|
-
httpStatus: 403,
|
|
623
|
-
});
|
|
624
|
-
if (typeof record.taskId === "string") {
|
|
625
|
-
if (record.roleRef !== authenticatedRoleRef)
|
|
626
|
-
throw Object.assign(new Error("EXECUTION_ROLE_SCOPE_MISMATCH"), {
|
|
627
|
-
httpStatus: 403,
|
|
628
|
-
});
|
|
629
|
-
if (typeof record.workerRef !== "string")
|
|
630
|
-
throw Object.assign(new Error("EXECUTION_WORKER_SCOPE_REQUIRED"), {
|
|
631
|
-
httpStatus: 403,
|
|
632
|
-
});
|
|
633
|
-
await admitTaskParticipant(record.taskId, authenticatedRoleRef, record.workerRef);
|
|
634
|
-
}
|
|
635
|
-
return rawRecord;
|
|
636
|
-
};
|
|
637
|
-
const admissionError = (code, httpStatus = 403) => {
|
|
638
|
-
throw Object.assign(new Error(code), { httpStatus });
|
|
639
|
-
};
|
|
640
|
-
const admitExactNodeExecution = async (input, authenticatedRoleRef) => {
|
|
641
|
-
const taskId = string(input.taskId, "taskId");
|
|
642
|
-
const nodeId = string(input.nodeId, "nodeId");
|
|
643
|
-
const runNo = positiveInteger(input.runNo, "runNo");
|
|
644
|
-
const workerRef = await admitTaskParticipant(taskId, authenticatedRoleRef);
|
|
645
|
-
await agent.validateWorker({ authenticatedRoleRef, taskId, workerRef });
|
|
646
|
-
const taskFact = taskFacts(taskId);
|
|
647
|
-
const nodeContext = unwrap(task.queries.getNodeContext({ taskId, nodeId }));
|
|
648
|
-
if (taskFact.status !== "ACTIVE")
|
|
649
|
-
admissionError("EXECUTION_TASK_NOT_ACTIVE");
|
|
650
|
-
if (taskFact.currentNodeId !== nodeId)
|
|
651
|
-
admissionError("EXECUTION_NODE_NOT_CURRENT");
|
|
652
|
-
if (nodeContext.node.status !== "IN_PROGRESS")
|
|
653
|
-
admissionError("EXECUTION_NODE_NOT_RUNNING");
|
|
654
|
-
if (nodeContext.node.runNo !== runNo)
|
|
655
|
-
admissionError("EXECUTION_GENERATION_MISMATCH");
|
|
656
|
-
const nodeBinding = taskFact.roleBindings.find((binding) => binding.agentPackageRef === nodeContext.node.requiredAgentPackageRef);
|
|
657
|
-
if (!nodeBinding)
|
|
658
|
-
throw Object.assign(new Error("TASK_ROLE_BINDING_REQUIRED"), {
|
|
659
|
-
httpStatus: 403,
|
|
660
|
-
});
|
|
661
|
-
if (nodeBinding.roleRef !== authenticatedRoleRef)
|
|
662
|
-
admissionError("EXECUTION_ROLE_SCOPE_MISMATCH");
|
|
663
|
-
if (nodeBinding.workerRef !== workerRef ||
|
|
664
|
-
nodeContext.node.workerRef !== workerRef)
|
|
665
|
-
admissionError("EXECUTION_WORKER_SCOPE_MISMATCH");
|
|
666
|
-
return { taskId, nodeId, runNo, workerRef };
|
|
667
|
-
};
|
|
668
656
|
const route = async (operationId, authenticatedRoleRef, rawInput, context) => {
|
|
669
657
|
const role = agent.getRegisteredRole(authenticatedRoleRef);
|
|
670
658
|
if (!roleOperations[role.agentPackageRef]?.has(operationId))
|
|
@@ -676,6 +664,63 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
676
664
|
return agent.askPeer({ ...input, authenticatedRoleRef });
|
|
677
665
|
if (operationId === "replyPeer")
|
|
678
666
|
return agent.replyPeer({ ...input, authenticatedRoleRef });
|
|
667
|
+
if (directToolActionIds.includes(operationId)) {
|
|
668
|
+
const tool = operationId;
|
|
669
|
+
const keys = Object.keys(input).sort();
|
|
670
|
+
if (keys.length !== 2 || keys[0] !== "input" || keys[1] !== "operation")
|
|
671
|
+
throw Object.assign(new Error("DIRECT_TOOL_INPUT_INVALID"), {
|
|
672
|
+
httpStatus: 400,
|
|
673
|
+
});
|
|
674
|
+
const toolOperation = string(input.operation, "operation");
|
|
675
|
+
const toolInput = object(input.input, "input");
|
|
676
|
+
assertNoDirectToolPlatformIdentity(toolInput);
|
|
677
|
+
if (!roleAllowsDirectToolOperation(role.agentPackageRef, operationId, toolOperation, toolInput))
|
|
678
|
+
throw Object.assign(new Error("ROLE_TOOL_OPERATION_DENIED"), {
|
|
679
|
+
httpStatus: 403,
|
|
680
|
+
});
|
|
681
|
+
const deadlineAt = context?.deadlineAt;
|
|
682
|
+
if (typeof deadlineAt !== "string" ||
|
|
683
|
+
Number.isNaN(Date.parse(deadlineAt)))
|
|
684
|
+
throw Object.assign(new Error("DIRECT_TOOL_DEADLINE_REQUIRED"), {
|
|
685
|
+
httpStatus: 400,
|
|
686
|
+
});
|
|
687
|
+
const connection = await resolveLocalToolConnection?.();
|
|
688
|
+
if (!connection)
|
|
689
|
+
throw Object.assign(new Error("LOCAL_TOOL_BRIDGE_UNAVAILABLE"), {
|
|
690
|
+
httpStatus: 503,
|
|
691
|
+
});
|
|
692
|
+
try {
|
|
693
|
+
return await createLocalToolBridgeHostClient({
|
|
694
|
+
endpoint: connection.endpoint,
|
|
695
|
+
token: connection.credential,
|
|
696
|
+
}).request({
|
|
697
|
+
authenticatedRoleRef,
|
|
698
|
+
workspaceRoot: config.workspaceRoot,
|
|
699
|
+
tool,
|
|
700
|
+
operation: toolOperation,
|
|
701
|
+
input: toolInput,
|
|
702
|
+
deadlineAt,
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
catch (error) {
|
|
706
|
+
const code = error instanceof LocalToolBridgeError
|
|
707
|
+
? error.code
|
|
708
|
+
: "LOCAL_TOOL_BRIDGE_UNAVAILABLE";
|
|
709
|
+
const httpStatus = code === "LOCAL_TOOL_COMMAND_TIMEOUT"
|
|
710
|
+
? 504
|
|
711
|
+
: code === "LOCAL_TOOL_RESULT_UNKNOWN"
|
|
712
|
+
? 409
|
|
713
|
+
: code === "LOCAL_TOOL_SCOPE_DENIED"
|
|
714
|
+
? 403
|
|
715
|
+
: code === "LOCAL_TOOL_OFFLINE" ||
|
|
716
|
+
code === "LOCAL_TOOL_PROVIDER_UNAVAILABLE"
|
|
717
|
+
? 503
|
|
718
|
+
: code === "LOCAL_TOOL_COMMAND_FAILED"
|
|
719
|
+
? 500
|
|
720
|
+
: 400;
|
|
721
|
+
throw Object.assign(new Error(code), { httpStatus });
|
|
722
|
+
}
|
|
723
|
+
}
|
|
679
724
|
const taskOperation = taskOperations.get(operationId);
|
|
680
725
|
if (taskOperation) {
|
|
681
726
|
let actorRef = authenticatedRoleRef;
|
|
@@ -722,66 +767,13 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
722
767
|
const taskResult = await taskOperation(queryOperations.has(operationId)
|
|
723
768
|
? canonicalTaskInput
|
|
724
769
|
: { ...canonicalTaskInput, actorRef });
|
|
770
|
+
if (taskMutationOperations.has(operationId) &&
|
|
771
|
+
typeof input.taskId === "string")
|
|
772
|
+
reconciliationCoordinator?.kick(input.taskId);
|
|
725
773
|
if (operationId === "getTaskDocument")
|
|
726
774
|
return fileBridgeOutputForTaskResult(taskResult);
|
|
727
|
-
if (operationId === "getNodeContext" &&
|
|
728
|
-
roleOperations[role.agentPackageRef]?.has("executeCapability")) {
|
|
729
|
-
const nodeContext = object(taskResult, "node context result");
|
|
730
|
-
const nodeContextData = object(nodeContext.data, "node context data");
|
|
731
|
-
const taskContext = object(nodeContextData.task, "node context task");
|
|
732
|
-
const executionNodeContext = object(nodeContextData.node, "node context node");
|
|
733
|
-
return {
|
|
734
|
-
...nodeContext,
|
|
735
|
-
executionCapabilityIds: [...executionCapabilityIds],
|
|
736
|
-
executionCapabilityInputSchemas: executionCapabilityInputJsonSchemas,
|
|
737
|
-
executionRequestContext: {
|
|
738
|
-
contract: "execution",
|
|
739
|
-
contractVersion: "1.0.0",
|
|
740
|
-
taskId: string(taskContext.taskId, "task.taskId"),
|
|
741
|
-
nodeId: string(executionNodeContext.nodeId, "node.nodeId"),
|
|
742
|
-
runNo: positiveInteger(executionNodeContext.runNo, "node.runNo"),
|
|
743
|
-
},
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
775
|
return taskResult;
|
|
747
776
|
}
|
|
748
|
-
if (operationId === "executeCapability") {
|
|
749
|
-
const scope = await admitExactNodeExecution(input, authenticatedRoleRef);
|
|
750
|
-
const { workerRef: _suppliedWorkerRef, projectRoot: _suppliedProjectRoot, roleRef: _suppliedRoleRef, callerRef: _suppliedCallerRef, ...ownerInput } = input;
|
|
751
|
-
const result = await execution.invoke(operationId, {
|
|
752
|
-
...ownerInput,
|
|
753
|
-
callerRef: authenticatedRoleRef,
|
|
754
|
-
roleRef: authenticatedRoleRef,
|
|
755
|
-
taskId: scope.taskId,
|
|
756
|
-
nodeId: scope.nodeId,
|
|
757
|
-
runNo: scope.runNo,
|
|
758
|
-
workerRef: scope.workerRef,
|
|
759
|
-
});
|
|
760
|
-
// A normal Action may complete synchronously inside the current Worker Turn.
|
|
761
|
-
// Do not manufacture a Browser RESUME for every terminal Execution record.
|
|
762
|
-
// Only a future explicit async-completion signal may emit EXECUTION_RESULT_READY.
|
|
763
|
-
return result;
|
|
764
|
-
}
|
|
765
|
-
if (operationId === "getExecution") {
|
|
766
|
-
const record = await execution.invoke(operationId, {
|
|
767
|
-
...input,
|
|
768
|
-
callerRef: authenticatedRoleRef,
|
|
769
|
-
});
|
|
770
|
-
return admitExecutionRead(authenticatedRoleRef, record);
|
|
771
|
-
}
|
|
772
|
-
if (operationId === "readExecutionOutput") {
|
|
773
|
-
const record = await execution.invoke("getExecution", {
|
|
774
|
-
contract: "execution",
|
|
775
|
-
contractVersion: "1.0.0",
|
|
776
|
-
executionRef: string(input.executionRef, "executionRef"),
|
|
777
|
-
callerRef: authenticatedRoleRef,
|
|
778
|
-
});
|
|
779
|
-
await admitExecutionRead(authenticatedRoleRef, record);
|
|
780
|
-
return execution.invoke(operationId, {
|
|
781
|
-
...input,
|
|
782
|
-
callerRef: authenticatedRoleRef,
|
|
783
|
-
});
|
|
784
|
-
}
|
|
785
777
|
throw new Error("OPERATION_NOT_ROUTED");
|
|
786
778
|
};
|
|
787
779
|
const browserOwnerPorts = Object.freeze({
|
|
@@ -814,6 +806,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
814
806
|
actorRef: "platform-host:worker-provisioning",
|
|
815
807
|
idempotencyKey: `browser-bind:${binding.taskId}:${binding.roleRef}:${binding.workerRef}`,
|
|
816
808
|
}));
|
|
809
|
+
reconciliationCoordinator?.kick(binding.taskId);
|
|
817
810
|
},
|
|
818
811
|
}),
|
|
819
812
|
agent: Object.freeze({
|
|
@@ -862,7 +855,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
862
855
|
request.capability === "worker.wake" ||
|
|
863
856
|
request.capability === "collaboration.deliver";
|
|
864
857
|
const internalBrowserCaller = request.callerRef === "platform-host:carrier-controller" ||
|
|
865
|
-
request.callerRef === "
|
|
858
|
+
request.callerRef === "platform-host:task-reconciliation" ||
|
|
866
859
|
request.callerRef === "extension:collaboration-carrier";
|
|
867
860
|
if (!internalBrowserCaller)
|
|
868
861
|
agent.getRegisteredRole(request.callerRef);
|
|
@@ -1132,7 +1125,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1132
1125
|
// Dispatch all three fixed Workers concurrently. When `waitFor` names a
|
|
1133
1126
|
// subset of roleRefs (the J1 Product path), only those results gate the
|
|
1134
1127
|
// return; the remaining Worker creation is a durable, idempotent Execution
|
|
1135
|
-
// effect whose completion
|
|
1128
|
+
// effect whose completion backend reconciliation recovers from the
|
|
1136
1129
|
// durable Task binding facts — never a bare in-memory promise.
|
|
1137
1130
|
const waitFor = new Set(options?.waitFor ?? []);
|
|
1138
1131
|
const shouldWait = (agentPackageRef) => waitFor.size === 0 ||
|
|
@@ -1146,7 +1139,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1146
1139
|
for (const entry of deferred) {
|
|
1147
1140
|
entry.promise.catch(() => {
|
|
1148
1141
|
// Deferred Worker creation failure is recoverable: the durable
|
|
1149
|
-
// binding stays unset, so a later
|
|
1142
|
+
// binding stays unset, so a later backend reconciliation pass re-provisions
|
|
1150
1143
|
// only the missing role.
|
|
1151
1144
|
});
|
|
1152
1145
|
}
|
|
@@ -1156,6 +1149,67 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1156
1149
|
throw failure.reason;
|
|
1157
1150
|
return unwrap(task.queries.getTask({ taskId }));
|
|
1158
1151
|
};
|
|
1152
|
+
const requestTaskWake = async (input) => {
|
|
1153
|
+
const underlyingRef = input.underlyingRef ?? "none";
|
|
1154
|
+
const wakeExecution = object(await execution.invoke("executeCapability", {
|
|
1155
|
+
contract: "execution",
|
|
1156
|
+
contractVersion: "1.0.0",
|
|
1157
|
+
idempotencyKey: `task-reconciliation-wake:${input.taskId}:${input.nodeId}:${input.runNo}:${input.trigger}:${underlyingRef}`,
|
|
1158
|
+
callerRef: "platform-host:task-reconciliation",
|
|
1159
|
+
correlationId: `task-reconciliation:${input.taskId}:${input.nodeId}:${input.runNo}`,
|
|
1160
|
+
taskId: input.taskId,
|
|
1161
|
+
nodeId: input.nodeId,
|
|
1162
|
+
runNo: input.runNo,
|
|
1163
|
+
roleRef: input.roleRef,
|
|
1164
|
+
workerRef: input.workerRef,
|
|
1165
|
+
capability: "worker.wake",
|
|
1166
|
+
input: {
|
|
1167
|
+
roleRef: input.roleRef,
|
|
1168
|
+
workerRef: input.workerRef,
|
|
1169
|
+
taskId: input.taskId,
|
|
1170
|
+
nodeId: input.nodeId,
|
|
1171
|
+
runNo: input.runNo,
|
|
1172
|
+
trigger: input.trigger,
|
|
1173
|
+
fingerprint: `wake:${input.taskId}:${input.nodeId}:${input.runNo}:${input.trigger}:${underlyingRef}`,
|
|
1174
|
+
},
|
|
1175
|
+
}), "task reconciliation wake execution");
|
|
1176
|
+
if (wakeExecution.status !== "SUCCEEDED" ||
|
|
1177
|
+
wakeExecution.sideEffectState !== "APPLIED")
|
|
1178
|
+
throw new Error(`TASK_WAKE_NOT_CONFIRMED:${String(wakeExecution.status)}:${String(wakeExecution.sideEffectState)}`);
|
|
1179
|
+
return wakeExecution;
|
|
1180
|
+
};
|
|
1181
|
+
reconciliationCoordinator = createReconciliationCoordinator({
|
|
1182
|
+
async listTaskPage(input) {
|
|
1183
|
+
return taskStore.listReconciliationTaskIds({
|
|
1184
|
+
statuses: [
|
|
1185
|
+
"PENDING",
|
|
1186
|
+
"READY",
|
|
1187
|
+
"ACTIVE",
|
|
1188
|
+
"WAITING",
|
|
1189
|
+
"FAILED",
|
|
1190
|
+
"PAUSED",
|
|
1191
|
+
],
|
|
1192
|
+
...input,
|
|
1193
|
+
});
|
|
1194
|
+
},
|
|
1195
|
+
async listExecutionSignals() {
|
|
1196
|
+
const batch = object(await execution.invoke("listExecutionObserverSignals", {
|
|
1197
|
+
limit: 100,
|
|
1198
|
+
consumer: "task-reconciliation",
|
|
1199
|
+
}), "execution observer signals");
|
|
1200
|
+
return Array.isArray(batch.signals) ? batch.signals : [];
|
|
1201
|
+
},
|
|
1202
|
+
async acknowledgeExecutionSignal(signalRef) {
|
|
1203
|
+
await execution.invoke("acknowledgeExecutionObserverSignal", {
|
|
1204
|
+
signalRef,
|
|
1205
|
+
});
|
|
1206
|
+
},
|
|
1207
|
+
async ensureWorkers(taskId) {
|
|
1208
|
+
await ensureTaskWorkers(taskId);
|
|
1209
|
+
},
|
|
1210
|
+
getProjection: (taskId) => taskDriverPorts.getTaskDriveProjection(taskId),
|
|
1211
|
+
requestWake: requestTaskWake,
|
|
1212
|
+
});
|
|
1159
1213
|
const taskApplication = Object.freeze({
|
|
1160
1214
|
async invoke(operation, rawInput) {
|
|
1161
1215
|
const value = object(rawInput, "task application input");
|
|
@@ -1186,12 +1240,12 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1186
1240
|
// J1: return once the Product Worker is durably bound; Dev/Test
|
|
1187
1241
|
// continue as recoverable durable effects without blocking Product
|
|
1188
1242
|
// requirement discussion.
|
|
1189
|
-
|
|
1243
|
+
const provisioned = await ensureTaskWorkers(created.taskId, {
|
|
1190
1244
|
waitFor: [roleForPackage("@tomflow/proflow-agent-product").roleRef],
|
|
1191
1245
|
});
|
|
1246
|
+
reconciliationCoordinator?.kick(created.taskId);
|
|
1247
|
+
return provisioned;
|
|
1192
1248
|
}
|
|
1193
|
-
if (operation === "task.ensureWorkers")
|
|
1194
|
-
return ensureTaskWorkers(string(value.taskId, "taskId"));
|
|
1195
1249
|
if (operation === "task.list")
|
|
1196
1250
|
return unwrap(task.queries.listTasks({
|
|
1197
1251
|
...(Array.isArray(value.statuses)
|
|
@@ -1200,13 +1254,17 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1200
1254
|
}));
|
|
1201
1255
|
if (operation === "task.get")
|
|
1202
1256
|
return unwrap(task.queries.getTask({ taskId: string(value.taskId, "taskId") }));
|
|
1203
|
-
if (operation === "task.start")
|
|
1204
|
-
|
|
1205
|
-
|
|
1257
|
+
if (operation === "task.start") {
|
|
1258
|
+
const taskId = string(value.taskId, "taskId");
|
|
1259
|
+
const result = unwrap(task.commands.startTask({
|
|
1260
|
+
taskId,
|
|
1206
1261
|
expectedTaskVersion: Number(value.expectedTaskVersion),
|
|
1207
1262
|
actorRef: "extension:human",
|
|
1208
1263
|
idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
|
|
1209
1264
|
}));
|
|
1265
|
+
reconciliationCoordinator?.kick(taskId);
|
|
1266
|
+
return result;
|
|
1267
|
+
}
|
|
1210
1268
|
if (operation === "message.acknowledge")
|
|
1211
1269
|
return unwrap(task.commands.acknowledgeMessage({
|
|
1212
1270
|
messageId: string(value.messageId, "messageId"),
|
|
@@ -1216,25 +1274,62 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1216
1274
|
actorRef: "extension:human",
|
|
1217
1275
|
idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
|
|
1218
1276
|
}));
|
|
1219
|
-
if (operation === "task.resume")
|
|
1220
|
-
|
|
1221
|
-
|
|
1277
|
+
if (operation === "task.resume") {
|
|
1278
|
+
const taskId = string(value.taskId, "taskId");
|
|
1279
|
+
const result = unwrap(task.commands.resumeTask({
|
|
1280
|
+
taskId,
|
|
1222
1281
|
expectedTaskVersion: positiveInteger(value.expectedTaskVersion, "expectedTaskVersion"),
|
|
1223
1282
|
actorRef: "extension:human",
|
|
1224
1283
|
idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
|
|
1225
1284
|
}));
|
|
1226
|
-
|
|
1227
|
-
return
|
|
1228
|
-
|
|
1285
|
+
reconciliationCoordinator?.kick(taskId);
|
|
1286
|
+
return result;
|
|
1287
|
+
}
|
|
1288
|
+
if (operation === "node.reopen") {
|
|
1289
|
+
const taskId = string(value.taskId, "taskId");
|
|
1290
|
+
const result = unwrap(task.commands.reopenNode({
|
|
1291
|
+
taskId,
|
|
1229
1292
|
nodeId: string(value.nodeId, "nodeId"),
|
|
1230
1293
|
reason: string(value.reason, "reason"),
|
|
1231
1294
|
expectedTaskVersion: Number(value.expectedTaskVersion),
|
|
1232
1295
|
actorRef: "extension:human",
|
|
1233
1296
|
idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
|
|
1234
1297
|
}));
|
|
1298
|
+
reconciliationCoordinator?.kick(taskId);
|
|
1299
|
+
return result;
|
|
1300
|
+
}
|
|
1235
1301
|
throw new Error("UNSUPPORTED_TASK_APPLICATION_OPERATION");
|
|
1236
1302
|
},
|
|
1237
1303
|
});
|
|
1304
|
+
async function approvalExecutionContext(approvalRef) {
|
|
1305
|
+
const approval = object(await execution.invoke("getExecutionApproval", { approvalRef }), "approval fact");
|
|
1306
|
+
const executionFact = object(await execution.invoke("getExecution", {
|
|
1307
|
+
executionRef: string(approval.executionRef, "executionRef"),
|
|
1308
|
+
callerRef: string(approval.callerRef, "callerRef"),
|
|
1309
|
+
}), "execution fact");
|
|
1310
|
+
const runNo = Number(executionFact.runNo);
|
|
1311
|
+
if (!Number.isInteger(runNo) || runNo <= 0)
|
|
1312
|
+
throw new Error("EXECUTION_RUN_GENERATION_REQUIRED");
|
|
1313
|
+
return {
|
|
1314
|
+
executionRef: string(executionFact.executionRef, "executionRef"),
|
|
1315
|
+
taskId: string(executionFact.taskId, "taskId"),
|
|
1316
|
+
nodeId: string(executionFact.nodeId, "nodeId"),
|
|
1317
|
+
runNo,
|
|
1318
|
+
roleRef: string(executionFact.roleRef, "roleRef"),
|
|
1319
|
+
workerRef: string(executionFact.workerRef, "workerRef"),
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
const reconcileApproval = async (approvalRef) => {
|
|
1323
|
+
const context = await approvalExecutionContext(approvalRef);
|
|
1324
|
+
reconciliationCoordinator?.kick(context.taskId, {
|
|
1325
|
+
trigger: "RECOVERY_RESUME",
|
|
1326
|
+
ref: approvalRef,
|
|
1327
|
+
targetWorkerRef: context.workerRef,
|
|
1328
|
+
nodeId: context.nodeId,
|
|
1329
|
+
runNo: context.runNo,
|
|
1330
|
+
});
|
|
1331
|
+
return context;
|
|
1332
|
+
};
|
|
1238
1333
|
const approvalApplication = Object.freeze({
|
|
1239
1334
|
async invoke(operation, rawInput) {
|
|
1240
1335
|
const value = object(rawInput, "approval application input");
|
|
@@ -1249,52 +1344,42 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1249
1344
|
...value,
|
|
1250
1345
|
actorRef: "extension:human",
|
|
1251
1346
|
});
|
|
1252
|
-
if (operation === "approval.allow" || operation === "approval.deny")
|
|
1253
|
-
|
|
1347
|
+
if (operation === "approval.allow" || operation === "approval.deny") {
|
|
1348
|
+
const approvalRef = string(value.approvalRef, "approvalRef");
|
|
1349
|
+
const result = await execution.invoke("decideExecutionApproval", {
|
|
1254
1350
|
contract: "execution.approval",
|
|
1255
1351
|
contractVersion: "1.0.0",
|
|
1256
|
-
approvalRef
|
|
1352
|
+
approvalRef,
|
|
1257
1353
|
actorRef: "extension:human",
|
|
1258
1354
|
expectedVersion: Number(value.expectedVersion),
|
|
1259
1355
|
decision: operation === "approval.allow" ? "ALLOW" : "DENY",
|
|
1260
1356
|
...(typeof value.reason === "string" ? { reason: value.reason } : {}),
|
|
1261
1357
|
});
|
|
1262
|
-
|
|
1263
|
-
return
|
|
1358
|
+
await reconcileApproval(approvalRef);
|
|
1359
|
+
return result;
|
|
1360
|
+
}
|
|
1361
|
+
if (operation === "approval.revoke") {
|
|
1362
|
+
const approvalRef = string(value.approvalRef, "approvalRef");
|
|
1363
|
+
const result = await execution.invoke("revokeExecutionApproval", {
|
|
1264
1364
|
contract: "execution.approval",
|
|
1265
1365
|
contractVersion: "1.0.0",
|
|
1266
|
-
approvalRef
|
|
1366
|
+
approvalRef,
|
|
1267
1367
|
actorRef: "extension:human",
|
|
1268
1368
|
expectedVersion: Number(value.expectedVersion),
|
|
1269
1369
|
reason: string(value.reason, "reason"),
|
|
1270
1370
|
});
|
|
1371
|
+
await reconcileApproval(approvalRef);
|
|
1372
|
+
return result;
|
|
1373
|
+
}
|
|
1271
1374
|
throw new Error("UNSUPPORTED_APPROVAL_APPLICATION_OPERATION");
|
|
1272
1375
|
},
|
|
1273
1376
|
});
|
|
1274
1377
|
const observerApplication = Object.freeze({
|
|
1275
1378
|
async invoke(operation, rawInput) {
|
|
1276
1379
|
const value = object(rawInput, "observer application input");
|
|
1277
|
-
if (operation === "task.
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
const approval = object(await execution.invoke("getExecutionApproval", {
|
|
1281
|
-
approvalRef: string(value.approvalRef, "approvalRef"),
|
|
1282
|
-
}), "approval fact");
|
|
1283
|
-
const executionFact = object(await execution.invoke("getExecution", {
|
|
1284
|
-
executionRef: string(approval.executionRef, "executionRef"),
|
|
1285
|
-
callerRef: string(approval.callerRef, "callerRef"),
|
|
1286
|
-
}), "execution fact");
|
|
1287
|
-
const runNo = Number(executionFact.runNo);
|
|
1288
|
-
if (!Number.isInteger(runNo) || runNo <= 0)
|
|
1289
|
-
throw new Error("EXECUTION_RUN_GENERATION_REQUIRED");
|
|
1290
|
-
return {
|
|
1291
|
-
executionRef: string(executionFact.executionRef, "executionRef"),
|
|
1292
|
-
taskId: string(executionFact.taskId, "taskId"),
|
|
1293
|
-
nodeId: string(executionFact.nodeId, "nodeId"),
|
|
1294
|
-
runNo,
|
|
1295
|
-
roleRef: string(executionFact.roleRef, "roleRef"),
|
|
1296
|
-
workerRef: string(executionFact.workerRef, "workerRef"),
|
|
1297
|
-
};
|
|
1380
|
+
if (operation === "task.reconcileAll") {
|
|
1381
|
+
void reconciliationCoordinator?.sweep();
|
|
1382
|
+
return { scheduled: true };
|
|
1298
1383
|
}
|
|
1299
1384
|
if (operation === "browser.permission.classify") {
|
|
1300
1385
|
const roleRef = string(value.roleRef, "roleRef");
|
|
@@ -1379,79 +1464,6 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1379
1464
|
});
|
|
1380
1465
|
return { reported: true };
|
|
1381
1466
|
}
|
|
1382
|
-
if (operation === "execution.listSignals")
|
|
1383
|
-
return execution.invoke("listExecutionObserverSignals", {
|
|
1384
|
-
limit: Number(value.limit ?? 50),
|
|
1385
|
-
});
|
|
1386
|
-
if (operation === "execution.ackSignal")
|
|
1387
|
-
return execution.invoke("acknowledgeExecutionObserverSignal", {
|
|
1388
|
-
signalRef: string(value.signalRef, "signalRef"),
|
|
1389
|
-
});
|
|
1390
|
-
if (operation === "task.wake") {
|
|
1391
|
-
const taskId = string(value.taskId, "taskId");
|
|
1392
|
-
const nodeId = string(value.nodeId, "nodeId");
|
|
1393
|
-
const runNo = Number(value.runNo);
|
|
1394
|
-
const roleRef = string(value.roleRef, "roleRef");
|
|
1395
|
-
const workerRef = string(value.workerRef, "workerRef");
|
|
1396
|
-
const trigger = string(value.trigger, "trigger");
|
|
1397
|
-
const underlyingRef = typeof value.underlyingRef === "string"
|
|
1398
|
-
? value.underlyingRef
|
|
1399
|
-
: "none";
|
|
1400
|
-
const wakeExecution = object(await execution.invoke("executeCapability", {
|
|
1401
|
-
contract: "execution",
|
|
1402
|
-
contractVersion: "1.0.0",
|
|
1403
|
-
idempotencyKey: `task-observer-wake:${taskId}:${nodeId}:${runNo}:${trigger}:${underlyingRef}`,
|
|
1404
|
-
callerRef: "extension:task-observer",
|
|
1405
|
-
correlationId: `task-observer:${taskId}:${nodeId}:${runNo}`,
|
|
1406
|
-
taskId,
|
|
1407
|
-
nodeId,
|
|
1408
|
-
runNo,
|
|
1409
|
-
roleRef,
|
|
1410
|
-
workerRef,
|
|
1411
|
-
capability: "worker.wake",
|
|
1412
|
-
input: {
|
|
1413
|
-
roleRef,
|
|
1414
|
-
workerRef,
|
|
1415
|
-
taskId,
|
|
1416
|
-
nodeId,
|
|
1417
|
-
runNo,
|
|
1418
|
-
trigger,
|
|
1419
|
-
fingerprint: `wake:${taskId}:${nodeId}:${runNo}:${trigger}:${underlyingRef}`,
|
|
1420
|
-
},
|
|
1421
|
-
}), "task wake execution");
|
|
1422
|
-
if (wakeExecution.status !== "SUCCEEDED" ||
|
|
1423
|
-
wakeExecution.sideEffectState !== "APPLIED")
|
|
1424
|
-
throw new Error(`TASK_WAKE_NOT_CONFIRMED:${String(wakeExecution.status)}:${String(wakeExecution.sideEffectState)}`);
|
|
1425
|
-
return wakeExecution;
|
|
1426
|
-
}
|
|
1427
|
-
if (operation === "task.diagnostic") {
|
|
1428
|
-
const response = object(await model.invoke("infer", {
|
|
1429
|
-
contractVersion: "1.0.0",
|
|
1430
|
-
specRef: "task.diagnostic.v1",
|
|
1431
|
-
mode: "reason",
|
|
1432
|
-
priority: "business",
|
|
1433
|
-
trace: {
|
|
1434
|
-
callerRef: "extension:task-observer",
|
|
1435
|
-
correlationId: string(value.correlationId, "correlationId"),
|
|
1436
|
-
taskId: string(value.taskId, "taskId"),
|
|
1437
|
-
nodeId: string(value.nodeId, "nodeId"),
|
|
1438
|
-
},
|
|
1439
|
-
payload: value.payload,
|
|
1440
|
-
}), "task diagnostic inference");
|
|
1441
|
-
if (response.status !== "SUCCEEDED") {
|
|
1442
|
-
const error = typeof response.error === "object" && response.error !== null
|
|
1443
|
-
? response.error
|
|
1444
|
-
: {};
|
|
1445
|
-
const code = error.code === "CONTEXT_TOO_LARGE"
|
|
1446
|
-
? "CONTEXT_TOO_LARGE"
|
|
1447
|
-
: error.code === "MODEL_UNAVAILABLE" ||
|
|
1448
|
-
error.code === "CAPABILITY_UNSUPPORTED"
|
|
1449
|
-
? "REASON_UNAVAILABLE"
|
|
1450
|
-
: "REASON_FAILED";
|
|
1451
|
-
return { ok: false, errorCode: code };
|
|
1452
|
-
}
|
|
1453
|
-
return taskDiagnosticReasonResultSchema.parse(response.data);
|
|
1454
|
-
}
|
|
1455
1467
|
if (operation === "system.view")
|
|
1456
1468
|
return boundedSystemView(string(value.view, "view"));
|
|
1457
1469
|
if (operation === "system.drilldown") {
|
|
@@ -1504,6 +1516,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1504
1516
|
throw new Error("UNSUPPORTED_OBSERVER_APPLICATION_OPERATION");
|
|
1505
1517
|
},
|
|
1506
1518
|
});
|
|
1519
|
+
reconciliationCoordinator.start();
|
|
1507
1520
|
return Object.freeze({
|
|
1508
1521
|
route,
|
|
1509
1522
|
browserOwnerPorts,
|
|
@@ -1514,30 +1527,10 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1514
1527
|
taskApplication,
|
|
1515
1528
|
approvalApplication,
|
|
1516
1529
|
observerApplication,
|
|
1517
|
-
async lookup(
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
const record = await execution.invoke("getExecution", {
|
|
1522
|
-
...value,
|
|
1523
|
-
callerRef: authenticatedRoleRef,
|
|
1524
|
-
});
|
|
1525
|
-
return admitExecutionRead(authenticatedRoleRef, record);
|
|
1526
|
-
}
|
|
1527
|
-
const scope = await admitExactNodeExecution(value, authenticatedRoleRef);
|
|
1528
|
-
const { workerRef: _suppliedWorkerRef, projectRoot: _suppliedProjectRoot, roleRef: _suppliedRoleRef, callerRef: _suppliedCallerRef, ...ownerInput } = value;
|
|
1529
|
-
const record = await execution.invoke("lookupExecutionIntent", {
|
|
1530
|
-
...ownerInput,
|
|
1531
|
-
callerRef: authenticatedRoleRef,
|
|
1532
|
-
roleRef: authenticatedRoleRef,
|
|
1533
|
-
taskId: scope.taskId,
|
|
1534
|
-
nodeId: scope.nodeId,
|
|
1535
|
-
runNo: scope.runNo,
|
|
1536
|
-
workerRef: scope.workerRef,
|
|
1537
|
-
});
|
|
1538
|
-
return admitExecutionRead(authenticatedRoleRef, record);
|
|
1539
|
-
}
|
|
1540
|
-
return route(operationId, authenticatedRoleRef, value);
|
|
1530
|
+
async lookup(_operationId, _authenticatedRoleRef, _input) {
|
|
1531
|
+
throw Object.assign(new Error("ACTION_RESULT_LOOKUP_UNSUPPORTED"), {
|
|
1532
|
+
httpStatus: 409,
|
|
1533
|
+
});
|
|
1541
1534
|
},
|
|
1542
1535
|
async readiness() {
|
|
1543
1536
|
const diagnostics = taskStore.diagnostics();
|
|
@@ -1566,6 +1559,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1566
1559
|
};
|
|
1567
1560
|
},
|
|
1568
1561
|
close() {
|
|
1562
|
+
reconciliationCoordinator?.stop();
|
|
1569
1563
|
agent.close();
|
|
1570
1564
|
taskStore.close();
|
|
1571
1565
|
},
|
|
@@ -1722,7 +1716,7 @@ export function createPlatformHost(input) {
|
|
|
1722
1716
|
liveness: lifecycle === "STOPPED" ? "DOWN" : "UP",
|
|
1723
1717
|
transport: server ? "UP" : "DOWN",
|
|
1724
1718
|
readiness: accepting &&
|
|
1725
|
-
|
|
1719
|
+
[dependencies.task, dependencies.agent].every((item) => item.status === "READY")
|
|
1726
1720
|
? "READY"
|
|
1727
1721
|
: "NOT_READY",
|
|
1728
1722
|
accepting,
|
|
@@ -1821,8 +1815,7 @@ export function createPlatformHost(input) {
|
|
|
1821
1815
|
log("DEPENDENCY_INITIALIZATION_STARTED", {
|
|
1822
1816
|
order: ["task", "agent", "execution-client", "model-client"],
|
|
1823
1817
|
});
|
|
1824
|
-
graph = await constructGraph(input.config, executionTransportCredential, modelTransportCredential);
|
|
1825
|
-
await graph.readiness();
|
|
1818
|
+
graph = await constructGraph(input.config, executionTransportCredential, modelTransportCredential, input.resolveOwnerConnection, input.resolveLocalToolConnection);
|
|
1826
1819
|
server = createServer((request, response) => {
|
|
1827
1820
|
const work = (async () => {
|
|
1828
1821
|
const url = new URL(request.url ?? "/", "http://platform-host.local");
|
|
@@ -2044,11 +2037,16 @@ export function createPlatformHost(input) {
|
|
|
2044
2037
|
const operationId = decodeURIComponent(lookup ? suffix.slice(0, -"/result".length) : suffix);
|
|
2045
2038
|
const result = lookup
|
|
2046
2039
|
? await graph.lookup(operationId, authenticatedRoleRef, body.input)
|
|
2047
|
-
: await graph.route(operationId, authenticatedRoleRef, body.input,
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2040
|
+
: await graph.route(operationId, authenticatedRoleRef, body.input, {
|
|
2041
|
+
...(body.fileMaterializationInputs === undefined
|
|
2042
|
+
? {}
|
|
2043
|
+
: {
|
|
2044
|
+
fileMaterializationInputs: body.fileMaterializationInputs,
|
|
2045
|
+
}),
|
|
2046
|
+
...(typeof body.deadlineAt === "string"
|
|
2047
|
+
? { deadlineAt: body.deadlineAt }
|
|
2048
|
+
: {}),
|
|
2049
|
+
});
|
|
2052
2050
|
respond(response, 200, result);
|
|
2053
2051
|
}
|
|
2054
2052
|
catch (error) {
|