@tomflow/proflow-platform-host 0.1.21 → 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 -270
- 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,65 +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
|
-
executionRequestContext: {
|
|
737
|
-
contract: "execution",
|
|
738
|
-
contractVersion: "1.0.0",
|
|
739
|
-
taskId: string(taskContext.taskId, "task.taskId"),
|
|
740
|
-
nodeId: string(executionNodeContext.nodeId, "node.nodeId"),
|
|
741
|
-
runNo: positiveInteger(executionNodeContext.runNo, "node.runNo"),
|
|
742
|
-
},
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
775
|
return taskResult;
|
|
746
776
|
}
|
|
747
|
-
if (operationId === "executeCapability") {
|
|
748
|
-
const scope = await admitExactNodeExecution(input, authenticatedRoleRef);
|
|
749
|
-
const { workerRef: _suppliedWorkerRef, projectRoot: _suppliedProjectRoot, roleRef: _suppliedRoleRef, callerRef: _suppliedCallerRef, ...ownerInput } = input;
|
|
750
|
-
const result = await execution.invoke(operationId, {
|
|
751
|
-
...ownerInput,
|
|
752
|
-
callerRef: authenticatedRoleRef,
|
|
753
|
-
roleRef: authenticatedRoleRef,
|
|
754
|
-
taskId: scope.taskId,
|
|
755
|
-
nodeId: scope.nodeId,
|
|
756
|
-
runNo: scope.runNo,
|
|
757
|
-
workerRef: scope.workerRef,
|
|
758
|
-
});
|
|
759
|
-
// A normal Action may complete synchronously inside the current Worker Turn.
|
|
760
|
-
// Do not manufacture a Browser RESUME for every terminal Execution record.
|
|
761
|
-
// Only a future explicit async-completion signal may emit EXECUTION_RESULT_READY.
|
|
762
|
-
return result;
|
|
763
|
-
}
|
|
764
|
-
if (operationId === "getExecution") {
|
|
765
|
-
const record = await execution.invoke(operationId, {
|
|
766
|
-
...input,
|
|
767
|
-
callerRef: authenticatedRoleRef,
|
|
768
|
-
});
|
|
769
|
-
return admitExecutionRead(authenticatedRoleRef, record);
|
|
770
|
-
}
|
|
771
|
-
if (operationId === "readExecutionOutput") {
|
|
772
|
-
const record = await execution.invoke("getExecution", {
|
|
773
|
-
contract: "execution",
|
|
774
|
-
contractVersion: "1.0.0",
|
|
775
|
-
executionRef: string(input.executionRef, "executionRef"),
|
|
776
|
-
callerRef: authenticatedRoleRef,
|
|
777
|
-
});
|
|
778
|
-
await admitExecutionRead(authenticatedRoleRef, record);
|
|
779
|
-
return execution.invoke(operationId, {
|
|
780
|
-
...input,
|
|
781
|
-
callerRef: authenticatedRoleRef,
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
777
|
throw new Error("OPERATION_NOT_ROUTED");
|
|
785
778
|
};
|
|
786
779
|
const browserOwnerPorts = Object.freeze({
|
|
@@ -813,6 +806,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
813
806
|
actorRef: "platform-host:worker-provisioning",
|
|
814
807
|
idempotencyKey: `browser-bind:${binding.taskId}:${binding.roleRef}:${binding.workerRef}`,
|
|
815
808
|
}));
|
|
809
|
+
reconciliationCoordinator?.kick(binding.taskId);
|
|
816
810
|
},
|
|
817
811
|
}),
|
|
818
812
|
agent: Object.freeze({
|
|
@@ -861,7 +855,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
861
855
|
request.capability === "worker.wake" ||
|
|
862
856
|
request.capability === "collaboration.deliver";
|
|
863
857
|
const internalBrowserCaller = request.callerRef === "platform-host:carrier-controller" ||
|
|
864
|
-
request.callerRef === "
|
|
858
|
+
request.callerRef === "platform-host:task-reconciliation" ||
|
|
865
859
|
request.callerRef === "extension:collaboration-carrier";
|
|
866
860
|
if (!internalBrowserCaller)
|
|
867
861
|
agent.getRegisteredRole(request.callerRef);
|
|
@@ -1131,7 +1125,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1131
1125
|
// Dispatch all three fixed Workers concurrently. When `waitFor` names a
|
|
1132
1126
|
// subset of roleRefs (the J1 Product path), only those results gate the
|
|
1133
1127
|
// return; the remaining Worker creation is a durable, idempotent Execution
|
|
1134
|
-
// effect whose completion
|
|
1128
|
+
// effect whose completion backend reconciliation recovers from the
|
|
1135
1129
|
// durable Task binding facts — never a bare in-memory promise.
|
|
1136
1130
|
const waitFor = new Set(options?.waitFor ?? []);
|
|
1137
1131
|
const shouldWait = (agentPackageRef) => waitFor.size === 0 ||
|
|
@@ -1145,7 +1139,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1145
1139
|
for (const entry of deferred) {
|
|
1146
1140
|
entry.promise.catch(() => {
|
|
1147
1141
|
// Deferred Worker creation failure is recoverable: the durable
|
|
1148
|
-
// binding stays unset, so a later
|
|
1142
|
+
// binding stays unset, so a later backend reconciliation pass re-provisions
|
|
1149
1143
|
// only the missing role.
|
|
1150
1144
|
});
|
|
1151
1145
|
}
|
|
@@ -1155,6 +1149,67 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1155
1149
|
throw failure.reason;
|
|
1156
1150
|
return unwrap(task.queries.getTask({ taskId }));
|
|
1157
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
|
+
});
|
|
1158
1213
|
const taskApplication = Object.freeze({
|
|
1159
1214
|
async invoke(operation, rawInput) {
|
|
1160
1215
|
const value = object(rawInput, "task application input");
|
|
@@ -1185,12 +1240,12 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1185
1240
|
// J1: return once the Product Worker is durably bound; Dev/Test
|
|
1186
1241
|
// continue as recoverable durable effects without blocking Product
|
|
1187
1242
|
// requirement discussion.
|
|
1188
|
-
|
|
1243
|
+
const provisioned = await ensureTaskWorkers(created.taskId, {
|
|
1189
1244
|
waitFor: [roleForPackage("@tomflow/proflow-agent-product").roleRef],
|
|
1190
1245
|
});
|
|
1246
|
+
reconciliationCoordinator?.kick(created.taskId);
|
|
1247
|
+
return provisioned;
|
|
1191
1248
|
}
|
|
1192
|
-
if (operation === "task.ensureWorkers")
|
|
1193
|
-
return ensureTaskWorkers(string(value.taskId, "taskId"));
|
|
1194
1249
|
if (operation === "task.list")
|
|
1195
1250
|
return unwrap(task.queries.listTasks({
|
|
1196
1251
|
...(Array.isArray(value.statuses)
|
|
@@ -1199,13 +1254,17 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1199
1254
|
}));
|
|
1200
1255
|
if (operation === "task.get")
|
|
1201
1256
|
return unwrap(task.queries.getTask({ taskId: string(value.taskId, "taskId") }));
|
|
1202
|
-
if (operation === "task.start")
|
|
1203
|
-
|
|
1204
|
-
|
|
1257
|
+
if (operation === "task.start") {
|
|
1258
|
+
const taskId = string(value.taskId, "taskId");
|
|
1259
|
+
const result = unwrap(task.commands.startTask({
|
|
1260
|
+
taskId,
|
|
1205
1261
|
expectedTaskVersion: Number(value.expectedTaskVersion),
|
|
1206
1262
|
actorRef: "extension:human",
|
|
1207
1263
|
idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
|
|
1208
1264
|
}));
|
|
1265
|
+
reconciliationCoordinator?.kick(taskId);
|
|
1266
|
+
return result;
|
|
1267
|
+
}
|
|
1209
1268
|
if (operation === "message.acknowledge")
|
|
1210
1269
|
return unwrap(task.commands.acknowledgeMessage({
|
|
1211
1270
|
messageId: string(value.messageId, "messageId"),
|
|
@@ -1215,25 +1274,62 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1215
1274
|
actorRef: "extension:human",
|
|
1216
1275
|
idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
|
|
1217
1276
|
}));
|
|
1218
|
-
if (operation === "task.resume")
|
|
1219
|
-
|
|
1220
|
-
|
|
1277
|
+
if (operation === "task.resume") {
|
|
1278
|
+
const taskId = string(value.taskId, "taskId");
|
|
1279
|
+
const result = unwrap(task.commands.resumeTask({
|
|
1280
|
+
taskId,
|
|
1221
1281
|
expectedTaskVersion: positiveInteger(value.expectedTaskVersion, "expectedTaskVersion"),
|
|
1222
1282
|
actorRef: "extension:human",
|
|
1223
1283
|
idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
|
|
1224
1284
|
}));
|
|
1225
|
-
|
|
1226
|
-
return
|
|
1227
|
-
|
|
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,
|
|
1228
1292
|
nodeId: string(value.nodeId, "nodeId"),
|
|
1229
1293
|
reason: string(value.reason, "reason"),
|
|
1230
1294
|
expectedTaskVersion: Number(value.expectedTaskVersion),
|
|
1231
1295
|
actorRef: "extension:human",
|
|
1232
1296
|
idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
|
|
1233
1297
|
}));
|
|
1298
|
+
reconciliationCoordinator?.kick(taskId);
|
|
1299
|
+
return result;
|
|
1300
|
+
}
|
|
1234
1301
|
throw new Error("UNSUPPORTED_TASK_APPLICATION_OPERATION");
|
|
1235
1302
|
},
|
|
1236
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
|
+
};
|
|
1237
1333
|
const approvalApplication = Object.freeze({
|
|
1238
1334
|
async invoke(operation, rawInput) {
|
|
1239
1335
|
const value = object(rawInput, "approval application input");
|
|
@@ -1248,52 +1344,42 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1248
1344
|
...value,
|
|
1249
1345
|
actorRef: "extension:human",
|
|
1250
1346
|
});
|
|
1251
|
-
if (operation === "approval.allow" || operation === "approval.deny")
|
|
1252
|
-
|
|
1347
|
+
if (operation === "approval.allow" || operation === "approval.deny") {
|
|
1348
|
+
const approvalRef = string(value.approvalRef, "approvalRef");
|
|
1349
|
+
const result = await execution.invoke("decideExecutionApproval", {
|
|
1253
1350
|
contract: "execution.approval",
|
|
1254
1351
|
contractVersion: "1.0.0",
|
|
1255
|
-
approvalRef
|
|
1352
|
+
approvalRef,
|
|
1256
1353
|
actorRef: "extension:human",
|
|
1257
1354
|
expectedVersion: Number(value.expectedVersion),
|
|
1258
1355
|
decision: operation === "approval.allow" ? "ALLOW" : "DENY",
|
|
1259
1356
|
...(typeof value.reason === "string" ? { reason: value.reason } : {}),
|
|
1260
1357
|
});
|
|
1261
|
-
|
|
1262
|
-
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", {
|
|
1263
1364
|
contract: "execution.approval",
|
|
1264
1365
|
contractVersion: "1.0.0",
|
|
1265
|
-
approvalRef
|
|
1366
|
+
approvalRef,
|
|
1266
1367
|
actorRef: "extension:human",
|
|
1267
1368
|
expectedVersion: Number(value.expectedVersion),
|
|
1268
1369
|
reason: string(value.reason, "reason"),
|
|
1269
1370
|
});
|
|
1371
|
+
await reconcileApproval(approvalRef);
|
|
1372
|
+
return result;
|
|
1373
|
+
}
|
|
1270
1374
|
throw new Error("UNSUPPORTED_APPROVAL_APPLICATION_OPERATION");
|
|
1271
1375
|
},
|
|
1272
1376
|
});
|
|
1273
1377
|
const observerApplication = Object.freeze({
|
|
1274
1378
|
async invoke(operation, rawInput) {
|
|
1275
1379
|
const value = object(rawInput, "observer application input");
|
|
1276
|
-
if (operation === "task.
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
const approval = object(await execution.invoke("getExecutionApproval", {
|
|
1280
|
-
approvalRef: string(value.approvalRef, "approvalRef"),
|
|
1281
|
-
}), "approval fact");
|
|
1282
|
-
const executionFact = object(await execution.invoke("getExecution", {
|
|
1283
|
-
executionRef: string(approval.executionRef, "executionRef"),
|
|
1284
|
-
callerRef: string(approval.callerRef, "callerRef"),
|
|
1285
|
-
}), "execution fact");
|
|
1286
|
-
const runNo = Number(executionFact.runNo);
|
|
1287
|
-
if (!Number.isInteger(runNo) || runNo <= 0)
|
|
1288
|
-
throw new Error("EXECUTION_RUN_GENERATION_REQUIRED");
|
|
1289
|
-
return {
|
|
1290
|
-
executionRef: string(executionFact.executionRef, "executionRef"),
|
|
1291
|
-
taskId: string(executionFact.taskId, "taskId"),
|
|
1292
|
-
nodeId: string(executionFact.nodeId, "nodeId"),
|
|
1293
|
-
runNo,
|
|
1294
|
-
roleRef: string(executionFact.roleRef, "roleRef"),
|
|
1295
|
-
workerRef: string(executionFact.workerRef, "workerRef"),
|
|
1296
|
-
};
|
|
1380
|
+
if (operation === "task.reconcileAll") {
|
|
1381
|
+
void reconciliationCoordinator?.sweep();
|
|
1382
|
+
return { scheduled: true };
|
|
1297
1383
|
}
|
|
1298
1384
|
if (operation === "browser.permission.classify") {
|
|
1299
1385
|
const roleRef = string(value.roleRef, "roleRef");
|
|
@@ -1378,79 +1464,6 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1378
1464
|
});
|
|
1379
1465
|
return { reported: true };
|
|
1380
1466
|
}
|
|
1381
|
-
if (operation === "execution.listSignals")
|
|
1382
|
-
return execution.invoke("listExecutionObserverSignals", {
|
|
1383
|
-
limit: Number(value.limit ?? 50),
|
|
1384
|
-
});
|
|
1385
|
-
if (operation === "execution.ackSignal")
|
|
1386
|
-
return execution.invoke("acknowledgeExecutionObserverSignal", {
|
|
1387
|
-
signalRef: string(value.signalRef, "signalRef"),
|
|
1388
|
-
});
|
|
1389
|
-
if (operation === "task.wake") {
|
|
1390
|
-
const taskId = string(value.taskId, "taskId");
|
|
1391
|
-
const nodeId = string(value.nodeId, "nodeId");
|
|
1392
|
-
const runNo = Number(value.runNo);
|
|
1393
|
-
const roleRef = string(value.roleRef, "roleRef");
|
|
1394
|
-
const workerRef = string(value.workerRef, "workerRef");
|
|
1395
|
-
const trigger = string(value.trigger, "trigger");
|
|
1396
|
-
const underlyingRef = typeof value.underlyingRef === "string"
|
|
1397
|
-
? value.underlyingRef
|
|
1398
|
-
: "none";
|
|
1399
|
-
const wakeExecution = object(await execution.invoke("executeCapability", {
|
|
1400
|
-
contract: "execution",
|
|
1401
|
-
contractVersion: "1.0.0",
|
|
1402
|
-
idempotencyKey: `task-observer-wake:${taskId}:${nodeId}:${runNo}:${trigger}:${underlyingRef}`,
|
|
1403
|
-
callerRef: "extension:task-observer",
|
|
1404
|
-
correlationId: `task-observer:${taskId}:${nodeId}:${runNo}`,
|
|
1405
|
-
taskId,
|
|
1406
|
-
nodeId,
|
|
1407
|
-
runNo,
|
|
1408
|
-
roleRef,
|
|
1409
|
-
workerRef,
|
|
1410
|
-
capability: "worker.wake",
|
|
1411
|
-
input: {
|
|
1412
|
-
roleRef,
|
|
1413
|
-
workerRef,
|
|
1414
|
-
taskId,
|
|
1415
|
-
nodeId,
|
|
1416
|
-
runNo,
|
|
1417
|
-
trigger,
|
|
1418
|
-
fingerprint: `wake:${taskId}:${nodeId}:${runNo}:${trigger}:${underlyingRef}`,
|
|
1419
|
-
},
|
|
1420
|
-
}), "task wake execution");
|
|
1421
|
-
if (wakeExecution.status !== "SUCCEEDED" ||
|
|
1422
|
-
wakeExecution.sideEffectState !== "APPLIED")
|
|
1423
|
-
throw new Error(`TASK_WAKE_NOT_CONFIRMED:${String(wakeExecution.status)}:${String(wakeExecution.sideEffectState)}`);
|
|
1424
|
-
return wakeExecution;
|
|
1425
|
-
}
|
|
1426
|
-
if (operation === "task.diagnostic") {
|
|
1427
|
-
const response = object(await model.invoke("infer", {
|
|
1428
|
-
contractVersion: "1.0.0",
|
|
1429
|
-
specRef: "task.diagnostic.v1",
|
|
1430
|
-
mode: "reason",
|
|
1431
|
-
priority: "business",
|
|
1432
|
-
trace: {
|
|
1433
|
-
callerRef: "extension:task-observer",
|
|
1434
|
-
correlationId: string(value.correlationId, "correlationId"),
|
|
1435
|
-
taskId: string(value.taskId, "taskId"),
|
|
1436
|
-
nodeId: string(value.nodeId, "nodeId"),
|
|
1437
|
-
},
|
|
1438
|
-
payload: value.payload,
|
|
1439
|
-
}), "task diagnostic inference");
|
|
1440
|
-
if (response.status !== "SUCCEEDED") {
|
|
1441
|
-
const error = typeof response.error === "object" && response.error !== null
|
|
1442
|
-
? response.error
|
|
1443
|
-
: {};
|
|
1444
|
-
const code = error.code === "CONTEXT_TOO_LARGE"
|
|
1445
|
-
? "CONTEXT_TOO_LARGE"
|
|
1446
|
-
: error.code === "MODEL_UNAVAILABLE" ||
|
|
1447
|
-
error.code === "CAPABILITY_UNSUPPORTED"
|
|
1448
|
-
? "REASON_UNAVAILABLE"
|
|
1449
|
-
: "REASON_FAILED";
|
|
1450
|
-
return { ok: false, errorCode: code };
|
|
1451
|
-
}
|
|
1452
|
-
return taskDiagnosticReasonResultSchema.parse(response.data);
|
|
1453
|
-
}
|
|
1454
1467
|
if (operation === "system.view")
|
|
1455
1468
|
return boundedSystemView(string(value.view, "view"));
|
|
1456
1469
|
if (operation === "system.drilldown") {
|
|
@@ -1503,6 +1516,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1503
1516
|
throw new Error("UNSUPPORTED_OBSERVER_APPLICATION_OPERATION");
|
|
1504
1517
|
},
|
|
1505
1518
|
});
|
|
1519
|
+
reconciliationCoordinator.start();
|
|
1506
1520
|
return Object.freeze({
|
|
1507
1521
|
route,
|
|
1508
1522
|
browserOwnerPorts,
|
|
@@ -1513,30 +1527,10 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1513
1527
|
taskApplication,
|
|
1514
1528
|
approvalApplication,
|
|
1515
1529
|
observerApplication,
|
|
1516
|
-
async lookup(
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
const record = await execution.invoke("getExecution", {
|
|
1521
|
-
...value,
|
|
1522
|
-
callerRef: authenticatedRoleRef,
|
|
1523
|
-
});
|
|
1524
|
-
return admitExecutionRead(authenticatedRoleRef, record);
|
|
1525
|
-
}
|
|
1526
|
-
const scope = await admitExactNodeExecution(value, authenticatedRoleRef);
|
|
1527
|
-
const { workerRef: _suppliedWorkerRef, projectRoot: _suppliedProjectRoot, roleRef: _suppliedRoleRef, callerRef: _suppliedCallerRef, ...ownerInput } = value;
|
|
1528
|
-
const record = await execution.invoke("lookupExecutionIntent", {
|
|
1529
|
-
...ownerInput,
|
|
1530
|
-
callerRef: authenticatedRoleRef,
|
|
1531
|
-
roleRef: authenticatedRoleRef,
|
|
1532
|
-
taskId: scope.taskId,
|
|
1533
|
-
nodeId: scope.nodeId,
|
|
1534
|
-
runNo: scope.runNo,
|
|
1535
|
-
workerRef: scope.workerRef,
|
|
1536
|
-
});
|
|
1537
|
-
return admitExecutionRead(authenticatedRoleRef, record);
|
|
1538
|
-
}
|
|
1539
|
-
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
|
+
});
|
|
1540
1534
|
},
|
|
1541
1535
|
async readiness() {
|
|
1542
1536
|
const diagnostics = taskStore.diagnostics();
|
|
@@ -1565,6 +1559,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1565
1559
|
};
|
|
1566
1560
|
},
|
|
1567
1561
|
close() {
|
|
1562
|
+
reconciliationCoordinator?.stop();
|
|
1568
1563
|
agent.close();
|
|
1569
1564
|
taskStore.close();
|
|
1570
1565
|
},
|
|
@@ -1721,7 +1716,7 @@ export function createPlatformHost(input) {
|
|
|
1721
1716
|
liveness: lifecycle === "STOPPED" ? "DOWN" : "UP",
|
|
1722
1717
|
transport: server ? "UP" : "DOWN",
|
|
1723
1718
|
readiness: accepting &&
|
|
1724
|
-
|
|
1719
|
+
[dependencies.task, dependencies.agent].every((item) => item.status === "READY")
|
|
1725
1720
|
? "READY"
|
|
1726
1721
|
: "NOT_READY",
|
|
1727
1722
|
accepting,
|
|
@@ -1820,8 +1815,7 @@ export function createPlatformHost(input) {
|
|
|
1820
1815
|
log("DEPENDENCY_INITIALIZATION_STARTED", {
|
|
1821
1816
|
order: ["task", "agent", "execution-client", "model-client"],
|
|
1822
1817
|
});
|
|
1823
|
-
graph = await constructGraph(input.config, executionTransportCredential, modelTransportCredential);
|
|
1824
|
-
await graph.readiness();
|
|
1818
|
+
graph = await constructGraph(input.config, executionTransportCredential, modelTransportCredential, input.resolveOwnerConnection, input.resolveLocalToolConnection);
|
|
1825
1819
|
server = createServer((request, response) => {
|
|
1826
1820
|
const work = (async () => {
|
|
1827
1821
|
const url = new URL(request.url ?? "/", "http://platform-host.local");
|
|
@@ -2043,11 +2037,16 @@ export function createPlatformHost(input) {
|
|
|
2043
2037
|
const operationId = decodeURIComponent(lookup ? suffix.slice(0, -"/result".length) : suffix);
|
|
2044
2038
|
const result = lookup
|
|
2045
2039
|
? await graph.lookup(operationId, authenticatedRoleRef, body.input)
|
|
2046
|
-
: await graph.route(operationId, authenticatedRoleRef, body.input,
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
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
|
+
});
|
|
2051
2050
|
respond(response, 200, result);
|
|
2052
2051
|
}
|
|
2053
2052
|
catch (error) {
|