@polos/sdk 0.2.2 → 0.2.3
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/dist/index.cjs +250 -191
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +250 -191
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -748,6 +748,11 @@ var OrchestratorClient = class {
|
|
|
748
748
|
if (request.waitForSubworkflow !== void 0)
|
|
749
749
|
body["wait_for_subworkflow"] = request.waitForSubworkflow;
|
|
750
750
|
if (request.otelTraceparent !== void 0) body["otel_traceparent"] = request.otelTraceparent;
|
|
751
|
+
if (request.channelContext !== void 0)
|
|
752
|
+
body["channel_context"] = {
|
|
753
|
+
channel_id: request.channelContext.channelId,
|
|
754
|
+
source: request.channelContext.source
|
|
755
|
+
};
|
|
751
756
|
return this.request("POST", "/api/v1/workflows/batch_run", {
|
|
752
757
|
body
|
|
753
758
|
});
|
|
@@ -3381,6 +3386,187 @@ function defineAgent(config) {
|
|
|
3381
3386
|
});
|
|
3382
3387
|
return agentWorkflow;
|
|
3383
3388
|
}
|
|
3389
|
+
|
|
3390
|
+
// src/channels/slack.ts
|
|
3391
|
+
var SlackChannel = class {
|
|
3392
|
+
id = "slack";
|
|
3393
|
+
outputMode = "per_step";
|
|
3394
|
+
config;
|
|
3395
|
+
constructor(config) {
|
|
3396
|
+
if (!config.botToken.startsWith("xoxb-")) {
|
|
3397
|
+
throw new Error(
|
|
3398
|
+
`Invalid Slack bot token: must start with "xoxb-". Use the Bot User OAuth Token from your Slack app's OAuth & Permissions page.`
|
|
3399
|
+
);
|
|
3400
|
+
}
|
|
3401
|
+
this.config = config;
|
|
3402
|
+
}
|
|
3403
|
+
async notify(notification) {
|
|
3404
|
+
const overrides = notification.channelOverrides;
|
|
3405
|
+
const channel = overrides?.["channel"] ?? this.config.defaultChannel;
|
|
3406
|
+
const threadTs = overrides?.["thread_ts"] ?? overrides?.["threadTs"];
|
|
3407
|
+
const blocks = this.buildBlocks(notification);
|
|
3408
|
+
const text = notification.title ?? "Agent needs your input";
|
|
3409
|
+
const messageTs = await this.postMessage(channel, threadTs, text, blocks);
|
|
3410
|
+
return {
|
|
3411
|
+
slack_channel: channel,
|
|
3412
|
+
slack_message_ts: messageTs,
|
|
3413
|
+
slack_blocks: blocks
|
|
3414
|
+
};
|
|
3415
|
+
}
|
|
3416
|
+
async sendOutput(context2, event) {
|
|
3417
|
+
const channel = context2.source["channel"];
|
|
3418
|
+
const threadTs = context2.source["threadTs"];
|
|
3419
|
+
if (!channel) return;
|
|
3420
|
+
const text = this.formatOutputEvent(event);
|
|
3421
|
+
if (!text) return;
|
|
3422
|
+
await this.postMessage(channel, threadTs, text);
|
|
3423
|
+
}
|
|
3424
|
+
async postMessage(channel, threadTs, text, blocks) {
|
|
3425
|
+
const body = { channel, text };
|
|
3426
|
+
if (threadTs) body["thread_ts"] = threadTs;
|
|
3427
|
+
if (blocks) body["blocks"] = blocks;
|
|
3428
|
+
const response = await fetch("https://slack.com/api/chat.postMessage", {
|
|
3429
|
+
method: "POST",
|
|
3430
|
+
headers: {
|
|
3431
|
+
Authorization: `Bearer ${this.config.botToken}`,
|
|
3432
|
+
"Content-Type": "application/json"
|
|
3433
|
+
},
|
|
3434
|
+
body: JSON.stringify(body)
|
|
3435
|
+
});
|
|
3436
|
+
const data = await response.json();
|
|
3437
|
+
if (!data.ok) {
|
|
3438
|
+
throw new Error(`Slack API error: ${data.error ?? "unknown"}`);
|
|
3439
|
+
}
|
|
3440
|
+
return data.ts ?? "";
|
|
3441
|
+
}
|
|
3442
|
+
formatOutputEvent(event) {
|
|
3443
|
+
const eventType = event.eventType;
|
|
3444
|
+
if (eventType === "workflow_finish" || eventType === "agent_finish") {
|
|
3445
|
+
const metadata = event.data["_metadata"];
|
|
3446
|
+
const rawResult = event.data["result"];
|
|
3447
|
+
const error = event.data["error"];
|
|
3448
|
+
const workflowId = metadata?.["workflow_id"];
|
|
3449
|
+
if (error) {
|
|
3450
|
+
return `\u274C *${workflowId ?? "Workflow"} failed:* ${error}`;
|
|
3451
|
+
}
|
|
3452
|
+
const result = typeof rawResult === "object" && rawResult !== null && "result" in rawResult ? rawResult["result"] : rawResult;
|
|
3453
|
+
const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
3454
|
+
if (resultStr) {
|
|
3455
|
+
return resultStr;
|
|
3456
|
+
}
|
|
3457
|
+
return `\u2705 *${workflowId ?? "Workflow"} finished*`;
|
|
3458
|
+
}
|
|
3459
|
+
if (eventType === "tool_call") {
|
|
3460
|
+
const toolCall = event.data["tool_call"];
|
|
3461
|
+
if (toolCall) {
|
|
3462
|
+
const fn = toolCall["function"];
|
|
3463
|
+
const name = fn?.["name"];
|
|
3464
|
+
if (name) {
|
|
3465
|
+
return `\u{1F527} Calling tool: \`${name}\``;
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
return null;
|
|
3469
|
+
}
|
|
3470
|
+
if (eventType === "step_finish") {
|
|
3471
|
+
const stepKey = event.data["step_key"];
|
|
3472
|
+
const error = event.data["error"];
|
|
3473
|
+
if (error) {
|
|
3474
|
+
return `\u26A0\uFE0F Step \`${stepKey ?? "unknown"}\` failed: ${error}`;
|
|
3475
|
+
}
|
|
3476
|
+
return null;
|
|
3477
|
+
}
|
|
3478
|
+
return null;
|
|
3479
|
+
}
|
|
3480
|
+
buildBlocks(n) {
|
|
3481
|
+
const blocks = [];
|
|
3482
|
+
blocks.push({
|
|
3483
|
+
type: "header",
|
|
3484
|
+
text: { type: "plain_text", text: n.title ?? "Agent needs your input" }
|
|
3485
|
+
});
|
|
3486
|
+
if (n.description) {
|
|
3487
|
+
blocks.push({
|
|
3488
|
+
type: "section",
|
|
3489
|
+
text: { type: "mrkdwn", text: n.description }
|
|
3490
|
+
});
|
|
3491
|
+
}
|
|
3492
|
+
if (n.source || n.tool) {
|
|
3493
|
+
const parts = [];
|
|
3494
|
+
if (n.source) parts.push(`*Source:* ${n.source}`);
|
|
3495
|
+
if (n.tool) parts.push(`*Tool:* \`${n.tool}\``);
|
|
3496
|
+
blocks.push({
|
|
3497
|
+
type: "context",
|
|
3498
|
+
elements: [{ type: "mrkdwn", text: parts.join(" | ") }]
|
|
3499
|
+
});
|
|
3500
|
+
}
|
|
3501
|
+
if (n.context && Object.keys(n.context).length > 0) {
|
|
3502
|
+
const contextText = JSON.stringify(n.context, null, 2);
|
|
3503
|
+
blocks.push({
|
|
3504
|
+
type: "section",
|
|
3505
|
+
text: { type: "mrkdwn", text: "```" + contextText + "```" }
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
if (n.expiresAt) {
|
|
3509
|
+
blocks.push({
|
|
3510
|
+
type: "context",
|
|
3511
|
+
elements: [{ type: "mrkdwn", text: `Expires: ${n.expiresAt}` }]
|
|
3512
|
+
});
|
|
3513
|
+
}
|
|
3514
|
+
if (this.isSimpleApproval(n)) {
|
|
3515
|
+
const approveValue = JSON.stringify({
|
|
3516
|
+
executionId: n.executionId,
|
|
3517
|
+
stepKey: n.stepKey,
|
|
3518
|
+
approved: true
|
|
3519
|
+
});
|
|
3520
|
+
const rejectValue = JSON.stringify({
|
|
3521
|
+
executionId: n.executionId,
|
|
3522
|
+
stepKey: n.stepKey,
|
|
3523
|
+
approved: false
|
|
3524
|
+
});
|
|
3525
|
+
blocks.push({
|
|
3526
|
+
type: "actions",
|
|
3527
|
+
elements: [
|
|
3528
|
+
{
|
|
3529
|
+
type: "button",
|
|
3530
|
+
action_id: "polos_approve",
|
|
3531
|
+
text: { type: "plain_text", text: "Approve" },
|
|
3532
|
+
style: "primary",
|
|
3533
|
+
value: approveValue
|
|
3534
|
+
},
|
|
3535
|
+
{
|
|
3536
|
+
type: "button",
|
|
3537
|
+
action_id: "polos_reject",
|
|
3538
|
+
text: { type: "plain_text", text: "Reject" },
|
|
3539
|
+
style: "danger",
|
|
3540
|
+
value: rejectValue
|
|
3541
|
+
},
|
|
3542
|
+
{
|
|
3543
|
+
type: "button",
|
|
3544
|
+
text: { type: "plain_text", text: "View Details" },
|
|
3545
|
+
url: n.approvalUrl
|
|
3546
|
+
}
|
|
3547
|
+
]
|
|
3548
|
+
});
|
|
3549
|
+
} else {
|
|
3550
|
+
blocks.push({
|
|
3551
|
+
type: "actions",
|
|
3552
|
+
elements: [
|
|
3553
|
+
{
|
|
3554
|
+
type: "button",
|
|
3555
|
+
text: { type: "plain_text", text: "Respond" },
|
|
3556
|
+
url: n.approvalUrl,
|
|
3557
|
+
style: "primary"
|
|
3558
|
+
}
|
|
3559
|
+
]
|
|
3560
|
+
});
|
|
3561
|
+
}
|
|
3562
|
+
return blocks;
|
|
3563
|
+
}
|
|
3564
|
+
isSimpleApproval(n) {
|
|
3565
|
+
const fields = n.formFields;
|
|
3566
|
+
if (!fields || fields.length === 0) return false;
|
|
3567
|
+
return fields.some((f) => f["key"] === "approved" && f["type"] === "boolean");
|
|
3568
|
+
}
|
|
3569
|
+
};
|
|
3384
3570
|
var logger4 = createLogger({ name: "worker-server" });
|
|
3385
3571
|
var WorkerServer = class {
|
|
3386
3572
|
app;
|
|
@@ -4234,7 +4420,8 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4234
4420
|
otelTraceparent: getCurrentTraceparent(),
|
|
4235
4421
|
waitForSubworkflow: options?.waitForSubworkflow ?? false,
|
|
4236
4422
|
initialState: options?.initialState,
|
|
4237
|
-
runTimeoutSeconds: options?.runTimeoutSeconds
|
|
4423
|
+
runTimeoutSeconds: options?.runTimeoutSeconds,
|
|
4424
|
+
channelContext
|
|
4238
4425
|
});
|
|
4239
4426
|
if (options?.waitForSubworkflow) {
|
|
4240
4427
|
return [void 0, false];
|
|
@@ -4422,7 +4609,8 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4422
4609
|
sessionId: execCtx.sessionId,
|
|
4423
4610
|
userId: execCtx.userId,
|
|
4424
4611
|
waitForSubworkflow: false,
|
|
4425
|
-
otelTraceparent: getCurrentTraceparent()
|
|
4612
|
+
otelTraceparent: getCurrentTraceparent(),
|
|
4613
|
+
channelContext
|
|
4426
4614
|
});
|
|
4427
4615
|
const handleData = response.executions.map((exec, i) => {
|
|
4428
4616
|
const item = items[i];
|
|
@@ -4498,7 +4686,8 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4498
4686
|
sessionId: execCtx.sessionId,
|
|
4499
4687
|
userId: execCtx.userId,
|
|
4500
4688
|
waitForSubworkflow: true,
|
|
4501
|
-
otelTraceparent: getCurrentTraceparent()
|
|
4689
|
+
otelTraceparent: getCurrentTraceparent(),
|
|
4690
|
+
channelContext
|
|
4502
4691
|
});
|
|
4503
4692
|
const workflowIds = items.map((item) => typeof item.workflow === "string" ? item.workflow : item.workflow.id).join(", ");
|
|
4504
4693
|
throw new WaitError(`Waiting for sub-workflows [${workflowIds}] to complete`, {
|
|
@@ -4694,7 +4883,7 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4694
4883
|
if (channelContext !== void 0) {
|
|
4695
4884
|
notification.channelContext = channelContext;
|
|
4696
4885
|
}
|
|
4697
|
-
await Promise.allSettled(
|
|
4886
|
+
const notifyResults = await Promise.allSettled(
|
|
4698
4887
|
channels.map(async (ch) => {
|
|
4699
4888
|
try {
|
|
4700
4889
|
let overrides = notifyConfig?.[ch.id];
|
|
@@ -4702,12 +4891,35 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4702
4891
|
overrides = { ...channelContext.source, ...overrides };
|
|
4703
4892
|
}
|
|
4704
4893
|
const n = overrides !== void 0 ? { ...notification, channelOverrides: overrides } : notification;
|
|
4705
|
-
await ch.notify(n);
|
|
4894
|
+
const meta = await ch.notify(n);
|
|
4895
|
+
return meta ? { channelId: ch.id, ...meta } : void 0;
|
|
4706
4896
|
} catch (err) {
|
|
4707
4897
|
logger6.warn(`Channel ${ch.id} notification failed`, { error: String(err) });
|
|
4898
|
+
return void 0;
|
|
4708
4899
|
}
|
|
4709
4900
|
})
|
|
4710
4901
|
);
|
|
4902
|
+
const metaEntries = [];
|
|
4903
|
+
for (const r of notifyResults) {
|
|
4904
|
+
if (r.status === "fulfilled" && r.value != null) {
|
|
4905
|
+
metaEntries.push(r.value);
|
|
4906
|
+
}
|
|
4907
|
+
}
|
|
4908
|
+
if (metaEntries.length > 0) {
|
|
4909
|
+
orchestratorClient.publishEvent({
|
|
4910
|
+
topic,
|
|
4911
|
+
events: [
|
|
4912
|
+
{
|
|
4913
|
+
eventType: `notification_meta_${key}`,
|
|
4914
|
+
data: { channels: metaEntries }
|
|
4915
|
+
}
|
|
4916
|
+
],
|
|
4917
|
+
executionId: execCtx.executionId,
|
|
4918
|
+
rootExecutionId: execCtx.rootExecutionId
|
|
4919
|
+
}).catch((err) => {
|
|
4920
|
+
logger6.warn("Failed to publish notification metadata", { error: String(err) });
|
|
4921
|
+
});
|
|
4922
|
+
}
|
|
4711
4923
|
}
|
|
4712
4924
|
throw new WaitError(`Waiting for resume event: ${topic}`, { topic });
|
|
4713
4925
|
},
|
|
@@ -6295,10 +6507,24 @@ var Worker = class {
|
|
|
6295
6507
|
state = "stopped";
|
|
6296
6508
|
heartbeatInterval = null;
|
|
6297
6509
|
activeExecutions = /* @__PURE__ */ new Map();
|
|
6510
|
+
/** Tracks running channel bridges so we don't start duplicates on re-dispatch. */
|
|
6511
|
+
activeChannelBridges = /* @__PURE__ */ new Set();
|
|
6298
6512
|
signalHandler = null;
|
|
6299
6513
|
constructor(config) {
|
|
6300
6514
|
this.config = config;
|
|
6301
6515
|
this.channels = config.channels ?? [];
|
|
6516
|
+
if (!this.channels.some((ch) => ch.id === "slack")) {
|
|
6517
|
+
const slackBotToken = process.env["SLACK_BOT_TOKEN"];
|
|
6518
|
+
if (slackBotToken) {
|
|
6519
|
+
this.channels.push(
|
|
6520
|
+
new SlackChannel({
|
|
6521
|
+
botToken: slackBotToken,
|
|
6522
|
+
defaultChannel: process.env["SLACK_CHANNEL"] ?? "#general"
|
|
6523
|
+
})
|
|
6524
|
+
);
|
|
6525
|
+
logger9.info("Auto-registered SlackChannel from SLACK_BOT_TOKEN env var");
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6302
6528
|
this.maxConcurrentWorkflows = config.maxConcurrentWorkflows ?? 100;
|
|
6303
6529
|
this.workerServerUrl = config.workerServerUrl ?? `http://localhost:${String(config.port ?? 8e3)}`;
|
|
6304
6530
|
this.port = config.port ?? 8e3;
|
|
@@ -6832,6 +7058,11 @@ var Worker = class {
|
|
|
6832
7058
|
};
|
|
6833
7059
|
const hasWorkflowChannels = workflow.config.channels !== void 0;
|
|
6834
7060
|
const resolvedChannels = workflow.config.channels ?? this.channels;
|
|
7061
|
+
if (data.channelContext && !data.parentExecutionId) {
|
|
7062
|
+
const rootExecId = data.rootExecutionId ?? executionId;
|
|
7063
|
+
const rootWfId = data.rootWorkflowId ?? workflowId;
|
|
7064
|
+
this.startChannelBridge(rootExecId, rootWfId, data.channelContext, workflow);
|
|
7065
|
+
}
|
|
6835
7066
|
result = await executeWorkflow({
|
|
6836
7067
|
workflow,
|
|
6837
7068
|
payload: data.payload,
|
|
@@ -6843,9 +7074,6 @@ var Worker = class {
|
|
|
6843
7074
|
sandboxManager: this.sandboxManager,
|
|
6844
7075
|
channelContext: hasWorkflowChannels ? void 0 : data.channelContext
|
|
6845
7076
|
});
|
|
6846
|
-
if (data.channelContext) {
|
|
6847
|
-
this.startChannelBridge(executionId, workflowId, data.channelContext, workflow);
|
|
6848
|
-
}
|
|
6849
7077
|
await this.handleExecutionResult(executionId, workflowId, context2, result);
|
|
6850
7078
|
retryableFailure = !result.success && !result.waiting && (result.retryable ?? true);
|
|
6851
7079
|
} catch (error) {
|
|
@@ -6988,15 +7216,20 @@ var Worker = class {
|
|
|
6988
7216
|
/**
|
|
6989
7217
|
* Start a channel bridge that streams execution events back to the originating channel.
|
|
6990
7218
|
*/
|
|
6991
|
-
startChannelBridge(
|
|
7219
|
+
startChannelBridge(rootExecutionId, rootWorkflowId, channelCtx, workflow) {
|
|
7220
|
+
if (this.activeChannelBridges.has(rootExecutionId)) return;
|
|
6992
7221
|
const channel = this.channels.find((ch) => ch.id === channelCtx.channelId);
|
|
6993
|
-
if (!channel
|
|
7222
|
+
if (!channel) {
|
|
7223
|
+
logger9.error(
|
|
7224
|
+
`No channel registered for "${channelCtx.channelId}". ` + (channelCtx.channelId === "slack" ? "Set the SLACK_BOT_TOKEN environment variable so the worker can respond to Slack." : `Register a channel with id "${channelCtx.channelId}" on the worker.`),
|
|
7225
|
+
{ executionId: rootExecutionId }
|
|
7226
|
+
);
|
|
7227
|
+
return;
|
|
7228
|
+
}
|
|
7229
|
+
if (!channel.sendOutput) return;
|
|
6994
7230
|
const outputMode = workflow.config["channelOutputMode"] ?? channel.outputMode ?? "per_step";
|
|
6995
7231
|
if (outputMode === "none") return;
|
|
6996
|
-
|
|
6997
|
-
if (!execution) return;
|
|
6998
|
-
const rootExecutionId = execution.abortController.signal.aborted ? executionId : executionId;
|
|
6999
|
-
const rootWorkflowId = workflowId;
|
|
7232
|
+
this.activeChannelBridges.add(rootExecutionId);
|
|
7000
7233
|
void (async () => {
|
|
7001
7234
|
try {
|
|
7002
7235
|
const stream = this.orchestratorClient.streamEvents({
|
|
@@ -7004,13 +7237,14 @@ var Worker = class {
|
|
|
7004
7237
|
workflowRunId: rootExecutionId
|
|
7005
7238
|
});
|
|
7006
7239
|
for await (const event of stream) {
|
|
7007
|
-
if (execution.abortController.signal.aborted) break;
|
|
7008
7240
|
if (shouldForwardEvent(event, outputMode)) {
|
|
7009
7241
|
await channel.sendOutput?.(channelCtx, event);
|
|
7010
7242
|
}
|
|
7011
7243
|
}
|
|
7012
7244
|
} catch (err) {
|
|
7013
|
-
logger9.warn("Channel bridge error", { error: String(err), executionId });
|
|
7245
|
+
logger9.warn("Channel bridge error", { error: String(err), executionId: rootExecutionId });
|
|
7246
|
+
} finally {
|
|
7247
|
+
this.activeChannelBridges.delete(rootExecutionId);
|
|
7014
7248
|
}
|
|
7015
7249
|
})();
|
|
7016
7250
|
}
|
|
@@ -7715,181 +7949,6 @@ function sandboxTools(config) {
|
|
|
7715
7949
|
return tools;
|
|
7716
7950
|
}
|
|
7717
7951
|
|
|
7718
|
-
// src/channels/slack.ts
|
|
7719
|
-
var SlackChannel = class {
|
|
7720
|
-
id = "slack";
|
|
7721
|
-
outputMode = "per_step";
|
|
7722
|
-
config;
|
|
7723
|
-
constructor(config) {
|
|
7724
|
-
if (!config.botToken.startsWith("xoxb-")) {
|
|
7725
|
-
throw new Error(
|
|
7726
|
-
`Invalid Slack bot token: must start with "xoxb-". Use the Bot User OAuth Token from your Slack app's OAuth & Permissions page.`
|
|
7727
|
-
);
|
|
7728
|
-
}
|
|
7729
|
-
this.config = config;
|
|
7730
|
-
}
|
|
7731
|
-
async notify(notification) {
|
|
7732
|
-
const overrides = notification.channelOverrides;
|
|
7733
|
-
const channel = overrides?.["channel"] ?? this.config.defaultChannel;
|
|
7734
|
-
const threadTs = overrides?.["thread_ts"];
|
|
7735
|
-
const blocks = this.buildBlocks(notification);
|
|
7736
|
-
const text = notification.title ?? "Agent needs your input";
|
|
7737
|
-
await this.postMessage(channel, threadTs, text, blocks);
|
|
7738
|
-
}
|
|
7739
|
-
async sendOutput(context2, event) {
|
|
7740
|
-
const channel = context2.source["channel"];
|
|
7741
|
-
const threadTs = context2.source["threadTs"];
|
|
7742
|
-
if (!channel) return;
|
|
7743
|
-
const text = this.formatOutputEvent(event);
|
|
7744
|
-
if (!text) return;
|
|
7745
|
-
await this.postMessage(channel, threadTs, text);
|
|
7746
|
-
}
|
|
7747
|
-
async postMessage(channel, threadTs, text, blocks) {
|
|
7748
|
-
const body = { channel, text };
|
|
7749
|
-
if (threadTs) body["thread_ts"] = threadTs;
|
|
7750
|
-
if (blocks) body["blocks"] = blocks;
|
|
7751
|
-
const response = await fetch("https://slack.com/api/chat.postMessage", {
|
|
7752
|
-
method: "POST",
|
|
7753
|
-
headers: {
|
|
7754
|
-
Authorization: `Bearer ${this.config.botToken}`,
|
|
7755
|
-
"Content-Type": "application/json"
|
|
7756
|
-
},
|
|
7757
|
-
body: JSON.stringify(body)
|
|
7758
|
-
});
|
|
7759
|
-
const data = await response.json();
|
|
7760
|
-
if (!data.ok) {
|
|
7761
|
-
throw new Error(`Slack API error: ${data.error ?? "unknown"}`);
|
|
7762
|
-
}
|
|
7763
|
-
}
|
|
7764
|
-
formatOutputEvent(event) {
|
|
7765
|
-
const eventType = event.eventType;
|
|
7766
|
-
if (eventType === "workflow_finish" || eventType === "agent_finish") {
|
|
7767
|
-
const metadata = event.data["_metadata"];
|
|
7768
|
-
const result = event.data["result"];
|
|
7769
|
-
const error = event.data["error"];
|
|
7770
|
-
const workflowId = metadata?.["workflow_id"];
|
|
7771
|
-
if (error) {
|
|
7772
|
-
return `\u274C *${workflowId ?? "Workflow"} failed:* ${error}`;
|
|
7773
|
-
}
|
|
7774
|
-
const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
7775
|
-
if (resultStr) {
|
|
7776
|
-
return `\u2705 *${workflowId ?? "Workflow"} finished:*
|
|
7777
|
-
${resultStr}`;
|
|
7778
|
-
}
|
|
7779
|
-
return `\u2705 *${workflowId ?? "Workflow"} finished*`;
|
|
7780
|
-
}
|
|
7781
|
-
if (eventType === "tool_call") {
|
|
7782
|
-
const toolCall = event.data["tool_call"];
|
|
7783
|
-
if (toolCall) {
|
|
7784
|
-
const fn = toolCall["function"];
|
|
7785
|
-
const name = fn?.["name"];
|
|
7786
|
-
if (name) {
|
|
7787
|
-
return `\u{1F527} Calling tool: \`${name}\``;
|
|
7788
|
-
}
|
|
7789
|
-
}
|
|
7790
|
-
return null;
|
|
7791
|
-
}
|
|
7792
|
-
if (eventType === "step_finish") {
|
|
7793
|
-
const stepKey = event.data["step_key"];
|
|
7794
|
-
const error = event.data["error"];
|
|
7795
|
-
if (error) {
|
|
7796
|
-
return `\u26A0\uFE0F Step \`${stepKey ?? "unknown"}\` failed: ${error}`;
|
|
7797
|
-
}
|
|
7798
|
-
return null;
|
|
7799
|
-
}
|
|
7800
|
-
return null;
|
|
7801
|
-
}
|
|
7802
|
-
buildBlocks(n) {
|
|
7803
|
-
const blocks = [];
|
|
7804
|
-
blocks.push({
|
|
7805
|
-
type: "header",
|
|
7806
|
-
text: { type: "plain_text", text: n.title ?? "Agent needs your input" }
|
|
7807
|
-
});
|
|
7808
|
-
if (n.description) {
|
|
7809
|
-
blocks.push({
|
|
7810
|
-
type: "section",
|
|
7811
|
-
text: { type: "mrkdwn", text: n.description }
|
|
7812
|
-
});
|
|
7813
|
-
}
|
|
7814
|
-
if (n.source || n.tool) {
|
|
7815
|
-
const parts = [];
|
|
7816
|
-
if (n.source) parts.push(`*Source:* ${n.source}`);
|
|
7817
|
-
if (n.tool) parts.push(`*Tool:* \`${n.tool}\``);
|
|
7818
|
-
blocks.push({
|
|
7819
|
-
type: "context",
|
|
7820
|
-
elements: [{ type: "mrkdwn", text: parts.join(" | ") }]
|
|
7821
|
-
});
|
|
7822
|
-
}
|
|
7823
|
-
if (n.context && Object.keys(n.context).length > 0) {
|
|
7824
|
-
const contextText = JSON.stringify(n.context, null, 2);
|
|
7825
|
-
blocks.push({
|
|
7826
|
-
type: "section",
|
|
7827
|
-
text: { type: "mrkdwn", text: "```" + contextText + "```" }
|
|
7828
|
-
});
|
|
7829
|
-
}
|
|
7830
|
-
if (n.expiresAt) {
|
|
7831
|
-
blocks.push({
|
|
7832
|
-
type: "context",
|
|
7833
|
-
elements: [{ type: "mrkdwn", text: `Expires: ${n.expiresAt}` }]
|
|
7834
|
-
});
|
|
7835
|
-
}
|
|
7836
|
-
if (this.isSimpleApproval(n)) {
|
|
7837
|
-
const approveValue = JSON.stringify({
|
|
7838
|
-
executionId: n.executionId,
|
|
7839
|
-
stepKey: n.stepKey,
|
|
7840
|
-
approved: true
|
|
7841
|
-
});
|
|
7842
|
-
const rejectValue = JSON.stringify({
|
|
7843
|
-
executionId: n.executionId,
|
|
7844
|
-
stepKey: n.stepKey,
|
|
7845
|
-
approved: false
|
|
7846
|
-
});
|
|
7847
|
-
blocks.push({
|
|
7848
|
-
type: "actions",
|
|
7849
|
-
elements: [
|
|
7850
|
-
{
|
|
7851
|
-
type: "button",
|
|
7852
|
-
action_id: "polos_approve",
|
|
7853
|
-
text: { type: "plain_text", text: "Approve" },
|
|
7854
|
-
style: "primary",
|
|
7855
|
-
value: approveValue
|
|
7856
|
-
},
|
|
7857
|
-
{
|
|
7858
|
-
type: "button",
|
|
7859
|
-
action_id: "polos_reject",
|
|
7860
|
-
text: { type: "plain_text", text: "Reject" },
|
|
7861
|
-
style: "danger",
|
|
7862
|
-
value: rejectValue
|
|
7863
|
-
},
|
|
7864
|
-
{
|
|
7865
|
-
type: "button",
|
|
7866
|
-
text: { type: "plain_text", text: "View Details" },
|
|
7867
|
-
url: n.approvalUrl
|
|
7868
|
-
}
|
|
7869
|
-
]
|
|
7870
|
-
});
|
|
7871
|
-
} else {
|
|
7872
|
-
blocks.push({
|
|
7873
|
-
type: "actions",
|
|
7874
|
-
elements: [
|
|
7875
|
-
{
|
|
7876
|
-
type: "button",
|
|
7877
|
-
text: { type: "plain_text", text: "Respond" },
|
|
7878
|
-
url: n.approvalUrl,
|
|
7879
|
-
style: "primary"
|
|
7880
|
-
}
|
|
7881
|
-
]
|
|
7882
|
-
});
|
|
7883
|
-
}
|
|
7884
|
-
return blocks;
|
|
7885
|
-
}
|
|
7886
|
-
isSimpleApproval(n) {
|
|
7887
|
-
const fields = n.formFields;
|
|
7888
|
-
if (!fields || fields.length === 0) return false;
|
|
7889
|
-
return fields.some((f) => f["key"] === "approved" && f["type"] === "boolean");
|
|
7890
|
-
}
|
|
7891
|
-
};
|
|
7892
|
-
|
|
7893
7952
|
exports.AgentRunConfig = AgentRunConfig;
|
|
7894
7953
|
exports.DockerEnvironment = DockerEnvironment;
|
|
7895
7954
|
exports.DuplicateWorkflowError = DuplicateWorkflowError;
|