@sentry/junior 0.205.0 → 0.207.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-D4nu8MQV.js → agent-CmQlX68D.js} +603 -394
- package/dist/{agent-hooks-DXHpTJ4m.js → agent-hooks-BBX9p6aG.js} +1171 -100
- package/dist/{agent-runner-C04colea.js → agent-runner-CQD4HKK2.js} +9 -9
- package/dist/api/schema.d.ts +116 -39
- package/dist/api/schema.js +3 -3
- package/dist/api.d.ts +1 -1
- package/dist/api.js +182 -649
- package/dist/{app-Cb776lL8.d.ts → app-DfNrn_gS.d.ts} +9 -2
- package/dist/app.d.ts +1 -1
- package/dist/app.js +205 -233
- package/dist/{auth-pause-state-C2P3guS1.js → auth-pause-state-uv8on6cT.js} +7 -7
- package/dist/briefs-BXEmjD9D.js +398 -0
- package/dist/{catalog-runtime-CXw3asJD.js → catalog-runtime-1BZEOR8J.js} +1 -1
- package/dist/{checkpoint-CrViJ8Ps.js → checkpoint-B-JO5F3c.js} +9 -8
- package/dist/cli/chat.js +14 -12
- package/dist/cli/check.js +3 -3
- package/dist/cli/init.js +1 -0
- package/dist/cli/main.js +5 -1
- package/dist/cli/plugins.js +3 -3
- package/dist/cli/run.d.ts +2 -1
- package/dist/cli/run.js +2 -1
- 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/{config-BOtJQ7Pc.js → client-Bp297GA0.js} +6 -369
- package/dist/config-G2gv97R1.js +386 -0
- package/dist/{context-B45_WqVX.js → context-Bplcgypl.js} +13 -13
- package/dist/{schema-Rn8-XEaN.js → conversation-AA4R1VUn.js} +580 -558
- package/dist/{conversation-privacy-BZRA6Pes.js → conversation-privacy-BUfel19D.js} +1 -1
- package/dist/{credential-sync-Bep34IB_.js → credential-sync-D04NWqMR.js} +21 -20
- package/dist/{db-BeyXKJOI.d.ts → db-D74z7BZH.d.ts} +1 -1
- package/dist/{execution-stats-B6lgWxCK.js → execution-stats-Ds4bSvam.js} +77 -77
- package/dist/{executor-BlUhFL7_.js → executor-BZu1Ta06.js} +41 -13
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-DLMqIK74.js → job-queue-rnAPfUhP.js} +1 -1
- package/dist/{logging-BMJzdDlP.js → logging-Dx0S5k2z.js} +1 -1
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-WwG5_Zgm.js → oauth-callback-server-xGpwURIj.js} +2 -2
- package/dist/{pending-auth-CUbXq06G.js → pending-auth-DIZ4iZjS.js} +2 -2
- package/dist/{profile-T0tXE4zu.js → profile-d-fKypxo.js} +4 -3
- package/dist/prompt-x-0ZtTyb.js +552 -0
- package/dist/{registry-BA0__8ai.js → registry-CWKdLCTG.js} +2 -2
- package/dist/{resolve-bvD1ORdv.js → resolve-BhRB1s2A.js} +2 -2
- package/dist/{runner-BXYxfIUL.js → runner-DNml3owD.js} +10 -10
- package/dist/{sandbox-egress-signals-ngEPjSEW.js → sandbox-egress-signals-DUIl3-gW.js} +1 -1
- package/dist/schema-CwTeb-BF.js +559 -0
- package/dist/{skills-DJOlYdBs.js → skills-D9WesdJJ.js} +2 -2
- package/dist/{spawn-h0R1bTEo.js → spawn-Eh_3-s0M.js} +2 -2
- package/dist/{stats-3YZn-Swi.js → stats-CFlUGqVA.js} +292 -268
- package/dist/{store-C5qxF2ac.d.ts → store-Det8MQ9g.d.ts} +1 -1
- package/dist/{task-queue-CoXwd2eq.js → task-queue-D8I1_5wP.js} +3 -3
- package/dist/{task-runner-B2_mJZj0.js → task-runner-DO79gdho.js} +18 -18
- package/dist/{thread-state-CqZ1m-es.js → thread-state-DD6BA4xz.js} +5 -5
- package/dist/{turn-execution-fI6PZ0iI.js → turn-execution-BSIHxmQJ.js} +3 -3
- package/dist/{turn-lifecycle-CcnwrOcp.js → turn-lifecycle-DSBK4Ars.js} +1 -1
- package/dist/{usage-B5SfMb3H.js → usage-WRXkNOHp.js} +4 -125
- package/dist/{validation-DfWcvhuS.js → validation-BbHZ4r4S.js} +1 -1
- package/dist/{version-oD-oPrbO.js → version-C71UcRWw.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/web-authorization-C73gJV7l.js +38 -0
- package/dist/{web-input-BOuq3oKK.js → web-input-Ce1FcNIM.js} +8 -8
- package/dist/{work-B91LQKHr.js → work-BEzDU_2x.js} +9 -9
- package/dist/{worker-BcRRTe6k.js → worker-MK2ubOv-.js} +5 -4
- package/migrations/0040_conversation_briefs.sql +17 -0
- package/migrations/0041_automation_outcomes_backfill.sql +27 -0
- package/migrations/meta/0040_snapshot.json +3516 -0
- package/migrations/meta/0041_snapshot.json +3516 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +4 -4
- package/dist/history-CqTuSAdI.js +0 -441
- package/dist/usage-schema-CKaDkH_j.js +0 -20
- package/dist/web-authorization-Crbzlmzo.js +0 -76
package/dist/api.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { f as logWarn, u as logException } from "./logging-
|
|
2
|
-
import { _ as parseSlackMessageTs, h as parseSlackTeamId, p as parseSlackChannelId } from "./context-
|
|
3
|
-
import {
|
|
1
|
+
import { f as logWarn, u as logException } from "./logging-Dx0S5k2z.js";
|
|
2
|
+
import { _ as parseSlackMessageTs, h as parseSlackTeamId, p as parseSlackChannelId } from "./context-Bplcgypl.js";
|
|
3
|
+
import { $ as resolveViewerUser, Ct as readConversationEventPage, D as SANDBOX_RUNTIME, Dt as listConversationAnnotationsById, Et as listConversationAnnotations, Ft as getConversationStore, Gt as resolveRootConversationId, Ht as locationFromRow, It as getDb, J as withSlackRetries, Lt as getSqlExecutor, S as WorkspaceValidationError, St as readLatestConversationBrief, U as getSlackClient, Wt as parseSessionSource, Z as findUserByEmail, _ as deleteWorkspace, b as listWorkspaces, bt as createPluginLogger, et as updateViewerDisplayName, g as createWorkspace, j as workspaceRepoCheckoutPath, u as getPlugins, v as getWorkspace, wt as buildSentryConversationUrl, x as updateWorkspace } from "./agent-hooks-BBX9p6aG.js";
|
|
4
4
|
import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
|
|
5
|
-
import { C as
|
|
6
|
-
import { o as
|
|
7
|
-
import { a as hasAgentTurnUsage, i as agentTurnUsageSchema,
|
|
8
|
-
import { n as hash, t as create } from "./profile-
|
|
9
|
-
import { A as
|
|
10
|
-
import { t as getCachedSnapshot } from "./resolve-
|
|
5
|
+
import { C as conversationReadColumns, E as juniorUsers, T as juniorIdentities, _ as juniorConversationEvents, b as juniorCodeChanges, g as juniorConversationBriefs, m as juniorConversationParticipants, o as juniorSchedulerTasks, v as juniorApiTokens, w as juniorConversations, x as juniorCodeRepositories } from "./executor-BZu1Ta06.js";
|
|
6
|
+
import { D as createConversationBodySchema, O as createConversationMessageBodySchema, T as conversationStatsReportSchema, _ as conversationParamsSchema, a as cancelConversationPendingMessagesBodySchema, c as conversationAttachmentParamsSchema, d as conversationDetailReportSchema, g as conversationFeedSchema, h as conversationFeedQuerySchema, i as archiveConversationResponseSchema, m as conversationEventsQuerySchema, o as cancelConversationPendingMessagesResponseSchema, p as conversationEventPageSchema, r as archiveConversationBodySchema, t as acceptedConversationMessageSchema, u as conversationDetailQuerySchema, x as conversationPendingMessagesReportSchema } from "./conversation-AA4R1VUn.js";
|
|
7
|
+
import { a as hasAgentTurnUsage, i as agentTurnUsageSchema, s as fallbackShortTitle } from "./usage-WRXkNOHp.js";
|
|
8
|
+
import { n as hash, t as create } from "./profile-d-fKypxo.js";
|
|
9
|
+
import { A as eventAutomationTriggerAvailable, F as effectiveTaskOutcomes, G as listDeletedEventAutomationsCreatedBy, H as eventAutomationBelongsToUser, J as listPublicEventAutomationsForTeams, K as listEventAutomationsCreatedBy, Tt as getConversation, V as deleteEventAutomation, W as getEventAutomation, X as getEventCatalog, _ as parseScheduledAutomationRow, c as parseSlackThreadId, ht as cancelHumanFacingPendingMessages, i as readLiveAttachment, m as listPublicScheduledAutomationsForTeams, n as readStats, p as isListedScheduledAutomation, v as readScheduledAutomation, y as saveScheduledAutomation } from "./stats-CFlUGqVA.js";
|
|
10
|
+
import { t as getCachedSnapshot } from "./resolve-BhRB1s2A.js";
|
|
11
11
|
import { t as getVercelConversationWorkQueue } from "./vercel-queue-DIkVcfK4.js";
|
|
12
12
|
import { c as runtimeInfoReportSchema, n as pluginOperationalReportFeedSchema, s as pluginsSchema, t as healthReportSchema, u as skillReportsSchema } from "./reporting-schema-DosTN__Q.js";
|
|
13
|
-
import { C as createVercelAttachmentStorage, S as legacyWebMailboxMetadataSchema, T as readHealthReport, _ as formatSlackConversationRedactedLabel, a as webActorFromEmail, b as resolveSlackConversationContextFromThreadId, c as conversationArchivedForEmail, d as conversationHasParticipantUser, f as conversationNotArchivedForEmail, l as conversationArchivedForUser, m as viewerConversationMembership, o as readConversationAccessFromSql, p as conversationNotArchivedForUser, r as createAndEnqueueConversation, s as conversationArchiveTimes, t as appendAndEnqueueWebMessage, u as conversationHasParticipantEmail, x as lookupSlackUser } from "./web-input-
|
|
14
|
-
import { a as
|
|
15
|
-
import {
|
|
16
|
-
import { $ as sumUtcHoursIntoSixHours, A as actorProfileReportSchema,
|
|
13
|
+
import { C as createVercelAttachmentStorage, S as legacyWebMailboxMetadataSchema, T as readHealthReport, _ as formatSlackConversationRedactedLabel, a as webActorFromEmail, b as resolveSlackConversationContextFromThreadId, c as conversationArchivedForEmail, d as conversationHasParticipantUser, f as conversationNotArchivedForEmail, l as conversationArchivedForUser, m as viewerConversationMembership, o as readConversationAccessFromSql, p as conversationNotArchivedForUser, r as createAndEnqueueConversation, s as conversationArchiveTimes, t as appendAndEnqueueWebMessage, u as conversationHasParticipantEmail, x as lookupSlackUser } from "./web-input-Ce1FcNIM.js";
|
|
14
|
+
import { a as readAutomationExecutionStatusDays, c as readAutomationExecutions, d as conversationActiveDaysColumn, f as conversationAggregateColumns, i as readAutomationExecutionHours, l as readAutomationRuns, n as readAutomationExecutionByConversationId, o as readAutomationExecutionStatusHours, p as conversationRangeColumns, r as readAutomationExecutionDays, s as readAutomationExecutionSummaries, t as emptyAutomationRunWindows } from "./execution-stats-Ds4bSvam.js";
|
|
15
|
+
import { r as getWebAuthorization } from "./web-authorization-C73gJV7l.js";
|
|
16
|
+
import { $ as sumUtcHoursIntoSixHours, A as actorProfileReportSchema, C as createPersonalTokenBodySchema, D as personalTokenParamsSchema, F as locationDirectoryReportSchema, G as codePersonReportSchema, I as locationParamsSchema, J as HOUR_MS$1, L as activityDays$1, M as personalSpendReportSchema, O as revokePersonalTokenResponseSchema, P as locationDetailReportSchema, Q as startOfUtcHour, R as activityHours$1, T as personalTokenListSchema, W as codeOverviewReportSchema, X as fillUtcHours, Y as fillUtcDays, Z as fillUtcSixHours, _ as automationRunListSchema, a as workspaceBodySchema, at as apiErrorSchema, et as trailingUtcDayWindow, g as automationParamsSchema, h as automationListSchema, i as deleteWorkspaceResponseSchema, it as utcHourKey, j as personParamsSchema, k as actorDirectoryReportSchema, l as workspaceSchema, m as automationListQuerySchema, n as statsReportSchema, nt as trailingUtcSixHourWindow, o as workspaceListSchema, q as DAY_MS$1, rt as utcDayKey, s as workspaceParamsSchema, tt as trailingUtcHourWindow, u as automationExecutionListSchema, w as createdPersonalTokenSchema, z as activitySixHours$1 } from "./schema-CwTeb-BF.js";
|
|
17
17
|
import { Hono } from "hono";
|
|
18
18
|
import path from "node:path";
|
|
19
19
|
import { z } from "zod";
|
|
20
20
|
import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
21
|
-
import { and, asc, desc, eq,
|
|
21
|
+
import { and, asc, desc, eq, gt, gte, inArray, isNotNull, isNull, lt, lte, notExists, notInArray, or, sql } from "drizzle-orm";
|
|
22
22
|
import { conversationSidebarAnnotationSchema, pluginUserPageContentSchema, pluginUserPageCursorSchema, pluginUserPageFilterSchema, pluginUserPageLinksSchema, pluginUserPageQuerySchema } from "@sentry/junior-plugin-api";
|
|
23
23
|
import { alias } from "drizzle-orm/pg-core";
|
|
24
24
|
import { readFileSync } from "node:fs";
|
|
@@ -460,491 +460,6 @@ function decodeConversationCursor(args) {
|
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
462
|
//#endregion
|
|
463
|
-
//#region src/api/conversations/events.ts
|
|
464
|
-
/** Canonical event types that can contribute to the reporting projection. */
|
|
465
|
-
const conversationReportSourceEventTypes = [
|
|
466
|
-
"message",
|
|
467
|
-
"message_handled",
|
|
468
|
-
"assistant_message",
|
|
469
|
-
"tool_result",
|
|
470
|
-
"tool_execution_started",
|
|
471
|
-
"guardian_action_reviewed",
|
|
472
|
-
"turn_started",
|
|
473
|
-
"turn_context",
|
|
474
|
-
"structured_event",
|
|
475
|
-
"attachments_delivered",
|
|
476
|
-
"turn_routed",
|
|
477
|
-
"turn_completed",
|
|
478
|
-
"turn_failed",
|
|
479
|
-
"compaction",
|
|
480
|
-
"handoff",
|
|
481
|
-
"subagent_started",
|
|
482
|
-
"subagent_ended"
|
|
483
|
-
];
|
|
484
|
-
const reportingAssistantMessageSchema = z.object({
|
|
485
|
-
type: z.literal("assistant_message"),
|
|
486
|
-
content: z.array(z.unknown())
|
|
487
|
-
}).passthrough();
|
|
488
|
-
const reportingToolCallPartSchema = z.object({
|
|
489
|
-
type: z.literal("toolCall"),
|
|
490
|
-
id: z.string().min(1),
|
|
491
|
-
name: z.string().min(1),
|
|
492
|
-
arguments: z.unknown()
|
|
493
|
-
}).passthrough();
|
|
494
|
-
const reportingToolResultMessageSchema = z.object({
|
|
495
|
-
type: z.literal("tool_result"),
|
|
496
|
-
toolCallId: z.string().min(1),
|
|
497
|
-
toolName: z.string().min(1).optional(),
|
|
498
|
-
name: z.string().min(1).optional(),
|
|
499
|
-
content: z.array(z.unknown()),
|
|
500
|
-
details: z.unknown().optional(),
|
|
501
|
-
isError: z.boolean().optional()
|
|
502
|
-
}).passthrough();
|
|
503
|
-
const reportingToolResultDetailsSchema = z.object({
|
|
504
|
-
ok: z.boolean().optional(),
|
|
505
|
-
status: z.string().optional()
|
|
506
|
-
}).passthrough();
|
|
507
|
-
const reportingTextPartSchema = z.object({
|
|
508
|
-
type: z.literal("text"),
|
|
509
|
-
text: z.string()
|
|
510
|
-
}).passthrough();
|
|
511
|
-
const reportingReasoningPartSchema = z.object({
|
|
512
|
-
type: z.literal("thinking"),
|
|
513
|
-
thinking: z.string(),
|
|
514
|
-
redacted: z.literal(true).optional()
|
|
515
|
-
}).passthrough();
|
|
516
|
-
const reportingMediaPartSchema = z.object({
|
|
517
|
-
type: z.enum(["image", "audio"]),
|
|
518
|
-
mimeType: z.string().min(1).optional()
|
|
519
|
-
}).passthrough();
|
|
520
|
-
const reportingMessageAuthorSchema = z.object({
|
|
521
|
-
fullName: z.string().min(1).optional(),
|
|
522
|
-
userId: z.string().min(1).optional(),
|
|
523
|
-
userName: z.string().min(1).optional()
|
|
524
|
-
}).passthrough();
|
|
525
|
-
/** Retain model-visible text and media descriptors, dropping all opaque fields. */
|
|
526
|
-
function sanitizeModelContentPart(value) {
|
|
527
|
-
const text = reportingTextPartSchema.safeParse(value);
|
|
528
|
-
if (text.success) return {
|
|
529
|
-
type: "text",
|
|
530
|
-
text: text.data.text
|
|
531
|
-
};
|
|
532
|
-
const media = reportingMediaPartSchema.safeParse(value);
|
|
533
|
-
if (!media.success) return void 0;
|
|
534
|
-
return {
|
|
535
|
-
type: media.data.type,
|
|
536
|
-
...media.data.mimeType ? { mimeType: media.data.mimeType } : void 0
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
/** Convert allowlisted Pi content parts into the reporting result value. */
|
|
540
|
-
function modelVisibleToolOutput(content) {
|
|
541
|
-
const sanitized = content.map(sanitizeModelContentPart).filter((part) => part !== void 0);
|
|
542
|
-
if (sanitized.length === 0) return void 0;
|
|
543
|
-
if (sanitized.length !== 1) return sanitized;
|
|
544
|
-
const only = sanitized[0];
|
|
545
|
-
return only.type === "text" ? only.text : only;
|
|
546
|
-
}
|
|
547
|
-
function reportMessageActorIdentity(data) {
|
|
548
|
-
if (data.role !== "user") return void 0;
|
|
549
|
-
const author = reportingMessageAuthorSchema.safeParse(data.meta?.author);
|
|
550
|
-
if (!author.success) return void 0;
|
|
551
|
-
const actorIdentity = {
|
|
552
|
-
...author.data.fullName ? { fullName: author.data.fullName } : void 0,
|
|
553
|
-
...author.data.userId ? { slackUserId: author.data.userId } : void 0,
|
|
554
|
-
...author.data.userName ? { slackUserName: author.data.userName } : void 0
|
|
555
|
-
};
|
|
556
|
-
return Object.keys(actorIdentity).length > 0 ? actorIdentity : void 0;
|
|
557
|
-
}
|
|
558
|
-
/** Project native assistant tool calls through the existing reporting shape. */
|
|
559
|
-
function reportToolCalls(args) {
|
|
560
|
-
const message = reportingAssistantMessageSchema.safeParse(args.message);
|
|
561
|
-
if (!message.success) return void 0;
|
|
562
|
-
const calls = message.data.content.flatMap((part) => {
|
|
563
|
-
const call = reportingToolCallPartSchema.safeParse(part);
|
|
564
|
-
if (!call.success) return [];
|
|
565
|
-
return [{
|
|
566
|
-
toolCallId: call.data.id,
|
|
567
|
-
name: call.data.name,
|
|
568
|
-
status: "running",
|
|
569
|
-
startedAt: new Date(args.createdAtMs).toISOString(),
|
|
570
|
-
startedSeq: args.seq,
|
|
571
|
-
...args.canExposePayload && call.data.arguments !== void 0 ? { input: call.data.arguments } : void 0
|
|
572
|
-
}];
|
|
573
|
-
});
|
|
574
|
-
return calls.length > 0 ? {
|
|
575
|
-
type: "tool_calls",
|
|
576
|
-
calls
|
|
577
|
-
} : void 0;
|
|
578
|
-
}
|
|
579
|
-
/** Project assistant reasoning with tool ids retained only for display order. */
|
|
580
|
-
function reportAssistantMessage(args) {
|
|
581
|
-
const message = reportingAssistantMessageSchema.safeParse(args.message);
|
|
582
|
-
if (!message.success) return void 0;
|
|
583
|
-
if (!message.data.content.some((part) => {
|
|
584
|
-
const reasoning = reportingReasoningPartSchema.safeParse(part);
|
|
585
|
-
return reasoning.success && reasoning.data.redacted !== true && reasoning.data.thinking.trim().length > 0;
|
|
586
|
-
})) return reportToolCalls(args);
|
|
587
|
-
const reasoningParts = [];
|
|
588
|
-
const assistantParts = [];
|
|
589
|
-
for (const part of message.data.content) {
|
|
590
|
-
const reasoning = reportingReasoningPartSchema.safeParse(part);
|
|
591
|
-
if (reasoning.success) {
|
|
592
|
-
if (reasoning.data.redacted !== true && reasoning.data.thinking.trim().length > 0) {
|
|
593
|
-
const projected = {
|
|
594
|
-
type: "reasoning",
|
|
595
|
-
...args.canExposePayload ? { text: reasoning.data.thinking } : { redacted: true }
|
|
596
|
-
};
|
|
597
|
-
reasoningParts.push(projected);
|
|
598
|
-
assistantParts.push(projected);
|
|
599
|
-
}
|
|
600
|
-
continue;
|
|
601
|
-
}
|
|
602
|
-
const call = reportingToolCallPartSchema.safeParse(part);
|
|
603
|
-
if (call.success) assistantParts.push({
|
|
604
|
-
type: "tool_call",
|
|
605
|
-
toolCallId: call.data.id
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
const toolCalls = reportToolCalls(args);
|
|
609
|
-
if (toolCalls?.type === "tool_calls") return {
|
|
610
|
-
...toolCalls,
|
|
611
|
-
assistant: { parts: assistantParts }
|
|
612
|
-
};
|
|
613
|
-
return reasoningParts.length > 0 ? {
|
|
614
|
-
type: "assistant_message",
|
|
615
|
-
parts: reasoningParts
|
|
616
|
-
} : void 0;
|
|
617
|
-
}
|
|
618
|
-
/** Project a native tool result without exposing host-only result details. */
|
|
619
|
-
function reportToolResult(args) {
|
|
620
|
-
const message = reportingToolResultMessageSchema.safeParse(args.message);
|
|
621
|
-
if (!message.success) return void 0;
|
|
622
|
-
const details = reportingToolResultDetailsSchema.safeParse(message.data.details);
|
|
623
|
-
const outcome = message.data.isError === true || details.success && (details.data.ok === false || details.data.status === "error") ? "error" : "completed";
|
|
624
|
-
const output = modelVisibleToolOutput(message.data.content);
|
|
625
|
-
const name = message.data.toolName ?? message.data.name ?? args.start?.name;
|
|
626
|
-
if (!name) return void 0;
|
|
627
|
-
return {
|
|
628
|
-
type: "tool_calls",
|
|
629
|
-
calls: [{
|
|
630
|
-
toolCallId: message.data.toolCallId,
|
|
631
|
-
name,
|
|
632
|
-
status: outcome,
|
|
633
|
-
...args.start ? {
|
|
634
|
-
startedAt: new Date(args.start.createdAtMs).toISOString(),
|
|
635
|
-
startedSeq: args.start.seq
|
|
636
|
-
} : void 0,
|
|
637
|
-
...args.canExposePayload && output !== void 0 ? { output } : void 0
|
|
638
|
-
}]
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
/** Return tool result ids that may need start metadata from outside a page. */
|
|
642
|
-
function conversationReportToolResultIds(events) {
|
|
643
|
-
return [...new Set(events.flatMap((event) => {
|
|
644
|
-
const result = reportingToolResultMessageSchema.safeParse(event.data);
|
|
645
|
-
return result.success ? [result.data.toolCallId] : [];
|
|
646
|
-
}))];
|
|
647
|
-
}
|
|
648
|
-
function reportEventData(args) {
|
|
649
|
-
const { data } = args;
|
|
650
|
-
if (data.type === "structured_event") {
|
|
651
|
-
if (!args.canExposePayload) return void 0;
|
|
652
|
-
const presentation = data.namespace === "junior" ? renderJuniorNativeConversationEvent(data) : renderPluginConversationEvent(data);
|
|
653
|
-
if (!presentation) return void 0;
|
|
654
|
-
return {
|
|
655
|
-
type: "structured_event",
|
|
656
|
-
namespace: data.namespace,
|
|
657
|
-
name: data.name,
|
|
658
|
-
version: data.version,
|
|
659
|
-
...data.turnId ? { turnId: data.turnId } : void 0,
|
|
660
|
-
presentation
|
|
661
|
-
};
|
|
662
|
-
}
|
|
663
|
-
switch (data.type) {
|
|
664
|
-
case "message": {
|
|
665
|
-
const actorIdentity = args.canExposePayload ? reportMessageActorIdentity(data) : void 0;
|
|
666
|
-
return {
|
|
667
|
-
type: "message",
|
|
668
|
-
messageId: data.messageId,
|
|
669
|
-
role: data.role,
|
|
670
|
-
...data.meta?.source === "web" || data.meta?.source === "slack" ? { source: data.meta.source } : void 0,
|
|
671
|
-
...actorIdentity ? { actorIdentity } : void 0,
|
|
672
|
-
...typeof data.meta?.eventType === "string" ? { eventType: data.meta.eventType } : void 0,
|
|
673
|
-
...typeof data.meta?.explicitMention === "boolean" ? { explicitMention: data.meta.explicitMention } : void 0,
|
|
674
|
-
...args.canExposePayload && typeof data.meta?.trustedSummary === "string" ? { trustedSummary: data.meta.trustedSummary } : void 0,
|
|
675
|
-
...args.canExposePayload ? { text: data.text } : { redacted: true }
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
|
-
case "message_handled": return {
|
|
679
|
-
type: "message_handled",
|
|
680
|
-
messageId: data.messageId
|
|
681
|
-
};
|
|
682
|
-
case "turn_started": return {
|
|
683
|
-
type: "turn_lifecycle",
|
|
684
|
-
turnId: data.turnId,
|
|
685
|
-
state: "started",
|
|
686
|
-
...data.inputMessageIds.length > 0 ? { inputMessageIds: data.inputMessageIds } : void 0
|
|
687
|
-
};
|
|
688
|
-
case "turn_context":
|
|
689
|
-
if (!args.canExposePayload) return;
|
|
690
|
-
return {
|
|
691
|
-
type: "turn_context",
|
|
692
|
-
turnId: data.turnId,
|
|
693
|
-
pluginName: data.pluginName,
|
|
694
|
-
kind: data.kind,
|
|
695
|
-
version: data.version,
|
|
696
|
-
content: data.content
|
|
697
|
-
};
|
|
698
|
-
case "attachments_delivered":
|
|
699
|
-
if (!args.canExposePayload) return;
|
|
700
|
-
return {
|
|
701
|
-
type: "attachments_delivered",
|
|
702
|
-
attachments: data.attachments
|
|
703
|
-
};
|
|
704
|
-
case "turn_routed": return {
|
|
705
|
-
type: "turn_routed",
|
|
706
|
-
turnId: data.turnId,
|
|
707
|
-
modelProfile: data.modelProfile,
|
|
708
|
-
modelId: data.modelId,
|
|
709
|
-
...data.costUsd !== void 0 ? { costUsd: data.costUsd } : void 0,
|
|
710
|
-
reasoningLevel: data.reasoningLevel,
|
|
711
|
-
...data.confidence !== void 0 ? { confidence: data.confidence } : void 0,
|
|
712
|
-
source: data.source
|
|
713
|
-
};
|
|
714
|
-
case "guardian_action_reviewed": return {
|
|
715
|
-
type: "guardian_action_reviewed",
|
|
716
|
-
turnId: data.turnId,
|
|
717
|
-
toolCallId: data.toolCallId,
|
|
718
|
-
toolName: data.toolName,
|
|
719
|
-
decision: data.decision,
|
|
720
|
-
riskLevel: data.riskLevel,
|
|
721
|
-
userAuthorization: data.userAuthorization
|
|
722
|
-
};
|
|
723
|
-
case "turn_completed": return {
|
|
724
|
-
type: "turn_lifecycle",
|
|
725
|
-
turnId: data.turnId,
|
|
726
|
-
state: data.outcome === "success" ? "succeeded" : "no_reply"
|
|
727
|
-
};
|
|
728
|
-
case "turn_failed": {
|
|
729
|
-
const eventId = data.eventId;
|
|
730
|
-
const sentryEventUrl = eventId ? buildSentryEventUrl(eventId) : void 0;
|
|
731
|
-
return {
|
|
732
|
-
type: "turn_lifecycle",
|
|
733
|
-
turnId: data.turnId,
|
|
734
|
-
state: "failed",
|
|
735
|
-
failureCode: data.failureCode,
|
|
736
|
-
...data.failureReason ? { failureReason: data.failureReason } : void 0,
|
|
737
|
-
...eventId ? { eventId } : void 0,
|
|
738
|
-
...sentryEventUrl ? { sentryEventUrl } : void 0
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
case "compaction": return {
|
|
742
|
-
type: "compaction",
|
|
743
|
-
modelProfile: data.modelProfile,
|
|
744
|
-
modelId: data.modelId,
|
|
745
|
-
...args.canExposePayload && data.summary ? { summary: data.summary } : void 0,
|
|
746
|
-
...data.details ? { details: data.details } : void 0
|
|
747
|
-
};
|
|
748
|
-
default: return;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* Project canonical events into the ordered reporting boundary.
|
|
753
|
-
*
|
|
754
|
-
* `canExposePayload` must come from the authorized conversation-detail policy;
|
|
755
|
-
* this projection never derives visibility or authorization from event data.
|
|
756
|
-
*/
|
|
757
|
-
function projectConversationReportEventPage(args) {
|
|
758
|
-
const subagentStarts = /* @__PURE__ */ new Map();
|
|
759
|
-
for (const event of args.subagentStartEvents ?? []) if (event.data.type === "subagent_started") subagentStarts.set(event.data.subagentInvocationId, {
|
|
760
|
-
createdAtMs: event.createdAtMs,
|
|
761
|
-
data: event.data,
|
|
762
|
-
seq: event.seq
|
|
763
|
-
});
|
|
764
|
-
const toolStarts = /* @__PURE__ */ new Map();
|
|
765
|
-
for (const event of args.toolStartEvents ?? []) if (event.data.type === "tool_execution_started") {
|
|
766
|
-
const current = toolStarts.get(event.data.toolCallId);
|
|
767
|
-
if (!current || event.seq < current.seq) toolStarts.set(event.data.toolCallId, {
|
|
768
|
-
createdAtMs: event.createdAtMs,
|
|
769
|
-
name: event.data.toolName,
|
|
770
|
-
seq: event.seq
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
const projected = [];
|
|
774
|
-
for (const event of args.events) {
|
|
775
|
-
let data;
|
|
776
|
-
if (event.data.type === "assistant_message" || event.data.type === "tool_result") {
|
|
777
|
-
const reportArgs = {
|
|
778
|
-
canExposePayload: args.canExposePayload,
|
|
779
|
-
createdAtMs: event.createdAtMs,
|
|
780
|
-
message: event.data,
|
|
781
|
-
seq: event.seq
|
|
782
|
-
};
|
|
783
|
-
const result = reportingToolResultMessageSchema.safeParse(event.data);
|
|
784
|
-
const start = result.success ? toolStarts.get(result.data.toolCallId) : void 0;
|
|
785
|
-
data = reportAssistantMessage(reportArgs) ?? reportToolResult({
|
|
786
|
-
canExposePayload: args.canExposePayload,
|
|
787
|
-
message: event.data,
|
|
788
|
-
...start && start.seq < event.seq ? { start } : void 0
|
|
789
|
-
});
|
|
790
|
-
} else if (event.data.type === "tool_execution_started") {
|
|
791
|
-
toolStarts.set(event.data.toolCallId, {
|
|
792
|
-
createdAtMs: event.createdAtMs,
|
|
793
|
-
name: event.data.toolName,
|
|
794
|
-
seq: event.seq
|
|
795
|
-
});
|
|
796
|
-
data = {
|
|
797
|
-
type: "tool_calls",
|
|
798
|
-
calls: [{
|
|
799
|
-
toolCallId: event.data.toolCallId,
|
|
800
|
-
name: event.data.toolName,
|
|
801
|
-
status: "running",
|
|
802
|
-
startedAt: new Date(event.createdAtMs).toISOString(),
|
|
803
|
-
startedSeq: event.seq
|
|
804
|
-
}]
|
|
805
|
-
};
|
|
806
|
-
} else if (event.data.type === "subagent_started") {
|
|
807
|
-
subagentStarts.set(event.data.subagentInvocationId, {
|
|
808
|
-
createdAtMs: event.createdAtMs,
|
|
809
|
-
data: event.data,
|
|
810
|
-
seq: event.seq
|
|
811
|
-
});
|
|
812
|
-
data = {
|
|
813
|
-
type: "subagent",
|
|
814
|
-
startedSeq: event.seq,
|
|
815
|
-
startedAt: new Date(event.createdAtMs).toISOString(),
|
|
816
|
-
childConversationId: event.data.childConversationId,
|
|
817
|
-
subagentKind: event.data.subagentKind,
|
|
818
|
-
status: "running",
|
|
819
|
-
...event.data.parentToolCallId ? { parentToolCallId: event.data.parentToolCallId } : void 0
|
|
820
|
-
};
|
|
821
|
-
} else if (event.data.type === "subagent_ended") {
|
|
822
|
-
const started = subagentStarts.get(event.data.subagentInvocationId);
|
|
823
|
-
if (started && started.seq < event.seq) {
|
|
824
|
-
data = {
|
|
825
|
-
type: "subagent",
|
|
826
|
-
startedSeq: started.seq,
|
|
827
|
-
startedAt: new Date(started.createdAtMs).toISOString(),
|
|
828
|
-
childConversationId: started.data.childConversationId,
|
|
829
|
-
subagentKind: started.data.subagentKind,
|
|
830
|
-
...started.data.parentToolCallId ? { parentToolCallId: started.data.parentToolCallId } : void 0,
|
|
831
|
-
status: event.data.outcome === "success" ? "completed" : event.data.outcome
|
|
832
|
-
};
|
|
833
|
-
subagentStarts.delete(event.data.subagentInvocationId);
|
|
834
|
-
}
|
|
835
|
-
} else if (event.data.type === "handoff") data = {
|
|
836
|
-
type: "handoff",
|
|
837
|
-
modelProfile: event.data.modelProfile,
|
|
838
|
-
modelId: event.data.modelId,
|
|
839
|
-
...event.data.reasoningLevel ? { reasoningLevel: event.data.reasoningLevel } : void 0,
|
|
840
|
-
...event.data.triggeringToolCallId ? { triggeringToolCallId: event.data.triggeringToolCallId } : void 0,
|
|
841
|
-
...args.canExposePayload && event.data.summary ? { summary: event.data.summary } : void 0
|
|
842
|
-
};
|
|
843
|
-
else data = reportEventData({
|
|
844
|
-
canExposePayload: args.canExposePayload,
|
|
845
|
-
data: event.data
|
|
846
|
-
});
|
|
847
|
-
if (!data) continue;
|
|
848
|
-
projected.push(conversationReportEventSchema.parse({
|
|
849
|
-
seq: event.seq,
|
|
850
|
-
createdAt: new Date(event.createdAtMs).toISOString(),
|
|
851
|
-
data
|
|
852
|
-
}));
|
|
853
|
-
}
|
|
854
|
-
return projected;
|
|
855
|
-
}
|
|
856
|
-
//#endregion
|
|
857
|
-
//#region src/api/conversations/event-page.ts
|
|
858
|
-
const conversationEventColumns = getTableColumns(juniorConversationEvents);
|
|
859
|
-
const MIN_SCAN_SIZE = 64;
|
|
860
|
-
/** Read bounded canonical rows while excluding model-only replacement history. */
|
|
861
|
-
async function readConversationEventRows(executor, args) {
|
|
862
|
-
return executor.db().select({
|
|
863
|
-
...conversationEventColumns,
|
|
864
|
-
payload: sql`case
|
|
865
|
-
when ${juniorConversationEvents.type} in ('compaction', 'handoff')
|
|
866
|
-
then jsonb_set(
|
|
867
|
-
${juniorConversationEvents.payload},
|
|
868
|
-
'{replacementHistory}',
|
|
869
|
-
'[]'::jsonb
|
|
870
|
-
)
|
|
871
|
-
else ${juniorConversationEvents.payload}
|
|
872
|
-
end`
|
|
873
|
-
}).from(juniorConversationEvents).where(and(eq(juniorConversationEvents.conversationId, args.conversationId), args.beforeSeq === void 0 ? void 0 : lt(juniorConversationEvents.seq, args.beforeSeq), inArray(juniorConversationEvents.type, args.types ?? [...conversationReportSourceEventTypes]), args.subagentInvocationIds === void 0 ? void 0 : inArray(sql`${juniorConversationEvents.payload}->>'subagentInvocationId'`, args.subagentInvocationIds), args.toolCallIds === void 0 ? void 0 : inArray(sql`${juniorConversationEvents.payload}->>'toolCallId'`, args.toolCallIds))).orderBy(args.direction === "forward" ? asc(juniorConversationEvents.seq) : desc(juniorConversationEvents.seq)).limit(args.limit);
|
|
874
|
-
}
|
|
875
|
-
function decodeConversationEventRow(row) {
|
|
876
|
-
return decodeStoredConversationEvent({
|
|
877
|
-
schemaVersion: row.schemaVersion,
|
|
878
|
-
seq: row.seq,
|
|
879
|
-
historyVersion: row.historyVersion,
|
|
880
|
-
...row.idempotencyKey ? { idempotencyKey: row.idempotencyKey } : void 0,
|
|
881
|
-
createdAtMs: row.createdAt.getTime(),
|
|
882
|
-
type: row.type,
|
|
883
|
-
payload: row.payload
|
|
884
|
-
});
|
|
885
|
-
}
|
|
886
|
-
/** Project stored rows and resolve entity starts that precede the page. */
|
|
887
|
-
async function projectConversationEventRows(executor, args) {
|
|
888
|
-
const events = args.rows.map(decodeConversationEventRow).sort((left, right) => left.seq - right.seq);
|
|
889
|
-
const endedInvocationIds = [...new Set(events.flatMap((event) => event.data.type === "subagent_ended" ? [event.data.subagentInvocationId] : []))];
|
|
890
|
-
const subagentStartRows = endedInvocationIds.length === 0 ? [] : await readConversationEventRows(executor, {
|
|
891
|
-
conversationId: args.conversationId,
|
|
892
|
-
direction: "forward",
|
|
893
|
-
limit: endedInvocationIds.length,
|
|
894
|
-
subagentInvocationIds: endedInvocationIds,
|
|
895
|
-
types: ["subagent_started"]
|
|
896
|
-
});
|
|
897
|
-
const toolResultIds = conversationReportToolResultIds(events);
|
|
898
|
-
const toolStartRows = toolResultIds.length === 0 ? [] : await readConversationEventRows(executor, {
|
|
899
|
-
conversationId: args.conversationId,
|
|
900
|
-
direction: "forward",
|
|
901
|
-
limit: toolResultIds.length,
|
|
902
|
-
toolCallIds: toolResultIds,
|
|
903
|
-
types: ["tool_execution_started"]
|
|
904
|
-
});
|
|
905
|
-
return projectConversationReportEventPage({
|
|
906
|
-
canExposePayload: args.canExposePayload,
|
|
907
|
-
events,
|
|
908
|
-
subagentStartEvents: subagentStartRows.map(decodeConversationEventRow),
|
|
909
|
-
toolStartEvents: toolStartRows.map(decodeConversationEventRow)
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
/**
|
|
913
|
-
* Read the latest reporting events before an exclusive boundary.
|
|
914
|
-
*
|
|
915
|
-
* Source rows are scanned in bounded chunks until the requested projected page
|
|
916
|
-
* and one older event are known. This keeps ordinary pages bounded without
|
|
917
|
-
* assuming every agent history event produces a reporting event.
|
|
918
|
-
*/
|
|
919
|
-
async function readConversationEventPage(executor, args) {
|
|
920
|
-
const scanSize = Math.max(args.limit + 1, MIN_SCAN_SIZE);
|
|
921
|
-
const rows = [];
|
|
922
|
-
let beforeSeq = args.beforeSeq;
|
|
923
|
-
let projected = [];
|
|
924
|
-
while (projected.length <= args.limit) {
|
|
925
|
-
const batch = await readConversationEventRows(executor, {
|
|
926
|
-
beforeSeq,
|
|
927
|
-
conversationId: args.conversationId,
|
|
928
|
-
direction: "backward",
|
|
929
|
-
limit: scanSize
|
|
930
|
-
});
|
|
931
|
-
if (batch.length === 0) break;
|
|
932
|
-
rows.push(...batch);
|
|
933
|
-
projected = await projectConversationEventRows(executor, {
|
|
934
|
-
canExposePayload: args.canExposePayload,
|
|
935
|
-
conversationId: args.conversationId,
|
|
936
|
-
rows
|
|
937
|
-
});
|
|
938
|
-
if (projected.length > args.limit || batch.length < scanSize) break;
|
|
939
|
-
beforeSeq = batch.at(-1).seq;
|
|
940
|
-
}
|
|
941
|
-
const events = projected.slice(-args.limit);
|
|
942
|
-
return {
|
|
943
|
-
events,
|
|
944
|
-
...projected.length > events.length && events[0] ? { previousSeq: events[0].seq } : void 0
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
//#endregion
|
|
948
463
|
//#region src/api/conversations/shared.ts
|
|
949
464
|
/** Format a Slack destination without exposing private channel names. */
|
|
950
465
|
function slackStatsLocationLabel(input) {
|
|
@@ -1389,19 +904,18 @@ async function readConversationActivityPreviews(db, conversationIds) {
|
|
|
1389
904
|
//#endregion
|
|
1390
905
|
//#region src/api/conversations/list.ts
|
|
1391
906
|
const CONVERSATION_FEED_LIMIT = 50;
|
|
1392
|
-
|
|
1393
|
-
function conversationFeedMembershipFilter(status, filter, archivedAfter) {
|
|
907
|
+
function conversationFeedMembershipFilter(status, filter) {
|
|
1394
908
|
if (!filter) return status === "archived" ? sql`false` : void 0;
|
|
1395
909
|
if (filter.kind === "viewer") return and(viewerConversationMembership({
|
|
1396
910
|
actorMatch: eq(juniorIdentities.userId, filter.userId),
|
|
1397
911
|
participantMatch: conversationHasParticipantUser(filter.userId)
|
|
1398
|
-
}), status === "archived" ? conversationArchivedForUser(filter.userId) : status === "all" ? void 0 :
|
|
912
|
+
}), status === "archived" ? conversationArchivedForUser(filter.userId) : status === "all" ? void 0 : conversationNotArchivedForUser(filter.userId));
|
|
1399
913
|
return and(viewerConversationMembership({
|
|
1400
914
|
actorMatch: eq(juniorUsers.primaryEmailNormalized, filter.email),
|
|
1401
915
|
participantMatch: conversationHasParticipantEmail(filter.email)
|
|
1402
|
-
}), status === "archived" ? conversationArchivedForEmail(filter.email) : status === "all" ? void 0 :
|
|
916
|
+
}), status === "archived" ? conversationArchivedForEmail(filter.email) : status === "all" ? void 0 : conversationNotArchivedForEmail(filter.email));
|
|
1403
917
|
}
|
|
1404
|
-
async function conversationRows(db, limit, status, filter,
|
|
918
|
+
async function conversationRows(db, limit, status, filter, query, includePrivateBriefs = false) {
|
|
1405
919
|
return db.select({
|
|
1406
920
|
conversation: conversationReadColumns(),
|
|
1407
921
|
location: juniorDestinations,
|
|
@@ -1412,7 +926,19 @@ async function conversationRows(db, limit, status, filter, archivedAfter, query)
|
|
|
1412
926
|
identitySubjectId: juniorIdentities.providerSubjectId,
|
|
1413
927
|
identityTenantId: juniorIdentities.providerTenantId,
|
|
1414
928
|
userDisplayName: juniorUsers.displayName
|
|
1415
|
-
}).from(juniorConversations).leftJoin(juniorDestinations, eq(juniorDestinations.id, juniorConversations.destinationId)).leftJoin(juniorIdentities, eq(juniorIdentities.id, juniorConversations.actorIdentityId)).leftJoin(juniorUsers, eq(juniorUsers.id, juniorIdentities.userId)).where(and(isNull(juniorConversations.parentConversationId), conversationFeedMembershipFilter(status, filter
|
|
929
|
+
}).from(juniorConversations).leftJoin(juniorDestinations, eq(juniorDestinations.id, juniorConversations.destinationId)).leftJoin(juniorIdentities, eq(juniorIdentities.id, juniorConversations.actorIdentityId)).leftJoin(juniorUsers, eq(juniorUsers.id, juniorIdentities.userId)).where(and(isNull(juniorConversations.parentConversationId), conversationFeedMembershipFilter(status, filter), query ? or(sql`strpos(lower(coalesce(${juniorConversations.title}, '')), ${query}) > 0`, sql`exists (
|
|
930
|
+
select 1
|
|
931
|
+
from ${juniorConversationBriefs}
|
|
932
|
+
where ${juniorConversationBriefs.conversationId} = ${juniorConversations.conversationId}
|
|
933
|
+
and ${includePrivateBriefs ? sql`true` : eq(juniorDestinations.visibility, "public")}
|
|
934
|
+
and ${juniorConversationBriefs.version} = (
|
|
935
|
+
select max(${juniorConversationBriefs.version})
|
|
936
|
+
from ${juniorConversationBriefs}
|
|
937
|
+
where ${juniorConversationBriefs.conversationId} = ${juniorConversations.conversationId}
|
|
938
|
+
)
|
|
939
|
+
and to_tsvector('english', ${juniorConversationBriefs.searchText})
|
|
940
|
+
@@ websearch_to_tsquery('english', ${query})
|
|
941
|
+
)`) : void 0)).orderBy(desc(juniorConversations.lastActivityAt), asc(juniorConversations.conversationId)).limit(limit);
|
|
1416
942
|
}
|
|
1417
943
|
/** Decode a conversation row with the linked user name and identity-scoped provider fields. */
|
|
1418
944
|
function conversationFromRow(row) {
|
|
@@ -1490,7 +1016,7 @@ async function readConversationFeedFromSql(options = {}) {
|
|
|
1490
1016
|
const db = getDb();
|
|
1491
1017
|
const filter = conversationFeedFilter(options);
|
|
1492
1018
|
const query = options.q?.trim().toLowerCase() || void 0;
|
|
1493
|
-
const rows = await conversationRows(db, options.limit ?? CONVERSATION_FEED_LIMIT, query ? "all" : options.status ?? "active", filter,
|
|
1019
|
+
const rows = await conversationRows(db, options.limit ?? CONVERSATION_FEED_LIMIT, query ? "all" : options.status ?? "active", filter, query, filter?.kind === "viewer");
|
|
1494
1020
|
const conversations = rows.map((row) => conversationFromRow(row));
|
|
1495
1021
|
const conversationIds = conversations.map((conversation) => conversation.conversationId);
|
|
1496
1022
|
const [accessByConversation, archiveTimesByConversation, annotationsByConversation, auxiliaryCostsByRoot, metricsByRoot, teamDomainByTeamId, conversationWork, lastUserMessageAtByConversation, activityPreviewByConversation] = await Promise.all([
|
|
@@ -1555,23 +1081,23 @@ async function readConversationFeed(options = {}) {
|
|
|
1555
1081
|
return conversationFeedSchema.parse(await readConversationFeedFromSql(options));
|
|
1556
1082
|
}
|
|
1557
1083
|
//#endregion
|
|
1558
|
-
//#region src/chat/scheduled-
|
|
1084
|
+
//#region src/chat/scheduled-automations/personal.ts
|
|
1559
1085
|
const cursorSchema = z.object({
|
|
1560
1086
|
createdAtMs: z.number().finite(),
|
|
1561
1087
|
id: z.string().min(1),
|
|
1562
1088
|
query: z.string().max(200).optional(),
|
|
1563
1089
|
version: z.literal(1)
|
|
1564
1090
|
}).strict();
|
|
1565
|
-
var
|
|
1091
|
+
var InvalidScheduledAutomationCursorError = class extends Error {
|
|
1566
1092
|
constructor() {
|
|
1567
|
-
super("Scheduled
|
|
1568
|
-
this.name = "
|
|
1093
|
+
super("Scheduled automation cursor is invalid.");
|
|
1094
|
+
this.name = "InvalidScheduledAutomationCursorError";
|
|
1569
1095
|
}
|
|
1570
1096
|
};
|
|
1571
|
-
var
|
|
1097
|
+
var PersonalScheduledAutomationNotFoundError = class extends Error {
|
|
1572
1098
|
constructor() {
|
|
1573
|
-
super("Scheduled
|
|
1574
|
-
this.name = "
|
|
1099
|
+
super("Scheduled automation was not found.");
|
|
1100
|
+
this.name = "PersonalScheduledAutomationNotFoundError";
|
|
1575
1101
|
}
|
|
1576
1102
|
};
|
|
1577
1103
|
function normalizeQuery(query) {
|
|
@@ -1581,13 +1107,13 @@ function decodeCursor(value, query) {
|
|
|
1581
1107
|
if (!value) return void 0;
|
|
1582
1108
|
try {
|
|
1583
1109
|
const parsed = cursorSchema.parse(JSON.parse(Buffer.from(value, "base64url").toString("utf8")));
|
|
1584
|
-
if (parsed.query !== query) throw new
|
|
1110
|
+
if (parsed.query !== query) throw new InvalidScheduledAutomationCursorError();
|
|
1585
1111
|
return {
|
|
1586
1112
|
createdAtMs: parsed.createdAtMs,
|
|
1587
1113
|
id: parsed.id
|
|
1588
1114
|
};
|
|
1589
1115
|
} catch {
|
|
1590
|
-
throw new
|
|
1116
|
+
throw new InvalidScheduledAutomationCursorError();
|
|
1591
1117
|
}
|
|
1592
1118
|
}
|
|
1593
1119
|
function encodeCursor(task, query) {
|
|
@@ -1598,12 +1124,12 @@ function encodeCursor(task, query) {
|
|
|
1598
1124
|
version: 1
|
|
1599
1125
|
}), "utf8").toString("base64url");
|
|
1600
1126
|
}
|
|
1601
|
-
/** Delete one scheduled
|
|
1602
|
-
async function
|
|
1127
|
+
/** Delete one scheduled automation created by the viewer. */
|
|
1128
|
+
async function deleteViewerScheduledAutomation(db, user, id, nowMs = Date.now()) {
|
|
1603
1129
|
const identityIds = new Set(user.identities.map((identity) => identity.id));
|
|
1604
|
-
const task = await
|
|
1605
|
-
if (!task || task.status === "deleted" || !identityIds.has(task.creatorIdentityId)) throw new
|
|
1606
|
-
await
|
|
1130
|
+
const task = await readScheduledAutomation(db, id);
|
|
1131
|
+
if (!task || task.status === "deleted" || !identityIds.has(task.creatorIdentityId)) throw new PersonalScheduledAutomationNotFoundError();
|
|
1132
|
+
await saveScheduledAutomation(db, {
|
|
1607
1133
|
...task,
|
|
1608
1134
|
nextRunAtMs: void 0,
|
|
1609
1135
|
runNowAtMs: void 0,
|
|
@@ -1611,16 +1137,16 @@ async function deleteViewerScheduledTask(db, user, id, nowMs = Date.now()) {
|
|
|
1611
1137
|
updatedAtMs: nowMs
|
|
1612
1138
|
});
|
|
1613
1139
|
}
|
|
1614
|
-
/** List scheduled
|
|
1615
|
-
async function
|
|
1140
|
+
/** List scheduled automations created by the viewer with a stable SQL cursor. */
|
|
1141
|
+
async function listViewerScheduledAutomations(db, user, input) {
|
|
1616
1142
|
const identityIds = user.identities.map((identity) => identity.id);
|
|
1617
|
-
if (identityIds.length === 0) return {
|
|
1143
|
+
if (identityIds.length === 0) return { automations: [] };
|
|
1618
1144
|
const query = normalizeQuery(input.query);
|
|
1619
1145
|
const cursor = decodeCursor(input.cursor, query);
|
|
1620
|
-
const
|
|
1146
|
+
const automations = [];
|
|
1621
1147
|
let before = cursor;
|
|
1622
1148
|
const fetchLimit = input.limit + 1;
|
|
1623
|
-
while (
|
|
1149
|
+
while (automations.length < fetchLimit) {
|
|
1624
1150
|
const cursorFilter = before ? or(lt(juniorSchedulerTasks.createdAtMs, before.createdAtMs), and(eq(juniorSchedulerTasks.createdAtMs, before.createdAtMs), lt(juniorSchedulerTasks.id, before.id))) : void 0;
|
|
1625
1151
|
const search = query ? sql`strpos(lower(coalesce(${juniorSchedulerTasks.title}, ${juniorSchedulerTasks.record}->'task'->>'text')), ${query}) > 0` : void 0;
|
|
1626
1152
|
const rows = await db.select({
|
|
@@ -1630,7 +1156,7 @@ async function listViewerScheduledTasks(db, user, input) {
|
|
|
1630
1156
|
record: juniorSchedulerTasks.record,
|
|
1631
1157
|
title: juniorSchedulerTasks.title
|
|
1632
1158
|
}).from(juniorSchedulerTasks).where(and(notInArray(juniorSchedulerTasks.status, ["deleted", "paused"]), inArray(juniorSchedulerTasks.creatorIdentityId, identityIds), cursorFilter, search)).orderBy(desc(juniorSchedulerTasks.createdAtMs), desc(juniorSchedulerTasks.id)).limit(fetchLimit);
|
|
1633
|
-
|
|
1159
|
+
automations.push(...rows.map(parseScheduledAutomationRow).filter(isListedScheduledAutomation));
|
|
1634
1160
|
if (rows.length < fetchLimit) break;
|
|
1635
1161
|
const last = rows.at(-1);
|
|
1636
1162
|
before = {
|
|
@@ -1638,14 +1164,14 @@ async function listViewerScheduledTasks(db, user, input) {
|
|
|
1638
1164
|
id: last.id
|
|
1639
1165
|
};
|
|
1640
1166
|
}
|
|
1641
|
-
const page =
|
|
1167
|
+
const page = automations.slice(0, input.limit);
|
|
1642
1168
|
return {
|
|
1643
|
-
|
|
1644
|
-
...
|
|
1169
|
+
automations: page,
|
|
1170
|
+
...automations.length > input.limit && page.length > 0 ? { nextCursor: encodeCursor(page.at(-1), query) } : void 0
|
|
1645
1171
|
};
|
|
1646
1172
|
}
|
|
1647
1173
|
//#endregion
|
|
1648
|
-
//#region src/chat/
|
|
1174
|
+
//#region src/chat/automations/read.ts
|
|
1649
1175
|
const TASK_LIST_LIMIT = 100;
|
|
1650
1176
|
const TASK_FETCH_LIMIT = 101;
|
|
1651
1177
|
const TASK_EXECUTION_LIST_LIMIT = 100;
|
|
@@ -1706,14 +1232,14 @@ function executionSummaryFields(stats) {
|
|
|
1706
1232
|
return {
|
|
1707
1233
|
...stats?.lastConversationId ? { lastConversationId: stats.lastConversationId } : void 0,
|
|
1708
1234
|
...stats?.lastExecutedAtMs ? { lastRunAt: new Date(stats.lastExecutedAtMs).toISOString() } : void 0,
|
|
1709
|
-
runs: stats?.runs ??
|
|
1235
|
+
runs: stats?.runs ?? emptyAutomationRunWindows(),
|
|
1710
1236
|
totalRuns: stats?.totalRuns ?? 0
|
|
1711
1237
|
};
|
|
1712
1238
|
}
|
|
1713
|
-
function
|
|
1714
|
-
if (task.status === "deleted") throw new Error("Deleted scheduled
|
|
1239
|
+
function scheduledAutomationSummary(task, ownedByViewer, destination, stats, createdByEmail) {
|
|
1240
|
+
if (task.status === "deleted") throw new Error("Deleted scheduled automations cannot enter the Automations view");
|
|
1715
1241
|
const nextRunAtMs = task.runNowAtMs ?? task.nextRunAtMs;
|
|
1716
|
-
const instruction = displayText(task.task.text, "Untitled scheduled
|
|
1242
|
+
const instruction = displayText(task.task.text, "Untitled scheduled automation");
|
|
1717
1243
|
return {
|
|
1718
1244
|
createdAt: new Date(task.createdAtMs).toISOString(),
|
|
1719
1245
|
createdBy: creatorLabel(task.createdBy),
|
|
@@ -1733,10 +1259,10 @@ function scheduledTaskSummary(task, ownedByViewer, destination, stats, createdBy
|
|
|
1733
1259
|
schedule: displayText(task.schedule.description, "Schedule unavailable"),
|
|
1734
1260
|
status: task.status,
|
|
1735
1261
|
outcomes: effectiveTaskOutcomes(task.outcomes, task.destination),
|
|
1736
|
-
title: taskDisplayTitle(task.title, instruction, "Untitled scheduled
|
|
1262
|
+
title: taskDisplayTitle(task.title, instruction, "Untitled scheduled automation")
|
|
1737
1263
|
};
|
|
1738
1264
|
}
|
|
1739
|
-
function
|
|
1265
|
+
function eventAutomationSummary(task, ownedByViewer, destination, stats, createdByEmail) {
|
|
1740
1266
|
const instruction = task.task.text;
|
|
1741
1267
|
return {
|
|
1742
1268
|
createdAt: new Date(task.createdAtMs).toISOString(),
|
|
@@ -1757,8 +1283,8 @@ function eventTaskSummary(task, ownedByViewer, destination, stats, createdByEmai
|
|
|
1757
1283
|
resource: `${task.trigger.label} · ${task.trigger.identifier}`,
|
|
1758
1284
|
source: task.trigger.namespace,
|
|
1759
1285
|
outcomes: effectiveTaskOutcomes(task.outcomes, task.destination),
|
|
1760
|
-
title: taskDisplayTitle(task.title, instruction, "Untitled event
|
|
1761
|
-
triggerAvailable:
|
|
1286
|
+
title: taskDisplayTitle(task.title, instruction, "Untitled event automation"),
|
|
1287
|
+
triggerAvailable: eventAutomationTriggerAvailable(task, getEventCatalog())
|
|
1762
1288
|
};
|
|
1763
1289
|
}
|
|
1764
1290
|
function viewerTeamIds(user) {
|
|
@@ -1769,7 +1295,7 @@ async function resolveViewerTaskCandidate(user, kind, id) {
|
|
|
1769
1295
|
const identityIds = new Set(user.identities.map((identity) => identity.id));
|
|
1770
1296
|
const teamIds = viewerTeamIds(user);
|
|
1771
1297
|
if (kind === "scheduled") {
|
|
1772
|
-
const task = await
|
|
1298
|
+
const task = await readScheduledAutomation(db, id);
|
|
1773
1299
|
if (!task || task.status === "deleted") return void 0;
|
|
1774
1300
|
const ownedByViewer = identityIds.has(task.creatorIdentityId);
|
|
1775
1301
|
const destination = (await destinationDetails([task.destination])).get(destinationKey(task.destination));
|
|
@@ -1782,9 +1308,9 @@ async function resolveViewerTaskCandidate(user, kind, id) {
|
|
|
1782
1308
|
task
|
|
1783
1309
|
};
|
|
1784
1310
|
}
|
|
1785
|
-
const task = await
|
|
1311
|
+
const task = await getEventAutomation(db, id);
|
|
1786
1312
|
if (!task || task.status === "deleted") return void 0;
|
|
1787
|
-
const ownedByViewer =
|
|
1313
|
+
const ownedByViewer = eventAutomationBelongsToUser(task, user);
|
|
1788
1314
|
const destination = (await destinationDetails([task.destination])).get(destinationKey(task.destination));
|
|
1789
1315
|
const publicToViewer = teamIds.includes(task.destination.teamId) && destination?.visibility === "public";
|
|
1790
1316
|
if (!ownedByViewer && !publicToViewer) return void 0;
|
|
@@ -1795,22 +1321,22 @@ async function resolveViewerTaskCandidate(user, kind, id) {
|
|
|
1795
1321
|
task
|
|
1796
1322
|
};
|
|
1797
1323
|
}
|
|
1798
|
-
async function
|
|
1324
|
+
async function automationSummaryForCandidate(candidate) {
|
|
1799
1325
|
const [destinations, creatorEmails, stats] = await Promise.all([
|
|
1800
1326
|
destinationDetails([candidate.task.destination]),
|
|
1801
1327
|
creatorProfileEmails([candidate]),
|
|
1802
|
-
|
|
1328
|
+
readAutomationExecutionSummaries(candidate.kind, "junior")
|
|
1803
1329
|
]);
|
|
1804
1330
|
const destination = destinations.get(destinationKey(candidate.task.destination)) ?? {
|
|
1805
1331
|
label: `Channel ${candidate.task.destination.channelId}`,
|
|
1806
1332
|
visibility: "private"
|
|
1807
1333
|
};
|
|
1808
1334
|
const createdByEmail = creatorEmails.get(creatorKey(candidate.task.destination.teamId, candidate.task.createdBy.slackUserId));
|
|
1809
|
-
if (candidate.kind === "scheduled") return
|
|
1810
|
-
return
|
|
1335
|
+
if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id), createdByEmail);
|
|
1336
|
+
return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id), createdByEmail);
|
|
1811
1337
|
}
|
|
1812
|
-
/** Read viewer-owned and public-workspace
|
|
1813
|
-
function
|
|
1338
|
+
/** Read viewer-owned and public-workspace automations as one bounded newest-first projection. */
|
|
1339
|
+
function emptyAutomationExecutionDay(date) {
|
|
1814
1340
|
return {
|
|
1815
1341
|
costUsd: 0,
|
|
1816
1342
|
date,
|
|
@@ -1818,7 +1344,7 @@ function emptyTaskExecutionDay(date) {
|
|
|
1818
1344
|
scheduled: 0
|
|
1819
1345
|
};
|
|
1820
1346
|
}
|
|
1821
|
-
function
|
|
1347
|
+
function emptyAutomationExecutionStatusDay(date) {
|
|
1822
1348
|
return {
|
|
1823
1349
|
blocked: 0,
|
|
1824
1350
|
completed: 0,
|
|
@@ -1826,51 +1352,51 @@ function emptyTaskExecutionStatusDay(date) {
|
|
|
1826
1352
|
failed: 0
|
|
1827
1353
|
};
|
|
1828
1354
|
}
|
|
1829
|
-
function
|
|
1355
|
+
function automationExecutionSixHours(hours, nowMs = Date.now()) {
|
|
1830
1356
|
return sumUtcHoursIntoSixHours({
|
|
1831
|
-
empty:
|
|
1357
|
+
empty: emptyAutomationExecutionDay,
|
|
1832
1358
|
hours,
|
|
1833
1359
|
nowMs
|
|
1834
1360
|
});
|
|
1835
1361
|
}
|
|
1836
|
-
function
|
|
1362
|
+
function automationExecutionStatusSixHours(hours, nowMs = Date.now()) {
|
|
1837
1363
|
return sumUtcHoursIntoSixHours({
|
|
1838
|
-
empty:
|
|
1364
|
+
empty: emptyAutomationExecutionStatusDay,
|
|
1839
1365
|
hours,
|
|
1840
1366
|
nowMs
|
|
1841
1367
|
});
|
|
1842
1368
|
}
|
|
1843
|
-
/** List one viewer's scheduled and event
|
|
1844
|
-
async function
|
|
1369
|
+
/** List one viewer's scheduled and event automations, optionally filtered by title or instruction search. */
|
|
1370
|
+
async function readViewerAutomations(user, input = {}) {
|
|
1845
1371
|
const db = getDb();
|
|
1846
1372
|
const query = input.q?.trim().toLowerCase() || void 0;
|
|
1847
1373
|
const identityIds = new Set(user.identities.map((identity) => identity.id));
|
|
1848
1374
|
const teamIds = viewerTeamIds(user);
|
|
1849
|
-
const [scheduledPage, publicScheduled,
|
|
1850
|
-
|
|
1375
|
+
const [scheduledPage, publicScheduled, eventAutomations, publicEventAutomations] = await Promise.all([
|
|
1376
|
+
listViewerScheduledAutomations(db, user, {
|
|
1851
1377
|
limit: TASK_FETCH_LIMIT,
|
|
1852
1378
|
query
|
|
1853
1379
|
}),
|
|
1854
|
-
|
|
1380
|
+
listPublicScheduledAutomationsForTeams(db, teamIds, {
|
|
1855
1381
|
limit: TASK_FETCH_LIMIT,
|
|
1856
1382
|
query
|
|
1857
1383
|
}),
|
|
1858
|
-
|
|
1859
|
-
|
|
1384
|
+
listEventAutomationsCreatedBy(db, user, TASK_FETCH_LIMIT, query),
|
|
1385
|
+
listPublicEventAutomationsForTeams(db, teamIds, TASK_FETCH_LIMIT, query)
|
|
1860
1386
|
]);
|
|
1861
1387
|
const candidatesById = /* @__PURE__ */ new Map();
|
|
1862
1388
|
const publicScheduledIds = new Set(publicScheduled.map((task) => task.id));
|
|
1863
|
-
const
|
|
1864
|
-
for (const task of [...scheduledPage.
|
|
1389
|
+
const publicEventAutomationIds = new Set(publicEventAutomations.map((task) => task.id));
|
|
1390
|
+
for (const task of [...scheduledPage.automations, ...publicScheduled]) candidatesById.set(`scheduled:${task.id}`, {
|
|
1865
1391
|
kind: "scheduled",
|
|
1866
1392
|
ownedByViewer: identityIds.has(task.creatorIdentityId),
|
|
1867
1393
|
publicToViewer: publicScheduledIds.has(task.id),
|
|
1868
1394
|
task
|
|
1869
1395
|
});
|
|
1870
|
-
for (const task of [...
|
|
1396
|
+
for (const task of [...eventAutomations, ...publicEventAutomations]) candidatesById.set(`event:${task.id}`, {
|
|
1871
1397
|
kind: "event",
|
|
1872
|
-
ownedByViewer:
|
|
1873
|
-
publicToViewer:
|
|
1398
|
+
ownedByViewer: eventAutomationBelongsToUser(task, user),
|
|
1399
|
+
publicToViewer: publicEventAutomationIds.has(task.id),
|
|
1874
1400
|
task
|
|
1875
1401
|
});
|
|
1876
1402
|
const candidates = [...candidatesById.values()].sort((left, right) => right.task.createdAtMs - left.task.createdAtMs || right.task.id.localeCompare(left.task.id));
|
|
@@ -1881,30 +1407,30 @@ async function readViewerTasks(user, input = {}) {
|
|
|
1881
1407
|
const [destinations, creatorEmails] = await Promise.all([destinationDetails(selected.map(({ task }) => task.destination)), creatorProfileEmails(selected)]);
|
|
1882
1408
|
const visible = selected.filter((candidate) => candidate.ownedByViewer || destinations.get(destinationKey(candidate.task.destination))?.visibility === "public");
|
|
1883
1409
|
const [executionDays, executionHours, scheduledStats, eventStats] = await Promise.all([
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1410
|
+
readAutomationExecutionDays(),
|
|
1411
|
+
readAutomationExecutionHours(),
|
|
1412
|
+
readAutomationExecutionSummaries("scheduled", "junior"),
|
|
1413
|
+
readAutomationExecutionSummaries("event", "junior")
|
|
1888
1414
|
]);
|
|
1889
|
-
const
|
|
1415
|
+
const automations = visible.map((candidate) => {
|
|
1890
1416
|
const destination = destinations.get(destinationKey(candidate.task.destination)) ?? {
|
|
1891
1417
|
label: `Channel ${candidate.task.destination.channelId}`,
|
|
1892
1418
|
visibility: "private"
|
|
1893
1419
|
};
|
|
1894
1420
|
const createdByEmail = creatorEmails.get(creatorKey(candidate.task.destination.teamId, candidate.task.createdBy.slackUserId));
|
|
1895
|
-
if (candidate.kind === "scheduled") return
|
|
1896
|
-
return
|
|
1421
|
+
if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, scheduledStats.get(candidate.task.id), createdByEmail);
|
|
1422
|
+
return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, eventStats.get(candidate.task.id), createdByEmail);
|
|
1897
1423
|
});
|
|
1898
1424
|
return {
|
|
1899
1425
|
executionDays,
|
|
1900
1426
|
executionHours,
|
|
1901
|
-
executionSixHours:
|
|
1902
|
-
|
|
1427
|
+
executionSixHours: automationExecutionSixHours(executionHours),
|
|
1428
|
+
automations,
|
|
1903
1429
|
truncated: ownedCandidates.length > TASK_LIST_LIMIT || publicCandidates.length > TASK_LIST_LIMIT
|
|
1904
1430
|
};
|
|
1905
1431
|
}
|
|
1906
|
-
/** Load deleted scheduled
|
|
1907
|
-
async function
|
|
1432
|
+
/** Load deleted scheduled automations the viewer still owns, newest first. */
|
|
1433
|
+
async function readDeletedOwnedScheduledAutomations(user) {
|
|
1908
1434
|
const identityIds = user.identities.map((identity) => identity.id);
|
|
1909
1435
|
if (identityIds.length === 0) return [];
|
|
1910
1436
|
return (await getDb().select({
|
|
@@ -1912,68 +1438,68 @@ async function readDeletedOwnedScheduledTasks(user) {
|
|
|
1912
1438
|
id: juniorSchedulerTasks.id,
|
|
1913
1439
|
record: juniorSchedulerTasks.record,
|
|
1914
1440
|
title: juniorSchedulerTasks.title
|
|
1915
|
-
}).from(juniorSchedulerTasks).where(and(eq(juniorSchedulerTasks.status, "deleted"), inArray(juniorSchedulerTasks.creatorIdentityId, identityIds))).orderBy(desc(juniorSchedulerTasks.createdAtMs), desc(juniorSchedulerTasks.id)).limit(TASK_EXECUTION_LIST_LIMIT)).map(
|
|
1441
|
+
}).from(juniorSchedulerTasks).where(and(eq(juniorSchedulerTasks.status, "deleted"), inArray(juniorSchedulerTasks.creatorIdentityId, identityIds))).orderBy(desc(juniorSchedulerTasks.createdAtMs), desc(juniorSchedulerTasks.id)).limit(TASK_EXECUTION_LIST_LIMIT)).map(parseScheduledAutomationRow).filter((task) => task?.status === "deleted");
|
|
1916
1442
|
}
|
|
1917
|
-
/** Load deleted event
|
|
1918
|
-
async function
|
|
1919
|
-
return await
|
|
1443
|
+
/** Load deleted event automations the viewer still owns, newest first. */
|
|
1444
|
+
async function readDeletedOwnedEventAutomations(user) {
|
|
1445
|
+
return await listDeletedEventAutomationsCreatedBy(getDb(), user, TASK_EXECUTION_LIST_LIMIT);
|
|
1920
1446
|
}
|
|
1921
|
-
function
|
|
1922
|
-
return
|
|
1447
|
+
function automationTitleForRun(run, automationTitles) {
|
|
1448
|
+
return automationTitles.get(`${run.kind}:${run.automationId}`) ?? run.title?.trim() ?? "Untitled task";
|
|
1923
1449
|
}
|
|
1924
|
-
function
|
|
1925
|
-
const key = `${args.kind}:${args.
|
|
1926
|
-
if (
|
|
1927
|
-
|
|
1928
|
-
|
|
1450
|
+
function addDeletedAutomationRun(args, automationTitles, automations) {
|
|
1451
|
+
const key = `${args.kind}:${args.automationId}`;
|
|
1452
|
+
if (automationTitles.has(key)) return;
|
|
1453
|
+
automationTitles.set(key, args.title);
|
|
1454
|
+
automations.push({
|
|
1929
1455
|
kind: args.kind,
|
|
1930
|
-
|
|
1456
|
+
automationId: args.automationId
|
|
1931
1457
|
});
|
|
1932
1458
|
}
|
|
1933
1459
|
/**
|
|
1934
|
-
* Read newest runs across live viewer-visible
|
|
1460
|
+
* Read newest runs across live viewer-visible automations and deleted automations the
|
|
1935
1461
|
* viewer owns. Deleted task rows stay available so history does not depend on
|
|
1936
1462
|
* conversation actor membership.
|
|
1937
1463
|
*/
|
|
1938
|
-
async function
|
|
1464
|
+
async function readViewerAutomationRuns(user) {
|
|
1939
1465
|
const [taskList, deletedScheduled, deletedEvent] = await Promise.all([
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1466
|
+
readViewerAutomations(user),
|
|
1467
|
+
readDeletedOwnedScheduledAutomations(user),
|
|
1468
|
+
readDeletedOwnedEventAutomations(user)
|
|
1943
1469
|
]);
|
|
1944
|
-
const
|
|
1945
|
-
const
|
|
1946
|
-
for (const task of taskList.
|
|
1947
|
-
|
|
1948
|
-
|
|
1470
|
+
const automationTitles = /* @__PURE__ */ new Map();
|
|
1471
|
+
const automations = [];
|
|
1472
|
+
for (const task of taskList.automations) {
|
|
1473
|
+
automationTitles.set(`${task.kind}:${task.id}`, task.title);
|
|
1474
|
+
automations.push({
|
|
1949
1475
|
kind: task.kind,
|
|
1950
|
-
|
|
1476
|
+
automationId: task.id
|
|
1951
1477
|
});
|
|
1952
1478
|
}
|
|
1953
1479
|
for (const task of deletedScheduled) {
|
|
1954
|
-
const instruction = displayText(task.task.text, "Untitled scheduled
|
|
1955
|
-
|
|
1480
|
+
const instruction = displayText(task.task.text, "Untitled scheduled automation");
|
|
1481
|
+
addDeletedAutomationRun({
|
|
1956
1482
|
kind: "scheduled",
|
|
1957
|
-
|
|
1958
|
-
title: taskDisplayTitle(task.title, instruction, "Untitled scheduled
|
|
1959
|
-
},
|
|
1483
|
+
automationId: task.id,
|
|
1484
|
+
title: taskDisplayTitle(task.title, instruction, "Untitled scheduled automation")
|
|
1485
|
+
}, automationTitles, automations);
|
|
1960
1486
|
}
|
|
1961
1487
|
for (const task of deletedEvent) {
|
|
1962
|
-
const instruction = displayText(task.task.text, "Untitled event
|
|
1963
|
-
|
|
1488
|
+
const instruction = displayText(task.task.text, "Untitled event automation");
|
|
1489
|
+
addDeletedAutomationRun({
|
|
1964
1490
|
kind: "event",
|
|
1965
|
-
|
|
1966
|
-
title: taskDisplayTitle(task.title, instruction, "Untitled event
|
|
1967
|
-
},
|
|
1491
|
+
automationId: task.id,
|
|
1492
|
+
title: taskDisplayTitle(task.title, instruction, "Untitled event automation")
|
|
1493
|
+
}, automationTitles, automations);
|
|
1968
1494
|
}
|
|
1969
|
-
const runs = await
|
|
1495
|
+
const runs = await readAutomationRuns({
|
|
1970
1496
|
limit: 101,
|
|
1971
|
-
|
|
1497
|
+
automations
|
|
1972
1498
|
});
|
|
1973
1499
|
return {
|
|
1974
1500
|
runs: runs.slice(0, TASK_EXECUTION_LIST_LIMIT).map((run) => ({
|
|
1975
1501
|
...run,
|
|
1976
|
-
|
|
1502
|
+
automationTitle: automationTitleForRun(run, automationTitles)
|
|
1977
1503
|
})),
|
|
1978
1504
|
truncated: runs.length > TASK_EXECUTION_LIST_LIMIT
|
|
1979
1505
|
};
|
|
@@ -1985,70 +1511,70 @@ var ViewerTaskNotFoundError = class extends Error {
|
|
|
1985
1511
|
}
|
|
1986
1512
|
};
|
|
1987
1513
|
/** Read one viewer-visible task and its newest terminal executions. */
|
|
1988
|
-
async function
|
|
1514
|
+
async function readViewerAutomationExecutions(user, kind, id) {
|
|
1989
1515
|
const candidate = await resolveViewerTaskCandidate(user, kind, id);
|
|
1990
1516
|
if (!candidate) throw new ViewerTaskNotFoundError();
|
|
1991
|
-
const [
|
|
1992
|
-
|
|
1993
|
-
|
|
1517
|
+
const [automation, executions, executionDays, executionHours] = await Promise.all([
|
|
1518
|
+
automationSummaryForCandidate(candidate),
|
|
1519
|
+
readAutomationExecutions({
|
|
1994
1520
|
kind,
|
|
1995
1521
|
limit: 101,
|
|
1996
|
-
|
|
1522
|
+
automationId: id
|
|
1997
1523
|
}),
|
|
1998
|
-
|
|
1524
|
+
readAutomationExecutionStatusDays({
|
|
1999
1525
|
kind,
|
|
2000
|
-
|
|
1526
|
+
automationId: id
|
|
2001
1527
|
}),
|
|
2002
|
-
|
|
1528
|
+
readAutomationExecutionStatusHours({
|
|
2003
1529
|
kind,
|
|
2004
|
-
|
|
1530
|
+
automationId: id
|
|
2005
1531
|
})
|
|
2006
1532
|
]);
|
|
2007
1533
|
return {
|
|
2008
1534
|
executionDays,
|
|
2009
1535
|
executionHours,
|
|
2010
|
-
executionSixHours:
|
|
1536
|
+
executionSixHours: automationExecutionStatusSixHours(executionHours),
|
|
2011
1537
|
executions: executions.slice(0, TASK_EXECUTION_LIST_LIMIT),
|
|
2012
|
-
|
|
1538
|
+
automation,
|
|
2013
1539
|
truncated: executions.length > TASK_EXECUTION_LIST_LIMIT
|
|
2014
1540
|
};
|
|
2015
1541
|
}
|
|
2016
1542
|
/** Resolve the source task for one conversation when a terminal execution links it. */
|
|
2017
1543
|
async function readConversationSourceTask(args) {
|
|
2018
|
-
const execution = await
|
|
1544
|
+
const execution = await readAutomationExecutionByConversationId({ conversationId: args.conversationId });
|
|
2019
1545
|
if (!execution) return void 0;
|
|
2020
1546
|
if (!args.viewer) return { kind: execution.kind };
|
|
2021
|
-
const candidate = await resolveViewerTaskCandidate(args.viewer, execution.kind, execution.
|
|
1547
|
+
const candidate = await resolveViewerTaskCandidate(args.viewer, execution.kind, execution.automationId);
|
|
2022
1548
|
if (!candidate) return { kind: execution.kind };
|
|
2023
1549
|
if (candidate.kind === "scheduled") {
|
|
2024
|
-
const instruction = displayText(candidate.task.task.text, "Untitled scheduled
|
|
1550
|
+
const instruction = displayText(candidate.task.task.text, "Untitled scheduled automation");
|
|
2025
1551
|
return {
|
|
2026
1552
|
id: candidate.task.id,
|
|
2027
1553
|
kind: "scheduled",
|
|
2028
1554
|
label: instruction,
|
|
2029
|
-
title: taskDisplayTitle(candidate.task.title, instruction, "Untitled scheduled
|
|
1555
|
+
title: taskDisplayTitle(candidate.task.title, instruction, "Untitled scheduled automation")
|
|
2030
1556
|
};
|
|
2031
1557
|
}
|
|
2032
|
-
const instruction = displayText(candidate.task.task.text, "Untitled event
|
|
1558
|
+
const instruction = displayText(candidate.task.task.text, "Untitled event automation");
|
|
2033
1559
|
return {
|
|
2034
1560
|
id: candidate.task.id,
|
|
2035
1561
|
kind: "event",
|
|
2036
1562
|
label: instruction,
|
|
2037
|
-
title: taskDisplayTitle(candidate.task.title, instruction, "Untitled event
|
|
1563
|
+
title: taskDisplayTitle(candidate.task.title, instruction, "Untitled event automation")
|
|
2038
1564
|
};
|
|
2039
1565
|
}
|
|
2040
|
-
/** Delete one viewer-owned scheduled or event
|
|
1566
|
+
/** Delete one viewer-owned scheduled or event automation. */
|
|
2041
1567
|
async function deleteViewerTask(user, kind, id) {
|
|
2042
1568
|
if (kind === "scheduled") try {
|
|
2043
|
-
await
|
|
1569
|
+
await deleteViewerScheduledAutomation(getDb(), user, id);
|
|
2044
1570
|
return;
|
|
2045
1571
|
} catch (error) {
|
|
2046
|
-
if (error instanceof
|
|
1572
|
+
if (error instanceof PersonalScheduledAutomationNotFoundError) throw new ViewerTaskNotFoundError();
|
|
2047
1573
|
throw error;
|
|
2048
1574
|
}
|
|
2049
|
-
const task = await
|
|
2050
|
-
if (!task || task.status === "deleted" || !
|
|
2051
|
-
await
|
|
1575
|
+
const task = await getEventAutomation(getDb(), id);
|
|
1576
|
+
if (!task || task.status === "deleted" || !eventAutomationBelongsToUser(task, user)) throw new ViewerTaskNotFoundError();
|
|
1577
|
+
await deleteEventAutomation(getDb(), id);
|
|
2052
1578
|
}
|
|
2053
1579
|
//#endregion
|
|
2054
1580
|
//#region src/api/conversations/detail.ts
|
|
@@ -2072,6 +1598,7 @@ function projectConversationDetail(args) {
|
|
|
2072
1598
|
usage: args.usage
|
|
2073
1599
|
}),
|
|
2074
1600
|
annotations: canExposePayload ? args.annotations : [],
|
|
1601
|
+
...canExposePayload && args.brief ? { brief: args.brief } : void 0,
|
|
2075
1602
|
events: args.events,
|
|
2076
1603
|
...args.previousSeq !== void 0 ? { previousCursor: encodeConversationCursor({
|
|
2077
1604
|
conversationId,
|
|
@@ -2093,10 +1620,11 @@ async function readConversationDetailFromSql(conversationId, options) {
|
|
|
2093
1620
|
const archivedAtMs = options.viewer ? await readConversationArchivedAt(options.viewer, conversationId) : void 0;
|
|
2094
1621
|
const executor = getSqlExecutor();
|
|
2095
1622
|
const includeDescendantMetrics = record.rootConversationId === conversationId;
|
|
2096
|
-
const [accessByConversation, annotations, auxiliaryCostsByConversation, modelUsage, metricsByRoot, sourceTask, teamDomainByTeamId] = await Promise.all([
|
|
1623
|
+
const [accessByConversation, annotations, auxiliaryCostsByConversation, briefVersion, modelUsage, metricsByRoot, sourceTask, teamDomainByTeamId] = await Promise.all([
|
|
2097
1624
|
readConversationAccessFromSql(getDb(), [conversationId], options.viewer),
|
|
2098
1625
|
listConversationAnnotations(getDb(), conversationId),
|
|
2099
1626
|
readConversationAuxiliaryCostsFromSql(getDb(), [conversationId], { includeDescendants: includeDescendantMetrics }),
|
|
1627
|
+
readLatestConversationBrief(getDb(), conversationId),
|
|
2100
1628
|
record.conversation.transcriptPurgedAtMs === void 0 ? readConversationModelUsageFromSql(executor, {
|
|
2101
1629
|
conversationId,
|
|
2102
1630
|
includeDescendants: includeDescendantMetrics
|
|
@@ -2121,6 +1649,11 @@ async function readConversationDetailFromSql(conversationId, options) {
|
|
|
2121
1649
|
...archivedAtMs === void 0 ? void 0 : { archivedAtMs },
|
|
2122
1650
|
annotations,
|
|
2123
1651
|
auxiliaryCosts: auxiliaryCostsByConversation.get(conversationId),
|
|
1652
|
+
...briefVersion ? { brief: {
|
|
1653
|
+
content: briefVersion.content,
|
|
1654
|
+
updatedAt: briefVersion.createdAt.toISOString(),
|
|
1655
|
+
version: briefVersion.version
|
|
1656
|
+
} } : void 0,
|
|
2124
1657
|
durationMs: metrics?.durationMs ?? record.durationMs,
|
|
2125
1658
|
events: page.events,
|
|
2126
1659
|
modelUsage,
|
|
@@ -3876,7 +3409,7 @@ async function readPeoplePluginReports(args) {
|
|
|
3876
3409
|
reports: [],
|
|
3877
3410
|
source: "plugins"
|
|
3878
3411
|
});
|
|
3879
|
-
const { getPluginProfileReports } = await import("./agent-hooks-
|
|
3412
|
+
const { getPluginProfileReports } = await import("./agent-hooks-BBX9p6aG.js").then((n) => n.t);
|
|
3880
3413
|
return pluginOperationalReportFeedSchema.parse({
|
|
3881
3414
|
generatedAt: new Date(nowMs).toISOString(),
|
|
3882
3415
|
reports: await getPluginProfileReports({
|
|
@@ -4324,30 +3857,30 @@ function createUserPageRoutes() {
|
|
|
4324
3857
|
return app;
|
|
4325
3858
|
}
|
|
4326
3859
|
//#endregion
|
|
4327
|
-
//#region src/api/
|
|
3860
|
+
//#region src/api/automations/routes.ts
|
|
4328
3861
|
/** Create authenticated native task list and action routes. */
|
|
4329
|
-
function
|
|
3862
|
+
function createAutomationRoutes() {
|
|
4330
3863
|
const app = new Hono();
|
|
4331
|
-
app.get("/", requireViewer, validateRequest("query",
|
|
3864
|
+
app.get("/", requireViewer, validateRequest("query", automationListQuerySchema, "Invalid task list query."), async (context) => {
|
|
4332
3865
|
const user = context.get("viewer");
|
|
4333
3866
|
const query = context.req.valid("query");
|
|
4334
|
-
return jsonResponse(
|
|
3867
|
+
return jsonResponse(automationListSchema, await readViewerAutomations(user, query));
|
|
4335
3868
|
});
|
|
4336
3869
|
app.get("/runs", requireViewer, async (context) => {
|
|
4337
3870
|
const user = context.get("viewer");
|
|
4338
|
-
return jsonResponse(
|
|
3871
|
+
return jsonResponse(automationRunListSchema, await readViewerAutomationRuns(user));
|
|
4339
3872
|
});
|
|
4340
|
-
app.get("/:kind/:id/executions", requireViewer, validateRequest("param",
|
|
3873
|
+
app.get("/:kind/:id/executions", requireViewer, validateRequest("param", automationParamsSchema, "Invalid task."), async (context) => {
|
|
4341
3874
|
const user = context.get("viewer");
|
|
4342
3875
|
const params = context.req.valid("param");
|
|
4343
3876
|
try {
|
|
4344
|
-
return jsonResponse(
|
|
3877
|
+
return jsonResponse(automationExecutionListSchema, await readViewerAutomationExecutions(user, params.kind, params.id));
|
|
4345
3878
|
} catch (error) {
|
|
4346
3879
|
if (error instanceof ViewerTaskNotFoundError) return jsonResponse(apiErrorSchema, { error: error.message }, { status: 404 });
|
|
4347
3880
|
throw error;
|
|
4348
3881
|
}
|
|
4349
3882
|
});
|
|
4350
|
-
app.delete("/:kind/:id", requireViewer, validateRequest("param",
|
|
3883
|
+
app.delete("/:kind/:id", requireViewer, validateRequest("param", automationParamsSchema, "Invalid task."), async (context) => {
|
|
4351
3884
|
const user = context.get("viewer");
|
|
4352
3885
|
const params = context.req.valid("param");
|
|
4353
3886
|
try {
|
|
@@ -4485,7 +4018,7 @@ function readDescriptionText(home) {
|
|
|
4485
4018
|
}
|
|
4486
4019
|
/** Read discovered skill names for authenticated runtime diagnostics. */
|
|
4487
4020
|
async function readSkillReports() {
|
|
4488
|
-
const { discoverSkills } = await import("./skills-
|
|
4021
|
+
const { discoverSkills } = await import("./skills-D9WesdJJ.js").then((n) => n.o);
|
|
4489
4022
|
const skills = await discoverSkills();
|
|
4490
4023
|
return skillReportsSchema.parse(skills.map((skill) => ({
|
|
4491
4024
|
name: skill.name,
|
|
@@ -4494,7 +4027,7 @@ async function readSkillReports() {
|
|
|
4494
4027
|
}
|
|
4495
4028
|
/** Read safe configured plugin metadata for authenticated runtime diagnostics. */
|
|
4496
4029
|
async function readPlugins() {
|
|
4497
|
-
const { pluginCatalogRuntime } = await import("./catalog-runtime-
|
|
4030
|
+
const { pluginCatalogRuntime } = await import("./catalog-runtime-1BZEOR8J.js").then((n) => n.t);
|
|
4498
4031
|
return pluginsSchema.parse(pluginCatalogRuntime.getProviders().map((plugin) => ({
|
|
4499
4032
|
configKeys: plugin.manifest.configKeys ?? [],
|
|
4500
4033
|
description: plugin.manifest.description,
|
|
@@ -4506,7 +4039,7 @@ async function readPlugins() {
|
|
|
4506
4039
|
async function readRuntimeInfoReport() {
|
|
4507
4040
|
const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
|
|
4508
4041
|
import("./package-discovery-7a9YqLhE.js").then((n) => n.r),
|
|
4509
|
-
import("./catalog-runtime-
|
|
4042
|
+
import("./catalog-runtime-1BZEOR8J.js").then((n) => n.t),
|
|
4510
4043
|
readPlugins(),
|
|
4511
4044
|
readSkillReports()
|
|
4512
4045
|
]);
|
|
@@ -4523,7 +4056,7 @@ async function readRuntimeInfoReport() {
|
|
|
4523
4056
|
/** Read sanitized operational summaries contributed by plugins. */
|
|
4524
4057
|
async function readPluginOperationalReportFeed() {
|
|
4525
4058
|
const nowMs = Date.now();
|
|
4526
|
-
const { getPluginOperationalReports } = await import("./agent-hooks-
|
|
4059
|
+
const { getPluginOperationalReports } = await import("./agent-hooks-BBX9p6aG.js").then((n) => n.t);
|
|
4527
4060
|
return pluginOperationalReportFeedSchema.parse({
|
|
4528
4061
|
source: "plugins",
|
|
4529
4062
|
generatedAt: new Date(nowMs).toISOString(),
|
|
@@ -4547,7 +4080,7 @@ function createJuniorApi() {
|
|
|
4547
4080
|
app.route("/api/people", createPeopleRoutes());
|
|
4548
4081
|
app.route("/api/locations", createLocationRoutes());
|
|
4549
4082
|
app.route("/api/user-pages", createUserPageRoutes());
|
|
4550
|
-
app.route("/api/
|
|
4083
|
+
app.route("/api/automations", createAutomationRoutes());
|
|
4551
4084
|
app.route("/api/workspaces", createWorkspaceRoutes());
|
|
4552
4085
|
app.notFound(() => jsonResponse(apiErrorSchema, { error: "Resource not found." }, { status: 404 }));
|
|
4553
4086
|
app.onError((error, _context) => {
|