@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.d.cts
CHANGED
|
@@ -657,8 +657,11 @@ interface Channel {
|
|
|
657
657
|
/**
|
|
658
658
|
* Send a notification when an agent suspends for user input.
|
|
659
659
|
* Implementations should throw on failure — the SDK catches and logs errors.
|
|
660
|
+
*
|
|
661
|
+
* May return channel-specific metadata (e.g., Slack message_ts) so the
|
|
662
|
+
* orchestrator can update the notification later (e.g., after approval via UI).
|
|
660
663
|
*/
|
|
661
|
-
notify(notification: SuspendNotification): Promise<
|
|
664
|
+
notify(notification: SuspendNotification): Promise<Record<string, unknown> | undefined>;
|
|
662
665
|
/** Default output mode for this channel. */
|
|
663
666
|
readonly outputMode?: ChannelOutputMode;
|
|
664
667
|
/** Send output events back to the originating channel. */
|
|
@@ -1641,6 +1644,10 @@ interface BatchInvokeWorkflowsRequest {
|
|
|
1641
1644
|
rootWorkflowId?: string | undefined;
|
|
1642
1645
|
waitForSubworkflow?: boolean | undefined;
|
|
1643
1646
|
otelTraceparent?: string | undefined;
|
|
1647
|
+
channelContext?: {
|
|
1648
|
+
channelId: string;
|
|
1649
|
+
source: Record<string, unknown>;
|
|
1650
|
+
} | undefined;
|
|
1644
1651
|
}
|
|
1645
1652
|
/**
|
|
1646
1653
|
* Response from batch workflow invocation.
|
|
@@ -2927,6 +2934,8 @@ declare class Worker {
|
|
|
2927
2934
|
private state;
|
|
2928
2935
|
private heartbeatInterval;
|
|
2929
2936
|
private activeExecutions;
|
|
2937
|
+
/** Tracks running channel bridges so we don't start duplicates on re-dispatch. */
|
|
2938
|
+
private activeChannelBridges;
|
|
2930
2939
|
private signalHandler;
|
|
2931
2940
|
constructor(config: WorkerConfig);
|
|
2932
2941
|
/**
|
|
@@ -4950,7 +4959,7 @@ declare class SlackChannel implements Channel {
|
|
|
4950
4959
|
readonly outputMode: ChannelOutputMode;
|
|
4951
4960
|
private readonly config;
|
|
4952
4961
|
constructor(config: SlackChannelConfig);
|
|
4953
|
-
notify(notification: SuspendNotification): Promise<
|
|
4962
|
+
notify(notification: SuspendNotification): Promise<Record<string, unknown> | undefined>;
|
|
4954
4963
|
sendOutput(context: ChannelContext, event: StreamEvent): Promise<void>;
|
|
4955
4964
|
private postMessage;
|
|
4956
4965
|
private formatOutputEvent;
|
package/dist/index.d.ts
CHANGED
|
@@ -657,8 +657,11 @@ interface Channel {
|
|
|
657
657
|
/**
|
|
658
658
|
* Send a notification when an agent suspends for user input.
|
|
659
659
|
* Implementations should throw on failure — the SDK catches and logs errors.
|
|
660
|
+
*
|
|
661
|
+
* May return channel-specific metadata (e.g., Slack message_ts) so the
|
|
662
|
+
* orchestrator can update the notification later (e.g., after approval via UI).
|
|
660
663
|
*/
|
|
661
|
-
notify(notification: SuspendNotification): Promise<
|
|
664
|
+
notify(notification: SuspendNotification): Promise<Record<string, unknown> | undefined>;
|
|
662
665
|
/** Default output mode for this channel. */
|
|
663
666
|
readonly outputMode?: ChannelOutputMode;
|
|
664
667
|
/** Send output events back to the originating channel. */
|
|
@@ -1641,6 +1644,10 @@ interface BatchInvokeWorkflowsRequest {
|
|
|
1641
1644
|
rootWorkflowId?: string | undefined;
|
|
1642
1645
|
waitForSubworkflow?: boolean | undefined;
|
|
1643
1646
|
otelTraceparent?: string | undefined;
|
|
1647
|
+
channelContext?: {
|
|
1648
|
+
channelId: string;
|
|
1649
|
+
source: Record<string, unknown>;
|
|
1650
|
+
} | undefined;
|
|
1644
1651
|
}
|
|
1645
1652
|
/**
|
|
1646
1653
|
* Response from batch workflow invocation.
|
|
@@ -2927,6 +2934,8 @@ declare class Worker {
|
|
|
2927
2934
|
private state;
|
|
2928
2935
|
private heartbeatInterval;
|
|
2929
2936
|
private activeExecutions;
|
|
2937
|
+
/** Tracks running channel bridges so we don't start duplicates on re-dispatch. */
|
|
2938
|
+
private activeChannelBridges;
|
|
2930
2939
|
private signalHandler;
|
|
2931
2940
|
constructor(config: WorkerConfig);
|
|
2932
2941
|
/**
|
|
@@ -4950,7 +4959,7 @@ declare class SlackChannel implements Channel {
|
|
|
4950
4959
|
readonly outputMode: ChannelOutputMode;
|
|
4951
4960
|
private readonly config;
|
|
4952
4961
|
constructor(config: SlackChannelConfig);
|
|
4953
|
-
notify(notification: SuspendNotification): Promise<
|
|
4962
|
+
notify(notification: SuspendNotification): Promise<Record<string, unknown> | undefined>;
|
|
4954
4963
|
sendOutput(context: ChannelContext, event: StreamEvent): Promise<void>;
|
|
4955
4964
|
private postMessage;
|
|
4956
4965
|
private formatOutputEvent;
|
package/dist/index.js
CHANGED
|
@@ -722,6 +722,11 @@ var OrchestratorClient = class {
|
|
|
722
722
|
if (request.waitForSubworkflow !== void 0)
|
|
723
723
|
body["wait_for_subworkflow"] = request.waitForSubworkflow;
|
|
724
724
|
if (request.otelTraceparent !== void 0) body["otel_traceparent"] = request.otelTraceparent;
|
|
725
|
+
if (request.channelContext !== void 0)
|
|
726
|
+
body["channel_context"] = {
|
|
727
|
+
channel_id: request.channelContext.channelId,
|
|
728
|
+
source: request.channelContext.source
|
|
729
|
+
};
|
|
725
730
|
return this.request("POST", "/api/v1/workflows/batch_run", {
|
|
726
731
|
body
|
|
727
732
|
});
|
|
@@ -3355,6 +3360,187 @@ function defineAgent(config) {
|
|
|
3355
3360
|
});
|
|
3356
3361
|
return agentWorkflow;
|
|
3357
3362
|
}
|
|
3363
|
+
|
|
3364
|
+
// src/channels/slack.ts
|
|
3365
|
+
var SlackChannel = class {
|
|
3366
|
+
id = "slack";
|
|
3367
|
+
outputMode = "per_step";
|
|
3368
|
+
config;
|
|
3369
|
+
constructor(config) {
|
|
3370
|
+
if (!config.botToken.startsWith("xoxb-")) {
|
|
3371
|
+
throw new Error(
|
|
3372
|
+
`Invalid Slack bot token: must start with "xoxb-". Use the Bot User OAuth Token from your Slack app's OAuth & Permissions page.`
|
|
3373
|
+
);
|
|
3374
|
+
}
|
|
3375
|
+
this.config = config;
|
|
3376
|
+
}
|
|
3377
|
+
async notify(notification) {
|
|
3378
|
+
const overrides = notification.channelOverrides;
|
|
3379
|
+
const channel = overrides?.["channel"] ?? this.config.defaultChannel;
|
|
3380
|
+
const threadTs = overrides?.["thread_ts"] ?? overrides?.["threadTs"];
|
|
3381
|
+
const blocks = this.buildBlocks(notification);
|
|
3382
|
+
const text = notification.title ?? "Agent needs your input";
|
|
3383
|
+
const messageTs = await this.postMessage(channel, threadTs, text, blocks);
|
|
3384
|
+
return {
|
|
3385
|
+
slack_channel: channel,
|
|
3386
|
+
slack_message_ts: messageTs,
|
|
3387
|
+
slack_blocks: blocks
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
3390
|
+
async sendOutput(context2, event) {
|
|
3391
|
+
const channel = context2.source["channel"];
|
|
3392
|
+
const threadTs = context2.source["threadTs"];
|
|
3393
|
+
if (!channel) return;
|
|
3394
|
+
const text = this.formatOutputEvent(event);
|
|
3395
|
+
if (!text) return;
|
|
3396
|
+
await this.postMessage(channel, threadTs, text);
|
|
3397
|
+
}
|
|
3398
|
+
async postMessage(channel, threadTs, text, blocks) {
|
|
3399
|
+
const body = { channel, text };
|
|
3400
|
+
if (threadTs) body["thread_ts"] = threadTs;
|
|
3401
|
+
if (blocks) body["blocks"] = blocks;
|
|
3402
|
+
const response = await fetch("https://slack.com/api/chat.postMessage", {
|
|
3403
|
+
method: "POST",
|
|
3404
|
+
headers: {
|
|
3405
|
+
Authorization: `Bearer ${this.config.botToken}`,
|
|
3406
|
+
"Content-Type": "application/json"
|
|
3407
|
+
},
|
|
3408
|
+
body: JSON.stringify(body)
|
|
3409
|
+
});
|
|
3410
|
+
const data = await response.json();
|
|
3411
|
+
if (!data.ok) {
|
|
3412
|
+
throw new Error(`Slack API error: ${data.error ?? "unknown"}`);
|
|
3413
|
+
}
|
|
3414
|
+
return data.ts ?? "";
|
|
3415
|
+
}
|
|
3416
|
+
formatOutputEvent(event) {
|
|
3417
|
+
const eventType = event.eventType;
|
|
3418
|
+
if (eventType === "workflow_finish" || eventType === "agent_finish") {
|
|
3419
|
+
const metadata = event.data["_metadata"];
|
|
3420
|
+
const rawResult = event.data["result"];
|
|
3421
|
+
const error = event.data["error"];
|
|
3422
|
+
const workflowId = metadata?.["workflow_id"];
|
|
3423
|
+
if (error) {
|
|
3424
|
+
return `\u274C *${workflowId ?? "Workflow"} failed:* ${error}`;
|
|
3425
|
+
}
|
|
3426
|
+
const result = typeof rawResult === "object" && rawResult !== null && "result" in rawResult ? rawResult["result"] : rawResult;
|
|
3427
|
+
const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
3428
|
+
if (resultStr) {
|
|
3429
|
+
return resultStr;
|
|
3430
|
+
}
|
|
3431
|
+
return `\u2705 *${workflowId ?? "Workflow"} finished*`;
|
|
3432
|
+
}
|
|
3433
|
+
if (eventType === "tool_call") {
|
|
3434
|
+
const toolCall = event.data["tool_call"];
|
|
3435
|
+
if (toolCall) {
|
|
3436
|
+
const fn = toolCall["function"];
|
|
3437
|
+
const name = fn?.["name"];
|
|
3438
|
+
if (name) {
|
|
3439
|
+
return `\u{1F527} Calling tool: \`${name}\``;
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
return null;
|
|
3443
|
+
}
|
|
3444
|
+
if (eventType === "step_finish") {
|
|
3445
|
+
const stepKey = event.data["step_key"];
|
|
3446
|
+
const error = event.data["error"];
|
|
3447
|
+
if (error) {
|
|
3448
|
+
return `\u26A0\uFE0F Step \`${stepKey ?? "unknown"}\` failed: ${error}`;
|
|
3449
|
+
}
|
|
3450
|
+
return null;
|
|
3451
|
+
}
|
|
3452
|
+
return null;
|
|
3453
|
+
}
|
|
3454
|
+
buildBlocks(n) {
|
|
3455
|
+
const blocks = [];
|
|
3456
|
+
blocks.push({
|
|
3457
|
+
type: "header",
|
|
3458
|
+
text: { type: "plain_text", text: n.title ?? "Agent needs your input" }
|
|
3459
|
+
});
|
|
3460
|
+
if (n.description) {
|
|
3461
|
+
blocks.push({
|
|
3462
|
+
type: "section",
|
|
3463
|
+
text: { type: "mrkdwn", text: n.description }
|
|
3464
|
+
});
|
|
3465
|
+
}
|
|
3466
|
+
if (n.source || n.tool) {
|
|
3467
|
+
const parts = [];
|
|
3468
|
+
if (n.source) parts.push(`*Source:* ${n.source}`);
|
|
3469
|
+
if (n.tool) parts.push(`*Tool:* \`${n.tool}\``);
|
|
3470
|
+
blocks.push({
|
|
3471
|
+
type: "context",
|
|
3472
|
+
elements: [{ type: "mrkdwn", text: parts.join(" | ") }]
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
if (n.context && Object.keys(n.context).length > 0) {
|
|
3476
|
+
const contextText = JSON.stringify(n.context, null, 2);
|
|
3477
|
+
blocks.push({
|
|
3478
|
+
type: "section",
|
|
3479
|
+
text: { type: "mrkdwn", text: "```" + contextText + "```" }
|
|
3480
|
+
});
|
|
3481
|
+
}
|
|
3482
|
+
if (n.expiresAt) {
|
|
3483
|
+
blocks.push({
|
|
3484
|
+
type: "context",
|
|
3485
|
+
elements: [{ type: "mrkdwn", text: `Expires: ${n.expiresAt}` }]
|
|
3486
|
+
});
|
|
3487
|
+
}
|
|
3488
|
+
if (this.isSimpleApproval(n)) {
|
|
3489
|
+
const approveValue = JSON.stringify({
|
|
3490
|
+
executionId: n.executionId,
|
|
3491
|
+
stepKey: n.stepKey,
|
|
3492
|
+
approved: true
|
|
3493
|
+
});
|
|
3494
|
+
const rejectValue = JSON.stringify({
|
|
3495
|
+
executionId: n.executionId,
|
|
3496
|
+
stepKey: n.stepKey,
|
|
3497
|
+
approved: false
|
|
3498
|
+
});
|
|
3499
|
+
blocks.push({
|
|
3500
|
+
type: "actions",
|
|
3501
|
+
elements: [
|
|
3502
|
+
{
|
|
3503
|
+
type: "button",
|
|
3504
|
+
action_id: "polos_approve",
|
|
3505
|
+
text: { type: "plain_text", text: "Approve" },
|
|
3506
|
+
style: "primary",
|
|
3507
|
+
value: approveValue
|
|
3508
|
+
},
|
|
3509
|
+
{
|
|
3510
|
+
type: "button",
|
|
3511
|
+
action_id: "polos_reject",
|
|
3512
|
+
text: { type: "plain_text", text: "Reject" },
|
|
3513
|
+
style: "danger",
|
|
3514
|
+
value: rejectValue
|
|
3515
|
+
},
|
|
3516
|
+
{
|
|
3517
|
+
type: "button",
|
|
3518
|
+
text: { type: "plain_text", text: "View Details" },
|
|
3519
|
+
url: n.approvalUrl
|
|
3520
|
+
}
|
|
3521
|
+
]
|
|
3522
|
+
});
|
|
3523
|
+
} else {
|
|
3524
|
+
blocks.push({
|
|
3525
|
+
type: "actions",
|
|
3526
|
+
elements: [
|
|
3527
|
+
{
|
|
3528
|
+
type: "button",
|
|
3529
|
+
text: { type: "plain_text", text: "Respond" },
|
|
3530
|
+
url: n.approvalUrl,
|
|
3531
|
+
style: "primary"
|
|
3532
|
+
}
|
|
3533
|
+
]
|
|
3534
|
+
});
|
|
3535
|
+
}
|
|
3536
|
+
return blocks;
|
|
3537
|
+
}
|
|
3538
|
+
isSimpleApproval(n) {
|
|
3539
|
+
const fields = n.formFields;
|
|
3540
|
+
if (!fields || fields.length === 0) return false;
|
|
3541
|
+
return fields.some((f) => f["key"] === "approved" && f["type"] === "boolean");
|
|
3542
|
+
}
|
|
3543
|
+
};
|
|
3358
3544
|
var logger4 = createLogger({ name: "worker-server" });
|
|
3359
3545
|
var WorkerServer = class {
|
|
3360
3546
|
app;
|
|
@@ -4208,7 +4394,8 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4208
4394
|
otelTraceparent: getCurrentTraceparent(),
|
|
4209
4395
|
waitForSubworkflow: options?.waitForSubworkflow ?? false,
|
|
4210
4396
|
initialState: options?.initialState,
|
|
4211
|
-
runTimeoutSeconds: options?.runTimeoutSeconds
|
|
4397
|
+
runTimeoutSeconds: options?.runTimeoutSeconds,
|
|
4398
|
+
channelContext
|
|
4212
4399
|
});
|
|
4213
4400
|
if (options?.waitForSubworkflow) {
|
|
4214
4401
|
return [void 0, false];
|
|
@@ -4396,7 +4583,8 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4396
4583
|
sessionId: execCtx.sessionId,
|
|
4397
4584
|
userId: execCtx.userId,
|
|
4398
4585
|
waitForSubworkflow: false,
|
|
4399
|
-
otelTraceparent: getCurrentTraceparent()
|
|
4586
|
+
otelTraceparent: getCurrentTraceparent(),
|
|
4587
|
+
channelContext
|
|
4400
4588
|
});
|
|
4401
4589
|
const handleData = response.executions.map((exec, i) => {
|
|
4402
4590
|
const item = items[i];
|
|
@@ -4472,7 +4660,8 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4472
4660
|
sessionId: execCtx.sessionId,
|
|
4473
4661
|
userId: execCtx.userId,
|
|
4474
4662
|
waitForSubworkflow: true,
|
|
4475
|
-
otelTraceparent: getCurrentTraceparent()
|
|
4663
|
+
otelTraceparent: getCurrentTraceparent(),
|
|
4664
|
+
channelContext
|
|
4476
4665
|
});
|
|
4477
4666
|
const workflowIds = items.map((item) => typeof item.workflow === "string" ? item.workflow : item.workflow.id).join(", ");
|
|
4478
4667
|
throw new WaitError(`Waiting for sub-workflows [${workflowIds}] to complete`, {
|
|
@@ -4668,7 +4857,7 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4668
4857
|
if (channelContext !== void 0) {
|
|
4669
4858
|
notification.channelContext = channelContext;
|
|
4670
4859
|
}
|
|
4671
|
-
await Promise.allSettled(
|
|
4860
|
+
const notifyResults = await Promise.allSettled(
|
|
4672
4861
|
channels.map(async (ch) => {
|
|
4673
4862
|
try {
|
|
4674
4863
|
let overrides = notifyConfig?.[ch.id];
|
|
@@ -4676,12 +4865,35 @@ function createOrchestratorStepHelper(orchestratorClient, cachedSteps, execCtx,
|
|
|
4676
4865
|
overrides = { ...channelContext.source, ...overrides };
|
|
4677
4866
|
}
|
|
4678
4867
|
const n = overrides !== void 0 ? { ...notification, channelOverrides: overrides } : notification;
|
|
4679
|
-
await ch.notify(n);
|
|
4868
|
+
const meta = await ch.notify(n);
|
|
4869
|
+
return meta ? { channelId: ch.id, ...meta } : void 0;
|
|
4680
4870
|
} catch (err) {
|
|
4681
4871
|
logger6.warn(`Channel ${ch.id} notification failed`, { error: String(err) });
|
|
4872
|
+
return void 0;
|
|
4682
4873
|
}
|
|
4683
4874
|
})
|
|
4684
4875
|
);
|
|
4876
|
+
const metaEntries = [];
|
|
4877
|
+
for (const r of notifyResults) {
|
|
4878
|
+
if (r.status === "fulfilled" && r.value != null) {
|
|
4879
|
+
metaEntries.push(r.value);
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4882
|
+
if (metaEntries.length > 0) {
|
|
4883
|
+
orchestratorClient.publishEvent({
|
|
4884
|
+
topic,
|
|
4885
|
+
events: [
|
|
4886
|
+
{
|
|
4887
|
+
eventType: `notification_meta_${key}`,
|
|
4888
|
+
data: { channels: metaEntries }
|
|
4889
|
+
}
|
|
4890
|
+
],
|
|
4891
|
+
executionId: execCtx.executionId,
|
|
4892
|
+
rootExecutionId: execCtx.rootExecutionId
|
|
4893
|
+
}).catch((err) => {
|
|
4894
|
+
logger6.warn("Failed to publish notification metadata", { error: String(err) });
|
|
4895
|
+
});
|
|
4896
|
+
}
|
|
4685
4897
|
}
|
|
4686
4898
|
throw new WaitError(`Waiting for resume event: ${topic}`, { topic });
|
|
4687
4899
|
},
|
|
@@ -6269,10 +6481,24 @@ var Worker = class {
|
|
|
6269
6481
|
state = "stopped";
|
|
6270
6482
|
heartbeatInterval = null;
|
|
6271
6483
|
activeExecutions = /* @__PURE__ */ new Map();
|
|
6484
|
+
/** Tracks running channel bridges so we don't start duplicates on re-dispatch. */
|
|
6485
|
+
activeChannelBridges = /* @__PURE__ */ new Set();
|
|
6272
6486
|
signalHandler = null;
|
|
6273
6487
|
constructor(config) {
|
|
6274
6488
|
this.config = config;
|
|
6275
6489
|
this.channels = config.channels ?? [];
|
|
6490
|
+
if (!this.channels.some((ch) => ch.id === "slack")) {
|
|
6491
|
+
const slackBotToken = process.env["SLACK_BOT_TOKEN"];
|
|
6492
|
+
if (slackBotToken) {
|
|
6493
|
+
this.channels.push(
|
|
6494
|
+
new SlackChannel({
|
|
6495
|
+
botToken: slackBotToken,
|
|
6496
|
+
defaultChannel: process.env["SLACK_CHANNEL"] ?? "#general"
|
|
6497
|
+
})
|
|
6498
|
+
);
|
|
6499
|
+
logger9.info("Auto-registered SlackChannel from SLACK_BOT_TOKEN env var");
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6276
6502
|
this.maxConcurrentWorkflows = config.maxConcurrentWorkflows ?? 100;
|
|
6277
6503
|
this.workerServerUrl = config.workerServerUrl ?? `http://localhost:${String(config.port ?? 8e3)}`;
|
|
6278
6504
|
this.port = config.port ?? 8e3;
|
|
@@ -6806,6 +7032,11 @@ var Worker = class {
|
|
|
6806
7032
|
};
|
|
6807
7033
|
const hasWorkflowChannels = workflow.config.channels !== void 0;
|
|
6808
7034
|
const resolvedChannels = workflow.config.channels ?? this.channels;
|
|
7035
|
+
if (data.channelContext && !data.parentExecutionId) {
|
|
7036
|
+
const rootExecId = data.rootExecutionId ?? executionId;
|
|
7037
|
+
const rootWfId = data.rootWorkflowId ?? workflowId;
|
|
7038
|
+
this.startChannelBridge(rootExecId, rootWfId, data.channelContext, workflow);
|
|
7039
|
+
}
|
|
6809
7040
|
result = await executeWorkflow({
|
|
6810
7041
|
workflow,
|
|
6811
7042
|
payload: data.payload,
|
|
@@ -6817,9 +7048,6 @@ var Worker = class {
|
|
|
6817
7048
|
sandboxManager: this.sandboxManager,
|
|
6818
7049
|
channelContext: hasWorkflowChannels ? void 0 : data.channelContext
|
|
6819
7050
|
});
|
|
6820
|
-
if (data.channelContext) {
|
|
6821
|
-
this.startChannelBridge(executionId, workflowId, data.channelContext, workflow);
|
|
6822
|
-
}
|
|
6823
7051
|
await this.handleExecutionResult(executionId, workflowId, context2, result);
|
|
6824
7052
|
retryableFailure = !result.success && !result.waiting && (result.retryable ?? true);
|
|
6825
7053
|
} catch (error) {
|
|
@@ -6962,15 +7190,20 @@ var Worker = class {
|
|
|
6962
7190
|
/**
|
|
6963
7191
|
* Start a channel bridge that streams execution events back to the originating channel.
|
|
6964
7192
|
*/
|
|
6965
|
-
startChannelBridge(
|
|
7193
|
+
startChannelBridge(rootExecutionId, rootWorkflowId, channelCtx, workflow) {
|
|
7194
|
+
if (this.activeChannelBridges.has(rootExecutionId)) return;
|
|
6966
7195
|
const channel = this.channels.find((ch) => ch.id === channelCtx.channelId);
|
|
6967
|
-
if (!channel
|
|
7196
|
+
if (!channel) {
|
|
7197
|
+
logger9.error(
|
|
7198
|
+
`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.`),
|
|
7199
|
+
{ executionId: rootExecutionId }
|
|
7200
|
+
);
|
|
7201
|
+
return;
|
|
7202
|
+
}
|
|
7203
|
+
if (!channel.sendOutput) return;
|
|
6968
7204
|
const outputMode = workflow.config["channelOutputMode"] ?? channel.outputMode ?? "per_step";
|
|
6969
7205
|
if (outputMode === "none") return;
|
|
6970
|
-
|
|
6971
|
-
if (!execution) return;
|
|
6972
|
-
const rootExecutionId = execution.abortController.signal.aborted ? executionId : executionId;
|
|
6973
|
-
const rootWorkflowId = workflowId;
|
|
7206
|
+
this.activeChannelBridges.add(rootExecutionId);
|
|
6974
7207
|
void (async () => {
|
|
6975
7208
|
try {
|
|
6976
7209
|
const stream = this.orchestratorClient.streamEvents({
|
|
@@ -6978,13 +7211,14 @@ var Worker = class {
|
|
|
6978
7211
|
workflowRunId: rootExecutionId
|
|
6979
7212
|
});
|
|
6980
7213
|
for await (const event of stream) {
|
|
6981
|
-
if (execution.abortController.signal.aborted) break;
|
|
6982
7214
|
if (shouldForwardEvent(event, outputMode)) {
|
|
6983
7215
|
await channel.sendOutput?.(channelCtx, event);
|
|
6984
7216
|
}
|
|
6985
7217
|
}
|
|
6986
7218
|
} catch (err) {
|
|
6987
|
-
logger9.warn("Channel bridge error", { error: String(err), executionId });
|
|
7219
|
+
logger9.warn("Channel bridge error", { error: String(err), executionId: rootExecutionId });
|
|
7220
|
+
} finally {
|
|
7221
|
+
this.activeChannelBridges.delete(rootExecutionId);
|
|
6988
7222
|
}
|
|
6989
7223
|
})();
|
|
6990
7224
|
}
|
|
@@ -7689,181 +7923,6 @@ function sandboxTools(config) {
|
|
|
7689
7923
|
return tools;
|
|
7690
7924
|
}
|
|
7691
7925
|
|
|
7692
|
-
// src/channels/slack.ts
|
|
7693
|
-
var SlackChannel = class {
|
|
7694
|
-
id = "slack";
|
|
7695
|
-
outputMode = "per_step";
|
|
7696
|
-
config;
|
|
7697
|
-
constructor(config) {
|
|
7698
|
-
if (!config.botToken.startsWith("xoxb-")) {
|
|
7699
|
-
throw new Error(
|
|
7700
|
-
`Invalid Slack bot token: must start with "xoxb-". Use the Bot User OAuth Token from your Slack app's OAuth & Permissions page.`
|
|
7701
|
-
);
|
|
7702
|
-
}
|
|
7703
|
-
this.config = config;
|
|
7704
|
-
}
|
|
7705
|
-
async notify(notification) {
|
|
7706
|
-
const overrides = notification.channelOverrides;
|
|
7707
|
-
const channel = overrides?.["channel"] ?? this.config.defaultChannel;
|
|
7708
|
-
const threadTs = overrides?.["thread_ts"];
|
|
7709
|
-
const blocks = this.buildBlocks(notification);
|
|
7710
|
-
const text = notification.title ?? "Agent needs your input";
|
|
7711
|
-
await this.postMessage(channel, threadTs, text, blocks);
|
|
7712
|
-
}
|
|
7713
|
-
async sendOutput(context2, event) {
|
|
7714
|
-
const channel = context2.source["channel"];
|
|
7715
|
-
const threadTs = context2.source["threadTs"];
|
|
7716
|
-
if (!channel) return;
|
|
7717
|
-
const text = this.formatOutputEvent(event);
|
|
7718
|
-
if (!text) return;
|
|
7719
|
-
await this.postMessage(channel, threadTs, text);
|
|
7720
|
-
}
|
|
7721
|
-
async postMessage(channel, threadTs, text, blocks) {
|
|
7722
|
-
const body = { channel, text };
|
|
7723
|
-
if (threadTs) body["thread_ts"] = threadTs;
|
|
7724
|
-
if (blocks) body["blocks"] = blocks;
|
|
7725
|
-
const response = await fetch("https://slack.com/api/chat.postMessage", {
|
|
7726
|
-
method: "POST",
|
|
7727
|
-
headers: {
|
|
7728
|
-
Authorization: `Bearer ${this.config.botToken}`,
|
|
7729
|
-
"Content-Type": "application/json"
|
|
7730
|
-
},
|
|
7731
|
-
body: JSON.stringify(body)
|
|
7732
|
-
});
|
|
7733
|
-
const data = await response.json();
|
|
7734
|
-
if (!data.ok) {
|
|
7735
|
-
throw new Error(`Slack API error: ${data.error ?? "unknown"}`);
|
|
7736
|
-
}
|
|
7737
|
-
}
|
|
7738
|
-
formatOutputEvent(event) {
|
|
7739
|
-
const eventType = event.eventType;
|
|
7740
|
-
if (eventType === "workflow_finish" || eventType === "agent_finish") {
|
|
7741
|
-
const metadata = event.data["_metadata"];
|
|
7742
|
-
const result = event.data["result"];
|
|
7743
|
-
const error = event.data["error"];
|
|
7744
|
-
const workflowId = metadata?.["workflow_id"];
|
|
7745
|
-
if (error) {
|
|
7746
|
-
return `\u274C *${workflowId ?? "Workflow"} failed:* ${error}`;
|
|
7747
|
-
}
|
|
7748
|
-
const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
7749
|
-
if (resultStr) {
|
|
7750
|
-
return `\u2705 *${workflowId ?? "Workflow"} finished:*
|
|
7751
|
-
${resultStr}`;
|
|
7752
|
-
}
|
|
7753
|
-
return `\u2705 *${workflowId ?? "Workflow"} finished*`;
|
|
7754
|
-
}
|
|
7755
|
-
if (eventType === "tool_call") {
|
|
7756
|
-
const toolCall = event.data["tool_call"];
|
|
7757
|
-
if (toolCall) {
|
|
7758
|
-
const fn = toolCall["function"];
|
|
7759
|
-
const name = fn?.["name"];
|
|
7760
|
-
if (name) {
|
|
7761
|
-
return `\u{1F527} Calling tool: \`${name}\``;
|
|
7762
|
-
}
|
|
7763
|
-
}
|
|
7764
|
-
return null;
|
|
7765
|
-
}
|
|
7766
|
-
if (eventType === "step_finish") {
|
|
7767
|
-
const stepKey = event.data["step_key"];
|
|
7768
|
-
const error = event.data["error"];
|
|
7769
|
-
if (error) {
|
|
7770
|
-
return `\u26A0\uFE0F Step \`${stepKey ?? "unknown"}\` failed: ${error}`;
|
|
7771
|
-
}
|
|
7772
|
-
return null;
|
|
7773
|
-
}
|
|
7774
|
-
return null;
|
|
7775
|
-
}
|
|
7776
|
-
buildBlocks(n) {
|
|
7777
|
-
const blocks = [];
|
|
7778
|
-
blocks.push({
|
|
7779
|
-
type: "header",
|
|
7780
|
-
text: { type: "plain_text", text: n.title ?? "Agent needs your input" }
|
|
7781
|
-
});
|
|
7782
|
-
if (n.description) {
|
|
7783
|
-
blocks.push({
|
|
7784
|
-
type: "section",
|
|
7785
|
-
text: { type: "mrkdwn", text: n.description }
|
|
7786
|
-
});
|
|
7787
|
-
}
|
|
7788
|
-
if (n.source || n.tool) {
|
|
7789
|
-
const parts = [];
|
|
7790
|
-
if (n.source) parts.push(`*Source:* ${n.source}`);
|
|
7791
|
-
if (n.tool) parts.push(`*Tool:* \`${n.tool}\``);
|
|
7792
|
-
blocks.push({
|
|
7793
|
-
type: "context",
|
|
7794
|
-
elements: [{ type: "mrkdwn", text: parts.join(" | ") }]
|
|
7795
|
-
});
|
|
7796
|
-
}
|
|
7797
|
-
if (n.context && Object.keys(n.context).length > 0) {
|
|
7798
|
-
const contextText = JSON.stringify(n.context, null, 2);
|
|
7799
|
-
blocks.push({
|
|
7800
|
-
type: "section",
|
|
7801
|
-
text: { type: "mrkdwn", text: "```" + contextText + "```" }
|
|
7802
|
-
});
|
|
7803
|
-
}
|
|
7804
|
-
if (n.expiresAt) {
|
|
7805
|
-
blocks.push({
|
|
7806
|
-
type: "context",
|
|
7807
|
-
elements: [{ type: "mrkdwn", text: `Expires: ${n.expiresAt}` }]
|
|
7808
|
-
});
|
|
7809
|
-
}
|
|
7810
|
-
if (this.isSimpleApproval(n)) {
|
|
7811
|
-
const approveValue = JSON.stringify({
|
|
7812
|
-
executionId: n.executionId,
|
|
7813
|
-
stepKey: n.stepKey,
|
|
7814
|
-
approved: true
|
|
7815
|
-
});
|
|
7816
|
-
const rejectValue = JSON.stringify({
|
|
7817
|
-
executionId: n.executionId,
|
|
7818
|
-
stepKey: n.stepKey,
|
|
7819
|
-
approved: false
|
|
7820
|
-
});
|
|
7821
|
-
blocks.push({
|
|
7822
|
-
type: "actions",
|
|
7823
|
-
elements: [
|
|
7824
|
-
{
|
|
7825
|
-
type: "button",
|
|
7826
|
-
action_id: "polos_approve",
|
|
7827
|
-
text: { type: "plain_text", text: "Approve" },
|
|
7828
|
-
style: "primary",
|
|
7829
|
-
value: approveValue
|
|
7830
|
-
},
|
|
7831
|
-
{
|
|
7832
|
-
type: "button",
|
|
7833
|
-
action_id: "polos_reject",
|
|
7834
|
-
text: { type: "plain_text", text: "Reject" },
|
|
7835
|
-
style: "danger",
|
|
7836
|
-
value: rejectValue
|
|
7837
|
-
},
|
|
7838
|
-
{
|
|
7839
|
-
type: "button",
|
|
7840
|
-
text: { type: "plain_text", text: "View Details" },
|
|
7841
|
-
url: n.approvalUrl
|
|
7842
|
-
}
|
|
7843
|
-
]
|
|
7844
|
-
});
|
|
7845
|
-
} else {
|
|
7846
|
-
blocks.push({
|
|
7847
|
-
type: "actions",
|
|
7848
|
-
elements: [
|
|
7849
|
-
{
|
|
7850
|
-
type: "button",
|
|
7851
|
-
text: { type: "plain_text", text: "Respond" },
|
|
7852
|
-
url: n.approvalUrl,
|
|
7853
|
-
style: "primary"
|
|
7854
|
-
}
|
|
7855
|
-
]
|
|
7856
|
-
});
|
|
7857
|
-
}
|
|
7858
|
-
return blocks;
|
|
7859
|
-
}
|
|
7860
|
-
isSimpleApproval(n) {
|
|
7861
|
-
const fields = n.formFields;
|
|
7862
|
-
if (!fields || fields.length === 0) return false;
|
|
7863
|
-
return fields.some((f) => f["key"] === "approved" && f["type"] === "boolean");
|
|
7864
|
-
}
|
|
7865
|
-
};
|
|
7866
|
-
|
|
7867
7926
|
export { AgentRunConfig, DockerEnvironment, DuplicateWorkflowError, ExecutionHandle, GuardrailError, GuardrailResult2 as GuardrailResult, HookExecutionError, HookResult, LLM, ManagedSandbox, GuardrailResult as MiddlewareGuardrailResult, OrchestratorApiError, OrchestratorClient, Polos, PolosClient, Queue, SandboxManager, SlackChannel, StateSizeError, StateValidationError, StepExecutionError, StreamResult, WaitError, Worker, WorkerServer, WorkflowNotFoundError, agentStreamFunction, assertSafePath, batchAgentInvoke, batchInvoke, buildSummaryMessages, compactIfNeeded, composeGuardrails, composeHooks, conditionalHook, configureLogging, convertFinishReason, convertMiddlewareToolCallToPython, convertPythonToolCallToMiddleware, convertToolResultsToMessages, convertToolsToVercel, convertVercelToolCallToPython, convertVercelUsageToPython, createAskUserTool, createEditTool, createExecTool, createGlobTool, createGrepTool, createLogger, createReadTool, createStepHelper, createStepStore, createWebSearchTool, createWorkflowRegistry, createWriteTool, defineAgent, defineGuardrail, defineHook, defineTool, defineWorkflow, estimateMessageTokens, estimateMessagesTokens, estimateTokens, evaluateAllowlist, executeGuardrailChain, executeGuardrailsOrThrow, executeHookChain, executeHooksOrThrow, executeWorkflow, executedTool, extractTraceparent, generateTraceIdFromExecutionId, getTracer, globalRegistry, hasText, initializeOtel, initializeState, isAgentWorkflow, isBinary, isGuardrail, isHook, isOtelAvailable, isToolWorkflow, isWaitError, llmGenerate, llmStream, maxSteps, maxTokens, normalizeGuardrail, normalizeGuardrails, normalizeHook, normalizeHooks, parseGrepOutput, retry, sandboxTools, serializeFinalState, sleep, stopCondition, stripAnsi, truncateOutput, validateState };
|
|
7868
7927
|
//# sourceMappingURL=index.js.map
|
|
7869
7928
|
//# sourceMappingURL=index.js.map
|