@sentry/junior 0.206.0 → 0.208.0
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/{agent-C9yuiZUF.js → agent-BfxfHsf1.js} +375 -346
- package/dist/{agent-hooks-DNjlz2A-.js → agent-hooks-CuGjLjmR.js} +224 -97
- package/dist/{agent-runner-E67lFrDG.js → agent-runner-Dz1PMKP0.js} +9 -9
- package/dist/api/schema.d.ts +39 -39
- package/dist/api/schema.js +2 -2
- package/dist/api.d.ts +1 -1
- package/dist/api.js +154 -154
- package/dist/{app-AxGmJHwK.d.ts → app-DfNrn_gS.d.ts} +2 -2
- package/dist/app.d.ts +1 -1
- package/dist/app.js +185 -183
- package/dist/{auth-pause-state-MVZ6xvEK.js → auth-pause-state-Bjoh8wCy.js} +7 -7
- package/dist/{briefs-CvJFQvhJ.js → briefs-q_onrGmf.js} +3 -3
- package/dist/{catalog-runtime-TBR8n4e1.js → catalog-runtime-D8YUMNhb.js} +1 -1
- package/dist/{checkpoint-BY47jJCB.js → checkpoint-FKMcINix.js} +8 -8
- package/dist/cli/chat.js +13 -13
- package/dist/cli/check.js +3 -3
- package/dist/cli/main.js +1 -1
- package/dist/cli/plugins.js +3 -3
- package/dist/cli/snapshot-create.js +3 -3
- package/dist/cli/upgrade.d.ts +1 -1
- package/dist/cli/upgrade.js +3 -3
- package/dist/{client-ceE7NtvC.js → client-bRyPqwAV.js} +4 -4
- package/dist/{config-CIE83sR2.js → config-EtF2uEaW.js} +6 -6
- package/dist/{context-B45_WqVX.js → context-Bplcgypl.js} +13 -13
- package/dist/{conversation-privacy-Auyq26XQ.js → conversation-privacy-whGaV2TG.js} +1 -1
- package/dist/{credential-sync-BO33aNQd.js → credential-sync-DM-EE1ey.js} +21 -21
- package/dist/{db-BeyXKJOI.d.ts → db-D74z7BZH.d.ts} +1 -1
- package/dist/{execution-stats-SiS2FLcv.js → execution-stats-C-f8zwMU.js} +77 -77
- package/dist/{executor-BfbYPW63.js → executor-BW5qp0jC.js} +43 -14
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-CyTc24Es.js → job-queue-BIHNIEIX.js} +1 -1
- package/dist/{logging-D37bayDc.js → logging-sNS84Yuu.js} +1 -1
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-BtfOeHTZ.js → oauth-callback-server-Co6D_yXP.js} +2 -2
- package/dist/{pending-auth-BPUzcs7n.js → pending-auth-CRJCt9Ux.js} +2 -2
- package/dist/{profile-CSixooTi.js → profile-BfLMVhWo.js} +4 -4
- package/dist/{prompt-3gqNY1Ka.js → prompt-x-0ZtTyb.js} +14 -14
- package/dist/{registry-DiR0-xRg.js → registry-Cyww_jnC.js} +2 -2
- package/dist/{resolve-Bn5q__9b.js → resolve-BApIFQXX.js} +2 -2
- package/dist/{runner-DFx3dIJH.js → runner-C9okx4GL.js} +10 -10
- package/dist/{sandbox-egress-signals-bwvjihxA.js → sandbox-egress-signals-BHfx0esj.js} +1 -1
- package/dist/{schema-8GXFfclK.js → schema-CwTeb-BF.js} +39 -39
- package/dist/{skills-BhM5qyCF.js → skills-DhL_wXEp.js} +2 -2
- package/dist/{spawn-Dk0PqdH6.js → spawn-msnzfE8_.js} +2 -2
- package/dist/{stats-DKRP4rsY.js → stats-Cq5w_Awk.js} +292 -268
- package/dist/{store-C5qxF2ac.d.ts → store-Det8MQ9g.d.ts} +1 -1
- package/dist/{task-queue-CdpTvu4R.js → task-queue-JfmWmpjS.js} +3 -3
- package/dist/{task-runner-alpjVSxQ.js → task-runner-Cm0gXoLH.js} +15 -15
- package/dist/{thread-state-CRK7aA41.js → thread-state-lw2QwQ-y.js} +5 -5
- package/dist/{turn-execution-XtyzIHog.js → turn-execution-C7K6lQp1.js} +3 -3
- package/dist/{turn-lifecycle-VbwZLfBv.js → turn-lifecycle-DVlJLwB0.js} +1 -1
- package/dist/{usage-D4A48Bdl.js → usage-BMuAlKIW.js} +2 -2
- package/dist/{validation-jn_46o4U.js → validation-BI-r3iQA.js} +1 -1
- package/dist/{version-DcfJxB4z.js → version-s6smK6lP.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{web-authorization-CsfLSfEs.js → web-authorization-C9MxPvXX.js} +1 -1
- package/dist/{web-input-BPp_sc7t.js → web-input-CwVlaLd0.js} +7 -109
- package/dist/{work-Di-ubSGg.js → work-BK0W1QAt.js} +9 -9
- package/dist/{worker-Cu_D5Mr8.js → worker-D-WWKHQD.js} +5 -5
- package/migrations/0041_automation_outcomes_backfill.sql +27 -0
- package/migrations/meta/0041_snapshot.json +3516 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +5 -5
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { F as isRecord$1, I as toOptionalNumber, L as toOptionalString } from "./logging-
|
|
2
|
-
import { a as getChatConfig } from "./config-
|
|
3
|
-
import { _ as parseSlackMessageTs, d as parseStoredSlackActor, h as parseSlackTeamId, p as parseSlackChannelId } from "./context-
|
|
4
|
-
import {
|
|
1
|
+
import { F as isRecord$1, I as toOptionalNumber, L as toOptionalString } from "./logging-sNS84Yuu.js";
|
|
2
|
+
import { a as getChatConfig } from "./config-EtF2uEaW.js";
|
|
3
|
+
import { _ as parseSlackMessageTs, d as parseStoredSlackActor, h as parseSlackTeamId, p as parseSlackChannelId } from "./context-Bplcgypl.js";
|
|
4
|
+
import { Gt as getDb, J as withSlackRetries, U as getSlackClient, Wt as getConversationStore, an as parseDestination, pt as JUNIOR_THREAD_STATE_TTL_MS, sn as sameDestination, u as getPlugins } from "./agent-hooks-CuGjLjmR.js";
|
|
5
5
|
import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
|
|
6
|
-
import { S as juniorAttachments, a as juniorSchedulerRuns, c as juniorStats, l as
|
|
7
|
-
import { o as ToolInputError } from "./usage-
|
|
8
|
-
import { C as getDefaultRedisStateAdapterFor, w as getStateAdapter } from "./profile-
|
|
6
|
+
import { S as juniorAttachments, a as juniorSchedulerRuns, c as juniorStats, l as juniorEventAutomations, o as juniorSchedulerTasks, w as juniorConversations } from "./executor-BW5qp0jC.js";
|
|
7
|
+
import { o as ToolInputError } from "./usage-BMuAlKIW.js";
|
|
8
|
+
import { C as getDefaultRedisStateAdapterFor, w as getStateAdapter } from "./profile-BfLMVhWo.js";
|
|
9
9
|
import "./vercel-queue-DIkVcfK4.js";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
12
|
import { and, asc, desc, eq, gte, inArray, isNotNull, isNull, lte, ne, notInArray, or, sql } from "drizzle-orm";
|
|
13
|
-
import { actorUserIdSchema, destinationSchema, destinationVisibilitySchema,
|
|
13
|
+
import { actorUserIdSchema, destinationSchema, destinationVisibilitySchema, eventMatchSchema, eventMatches, eventTypeSchema, normalizeEventIdentifier, slackDestinationSchema, taskOutcomeSchema } from "@sentry/junior-plugin-api";
|
|
14
14
|
//#region src/chat/state/turn-id.ts
|
|
15
15
|
/** Build a stable turn identifier from a message ID. */
|
|
16
16
|
function buildDeterministicTurnId(messageId) {
|
|
@@ -109,7 +109,7 @@ const inboundMessageSourceSchema = z.enum([
|
|
|
109
109
|
"internal",
|
|
110
110
|
"local",
|
|
111
111
|
"plugin",
|
|
112
|
-
"
|
|
112
|
+
"event",
|
|
113
113
|
"scheduler",
|
|
114
114
|
"slack",
|
|
115
115
|
"web"
|
|
@@ -583,7 +583,7 @@ async function writeConversation(state, lock, conversation) {
|
|
|
583
583
|
...next,
|
|
584
584
|
execution: {
|
|
585
585
|
...next.execution,
|
|
586
|
-
pendingMessages: next.execution.pendingMessages.map((message) => storeMessage(message, hasSlackDestination && (message.source === "slack" || message.source === "
|
|
586
|
+
pendingMessages: next.execution.pendingMessages.map((message) => storeMessage(message, hasSlackDestination && (message.source === "slack" || message.source === "event" || message.source === "plugin" || message.source === "scheduler")))
|
|
587
587
|
}
|
|
588
588
|
};
|
|
589
589
|
await state.set(conversationKey(next.conversationId), stored, JUNIOR_THREAD_STATE_TTL_MS);
|
|
@@ -1445,43 +1445,43 @@ async function listActiveConversationIds(args = {}) {
|
|
|
1445
1445
|
return await listActiveConversationIds$1(args);
|
|
1446
1446
|
}
|
|
1447
1447
|
//#endregion
|
|
1448
|
-
//#region src/chat/
|
|
1449
|
-
/** Core-owned
|
|
1450
|
-
const
|
|
1448
|
+
//#region src/chat/events/catalog.ts
|
|
1449
|
+
/** Core-owned event namespace. Not a plugin publisher. */
|
|
1450
|
+
const CORE_EVENT_NAMESPACE = "junior";
|
|
1451
1451
|
/** True when the catalog has any enabled registration. */
|
|
1452
|
-
function
|
|
1452
|
+
function hasEventCatalogEntries(catalog) {
|
|
1453
1453
|
return Object.keys(catalog).length > 0;
|
|
1454
1454
|
}
|
|
1455
1455
|
/**
|
|
1456
1456
|
* True when at least one plugin namespace is enabled.
|
|
1457
1457
|
*
|
|
1458
1458
|
* Core Workspace snapshot events stay in the catalog for search and temporary
|
|
1459
|
-
* watches. Durable event
|
|
1459
|
+
* watches. Durable event automations still require a plugin publisher.
|
|
1460
1460
|
*/
|
|
1461
|
-
function
|
|
1462
|
-
return Object.keys(
|
|
1461
|
+
function hasPluginEventCatalogEntries(catalog) {
|
|
1462
|
+
return Object.keys(pluginEventCatalog(catalog)).length > 0;
|
|
1463
1463
|
}
|
|
1464
1464
|
/**
|
|
1465
|
-
* Catalog entries that durable event
|
|
1465
|
+
* Catalog entries that durable event automations may select.
|
|
1466
1466
|
*
|
|
1467
1467
|
* Core Workspace snapshot events only wake temporary watches. Snapshot finish
|
|
1468
|
-
* does not run event-
|
|
1468
|
+
* does not run event-automation ingest, so those namespaces stay out of create/update
|
|
1469
1469
|
* schemas and trigger availability.
|
|
1470
1470
|
*/
|
|
1471
|
-
function
|
|
1472
|
-
return Object.fromEntries(Object.entries(catalog).filter(([namespace]) => namespace !==
|
|
1471
|
+
function pluginEventCatalog(catalog) {
|
|
1472
|
+
return Object.fromEntries(Object.entries(catalog).filter(([namespace]) => namespace !== CORE_EVENT_NAMESPACE));
|
|
1473
1473
|
}
|
|
1474
1474
|
function enumSchema(values, unavailableMessage) {
|
|
1475
1475
|
if (values.length === 0) return z.string().refine(() => false, unavailableMessage);
|
|
1476
1476
|
return z.enum(values);
|
|
1477
1477
|
}
|
|
1478
|
-
/** Build the model-facing namespace enum from enabled
|
|
1478
|
+
/** Build the model-facing namespace enum from enabled event plugins. */
|
|
1479
1479
|
function eventNamespaceSchema(catalog) {
|
|
1480
|
-
return enumSchema(Object.keys(catalog).sort(), "No
|
|
1480
|
+
return enumSchema(Object.keys(catalog).sort(), "No event namespaces are enabled.");
|
|
1481
1481
|
}
|
|
1482
1482
|
/** Build the model-facing event enum from enabled plugin registrations. */
|
|
1483
1483
|
function registeredEventTypeSchema(catalog) {
|
|
1484
|
-
return enumSchema([...new Set(Object.values(catalog).flatMap((registration) => registration.resourceTypes.flatMap((resourceType) => resourceType.supportedEvents)))].sort(), "No
|
|
1484
|
+
return enumSchema([...new Set(Object.values(catalog).flatMap((registration) => registration.resourceTypes.flatMap((resourceType) => resourceType.supportedEvents)))].sort(), "No event types are enabled.");
|
|
1485
1485
|
}
|
|
1486
1486
|
/** Build the model-facing resource type enum from enabled plugin registrations. */
|
|
1487
1487
|
function registeredResourceTypeSchema(catalog) {
|
|
@@ -1492,22 +1492,22 @@ function pluginSupportsEvent(catalog, namespace, resourceType, eventType) {
|
|
|
1492
1492
|
return catalog[namespace]?.resourceTypes.find((candidate) => candidate.type === resourceType)?.supportedEvents.includes(eventType) ?? false;
|
|
1493
1493
|
}
|
|
1494
1494
|
/** Return app guidance for one registered resource type and event type. */
|
|
1495
|
-
function
|
|
1495
|
+
function eventGuidance(catalog, namespace, resourceType, eventType) {
|
|
1496
1496
|
return catalog[namespace]?.resourceTypes.find((candidate) => candidate.type === resourceType)?.guidance?.[eventType];
|
|
1497
1497
|
}
|
|
1498
1498
|
/** Return declared match keys for one registered resource type. */
|
|
1499
|
-
function
|
|
1499
|
+
function eventMatchFields(catalog, namespace, resourceType) {
|
|
1500
1500
|
return catalog[namespace]?.resourceTypes.find((candidate) => candidate.type === resourceType)?.matchFields;
|
|
1501
1501
|
}
|
|
1502
1502
|
/** Build the match object the tools accept for one resource type. */
|
|
1503
|
-
function
|
|
1504
|
-
return
|
|
1503
|
+
function registeredEventMatchSchema() {
|
|
1504
|
+
return eventMatchSchema.optional().describe("Optional exact values from the resource type matchFields. A single value must equal the event data. A list means the event value may be any one entry. Omit a key to ignore it. Unmatched events are dropped with no agent turn.");
|
|
1505
1505
|
}
|
|
1506
1506
|
/** Reject match keys the resource type does not list. */
|
|
1507
|
-
function
|
|
1507
|
+
function requireSupportedEventMatch(catalog, input) {
|
|
1508
1508
|
const match = input.match;
|
|
1509
1509
|
if (!match || Object.keys(match).length === 0) return void 0;
|
|
1510
|
-
const fields =
|
|
1510
|
+
const fields = eventMatchFields(catalog, input.namespace, input.resourceType);
|
|
1511
1511
|
if (!fields || Object.keys(fields).length === 0) throw new ToolInputError(`Resource type "${input.namespace}:${input.resourceType}" does not support match.`);
|
|
1512
1512
|
const normalized = {};
|
|
1513
1513
|
for (const [key, value] of Object.entries(match)) {
|
|
@@ -1529,12 +1529,12 @@ function requireSupportedResourceEventMatch(catalog, input) {
|
|
|
1529
1529
|
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
1530
1530
|
}
|
|
1531
1531
|
/** Normalize one selector with the convention declared by its plugin. */
|
|
1532
|
-
function
|
|
1533
|
-
return
|
|
1532
|
+
function normalizeCatalogEventIdentifier(catalog, namespace, identifier) {
|
|
1533
|
+
return normalizeEventIdentifier(catalog[namespace], identifier);
|
|
1534
1534
|
}
|
|
1535
1535
|
//#endregion
|
|
1536
1536
|
//#region src/chat/sandbox/snapshot/events.ts
|
|
1537
|
-
const WORKSPACE_SNAPSHOT_NAMESPACE =
|
|
1537
|
+
const WORKSPACE_SNAPSHOT_NAMESPACE = CORE_EVENT_NAMESPACE;
|
|
1538
1538
|
const WORKSPACE_SNAPSHOT_RESOURCE_TYPE = "workspace_snapshot";
|
|
1539
1539
|
const WORKSPACE_SNAPSHOT_READY_EVENT = "workspace_snapshot.ready";
|
|
1540
1540
|
const WORKSPACE_SNAPSHOT_FAILED_EVENT = "workspace_snapshot.failed";
|
|
@@ -1544,7 +1544,7 @@ const WORKSPACE_SNAPSHOT_FAILED_EVENT = "workspace_snapshot.failed";
|
|
|
1544
1544
|
* Uses the same registration shape as plugins so search, guidance, and tool
|
|
1545
1545
|
* schemas treat these events like any other resource type.
|
|
1546
1546
|
*/
|
|
1547
|
-
function
|
|
1547
|
+
function workspaceSnapshotEvents() {
|
|
1548
1548
|
return { resourceTypes: [{
|
|
1549
1549
|
type: WORKSPACE_SNAPSHOT_RESOURCE_TYPE,
|
|
1550
1550
|
supportedEvents: [WORKSPACE_SNAPSHOT_READY_EVENT, WORKSPACE_SNAPSHOT_FAILED_EVENT],
|
|
@@ -1588,62 +1588,66 @@ function workspaceSnapshotFinishedEvent(input) {
|
|
|
1588
1588
|
return event;
|
|
1589
1589
|
}
|
|
1590
1590
|
//#endregion
|
|
1591
|
-
//#region src/chat/
|
|
1591
|
+
//#region src/chat/events/runtime-catalog.ts
|
|
1592
1592
|
/**
|
|
1593
|
-
* Enabled
|
|
1593
|
+
* Enabled event registrations for search, guidance, and tool schemas.
|
|
1594
1594
|
*
|
|
1595
1595
|
* Includes core Workspace snapshot events under the `junior` namespace, plus
|
|
1596
1596
|
* every enabled plugin registration. Plugins may not use the `junior` namespace.
|
|
1597
1597
|
*/
|
|
1598
|
-
function
|
|
1599
|
-
const catalog = { [
|
|
1598
|
+
function getEventCatalog() {
|
|
1599
|
+
const catalog = { [CORE_EVENT_NAMESPACE]: workspaceSnapshotEvents() };
|
|
1600
1600
|
for (const plugin of getPlugins()) {
|
|
1601
|
-
const registration = plugin.
|
|
1601
|
+
const registration = plugin.events;
|
|
1602
1602
|
if (!registration || registration.isEnabled?.() === false) continue;
|
|
1603
|
-
if (plugin.manifest.name === "junior") throw new Error(`Plugin "${
|
|
1603
|
+
if (plugin.manifest.name === "junior") throw new Error(`Plugin "${CORE_EVENT_NAMESPACE}" cannot register events; that namespace is reserved for core Workspace snapshots`);
|
|
1604
1604
|
catalog[plugin.manifest.name] = registration;
|
|
1605
1605
|
}
|
|
1606
1606
|
return catalog;
|
|
1607
1607
|
}
|
|
1608
|
-
/** Validate the persisted Slack creator identity for an event
|
|
1609
|
-
const
|
|
1608
|
+
/** Validate the persisted Slack creator identity for an event automation. */
|
|
1609
|
+
const eventAutomationPrincipalSchema = z.object({
|
|
1610
1610
|
slackUserId: actorUserIdSchema,
|
|
1611
1611
|
fullName: z.string().optional(),
|
|
1612
1612
|
userName: z.string().optional()
|
|
1613
1613
|
}).strict();
|
|
1614
|
-
/** Validate one persisted event-
|
|
1615
|
-
const
|
|
1616
|
-
events: z.array(
|
|
1614
|
+
/** Validate one persisted event-automation selector and its presentation metadata. */
|
|
1615
|
+
const eventAutomationTriggerSchema = z.object({
|
|
1616
|
+
events: z.array(eventTypeSchema).min(1),
|
|
1617
1617
|
label: z.string().min(1),
|
|
1618
|
-
match:
|
|
1618
|
+
match: eventMatchSchema.optional(),
|
|
1619
1619
|
namespace: z.string().min(1),
|
|
1620
1620
|
identifier: z.string().min(1).max(300),
|
|
1621
1621
|
resourceType: z.string().min(1)
|
|
1622
1622
|
}).strict();
|
|
1623
|
-
/** Validate one persisted event
|
|
1624
|
-
const
|
|
1623
|
+
/** Validate one persisted event automation. */
|
|
1624
|
+
const eventAutomationSchema = z.object({
|
|
1625
1625
|
id: z.string().min(1),
|
|
1626
1626
|
createdAtMs: z.number().finite(),
|
|
1627
|
-
createdBy:
|
|
1627
|
+
createdBy: eventAutomationPrincipalSchema,
|
|
1628
1628
|
credentialMode: z.enum(["system", "creator"]),
|
|
1629
1629
|
destination: slackDestinationSchema,
|
|
1630
1630
|
destinationVisibility: destinationVisibilitySchema,
|
|
1631
|
-
/**
|
|
1632
|
-
outcomes: z.array(taskOutcomeSchema).max(5)
|
|
1631
|
+
/** Explicit visible effects after successful work. An empty list is silent. */
|
|
1632
|
+
outcomes: z.array(taskOutcomeSchema).max(5),
|
|
1633
1633
|
task: z.object({ text: z.string().min(1) }).strict(),
|
|
1634
|
-
trigger:
|
|
1634
|
+
trigger: eventAutomationTriggerSchema
|
|
1635
1635
|
}).strict();
|
|
1636
1636
|
//#endregion
|
|
1637
|
-
//#region src/chat/event-
|
|
1637
|
+
//#region src/chat/event-automations/store.ts
|
|
1638
1638
|
/** JSON task payload must not carry SQL-backed columns. */
|
|
1639
|
-
function
|
|
1639
|
+
function eventAutomationJsonPayload(task) {
|
|
1640
1640
|
const { status: _status, title: _title, ...payload } = task;
|
|
1641
1641
|
return payload;
|
|
1642
1642
|
}
|
|
1643
1643
|
function parseTask(row) {
|
|
1644
1644
|
const raw = row.task && typeof row.task === "object" ? { ...row.task } : row.task;
|
|
1645
1645
|
if (raw && typeof raw === "object" && "title" in raw) delete raw.title;
|
|
1646
|
-
|
|
1646
|
+
if (raw && typeof raw === "object" && !("outcomes" in raw) && "destination" in raw) raw.outcomes = [{
|
|
1647
|
+
action: "send_message",
|
|
1648
|
+
destination: raw.destination
|
|
1649
|
+
}];
|
|
1650
|
+
const payload = eventAutomationSchema.parse(raw);
|
|
1647
1651
|
const title = row.title?.trim();
|
|
1648
1652
|
return {
|
|
1649
1653
|
...payload,
|
|
@@ -1651,23 +1655,23 @@ function parseTask(row) {
|
|
|
1651
1655
|
...title ? { title } : void 0
|
|
1652
1656
|
};
|
|
1653
1657
|
}
|
|
1654
|
-
function
|
|
1655
|
-
return ne(
|
|
1658
|
+
function activeEventAutomationWhere() {
|
|
1659
|
+
return ne(juniorEventAutomations.status, "deleted");
|
|
1656
1660
|
}
|
|
1657
|
-
/** Read one event
|
|
1658
|
-
async function
|
|
1661
|
+
/** Read one event automation by id, including deleted rows when present. */
|
|
1662
|
+
async function getEventAutomation(db, id) {
|
|
1659
1663
|
const rows = await db.select({
|
|
1660
|
-
status:
|
|
1661
|
-
task:
|
|
1662
|
-
title:
|
|
1663
|
-
}).from(
|
|
1664
|
+
status: juniorEventAutomations.status,
|
|
1665
|
+
task: juniorEventAutomations.task,
|
|
1666
|
+
title: juniorEventAutomations.title
|
|
1667
|
+
}).from(juniorEventAutomations).where(eq(juniorEventAutomations.id, id)).limit(1);
|
|
1664
1668
|
return rows[0] ? parseTask(rows[0]) : void 0;
|
|
1665
1669
|
}
|
|
1666
|
-
/** Create one retry-stable event
|
|
1667
|
-
async function
|
|
1668
|
-
const parsed =
|
|
1670
|
+
/** Create one retry-stable event automation, or revive a deleted row with the new payload. */
|
|
1671
|
+
async function createEventAutomation(db, task) {
|
|
1672
|
+
const parsed = eventAutomationSchema.parse(eventAutomationJsonPayload(task));
|
|
1669
1673
|
const title = task.title?.trim() || null;
|
|
1670
|
-
await db.insert(
|
|
1674
|
+
await db.insert(juniorEventAutomations).values({
|
|
1671
1675
|
id: parsed.id,
|
|
1672
1676
|
teamId: parsed.destination.teamId,
|
|
1673
1677
|
namespace: parsed.trigger.namespace,
|
|
@@ -1677,118 +1681,118 @@ async function createEventTask(db, task) {
|
|
|
1677
1681
|
title,
|
|
1678
1682
|
task: parsed
|
|
1679
1683
|
}).onConflictDoNothing();
|
|
1680
|
-
const existing = await
|
|
1684
|
+
const existing = await getEventAutomation(db, parsed.id);
|
|
1681
1685
|
if (!existing) return {
|
|
1682
1686
|
...parsed,
|
|
1683
1687
|
status: "active",
|
|
1684
1688
|
...title ? { title } : void 0
|
|
1685
1689
|
};
|
|
1686
1690
|
if (existing.status !== "deleted") return existing;
|
|
1687
|
-
const rows = await db.update(
|
|
1691
|
+
const rows = await db.update(juniorEventAutomations).set({
|
|
1688
1692
|
teamId: parsed.destination.teamId,
|
|
1689
1693
|
namespace: parsed.trigger.namespace,
|
|
1690
1694
|
identifier: parsed.trigger.identifier,
|
|
1691
1695
|
status: "active",
|
|
1692
1696
|
title,
|
|
1693
1697
|
task: parsed
|
|
1694
|
-
}).where(and(eq(
|
|
1695
|
-
status:
|
|
1696
|
-
task:
|
|
1697
|
-
title:
|
|
1698
|
+
}).where(and(eq(juniorEventAutomations.id, parsed.id), eq(juniorEventAutomations.status, "deleted"))).returning({
|
|
1699
|
+
status: juniorEventAutomations.status,
|
|
1700
|
+
task: juniorEventAutomations.task,
|
|
1701
|
+
title: juniorEventAutomations.title
|
|
1698
1702
|
});
|
|
1699
|
-
return rows[0] ? parseTask(rows[0]) : await
|
|
1703
|
+
return rows[0] ? parseTask(rows[0]) : await getEventAutomation(db, parsed.id) ?? {
|
|
1700
1704
|
...parsed,
|
|
1701
1705
|
status: "active",
|
|
1702
1706
|
...title ? { title } : void 0
|
|
1703
1707
|
};
|
|
1704
1708
|
}
|
|
1705
|
-
/** Replace an existing non-deleted event
|
|
1706
|
-
async function
|
|
1707
|
-
const parsed =
|
|
1709
|
+
/** Replace an existing non-deleted event automation. */
|
|
1710
|
+
async function saveEventAutomation(db, task) {
|
|
1711
|
+
const parsed = eventAutomationSchema.parse(eventAutomationJsonPayload(task));
|
|
1708
1712
|
const title = task.title?.trim() || null;
|
|
1709
|
-
const rows = await db.update(
|
|
1713
|
+
const rows = await db.update(juniorEventAutomations).set({
|
|
1710
1714
|
namespace: parsed.trigger.namespace,
|
|
1711
1715
|
identifier: parsed.trigger.identifier,
|
|
1712
1716
|
title,
|
|
1713
1717
|
task: parsed
|
|
1714
|
-
}).where(and(eq(
|
|
1715
|
-
status:
|
|
1716
|
-
task:
|
|
1717
|
-
title:
|
|
1718
|
+
}).where(and(eq(juniorEventAutomations.id, parsed.id), activeEventAutomationWhere())).returning({
|
|
1719
|
+
status: juniorEventAutomations.status,
|
|
1720
|
+
task: juniorEventAutomations.task,
|
|
1721
|
+
title: juniorEventAutomations.title
|
|
1718
1722
|
});
|
|
1719
1723
|
return rows[0] ? parseTask(rows[0]) : void 0;
|
|
1720
1724
|
}
|
|
1721
|
-
/** Mark one existing event
|
|
1722
|
-
async function
|
|
1723
|
-
const rows = await db.update(
|
|
1724
|
-
status:
|
|
1725
|
-
task:
|
|
1726
|
-
title:
|
|
1725
|
+
/** Mark one existing event automation deleted while retaining the row for history. */
|
|
1726
|
+
async function deleteEventAutomation(db, id) {
|
|
1727
|
+
const rows = await db.update(juniorEventAutomations).set({ status: "deleted" }).where(and(eq(juniorEventAutomations.id, id), activeEventAutomationWhere())).returning({
|
|
1728
|
+
status: juniorEventAutomations.status,
|
|
1729
|
+
task: juniorEventAutomations.task,
|
|
1730
|
+
title: juniorEventAutomations.title
|
|
1727
1731
|
});
|
|
1728
1732
|
return rows[0] ? parseTask(rows[0]) : void 0;
|
|
1729
1733
|
}
|
|
1730
|
-
/** List live event
|
|
1731
|
-
async function
|
|
1734
|
+
/** List live event automations in one Slack workspace. */
|
|
1735
|
+
async function listEventAutomationsForTeam(db, teamId) {
|
|
1732
1736
|
return (await db.select({
|
|
1733
|
-
status:
|
|
1734
|
-
task:
|
|
1735
|
-
title:
|
|
1736
|
-
}).from(
|
|
1737
|
+
status: juniorEventAutomations.status,
|
|
1738
|
+
task: juniorEventAutomations.task,
|
|
1739
|
+
title: juniorEventAutomations.title
|
|
1740
|
+
}).from(juniorEventAutomations).where(and(eq(juniorEventAutomations.teamId, teamId), activeEventAutomationWhere())).orderBy(asc(juniorEventAutomations.createdAtMs), asc(juniorEventAutomations.id))).map(parseTask);
|
|
1737
1741
|
}
|
|
1738
1742
|
function viewerSlackIdentities(user) {
|
|
1739
1743
|
return user.identities.filter((identity) => identity.provider === "slack" && Boolean(identity.providerTenantId) && Boolean(identity.providerSubjectId));
|
|
1740
1744
|
}
|
|
1741
|
-
/** Return whether one event
|
|
1742
|
-
function
|
|
1745
|
+
/** Return whether one event automation was created by a viewer-linked Slack identity. */
|
|
1746
|
+
function eventAutomationBelongsToUser(task, user) {
|
|
1743
1747
|
return viewerSlackIdentities(user).some((identity) => identity.providerTenantId === task.destination.teamId && identity.providerSubjectId === task.createdBy.slackUserId);
|
|
1744
1748
|
}
|
|
1745
|
-
/** List a bounded newest-first page of live event
|
|
1746
|
-
async function
|
|
1749
|
+
/** List a bounded newest-first page of live event automations created by one user. */
|
|
1750
|
+
async function listEventAutomationsCreatedBy(db, user, limit, query) {
|
|
1747
1751
|
const identities = viewerSlackIdentities(user);
|
|
1748
|
-
const ownership = or(...identities.map((identity) => and(eq(
|
|
1752
|
+
const ownership = or(...identities.map((identity) => and(eq(juniorEventAutomations.teamId, identity.providerTenantId), sql`${juniorEventAutomations.task}->'createdBy'->>'slackUserId' = ${identity.providerSubjectId}`)));
|
|
1749
1753
|
if (!ownership) return [];
|
|
1750
1754
|
return (await db.select({
|
|
1751
|
-
status:
|
|
1752
|
-
task:
|
|
1753
|
-
title:
|
|
1754
|
-
}).from(
|
|
1755
|
+
status: juniorEventAutomations.status,
|
|
1756
|
+
task: juniorEventAutomations.task,
|
|
1757
|
+
title: juniorEventAutomations.title
|
|
1758
|
+
}).from(juniorEventAutomations).where(and(ownership, activeEventAutomationWhere(), query ? sql`strpos(lower(coalesce(${juniorEventAutomations.title}, ${juniorEventAutomations.task}->'task'->>'text')), ${query}) > 0` : void 0)).orderBy(desc(juniorEventAutomations.createdAtMs), desc(juniorEventAutomations.id)).limit(limit)).map(parseTask);
|
|
1755
1759
|
}
|
|
1756
1760
|
/**
|
|
1757
|
-
* List deleted event
|
|
1758
|
-
* Used only to keep historical runs after the live
|
|
1761
|
+
* List deleted event automations created by one user, newest first.
|
|
1762
|
+
* Used only to keep historical runs after the live automation list drops the row.
|
|
1759
1763
|
*/
|
|
1760
|
-
async function
|
|
1764
|
+
async function listDeletedEventAutomationsCreatedBy(db, user, limit) {
|
|
1761
1765
|
const identities = viewerSlackIdentities(user);
|
|
1762
|
-
const ownership = or(...identities.map((identity) => and(eq(
|
|
1766
|
+
const ownership = or(...identities.map((identity) => and(eq(juniorEventAutomations.teamId, identity.providerTenantId), sql`${juniorEventAutomations.task}->'createdBy'->>'slackUserId' = ${identity.providerSubjectId}`)));
|
|
1763
1767
|
if (!ownership) return [];
|
|
1764
1768
|
return (await db.select({
|
|
1765
|
-
status:
|
|
1766
|
-
task:
|
|
1767
|
-
title:
|
|
1768
|
-
}).from(
|
|
1769
|
+
status: juniorEventAutomations.status,
|
|
1770
|
+
task: juniorEventAutomations.task,
|
|
1771
|
+
title: juniorEventAutomations.title
|
|
1772
|
+
}).from(juniorEventAutomations).where(and(ownership, eq(juniorEventAutomations.status, "deleted"))).orderBy(desc(juniorEventAutomations.createdAtMs), desc(juniorEventAutomations.id)).limit(limit)).map(parseTask);
|
|
1769
1773
|
}
|
|
1770
|
-
/** List live event
|
|
1771
|
-
async function
|
|
1774
|
+
/** List live event automations whose current Slack destination is public. */
|
|
1775
|
+
async function listPublicEventAutomationsForTeams(db, teamIds, limit, query) {
|
|
1772
1776
|
if (teamIds.length === 0) return [];
|
|
1773
1777
|
return (await db.select({
|
|
1774
|
-
status:
|
|
1775
|
-
task:
|
|
1776
|
-
title:
|
|
1777
|
-
}).from(
|
|
1778
|
+
status: juniorEventAutomations.status,
|
|
1779
|
+
task: juniorEventAutomations.task,
|
|
1780
|
+
title: juniorEventAutomations.title
|
|
1781
|
+
}).from(juniorEventAutomations).innerJoin(juniorDestinations, and(eq(juniorDestinations.provider, "slack"), eq(juniorDestinations.providerTenantId, juniorEventAutomations.teamId), sql`${juniorDestinations.providerDestinationId} = ${juniorEventAutomations.task}->'destination'->>'channelId'`)).where(and(inArray(juniorEventAutomations.teamId, teamIds), activeEventAutomationWhere(), query ? sql`strpos(lower(coalesce(${juniorEventAutomations.title}, ${juniorEventAutomations.task}->'task'->>'text')), ${query}) > 0` : void 0, eq(juniorDestinations.visibility, "public"))).orderBy(desc(juniorEventAutomations.createdAtMs), desc(juniorEventAutomations.id)).limit(limit)).map(parseTask);
|
|
1778
1782
|
}
|
|
1779
1783
|
/**
|
|
1780
|
-
* Collect match keys from live event
|
|
1784
|
+
* Collect match keys from live event automations for these identifiers and event types.
|
|
1781
1785
|
* Does not evaluate match values — only reports keys that filters use.
|
|
1782
1786
|
*/
|
|
1783
|
-
async function
|
|
1787
|
+
async function collectEventAutomationMatchKeys(db, input) {
|
|
1784
1788
|
const eventTypes = new Set(input.eventTypes.map((eventType) => eventType.trim()).filter(Boolean));
|
|
1785
1789
|
const identifiers = [...new Set(input.identifiers.map((value) => value.trim()).filter(Boolean))];
|
|
1786
1790
|
if (eventTypes.size === 0 || identifiers.length === 0) return [];
|
|
1787
1791
|
const rows = await db.select({
|
|
1788
|
-
status:
|
|
1789
|
-
task:
|
|
1790
|
-
title:
|
|
1791
|
-
}).from(
|
|
1792
|
+
status: juniorEventAutomations.status,
|
|
1793
|
+
task: juniorEventAutomations.task,
|
|
1794
|
+
title: juniorEventAutomations.title
|
|
1795
|
+
}).from(juniorEventAutomations).where(and(eq(juniorEventAutomations.teamId, input.teamId), eq(juniorEventAutomations.namespace, input.namespace), inArray(juniorEventAutomations.identifier, identifiers), activeEventAutomationWhere())).orderBy(asc(juniorEventAutomations.createdAtMs), asc(juniorEventAutomations.id));
|
|
1792
1796
|
const keys = /* @__PURE__ */ new Set();
|
|
1793
1797
|
for (const row of rows) {
|
|
1794
1798
|
const task = parseTask(row);
|
|
@@ -1797,45 +1801,35 @@ async function collectEventTaskMatchKeys(db, input) {
|
|
|
1797
1801
|
}
|
|
1798
1802
|
return [...keys].sort();
|
|
1799
1803
|
}
|
|
1800
|
-
/** Find every live task matching one normalized
|
|
1801
|
-
async function
|
|
1804
|
+
/** Find every live task matching one normalized event. */
|
|
1805
|
+
async function findMatchingEventAutomations(db, event, teamId) {
|
|
1802
1806
|
return (await db.select({
|
|
1803
|
-
status:
|
|
1804
|
-
task:
|
|
1805
|
-
title:
|
|
1806
|
-
}).from(
|
|
1807
|
+
status: juniorEventAutomations.status,
|
|
1808
|
+
task: juniorEventAutomations.task,
|
|
1809
|
+
title: juniorEventAutomations.title
|
|
1810
|
+
}).from(juniorEventAutomations).where(and(eq(juniorEventAutomations.teamId, teamId), eq(juniorEventAutomations.namespace, event.namespace), eq(juniorEventAutomations.identifier, event.identifier), activeEventAutomationWhere())).orderBy(asc(juniorEventAutomations.createdAtMs), asc(juniorEventAutomations.id))).map(parseTask).filter((task) => task.trigger.events.includes(event.eventType) && eventMatches(task.trigger.match, event.data));
|
|
1807
1811
|
}
|
|
1808
1812
|
//#endregion
|
|
1809
1813
|
//#region src/chat/task-outcomes.ts
|
|
1810
|
-
const taskMessageDestinationInputSchema = z.
|
|
1811
|
-
platform: z.literal("slack"),
|
|
1812
|
-
teamId: z.string().min(1),
|
|
1813
|
-
userId: actorUserIdSchema
|
|
1814
|
-
}).strict()]);
|
|
1814
|
+
const taskMessageDestinationInputSchema = z.enum(["current_conversation", "task_creator"]).describe("Where to send the message. Use current_conversation for posts, digests, summaries, and channel reminders. Use task_creator only when the user asks for a direct reminder or notification.");
|
|
1815
1815
|
/** Input accepted by task authoring tools before a user becomes a DM Destination. */
|
|
1816
1816
|
const taskOutcomeInputSchema = z.object({
|
|
1817
1817
|
action: z.literal("send_message"),
|
|
1818
1818
|
destination: taskMessageDestinationInputSchema
|
|
1819
1819
|
}).strict();
|
|
1820
|
-
/** Resolve
|
|
1820
|
+
/** Resolve explicit message outcomes to the Slack Destinations stored on an Automation. */
|
|
1821
1821
|
async function resolveTaskOutcomes(outcomes, currentDestination, creatorSlackUserId) {
|
|
1822
|
-
if (outcomes === void 0) return [
|
|
1823
|
-
action: "send_message",
|
|
1824
|
-
destination: currentDestination
|
|
1825
|
-
}];
|
|
1822
|
+
if (outcomes === void 0) return [];
|
|
1826
1823
|
const resolved = [];
|
|
1827
1824
|
for (const outcome of outcomes) {
|
|
1828
|
-
if (outcome.destination
|
|
1829
|
-
if ("channelId" in outcome.destination) {
|
|
1830
|
-
if (outcome.destination.channelId !== currentDestination.channelId) throw new Error("Messages can only be sent to the current Slack conversation or the task creator.");
|
|
1825
|
+
if (outcome.destination === "current_conversation") {
|
|
1831
1826
|
resolved.push({
|
|
1832
1827
|
action: "send_message",
|
|
1833
1828
|
destination: currentDestination
|
|
1834
1829
|
});
|
|
1835
1830
|
continue;
|
|
1836
1831
|
}
|
|
1837
|
-
const userId =
|
|
1838
|
-
if (userId !== creatorSlackUserId) throw new Error("Direct messages can only be sent to the task creator.");
|
|
1832
|
+
const userId = creatorSlackUserId;
|
|
1839
1833
|
const channelId = (await withSlackRetries(() => getSlackClient().conversations.open({ users: userId }), 3, { action: "conversations.open" })).channel?.id;
|
|
1840
1834
|
if (!channelId) throw new Error("Slack did not return a direct message destination.");
|
|
1841
1835
|
resolved.push({
|
|
@@ -1852,12 +1846,9 @@ async function resolveTaskOutcomes(outcomes, currentDestination, creatorSlackUse
|
|
|
1852
1846
|
function outcomeTargetsDestination(outcome, destination) {
|
|
1853
1847
|
return outcome.destination.teamId === destination.teamId && outcome.destination.channelId === destination.channelId && (!outcome.destination.threadTs || outcome.destination.threadTs === destination.threadTs);
|
|
1854
1848
|
}
|
|
1855
|
-
/**
|
|
1849
|
+
/** Bind same-channel message outcomes to the Automation Destination. */
|
|
1856
1850
|
function effectiveTaskOutcomes(outcomes, destination) {
|
|
1857
|
-
return (
|
|
1858
|
-
action: "send_message",
|
|
1859
|
-
destination
|
|
1860
|
-
}]).map((outcome) => destination.threadTs && outcomeTargetsDestination(outcome, destination) ? {
|
|
1851
|
+
return outcomes.map((outcome) => destination.threadTs && outcomeTargetsDestination(outcome, destination) ? {
|
|
1861
1852
|
...outcome,
|
|
1862
1853
|
destination
|
|
1863
1854
|
} : outcome);
|
|
@@ -1870,53 +1861,66 @@ function moveTaskOutcomes(outcomes, currentDestination, nextDestination) {
|
|
|
1870
1861
|
} : outcome);
|
|
1871
1862
|
}
|
|
1872
1863
|
//#endregion
|
|
1873
|
-
//#region src/chat/event-
|
|
1874
|
-
const
|
|
1864
|
+
//#region src/chat/event-automations/tool-support.ts
|
|
1865
|
+
const compactEventAutomationResultSchema = z.object({
|
|
1875
1866
|
id: z.string().min(1),
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1867
|
+
title: z.string().min(1).nullable(),
|
|
1868
|
+
instruction: z.string().min(1),
|
|
1869
|
+
trigger: z.object({
|
|
1870
|
+
namespace: z.string().min(1),
|
|
1871
|
+
identifier: z.string().min(1),
|
|
1872
|
+
resourceType: z.string().min(1),
|
|
1873
|
+
label: z.string().min(1),
|
|
1874
|
+
events: z.array(z.string().min(1)).min(1),
|
|
1875
|
+
match: z.record(z.string(), z.unknown()).optional(),
|
|
1876
|
+
available: z.boolean()
|
|
1877
|
+
}).strict(),
|
|
1883
1878
|
credentialMode: z.enum(["system", "creator"]),
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1879
|
+
isCreator: z.boolean(),
|
|
1880
|
+
outcomes: z.array(z.object({
|
|
1881
|
+
action: z.literal("send_message"),
|
|
1882
|
+
destination: z.object({
|
|
1883
|
+
channel: z.string().min(1),
|
|
1884
|
+
thread: z.string().min(1).nullable()
|
|
1885
|
+
}).strict()
|
|
1886
|
+
}).strict()).max(5),
|
|
1887
|
+
createdBy: z.object({
|
|
1888
|
+
name: z.string().min(1).nullable(),
|
|
1889
|
+
username: z.string().min(1).nullable()
|
|
1890
|
+
}).strict()
|
|
1887
1891
|
}).strict();
|
|
1888
|
-
/** Validate one successful event-
|
|
1889
|
-
const
|
|
1890
|
-
/** Validate one successful event-
|
|
1891
|
-
const
|
|
1892
|
-
|
|
1892
|
+
/** Validate one successful event-automation mutation result. */
|
|
1893
|
+
const eventAutomationToolResultSchema = juniorToolOutputSchema.extend({ automation: compactEventAutomationResultSchema }).strict();
|
|
1894
|
+
/** Validate one successful event-automation list result. */
|
|
1895
|
+
const eventAutomationListToolResultSchema = juniorToolOutputSchema.extend({
|
|
1896
|
+
automations: z.array(compactEventAutomationResultSchema),
|
|
1893
1897
|
truncated: z.boolean()
|
|
1894
1898
|
}).strict();
|
|
1895
|
-
/** Build the validated
|
|
1896
|
-
function
|
|
1897
|
-
const plugins =
|
|
1899
|
+
/** Build the validated event trigger accepted by event-automation tools. */
|
|
1900
|
+
function registeredEventAutomationTriggerSchema(catalog) {
|
|
1901
|
+
const plugins = pluginEventCatalog(catalog);
|
|
1898
1902
|
return z.object({
|
|
1899
1903
|
namespace: eventNamespaceSchema(plugins),
|
|
1900
1904
|
identifier: z.string().trim().min(1).max(300),
|
|
1901
1905
|
resourceType: registeredResourceTypeSchema(plugins),
|
|
1902
1906
|
label: z.string().trim().min(1).max(500),
|
|
1903
1907
|
events: z.array(registeredEventTypeSchema(plugins)).min(1),
|
|
1904
|
-
match:
|
|
1908
|
+
match: registeredEventMatchSchema()
|
|
1905
1909
|
}).strict();
|
|
1906
1910
|
}
|
|
1907
|
-
/** Reject an event-
|
|
1908
|
-
function
|
|
1909
|
-
const plugins =
|
|
1911
|
+
/** Reject an event-automation trigger that is no longer supported by the catalog. */
|
|
1912
|
+
function requireSupportedEventAutomationTrigger(catalog, trigger) {
|
|
1913
|
+
const plugins = pluginEventCatalog(catalog);
|
|
1910
1914
|
for (const eventType of trigger.events) if (!pluginSupportsEvent(plugins, trigger.namespace, trigger.resourceType, eventType)) throw new ToolInputError(`Resource type "${trigger.namespace}:${trigger.resourceType}" does not support event "${eventType}".`);
|
|
1911
|
-
return
|
|
1915
|
+
return requireSupportedEventMatch(plugins, {
|
|
1912
1916
|
match: trigger.match,
|
|
1913
1917
|
namespace: trigger.namespace,
|
|
1914
1918
|
resourceType: trigger.resourceType
|
|
1915
1919
|
});
|
|
1916
1920
|
}
|
|
1917
|
-
/** Require the active Slack authority used for event-
|
|
1918
|
-
function
|
|
1919
|
-
if (context.source.kind !== "slack" || context.destination.platform !== "slack" || context.actor?.platform !== "slack" || context.actor.teamId !== context.source.teamId) throw new ToolInputError("Event
|
|
1921
|
+
/** Require the active Slack authority used for event-automation management. */
|
|
1922
|
+
function requireEventAutomationSlackContext(context) {
|
|
1923
|
+
if (context.source.kind !== "slack" || context.destination.platform !== "slack" || context.actor?.platform !== "slack" || context.actor.teamId !== context.source.teamId) throw new ToolInputError("Event automations require an active Slack channel or DM and actor.");
|
|
1920
1924
|
return {
|
|
1921
1925
|
actor: context.actor,
|
|
1922
1926
|
destination: {
|
|
@@ -1928,61 +1932,74 @@ function requireEventTaskSlackContext(context) {
|
|
|
1928
1932
|
source: context.source
|
|
1929
1933
|
};
|
|
1930
1934
|
}
|
|
1931
|
-
/** Return whether an event
|
|
1932
|
-
function
|
|
1935
|
+
/** Return whether an event automation belongs to the active Slack destination. */
|
|
1936
|
+
function eventAutomationMatchesDestination(task, destination) {
|
|
1933
1937
|
return task.destination.teamId === destination.teamId && task.destination.channelId === destination.channelId;
|
|
1934
1938
|
}
|
|
1935
1939
|
/**
|
|
1936
|
-
* Return whether the active destination may update or delete this
|
|
1937
|
-
* Same-destination
|
|
1940
|
+
* Return whether the active destination may update or delete this automation.
|
|
1941
|
+
* Same-destination automations stay local. Public automations may be managed by id from
|
|
1938
1942
|
* another destination in the same workspace.
|
|
1939
1943
|
*/
|
|
1940
|
-
function
|
|
1944
|
+
function eventAutomationIsWritableFrom(task, destination) {
|
|
1941
1945
|
if (task.destination.teamId !== destination.teamId) return false;
|
|
1942
1946
|
return task.destination.channelId === destination.channelId || task.destinationVisibility === "public";
|
|
1943
1947
|
}
|
|
1944
|
-
/** Load one
|
|
1945
|
-
async function
|
|
1946
|
-
const { destination } =
|
|
1947
|
-
const task = await
|
|
1948
|
-
if (!task || task.status === "deleted" || !
|
|
1948
|
+
/** Load one automation the active destination may update or delete. */
|
|
1949
|
+
async function writableEventAutomation(context, id) {
|
|
1950
|
+
const { destination } = requireEventAutomationSlackContext(context);
|
|
1951
|
+
const task = await getEventAutomation(getDb(), id);
|
|
1952
|
+
if (!task || task.status === "deleted" || !eventAutomationIsWritableFrom(task, destination)) throw new ToolInputError("Event automation was not found.");
|
|
1949
1953
|
return task;
|
|
1950
1954
|
}
|
|
1951
|
-
function
|
|
1952
|
-
const registration =
|
|
1955
|
+
function eventAutomationTriggerAvailable(task, catalog) {
|
|
1956
|
+
const registration = pluginEventCatalog(catalog)[task.trigger.namespace];
|
|
1953
1957
|
return Boolean(registration && task.trigger.events.every((eventType) => registration.resourceTypes.some((resourceType) => resourceType.supportedEvents.includes(eventType))));
|
|
1954
1958
|
}
|
|
1955
|
-
/** Project an event
|
|
1956
|
-
function
|
|
1957
|
-
return
|
|
1959
|
+
/** Project an event automation into the bounded tool-result shape. */
|
|
1960
|
+
function compactEventAutomation(task, catalog, requesterSlackUserId) {
|
|
1961
|
+
return compactEventAutomationResultSchema.parse({
|
|
1958
1962
|
id: task.id,
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1963
|
+
title: task.title?.trim() || null,
|
|
1964
|
+
instruction: task.task.text,
|
|
1965
|
+
trigger: {
|
|
1966
|
+
namespace: task.trigger.namespace,
|
|
1967
|
+
identifier: task.trigger.identifier,
|
|
1968
|
+
resourceType: task.trigger.resourceType,
|
|
1969
|
+
label: task.trigger.label,
|
|
1970
|
+
events: task.trigger.events,
|
|
1971
|
+
...task.trigger.match ? { match: task.trigger.match } : void 0,
|
|
1972
|
+
available: eventAutomationTriggerAvailable(task, catalog)
|
|
1973
|
+
},
|
|
1966
1974
|
credentialMode: task.credentialMode,
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1975
|
+
isCreator: task.createdBy.slackUserId === requesterSlackUserId,
|
|
1976
|
+
outcomes: effectiveTaskOutcomes(task.outcomes, task.destination).map((outcome) => ({
|
|
1977
|
+
action: outcome.action,
|
|
1978
|
+
destination: {
|
|
1979
|
+
channel: outcome.destination.channelId,
|
|
1980
|
+
thread: outcome.destination.threadTs ?? null
|
|
1981
|
+
}
|
|
1982
|
+
})),
|
|
1983
|
+
createdBy: {
|
|
1984
|
+
name: task.createdBy.fullName ?? null,
|
|
1985
|
+
username: task.createdBy.userName ?? null
|
|
1986
|
+
}
|
|
1970
1987
|
});
|
|
1971
1988
|
}
|
|
1972
|
-
/** Return the standard successful event-
|
|
1973
|
-
function
|
|
1974
|
-
return {
|
|
1989
|
+
/** Return the standard successful event-automation tool result. */
|
|
1990
|
+
function eventAutomationToolResult(task, catalog, requesterSlackUserId) {
|
|
1991
|
+
return { automation: compactEventAutomation(task, catalog, requesterSlackUserId) };
|
|
1975
1992
|
}
|
|
1976
1993
|
//#endregion
|
|
1977
|
-
//#region src/chat/scheduled-
|
|
1978
|
-
/** Durable scheduled-
|
|
1979
|
-
const
|
|
1994
|
+
//#region src/chat/scheduled-automations/types.ts
|
|
1995
|
+
/** Durable scheduled-automation domain types owned by Junior core. */
|
|
1996
|
+
const scheduledAutomationStatusSchema = z.enum([
|
|
1980
1997
|
"active",
|
|
1981
1998
|
"blocked",
|
|
1982
1999
|
"completed",
|
|
1983
2000
|
"deleted"
|
|
1984
2001
|
]);
|
|
1985
|
-
const
|
|
2002
|
+
const scheduledAutomationCredentialModeSchema = z.enum(["system", "creator"]);
|
|
1986
2003
|
const scheduledRunStatusSchema = z.enum([
|
|
1987
2004
|
"pending",
|
|
1988
2005
|
"running",
|
|
@@ -1991,12 +2008,12 @@ const scheduledRunStatusSchema = z.enum([
|
|
|
1991
2008
|
"blocked",
|
|
1992
2009
|
"skipped"
|
|
1993
2010
|
]);
|
|
1994
|
-
const
|
|
2011
|
+
const scheduledAutomationPrincipalSchema = z.object({
|
|
1995
2012
|
slackUserId: actorUserIdSchema,
|
|
1996
2013
|
fullName: z.string().optional(),
|
|
1997
2014
|
userName: z.string().optional()
|
|
1998
2015
|
}).strict();
|
|
1999
|
-
const
|
|
2016
|
+
const scheduledAutomationRecurrenceSchema = z.object({
|
|
2000
2017
|
dayOfMonth: z.number().optional(),
|
|
2001
2018
|
frequency: z.enum([
|
|
2002
2019
|
"daily",
|
|
@@ -2013,18 +2030,18 @@ const scheduledTaskRecurrenceSchema = z.object({
|
|
|
2013
2030
|
}).strict(),
|
|
2014
2031
|
weekdays: z.array(z.number()).optional()
|
|
2015
2032
|
}).strict();
|
|
2016
|
-
const
|
|
2033
|
+
const scheduledAutomationScheduleSchema = z.object({
|
|
2017
2034
|
description: z.string(),
|
|
2018
2035
|
kind: z.enum(["one_off", "recurring"]),
|
|
2019
|
-
recurrence:
|
|
2036
|
+
recurrence: scheduledAutomationRecurrenceSchema.optional(),
|
|
2020
2037
|
timezone: z.string()
|
|
2021
2038
|
}).strict();
|
|
2022
|
-
const
|
|
2039
|
+
const scheduledAutomationExecutionActorSchema = z.object({
|
|
2023
2040
|
platform: z.literal("system"),
|
|
2024
2041
|
name: z.string()
|
|
2025
2042
|
}).strict();
|
|
2026
|
-
/** Validate the current scheduled-
|
|
2027
|
-
const
|
|
2043
|
+
/** Validate the current scheduled-automation domain shape. */
|
|
2044
|
+
const scheduledAutomationSchema = z.object({
|
|
2028
2045
|
id: z.string(),
|
|
2029
2046
|
conversationAccess: z.object({
|
|
2030
2047
|
audience: z.enum([
|
|
@@ -2035,22 +2052,22 @@ const scheduledTaskSchema = z.object({
|
|
|
2035
2052
|
visibility: z.enum(["private", "public"])
|
|
2036
2053
|
}).strict(),
|
|
2037
2054
|
createdAtMs: z.number(),
|
|
2038
|
-
createdBy:
|
|
2055
|
+
createdBy: scheduledAutomationPrincipalSchema,
|
|
2039
2056
|
/** Authoritative provider identity that created the task. */
|
|
2040
2057
|
creatorIdentityId: z.string(),
|
|
2041
2058
|
/** Selects system credentials or task-bound creator credential delegation. */
|
|
2042
|
-
credentialMode:
|
|
2059
|
+
credentialMode: scheduledAutomationCredentialModeSchema,
|
|
2043
2060
|
destination: slackDestinationSchema,
|
|
2044
|
-
executionActor:
|
|
2061
|
+
executionActor: scheduledAutomationExecutionActorSchema.optional(),
|
|
2045
2062
|
lastRunAtMs: z.number().optional(),
|
|
2046
2063
|
nextRunAtMs: z.number().optional(),
|
|
2047
2064
|
originalRequest: z.string().optional(),
|
|
2048
2065
|
runNowAtMs: z.number().optional(),
|
|
2049
|
-
schedule:
|
|
2050
|
-
status:
|
|
2066
|
+
schedule: scheduledAutomationScheduleSchema,
|
|
2067
|
+
status: scheduledAutomationStatusSchema,
|
|
2051
2068
|
statusReason: z.string().optional(),
|
|
2052
|
-
/**
|
|
2053
|
-
outcomes: z.array(taskOutcomeSchema).max(5)
|
|
2069
|
+
/** Explicit visible effects after successful work. An empty list is silent. */
|
|
2070
|
+
outcomes: z.array(taskOutcomeSchema).max(5),
|
|
2054
2071
|
task: z.object({ text: z.string() }).strict(),
|
|
2055
2072
|
/** SQL-backed short display title generated from the task instruction. */
|
|
2056
2073
|
title: z.string().optional(),
|
|
@@ -2070,14 +2087,14 @@ const scheduledRunSchema = z.object({
|
|
|
2070
2087
|
status: scheduledRunStatusSchema,
|
|
2071
2088
|
taskId: z.string()
|
|
2072
2089
|
}).strict();
|
|
2073
|
-
const
|
|
2090
|
+
const SCHEDULED_AUTOMATION_SYSTEM_ACTOR = Object.freeze({
|
|
2074
2091
|
platform: "system",
|
|
2075
|
-
name: "scheduled-
|
|
2092
|
+
name: "scheduled-automation"
|
|
2076
2093
|
});
|
|
2077
2094
|
//#endregion
|
|
2078
|
-
//#region src/chat/scheduled-
|
|
2095
|
+
//#region src/chat/scheduled-automations/tasks.ts
|
|
2079
2096
|
const SCHEDULER_KEY_PREFIX = "junior:scheduler";
|
|
2080
|
-
const
|
|
2097
|
+
const retainedScheduledAutomationSchema = scheduledAutomationSchema.omit({
|
|
2081
2098
|
creatorIdentityId: true,
|
|
2082
2099
|
title: true
|
|
2083
2100
|
}).extend({
|
|
@@ -2091,11 +2108,18 @@ const retainedScheduledTaskSchema = scheduledTaskSchema.omit({
|
|
|
2091
2108
|
version: z.number().optional()
|
|
2092
2109
|
}).strict();
|
|
2093
2110
|
/** Decode a retained scheduler task row and reject invalid routing context. */
|
|
2094
|
-
function
|
|
2111
|
+
function parseScheduledAutomationRow(row) {
|
|
2095
2112
|
const record = z.record(z.string(), z.unknown()).safeParse(row.record);
|
|
2096
2113
|
if (!record.success) return void 0;
|
|
2097
2114
|
const { creatorIdentityId: legacyCreatorIdentityId, title: legacyTitle, ...retained } = record.data;
|
|
2098
|
-
const
|
|
2115
|
+
const current = retained.outcomes === void 0 && retained.destination !== void 0 ? {
|
|
2116
|
+
...retained,
|
|
2117
|
+
outcomes: [{
|
|
2118
|
+
action: "send_message",
|
|
2119
|
+
destination: retained.destination
|
|
2120
|
+
}]
|
|
2121
|
+
} : retained;
|
|
2122
|
+
const parsed = retainedScheduledAutomationSchema.safeParse(current);
|
|
2099
2123
|
if (!parsed.success) return void 0;
|
|
2100
2124
|
const { status, version: _version, ...task } = parsed.data;
|
|
2101
2125
|
const fallbackIdentity = row.creatorIdentityId === null ? z.string().safeParse(legacyCreatorIdentityId) : void 0;
|
|
@@ -2120,23 +2144,23 @@ function parseScheduledTaskRow(row) {
|
|
|
2120
2144
|
};
|
|
2121
2145
|
}
|
|
2122
2146
|
/** Keep scan paths from returning current or legacy tombstones. */
|
|
2123
|
-
function
|
|
2147
|
+
function isListedScheduledAutomation(task) {
|
|
2124
2148
|
return task !== void 0 && task.status !== "deleted";
|
|
2125
2149
|
}
|
|
2126
2150
|
/** Build the stable run id for one task occurrence. */
|
|
2127
2151
|
function scheduledRunId(taskId, scheduledForMs) {
|
|
2128
2152
|
return `${taskId}:${scheduledForMs}`;
|
|
2129
2153
|
}
|
|
2130
|
-
/** Acquire the deployed advisory lock for one scheduled
|
|
2131
|
-
function
|
|
2154
|
+
/** Acquire the deployed advisory lock for one scheduled automation. */
|
|
2155
|
+
function withScheduledAutomationLock(db, taskId, callback) {
|
|
2132
2156
|
return db.transaction(async (tx) => {
|
|
2133
2157
|
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${`${SCHEDULER_KEY_PREFIX}:task:${taskId}:lock`}))`);
|
|
2134
2158
|
return await callback(tx);
|
|
2135
2159
|
});
|
|
2136
2160
|
}
|
|
2137
2161
|
function requireStoredTask(task) {
|
|
2138
|
-
const parsed =
|
|
2139
|
-
if (!parsed.success) throw new Error("Scheduled
|
|
2162
|
+
const parsed = scheduledAutomationSchema.safeParse(task);
|
|
2163
|
+
if (!parsed.success) throw new Error("Scheduled automation routing context is invalid.");
|
|
2140
2164
|
const { title, ...current } = parsed.data;
|
|
2141
2165
|
const normalizedTitle = title?.trim() || void 0;
|
|
2142
2166
|
return {
|
|
@@ -2144,11 +2168,11 @@ function requireStoredTask(task) {
|
|
|
2144
2168
|
...normalizedTitle ? { title: normalizedTitle } : void 0
|
|
2145
2169
|
};
|
|
2146
2170
|
}
|
|
2147
|
-
function
|
|
2171
|
+
function scheduledAutomationJsonRecord(task) {
|
|
2148
2172
|
const { title: _title, ...record } = task;
|
|
2149
2173
|
return record;
|
|
2150
2174
|
}
|
|
2151
|
-
async function
|
|
2175
|
+
async function upsertScheduledAutomation(db, task) {
|
|
2152
2176
|
const title = task.title?.trim() || null;
|
|
2153
2177
|
await db.insert(juniorSchedulerTasks).values({
|
|
2154
2178
|
createdAtMs: task.createdAtMs,
|
|
@@ -2156,7 +2180,7 @@ async function upsertScheduledTask(db, task) {
|
|
|
2156
2180
|
creatorSlackUserId: task.createdBy.slackUserId,
|
|
2157
2181
|
id: task.id,
|
|
2158
2182
|
nextRunAtMs: task.nextRunAtMs,
|
|
2159
|
-
record:
|
|
2183
|
+
record: scheduledAutomationJsonRecord(task),
|
|
2160
2184
|
runNowAtMs: task.runNowAtMs,
|
|
2161
2185
|
status: task.status,
|
|
2162
2186
|
teamId: task.destination.teamId,
|
|
@@ -2176,37 +2200,37 @@ async function upsertScheduledTask(db, task) {
|
|
|
2176
2200
|
}
|
|
2177
2201
|
});
|
|
2178
2202
|
}
|
|
2179
|
-
/** Read and decode one scheduled
|
|
2180
|
-
async function
|
|
2203
|
+
/** Read and decode one scheduled automation. */
|
|
2204
|
+
async function readScheduledAutomation(db, taskId) {
|
|
2181
2205
|
const rows = await db.select({
|
|
2182
2206
|
creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
|
|
2183
2207
|
record: juniorSchedulerTasks.record,
|
|
2184
2208
|
title: juniorSchedulerTasks.title
|
|
2185
2209
|
}).from(juniorSchedulerTasks).where(eq(juniorSchedulerTasks.id, taskId)).limit(1);
|
|
2186
|
-
return rows[0] ?
|
|
2210
|
+
return rows[0] ? parseScheduledAutomationRow(rows[0]) : void 0;
|
|
2187
2211
|
}
|
|
2188
|
-
async function
|
|
2212
|
+
async function readListedScheduledAutomations(db, teamId) {
|
|
2189
2213
|
return (await db.select({
|
|
2190
2214
|
creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
|
|
2191
2215
|
record: juniorSchedulerTasks.record,
|
|
2192
2216
|
title: juniorSchedulerTasks.title
|
|
2193
|
-
}).from(juniorSchedulerTasks).where(and(notInArray(juniorSchedulerTasks.status, ["deleted", "paused"]), teamId === void 0 ? void 0 : eq(juniorSchedulerTasks.teamId, teamId))).orderBy(asc(juniorSchedulerTasks.createdAtMs), asc(juniorSchedulerTasks.id))).map(
|
|
2217
|
+
}).from(juniorSchedulerTasks).where(and(notInArray(juniorSchedulerTasks.status, ["deleted", "paused"]), teamId === void 0 ? void 0 : eq(juniorSchedulerTasks.teamId, teamId))).orderBy(asc(juniorSchedulerTasks.createdAtMs), asc(juniorSchedulerTasks.id))).map(parseScheduledAutomationRow).filter(isListedScheduledAutomation);
|
|
2194
2218
|
}
|
|
2195
|
-
/** List decoded scheduled
|
|
2196
|
-
async function
|
|
2197
|
-
return await
|
|
2219
|
+
/** List decoded scheduled automations in stable creation order. */
|
|
2220
|
+
async function listScheduledAutomations(db) {
|
|
2221
|
+
return await readListedScheduledAutomations(db);
|
|
2198
2222
|
}
|
|
2199
|
-
/** List decoded scheduled
|
|
2200
|
-
async function
|
|
2201
|
-
return await
|
|
2223
|
+
/** List decoded scheduled automations for one team in stable creation order. */
|
|
2224
|
+
async function listScheduledAutomationsForTeam(db, teamId) {
|
|
2225
|
+
return await readListedScheduledAutomations(db, teamId);
|
|
2202
2226
|
}
|
|
2203
|
-
async function
|
|
2227
|
+
async function writeScheduledAutomation(db, task, current) {
|
|
2204
2228
|
if (current?.status === "blocked" && task.status === "active" && typeof task.nextRunAtMs === "number" && Number.isFinite(task.nextRunAtMs)) await db.delete(juniorSchedulerRuns).where(and(eq(juniorSchedulerRuns.id, scheduledRunId(task.id, task.nextRunAtMs)), eq(juniorSchedulerRuns.status, "blocked")));
|
|
2205
2229
|
if (task.status === "deleted" && current?.status !== "deleted") await skipPendingRunsForDeletedTask(db, task);
|
|
2206
|
-
await
|
|
2230
|
+
await upsertScheduledAutomation(db, task);
|
|
2207
2231
|
}
|
|
2208
2232
|
async function skipPendingRunsForDeletedTask(db, task) {
|
|
2209
|
-
const errorMessage = `Scheduled
|
|
2233
|
+
const errorMessage = `Scheduled automation ${task.id} was deleted before the run started.`;
|
|
2210
2234
|
const rows = await db.select({ record: juniorSchedulerRuns.record }).from(juniorSchedulerRuns).where(and(eq(juniorSchedulerRuns.taskId, task.id), eq(juniorSchedulerRuns.status, "pending")));
|
|
2211
2235
|
for (const row of rows) {
|
|
2212
2236
|
const parsed = scheduledRunSchema.safeParse(row.record);
|
|
@@ -2223,31 +2247,31 @@ async function skipPendingRunsForDeletedTask(db, task) {
|
|
|
2223
2247
|
}).where(and(eq(juniorSchedulerRuns.id, next.id), eq(juniorSchedulerRuns.status, "pending")));
|
|
2224
2248
|
}
|
|
2225
2249
|
}
|
|
2226
|
-
/** Create a scheduled
|
|
2227
|
-
async function
|
|
2250
|
+
/** Create a scheduled automation once under its retry-stable task lock. */
|
|
2251
|
+
async function createScheduledAutomation(db, task) {
|
|
2228
2252
|
const next = requireStoredTask(task);
|
|
2229
|
-
return await
|
|
2230
|
-
const current = await
|
|
2253
|
+
return await withScheduledAutomationLock(db, task.id, async (tx) => {
|
|
2254
|
+
const current = await readScheduledAutomation(tx, task.id);
|
|
2231
2255
|
if (current) return current;
|
|
2232
|
-
await
|
|
2256
|
+
await writeScheduledAutomation(tx, next, void 0);
|
|
2233
2257
|
return next;
|
|
2234
2258
|
});
|
|
2235
2259
|
}
|
|
2236
|
-
/** Save a scheduled
|
|
2237
|
-
async function
|
|
2260
|
+
/** Save a scheduled automation and clear its blocked occurrence on reactivation. */
|
|
2261
|
+
async function saveScheduledAutomation(db, task) {
|
|
2238
2262
|
const next = requireStoredTask(task);
|
|
2239
|
-
await
|
|
2240
|
-
const current = await
|
|
2241
|
-
await
|
|
2263
|
+
await withScheduledAutomationLock(db, task.id, async (tx) => {
|
|
2264
|
+
const current = await readScheduledAutomation(tx, task.id);
|
|
2265
|
+
await writeScheduledAutomation(tx, next, current);
|
|
2242
2266
|
});
|
|
2243
2267
|
return next;
|
|
2244
2268
|
}
|
|
2245
|
-
/** Save a scheduled
|
|
2246
|
-
async function
|
|
2247
|
-
await
|
|
2269
|
+
/** Save a scheduled automation inside a task lock already held by the caller. */
|
|
2270
|
+
async function saveScheduledAutomationInLock(db, task, current) {
|
|
2271
|
+
await writeScheduledAutomation(db, requireStoredTask(task), current);
|
|
2248
2272
|
}
|
|
2249
|
-
/** List scheduled
|
|
2250
|
-
async function
|
|
2273
|
+
/** List scheduled automations whose current Slack destination is public. */
|
|
2274
|
+
async function listPublicScheduledAutomationsForTeams(db, teamIds, input) {
|
|
2251
2275
|
if (teamIds.length === 0) return [];
|
|
2252
2276
|
return (await db.select({
|
|
2253
2277
|
creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
|
|
@@ -2257,7 +2281,7 @@ async function listPublicScheduledTasksForTeams(db, teamIds, input) {
|
|
|
2257
2281
|
"completed",
|
|
2258
2282
|
"deleted",
|
|
2259
2283
|
"paused"
|
|
2260
|
-
]), input.query ? sql`strpos(lower(coalesce(${juniorSchedulerTasks.title}, ${juniorSchedulerTasks.record}->'task'->>'text')), ${input.query}) > 0` : void 0, eq(juniorDestinations.visibility, "public"))).orderBy(desc(juniorSchedulerTasks.createdAtMs), desc(juniorSchedulerTasks.id)).limit(input.limit)).map(
|
|
2284
|
+
]), input.query ? sql`strpos(lower(coalesce(${juniorSchedulerTasks.title}, ${juniorSchedulerTasks.record}->'task'->>'text')), ${input.query}) > 0` : void 0, eq(juniorDestinations.visibility, "public"))).orderBy(desc(juniorSchedulerTasks.createdAtMs), desc(juniorSchedulerTasks.id)).limit(input.limit)).map(parseScheduledAutomationRow).filter(isListedScheduledAutomation);
|
|
2261
2285
|
}
|
|
2262
2286
|
//#endregion
|
|
2263
2287
|
//#region src/chat/slack/context.ts
|
|
@@ -2645,4 +2669,4 @@ async function readStats(start, end) {
|
|
|
2645
2669
|
return await getDb().select().from(juniorStats).where(and(gte(juniorStats.date, start), lte(juniorStats.date, end))).orderBy(asc(juniorStats.date), asc(juniorStats.namespace), asc(juniorStats.metric), asc(juniorStats.name));
|
|
2646
2670
|
}
|
|
2647
2671
|
//#endregion
|
|
2648
|
-
export { workspaceSnapshotFinishedEvent as $,
|
|
2672
|
+
export { workspaceSnapshotFinishedEvent as $, eventAutomationTriggerAvailable as A, recordConversationRetry as At, createEventAutomation as B, isFinalAttempt as Bt, SCHEDULED_AUTOMATION_SYSTEM_ACTOR as C, drainConversationMailbox as Ct, eventAutomationMatchesDestination as D, hasRunnableConversationWork as Dt, eventAutomationListToolResultSchema as E, getConversationWorkState as Et, effectiveTaskOutcomes as F, startConversationWork as Ft, listDeletedEventAutomationsCreatedBy as G, buildDeterministicAssistantMessageId as Gt, eventAutomationBelongsToUser as H, injectContinuationToolName as Ht, moveTaskOutcomes as I, stopConversationWork as It, listPublicEventAutomationsForTeams as J, listEventAutomationsCreatedBy as K, buildDeterministicTurnId as Kt, resolveTaskOutcomes as L, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS as Lt, requireEventAutomationSlackContext as M, removeActiveConversation as Mt, requireSupportedEventAutomationTrigger as N, requestAnotherSlice as Nt, eventAutomationToolResult as O, listActiveConversationIds as Ot, writableEventAutomation as P, requestConversationWork as Pt, WORKSPACE_SNAPSHOT_READY_EVENT as Q, taskOutcomeInputSchema as R, CONVERSATION_WORK_STALE_ENQUEUE_MS as Rt, withScheduledAutomationLock as S, deadLetterAttempt as St, compactEventAutomation as T, getConversation as Tt, findMatchingEventAutomations as U, juniorToolOutputSchema as Ut, deleteEventAutomation as V, isInvalidConversationRecordError as Vt, getEventAutomation as W, makeStructuredToolOutput as Wt, getEventCatalog as X, saveEventAutomation as Y, WORKSPACE_SNAPSHOT_FAILED_EVENT as Z, parseScheduledAutomationRow as _, clearConsumedConversationWake as _t, requestAttachmentDeletion as a, normalizeCatalogEventIdentifier as at, saveScheduledAutomationInLock as b, completeConversationWork as bt, parseSlackThreadId as c, registeredEventTypeSchema as ct, resolveSlackChannelIdFromThreadId as d, ackMessages as dt, workspaceSnapshotWatch as et, createScheduledAutomation as f, appendAndEnqueueExclusiveInboundMessage as ft, listScheduledAutomationsForTeam as g, checkInConversationWork as gt, listScheduledAutomations as h, cancelHumanFacingPendingMessages as ht, readLiveAttachment as i, hasPluginEventCatalogEntries as it, registeredEventAutomationTriggerSchema as j, releaseConversationWork as jt, eventAutomationToolResultSchema as k, recordAttemptFailure as kt, readSlackRawMessageContext as l, registeredResourceTypeSchema as lt, listPublicScheduledAutomationsForTeams as m, beginConversationResume as mt, readStats as n, eventNamespaceSchema as nt, storeAttachment as o, pluginSupportsEvent as ot, isListedScheduledAutomation as p, appendAndEnqueueInboundMessage as pt, listEventAutomationsForTeam as q, collectAttachmentGarbage as r, hasEventCatalogEntries as rt, storeAttachments as s, registeredEventMatchSchema as st, incrementStat as t, eventGuidance as tt, resolveSlackChannelIdFromMessage as u, requireSupportedEventMatch as ut, readScheduledAutomation as v, clearExpiredConversationLease as vt, scheduledRunSchema as w, ensureConversationWake as wt, scheduledRunId as x, countPendingConversationMessages as xt, saveScheduledAutomation as y, completeConversationStop as yt, collectEventAutomationMatchKeys as z, hasRunnableConversationWork$1 as zt };
|