@sentry/junior 0.82.0 → 0.84.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-hooks-5ZRILRC3.js → agent-hooks-XDS5DIVS.js} +8 -8
- package/dist/api-reference.d.ts +1 -1
- package/dist/app.d.ts +1 -1
- package/dist/app.js +3833 -3263
- package/dist/chat/config.d.ts +1 -1
- package/dist/chat/conversations/store.d.ts +1 -1
- package/dist/chat/plugins/agent-hooks.d.ts +3 -3
- package/dist/chat/resource-events/ingest.d.ts +23 -0
- package/dist/chat/resource-events/notification.d.ts +20 -0
- package/dist/chat/resource-events/store.d.ts +79 -0
- package/dist/chat/services/pending-auth.d.ts +5 -0
- package/dist/chat/slack/client.d.ts +3 -1
- package/dist/chat/state/session-log.d.ts +4 -0
- package/dist/chat/task-execution/slack-work.d.ts +22 -0
- package/dist/chat/task-execution/state.d.ts +8 -2
- package/dist/chat/task-execution/store.d.ts +30 -8
- package/dist/chat/tools/resource-events.d.ts +19 -0
- package/dist/{chunk-PQ2U2AO3.js → chunk-AAD2D7K3.js} +11 -11
- package/dist/{chunk-ABRNFE3N.js → chunk-ALL7GG6U.js} +7 -6
- package/dist/{chunk-4XHCVBXH.js → chunk-BAPRSWNW.js} +81 -13
- package/dist/{chunk-NC6LR6U4.js → chunk-BFPOSIYV.js} +725 -169
- package/dist/{chunk-GUO4EE7L.js → chunk-CWMMGUWM.js} +1 -1
- package/dist/{chunk-YA2JCC7G.js → chunk-JUI6XIHU.js} +13 -5
- package/dist/{chunk-TR2G37II.js → chunk-KNVN4HUT.js} +8 -8
- package/dist/{chunk-X2FL5ZH5.js → chunk-MJ4E2PK7.js} +1 -1
- package/dist/{chunk-3JKTVW4R.js → chunk-MZ4DUVFD.js} +2 -2
- package/dist/{chunk-2AJ4TEKE.js → chunk-S7SC5IUJ.js} +26 -4
- package/dist/{chunk-IOBSRZK5.js → chunk-SBYMRDH7.js} +1 -1
- package/dist/{chunk-LX5GBMEP.js → chunk-ZPD5YQFZ.js} +2 -2
- package/dist/cli/chat.js +4 -4
- package/dist/cli/plugins.js +6 -6
- package/dist/cli/snapshot-warmup.js +3 -3
- package/dist/cli/upgrade.js +16 -32
- package/dist/{db-GQJKBX5W.js → db-7RRWCYF3.js} +3 -3
- package/dist/handlers/github-webhook/check-suite.d.ts +3 -0
- package/dist/handlers/github-webhook/pull-request-review.d.ts +3 -0
- package/dist/handlers/github-webhook/pull-request.d.ts +3 -0
- package/dist/handlers/github-webhook/resource.d.ts +10 -0
- package/dist/handlers/github-webhook.d.ts +15 -0
- package/dist/nitro.js +3 -3
- package/dist/reporting/conversations.d.ts +79 -2
- package/dist/reporting.d.ts +12 -4
- package/dist/reporting.js +431 -27
- package/dist/{runner-VRFJLG2M.js → runner-CJL5IKFO.js} +9 -9
- package/package.json +6 -6
package/dist/chat/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Destination } from "@sentry/junior-plugin-api";
|
|
2
2
|
import type { StoredSlackRequester } from "@/chat/requester";
|
|
3
|
-
export type ConversationSource = "api" | "internal" | "local" | "plugin" | "scheduler" | "slack";
|
|
3
|
+
export type ConversationSource = "api" | "internal" | "local" | "plugin" | "resource_event" | "scheduler" | "slack";
|
|
4
4
|
export type ConversationStatus = "awaiting_resume" | "failed" | "idle" | "pending" | "running";
|
|
5
5
|
export interface ConversationExecution {
|
|
6
6
|
lastCheckpointAtMs?: number;
|
|
@@ -19,7 +19,7 @@ export interface ToolHookResult {
|
|
|
19
19
|
export interface PluginRouteRegistration extends PluginRoute {
|
|
20
20
|
pluginName: string;
|
|
21
21
|
}
|
|
22
|
-
export interface
|
|
22
|
+
export interface PluginApiRouteRegistration {
|
|
23
23
|
app: PluginRouteApp;
|
|
24
24
|
pluginName: string;
|
|
25
25
|
}
|
|
@@ -43,8 +43,8 @@ export declare function getPluginUserPromptContributions(args: {
|
|
|
43
43
|
export declare function getPluginTools(context: ToolRuntimeContext): Record<string, ToolDefinition<any>>;
|
|
44
44
|
/** Collect route handlers exposed by plugins for app-level mounting. */
|
|
45
45
|
export declare function getPluginRoutes(): PluginRouteRegistration[];
|
|
46
|
-
/** Collect
|
|
47
|
-
export declare function
|
|
46
|
+
/** Collect authenticated product API route apps exposed by plugins. */
|
|
47
|
+
export declare function getPluginApiRoutes(): PluginApiRouteRegistration[];
|
|
48
48
|
/** Resolve the first plugin conversation URL for finalized Slack footers. */
|
|
49
49
|
export declare function getPluginSlackConversationLink(conversationId: string): SlackConversationLink | undefined;
|
|
50
50
|
/** Collect read-only operational summaries exposed by plugins. */
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { StateAdapter } from "chat";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { ConversationWorkQueue } from "@/chat/task-execution/queue";
|
|
4
|
+
declare const ingestResourceEventInputSchema: z.ZodObject<{
|
|
5
|
+
eventKey: z.ZodString;
|
|
6
|
+
eventType: z.ZodString;
|
|
7
|
+
occurredAtMs: z.ZodNumber;
|
|
8
|
+
provider: z.ZodString;
|
|
9
|
+
resourceRef: z.ZodString;
|
|
10
|
+
terminal: z.ZodOptional<z.ZodBoolean>;
|
|
11
|
+
trustedSummary: z.ZodString;
|
|
12
|
+
untrustedText: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
export type IngestResourceEventInput = z.output<typeof ingestResourceEventInputSchema>;
|
|
15
|
+
/** Match a normalized provider event and enqueue notifications into conversations. */
|
|
16
|
+
export declare function ingestResourceEvent(input: unknown, options: {
|
|
17
|
+
nowMs?: number;
|
|
18
|
+
queue: ConversationWorkQueue;
|
|
19
|
+
state?: StateAdapter;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
enqueued: number;
|
|
22
|
+
}>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ConversationWorkQueue } from "@/chat/task-execution/queue";
|
|
2
|
+
import { appendAndEnqueueInboundMessage } from "@/chat/task-execution/store";
|
|
3
|
+
import type { ResourceEventSubscription } from "@/chat/resource-events/store";
|
|
4
|
+
export interface ResourceEventNotification {
|
|
5
|
+
eventKey: string;
|
|
6
|
+
eventType: string;
|
|
7
|
+
occurredAtMs: number;
|
|
8
|
+
provider: string;
|
|
9
|
+
resourceRef: string;
|
|
10
|
+
terminal?: boolean;
|
|
11
|
+
trustedSummary: string;
|
|
12
|
+
untrustedText?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Enqueue a resource event as normal conversation mailbox input. */
|
|
15
|
+
export declare function enqueueResourceEventNotification(args: {
|
|
16
|
+
event: ResourceEventNotification;
|
|
17
|
+
queue: ConversationWorkQueue;
|
|
18
|
+
subscription: ResourceEventSubscription;
|
|
19
|
+
state?: Parameters<typeof appendAndEnqueueInboundMessage>[0]["state"];
|
|
20
|
+
}): Promise<Awaited<ReturnType<typeof appendAndEnqueueInboundMessage>>>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { StateAdapter } from "chat";
|
|
2
|
+
import { type Destination } from "@sentry/junior-plugin-api";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
declare const subscriptionSchema: z.ZodObject<{
|
|
5
|
+
conversationId: z.ZodString;
|
|
6
|
+
createdAtMs: z.ZodNumber;
|
|
7
|
+
destination: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
8
|
+
platform: z.ZodLiteral<"slack">;
|
|
9
|
+
teamId: z.ZodString;
|
|
10
|
+
channelId: z.ZodString;
|
|
11
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
12
|
+
platform: z.ZodLiteral<"local">;
|
|
13
|
+
conversationId: z.ZodString;
|
|
14
|
+
}, z.core.$strict>], "platform">;
|
|
15
|
+
events: z.ZodArray<z.ZodString>;
|
|
16
|
+
expiresAtMs: z.ZodNumber;
|
|
17
|
+
id: z.ZodString;
|
|
18
|
+
intent: z.ZodString;
|
|
19
|
+
label: z.ZodString;
|
|
20
|
+
provider: z.ZodString;
|
|
21
|
+
resourceRef: z.ZodString;
|
|
22
|
+
resourceType: z.ZodString;
|
|
23
|
+
status: z.ZodEnum<{
|
|
24
|
+
completed: "completed";
|
|
25
|
+
active: "active";
|
|
26
|
+
cancelled: "cancelled";
|
|
27
|
+
}>;
|
|
28
|
+
updatedAtMs: z.ZodNumber;
|
|
29
|
+
}, z.core.$strict>;
|
|
30
|
+
export type ResourceEventSubscription = z.output<typeof subscriptionSchema>;
|
|
31
|
+
export interface CreateResourceEventSubscriptionInput {
|
|
32
|
+
conversationId: string;
|
|
33
|
+
destination: Destination;
|
|
34
|
+
events: string[];
|
|
35
|
+
expiresAtMs: number;
|
|
36
|
+
intent: string;
|
|
37
|
+
label: string;
|
|
38
|
+
provider: string;
|
|
39
|
+
resourceRef: string;
|
|
40
|
+
resourceType: string;
|
|
41
|
+
}
|
|
42
|
+
/** Create or replace the active subscription for one conversation/resource/event set. */
|
|
43
|
+
export declare function createResourceEventSubscription(input: CreateResourceEventSubscriptionInput, options?: {
|
|
44
|
+
nowMs?: number;
|
|
45
|
+
state?: StateAdapter;
|
|
46
|
+
}): Promise<ResourceEventSubscription>;
|
|
47
|
+
/** List active subscriptions bound to one conversation. */
|
|
48
|
+
export declare function listResourceEventSubscriptions(input: {
|
|
49
|
+
conversationId: string;
|
|
50
|
+
nowMs?: number;
|
|
51
|
+
state?: StateAdapter;
|
|
52
|
+
}): Promise<ResourceEventSubscription[]>;
|
|
53
|
+
/** Cancel a current-conversation subscription and remove it from match indexes. */
|
|
54
|
+
export declare function cancelResourceEventSubscription(input: {
|
|
55
|
+
conversationId: string;
|
|
56
|
+
id: string;
|
|
57
|
+
nowMs?: number;
|
|
58
|
+
state?: StateAdapter;
|
|
59
|
+
}): Promise<ResourceEventSubscription | undefined>;
|
|
60
|
+
/** Find active subscriptions interested in a normalized provider event. */
|
|
61
|
+
export declare function findMatchingResourceEventSubscriptions(input: {
|
|
62
|
+
eventType: string;
|
|
63
|
+
nowMs?: number;
|
|
64
|
+
provider: string;
|
|
65
|
+
resourceRef: string;
|
|
66
|
+
state?: StateAdapter;
|
|
67
|
+
}): Promise<ResourceEventSubscription[]>;
|
|
68
|
+
/** Recheck and deliver a matched subscription while holding its status lock. */
|
|
69
|
+
export declare function deliverResourceEventSubscription(input: {
|
|
70
|
+
deliver: (subscription: ResourceEventSubscription) => Promise<boolean>;
|
|
71
|
+
eventType: string;
|
|
72
|
+
nowMs?: number;
|
|
73
|
+
provider: string;
|
|
74
|
+
resourceRef: string;
|
|
75
|
+
state?: StateAdapter;
|
|
76
|
+
subscription: ResourceEventSubscription;
|
|
77
|
+
terminal?: boolean;
|
|
78
|
+
}): Promise<boolean>;
|
|
79
|
+
export {};
|
|
@@ -28,4 +28,9 @@ export declare function applyPendingAuthUpdate(args: {
|
|
|
28
28
|
conversationId: string | undefined;
|
|
29
29
|
nextPendingAuth: ConversationPendingAuthState;
|
|
30
30
|
}): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Decide whether an auth callback still belongs to the latest real human
|
|
33
|
+
* request, ignoring bot-authored and passive bystander rows while treating
|
|
34
|
+
* failed human turns and opt-outs as newer freshness blockers.
|
|
35
|
+
*/
|
|
31
36
|
export declare function isPendingAuthLatestRequest(conversation: ThreadConversationState, pendingAuth: ConversationPendingAuthState): boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WebClient } from "@slack/web-api";
|
|
2
|
-
export type SlackActionErrorCode = "missing_token" | "missing_scope" | "rate_limited" | "feature_unavailable" | "canvas_creation_failed" | "canvas_editing_failed" | "invalid_arguments" | "not_found" | "not_in_channel" | "already_reacted" | "no_reaction" | "internal_error";
|
|
2
|
+
export type SlackActionErrorCode = "missing_token" | "missing_scope" | "rate_limited" | "feature_unavailable" | "canvas_creation_failed" | "canvas_editing_failed" | "invalid_arguments" | "not_found" | "not_in_channel" | "already_reacted" | "no_reaction" | "read_only_channel" | "internal_error";
|
|
3
3
|
export declare class SlackActionError extends Error {
|
|
4
4
|
code: SlackActionErrorCode;
|
|
5
5
|
apiError?: string;
|
|
@@ -28,6 +28,8 @@ export declare class SlackActionError extends Error {
|
|
|
28
28
|
interface SlackRetryContext {
|
|
29
29
|
action?: string;
|
|
30
30
|
attributes?: Record<string, string | number | boolean>;
|
|
31
|
+
/** Extra attributes forwarded onto the per-attempt Sentry span. */
|
|
32
|
+
spanAttributes?: Record<string, string | number | boolean>;
|
|
31
33
|
}
|
|
32
34
|
/** Extract a header value by case-insensitive name from a raw headers object. */
|
|
33
35
|
export declare function getHeaderString(headers: unknown, name: string): string | undefined;
|
|
@@ -110,6 +110,8 @@ declare const sessionLogEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
110
110
|
subagentInvocationId: z.ZodString;
|
|
111
111
|
outcome: z.ZodUnion<readonly [z.ZodLiteral<"success">, z.ZodLiteral<"error">, z.ZodLiteral<"aborted">]>;
|
|
112
112
|
errorCode: z.ZodOptional<z.ZodString>;
|
|
113
|
+
transcriptEndMessageIndex: z.ZodOptional<z.ZodNumber>;
|
|
114
|
+
transcriptStartMessageIndex: z.ZodOptional<z.ZodNumber>;
|
|
113
115
|
createdAtMs: z.ZodNumber;
|
|
114
116
|
}, z.core.$strip>], "type">;
|
|
115
117
|
/** Requester identity stored with turn-start messages for durable continuation. */
|
|
@@ -224,6 +226,8 @@ export declare function recordSubagentEnded(args: Scope & {
|
|
|
224
226
|
sessionId?: string;
|
|
225
227
|
store?: SessionLogStore;
|
|
226
228
|
subagentInvocationId: string;
|
|
229
|
+
transcriptEndMessageIndex?: number;
|
|
230
|
+
transcriptStartMessageIndex?: number;
|
|
227
231
|
ttlMs: number;
|
|
228
232
|
}): Promise<void>;
|
|
229
233
|
/**
|
|
@@ -15,6 +15,25 @@ export interface SlackConversationMessageMetadata {
|
|
|
15
15
|
route: SlackConversationRoute;
|
|
16
16
|
thread: SerializedThread;
|
|
17
17
|
}
|
|
18
|
+
interface SlackResourceEventInboundInput {
|
|
19
|
+
event: {
|
|
20
|
+
eventKey: string;
|
|
21
|
+
eventType: string;
|
|
22
|
+
occurredAtMs: number;
|
|
23
|
+
provider: string;
|
|
24
|
+
resourceRef: string;
|
|
25
|
+
};
|
|
26
|
+
subscription: {
|
|
27
|
+
conversationId: string;
|
|
28
|
+
destination: {
|
|
29
|
+
channelId: string;
|
|
30
|
+
platform: "slack";
|
|
31
|
+
teamId: string;
|
|
32
|
+
};
|
|
33
|
+
id: string;
|
|
34
|
+
};
|
|
35
|
+
text: string;
|
|
36
|
+
}
|
|
18
37
|
export interface CreateSlackConversationWorkerOptions {
|
|
19
38
|
getSlackAdapter: () => SlackAdapter;
|
|
20
39
|
lookupSlackUser?: (teamId: string, userId: string) => Promise<SlackRequesterProfile | null | undefined>;
|
|
@@ -23,6 +42,8 @@ export interface CreateSlackConversationWorkerOptions {
|
|
|
23
42
|
runtime: Pick<SlackTurnRuntime<unknown>, "handleNewMention" | "handleSubscribedMessage">;
|
|
24
43
|
state?: StateAdapter;
|
|
25
44
|
}
|
|
45
|
+
/** Create a Slack mailbox record for a subscribed resource-event notification. */
|
|
46
|
+
export declare function createSlackResourceEventInboundMessage(input: SlackResourceEventInboundInput): InboundMessage;
|
|
26
47
|
/** Build the worker run function for queued Slack conversation work. */
|
|
27
48
|
export declare function createSlackConversationWorker(options: CreateSlackConversationWorkerOptions): (context: ConversationWorkerContext) => Promise<ConversationWorkerResult>;
|
|
28
49
|
/** Serialize a Slack message into the generic durable conversation mailbox. */
|
|
@@ -34,3 +55,4 @@ export declare function buildSlackInboundMessage(args: {
|
|
|
34
55
|
route: SlackConversationRoute;
|
|
35
56
|
thread: ThreadImpl;
|
|
36
57
|
}): InboundMessage;
|
|
58
|
+
export {};
|
|
@@ -6,7 +6,7 @@ export declare const CONVERSATION_ACTIVE_INDEX_KEY = "junior:conversation:active
|
|
|
6
6
|
export declare const CONVERSATION_WORK_LEASE_TTL_MS = 90000;
|
|
7
7
|
export declare const CONVERSATION_WORK_CHECK_IN_INTERVAL_MS = 15000;
|
|
8
8
|
export declare const CONVERSATION_WORK_STALE_ENQUEUE_MS = 60000;
|
|
9
|
-
export type Source = "api" | "internal" | "local" | "plugin" | "scheduler" | "slack";
|
|
9
|
+
export type Source = "api" | "internal" | "local" | "plugin" | "resource_event" | "scheduler" | "slack";
|
|
10
10
|
export type ExecutionStatus = "awaiting_resume" | "failed" | "idle" | "pending" | "running";
|
|
11
11
|
export interface AgentInput {
|
|
12
12
|
attachments?: unknown[];
|
|
@@ -127,7 +127,7 @@ export declare function recordConversationActivity(args: {
|
|
|
127
127
|
state?: StateAdapter;
|
|
128
128
|
title?: string;
|
|
129
129
|
}): Promise<void>;
|
|
130
|
-
/** Store task-execution metadata for
|
|
130
|
+
/** Store task-execution metadata for legacy SQL import. */
|
|
131
131
|
export declare function recordConversationExecution(args: {
|
|
132
132
|
channelName?: string;
|
|
133
133
|
conversationId: string;
|
|
@@ -153,6 +153,12 @@ export declare function markConversationWorkEnqueued(args: {
|
|
|
153
153
|
nowMs?: number;
|
|
154
154
|
state?: StateAdapter;
|
|
155
155
|
}): Promise<void>;
|
|
156
|
+
/** Clear a wake marker after its queue delivery finds no runnable work. */
|
|
157
|
+
export declare function clearConsumedConversationWake(args: {
|
|
158
|
+
conversationId: string;
|
|
159
|
+
nowMs?: number;
|
|
160
|
+
state?: StateAdapter;
|
|
161
|
+
}): Promise<boolean>;
|
|
156
162
|
/** Try to acquire the durable execution lease for one conversation. */
|
|
157
163
|
export declare function startConversationWork(args: {
|
|
158
164
|
conversationId: string;
|
|
@@ -4,6 +4,12 @@ import type { ConversationWorkQueue } from "./queue";
|
|
|
4
4
|
import * as workState from "./state";
|
|
5
5
|
export { CONVERSATION_ACTIVE_INDEX_KEY, CONVERSATION_BY_ACTIVITY_INDEX_KEY, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS, CONVERSATION_WORK_LEASE_TTL_MS, CONVERSATION_WORK_STALE_ENQUEUE_MS, type AgentInput, type AppendAndEnqueueInboundMessageResult, type AppendInboundMessageResult, type Conversation, type ConversationExecution, type ConversationWorkLease, type ConversationWorkState, type ExecutionStatus, type InboundMessage, type Lease, type RequestConversationWorkResult, type Source, type StartConversationWorkAcquired, type StartConversationWorkActive, type StartConversationWorkNoWork, type StartConversationWorkResult, } from "@/chat/task-execution/state";
|
|
6
6
|
import type { AppendAndEnqueueInboundMessageResult, Conversation, InboundMessage } from "@/chat/task-execution/state";
|
|
7
|
+
export type EnsureConversationWakeResult = {
|
|
8
|
+
queueMessageId?: string;
|
|
9
|
+
status: "enqueued";
|
|
10
|
+
} | {
|
|
11
|
+
status: "already_enqueued" | "no_work";
|
|
12
|
+
};
|
|
7
13
|
/** Return a persisted conversation record, if one exists. */
|
|
8
14
|
export declare function getConversation(args: {
|
|
9
15
|
conversationId: string;
|
|
@@ -18,6 +24,22 @@ export declare function getConversationWorkState(args: {
|
|
|
18
24
|
export declare function countPendingConversationMessages(conversation: Conversation): number;
|
|
19
25
|
/** Return whether a conversation has pending or resumable execution work. */
|
|
20
26
|
export declare function hasRunnableConversationWork(conversation: Conversation): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Ensure runnable conversation work has one accepted queue wake-up nudge.
|
|
29
|
+
*
|
|
30
|
+
* Ordinary wakes coalesce on a recent accepted marker. Replacement is only for
|
|
31
|
+
* consumed or known-stale deliveries where another queue nudge must exist.
|
|
32
|
+
*/
|
|
33
|
+
export declare function ensureConversationWake(args: {
|
|
34
|
+
conversationId: string;
|
|
35
|
+
conversationStore?: ConversationStore;
|
|
36
|
+
delayMs?: number;
|
|
37
|
+
idempotencyKey: string;
|
|
38
|
+
nowMs?: number;
|
|
39
|
+
queue: ConversationWorkQueue;
|
|
40
|
+
replaceExistingWake?: true;
|
|
41
|
+
state?: StateAdapter;
|
|
42
|
+
}): Promise<EnsureConversationWakeResult>;
|
|
21
43
|
/** Persist one inbound message idempotently in its conversation mailbox. */
|
|
22
44
|
export declare function appendInboundMessage(args: {
|
|
23
45
|
message: InboundMessage;
|
|
@@ -25,7 +47,7 @@ export declare function appendInboundMessage(args: {
|
|
|
25
47
|
nowMs?: number;
|
|
26
48
|
state?: StateAdapter;
|
|
27
49
|
}): Promise<workState.AppendInboundMessageResult>;
|
|
28
|
-
/** Persist inbound work and
|
|
50
|
+
/** Persist inbound work and ensure a worker wake-up. */
|
|
29
51
|
export declare function appendAndEnqueueInboundMessage(args: {
|
|
30
52
|
message: InboundMessage;
|
|
31
53
|
conversationStore?: ConversationStore;
|
|
@@ -33,6 +55,13 @@ export declare function appendAndEnqueueInboundMessage(args: {
|
|
|
33
55
|
queue: ConversationWorkQueue;
|
|
34
56
|
state?: StateAdapter;
|
|
35
57
|
}): Promise<AppendAndEnqueueInboundMessageResult>;
|
|
58
|
+
/** Clear an accepted wake marker after its delivery finds no runnable work. */
|
|
59
|
+
export declare function clearConsumedConversationWake(args: {
|
|
60
|
+
conversationId: string;
|
|
61
|
+
conversationStore?: ConversationStore;
|
|
62
|
+
nowMs?: number;
|
|
63
|
+
state?: StateAdapter;
|
|
64
|
+
}): Promise<boolean>;
|
|
36
65
|
/** Mark a conversation runnable when there is no new mailbox message. */
|
|
37
66
|
export declare function requestConversationWork(args: {
|
|
38
67
|
conversationId: string;
|
|
@@ -46,13 +75,6 @@ export declare function recordConversationActivity(args: Parameters<Conversation
|
|
|
46
75
|
conversationStore?: ConversationStore;
|
|
47
76
|
state?: StateAdapter;
|
|
48
77
|
}): Promise<void>;
|
|
49
|
-
/** Record that a wake-up nudge was accepted for the conversation. */
|
|
50
|
-
export declare function markConversationWorkEnqueued(args: {
|
|
51
|
-
conversationId: string;
|
|
52
|
-
conversationStore?: ConversationStore;
|
|
53
|
-
nowMs?: number;
|
|
54
|
-
state?: StateAdapter;
|
|
55
|
-
}): Promise<void>;
|
|
56
78
|
/** Try to acquire the durable execution lease for one conversation. */
|
|
57
79
|
export declare function startConversationWork(args: {
|
|
58
80
|
conversationId: string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ToolRuntimeContext } from "@/chat/tools/types";
|
|
2
|
+
/** Return whether the current runtime can safely manage conversation subscriptions. */
|
|
3
|
+
export declare function canUseResourceEventSubscriptionTools(context: ToolRuntimeContext): boolean;
|
|
4
|
+
/** Create the tool that subscribes the current conversation to resource events. */
|
|
5
|
+
export declare function createSubscribeToResourceEventsTool(context: ToolRuntimeContext): import("@/chat/tools/definition").ToolDefinition<import("@sinclair/typebox").TObject<{
|
|
6
|
+
resourceRef: import("@sinclair/typebox").TString;
|
|
7
|
+
provider: import("@sinclair/typebox").TString;
|
|
8
|
+
resourceType: import("@sinclair/typebox").TString;
|
|
9
|
+
label: import("@sinclair/typebox").TString;
|
|
10
|
+
events: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
|
|
11
|
+
intent: import("@sinclair/typebox").TString;
|
|
12
|
+
ttlMs: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
13
|
+
}>>;
|
|
14
|
+
/** Create the tool that lists active resource subscriptions for this conversation. */
|
|
15
|
+
export declare function createListResourceEventSubscriptionsTool(context: ToolRuntimeContext): import("@/chat/tools/definition").ToolDefinition<import("@sinclair/typebox").TObject<{}>>;
|
|
16
|
+
/** Create the tool that cancels a current-conversation resource subscription. */
|
|
17
|
+
export declare function createCancelResourceEventSubscriptionTool(context: ToolRuntimeContext): import("@/chat/tools/definition").ToolDefinition<import("@sinclair/typebox").TObject<{
|
|
18
|
+
subscriptionId: import("@sinclair/typebox").TString;
|
|
19
|
+
}>>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseDestination,
|
|
3
3
|
sameDestination
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BAPRSWNW.js";
|
|
5
5
|
import {
|
|
6
6
|
getChatConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ALL7GG6U.js";
|
|
8
8
|
import {
|
|
9
9
|
parseStoredSlackRequester
|
|
10
10
|
} from "./chunk-ZPCGQRFJ.js";
|
|
@@ -371,7 +371,7 @@ function requiredMsFromDate(value) {
|
|
|
371
371
|
return ms;
|
|
372
372
|
}
|
|
373
373
|
function sourceFromValue(value) {
|
|
374
|
-
if (value === "api" || value === "internal" || value === "local" || value === "plugin" || value === "scheduler" || value === "slack") {
|
|
374
|
+
if (value === "api" || value === "internal" || value === "local" || value === "plugin" || value === "resource_event" || value === "scheduler" || value === "slack") {
|
|
375
375
|
return value;
|
|
376
376
|
}
|
|
377
377
|
return void 0;
|
|
@@ -408,6 +408,14 @@ function systemIdentityFromSource(source) {
|
|
|
408
408
|
displayName: "Local CLI"
|
|
409
409
|
};
|
|
410
410
|
}
|
|
411
|
+
if (source === "resource_event") {
|
|
412
|
+
return {
|
|
413
|
+
kind: "system",
|
|
414
|
+
provider: "junior",
|
|
415
|
+
providerSubjectId: "resource-event",
|
|
416
|
+
displayName: "Resource Event"
|
|
417
|
+
};
|
|
418
|
+
}
|
|
411
419
|
return void 0;
|
|
412
420
|
}
|
|
413
421
|
function actorIdentityForConversation(conversation) {
|
|
@@ -1033,14 +1041,6 @@ function createDb(args) {
|
|
|
1033
1041
|
}
|
|
1034
1042
|
function getSqlExecutor() {
|
|
1035
1043
|
const { sql: sql3 } = getChatConfig();
|
|
1036
|
-
if (!sql3.databaseUrl) {
|
|
1037
|
-
if (current) {
|
|
1038
|
-
const previous = current;
|
|
1039
|
-
current = void 0;
|
|
1040
|
-
void previous.db.close().catch(() => void 0);
|
|
1041
|
-
}
|
|
1042
|
-
throw new Error("DATABASE_URL or JUNIOR_DATABASE_URL is required");
|
|
1043
|
-
}
|
|
1044
1044
|
if (current?.databaseUrl !== sql3.databaseUrl || current.driver !== sql3.driver) {
|
|
1045
1045
|
if (current) {
|
|
1046
1046
|
const previous = current;
|
|
@@ -757,13 +757,14 @@ function readBotConfig(env) {
|
|
|
757
757
|
advisor: readAdvisorConfig(env)
|
|
758
758
|
};
|
|
759
759
|
}
|
|
760
|
-
function
|
|
761
|
-
|
|
762
|
-
}
|
|
763
|
-
function isLocalDatabaseUrl(databaseUrl) {
|
|
760
|
+
function readDatabaseUrl(env) {
|
|
761
|
+
const databaseUrl = toOptionalTrimmed(env.DATABASE_URL);
|
|
764
762
|
if (!databaseUrl) {
|
|
765
|
-
|
|
763
|
+
throw new Error("DATABASE_URL is required");
|
|
766
764
|
}
|
|
765
|
+
return databaseUrl;
|
|
766
|
+
}
|
|
767
|
+
function isLocalDatabaseUrl(databaseUrl) {
|
|
767
768
|
try {
|
|
768
769
|
const { hostname } = new URL(databaseUrl);
|
|
769
770
|
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
@@ -785,7 +786,7 @@ function readSqlDriver(env, databaseUrl) {
|
|
|
785
786
|
throw new Error("JUNIOR_DATABASE_DRIVER must be postgres or neon");
|
|
786
787
|
}
|
|
787
788
|
function readChatConfig(env = process.env) {
|
|
788
|
-
const databaseUrl =
|
|
789
|
+
const databaseUrl = readDatabaseUrl(env);
|
|
789
790
|
return {
|
|
790
791
|
bot: readBotConfig(env),
|
|
791
792
|
functionMaxDurationSeconds: resolveFunctionMaxDurationSeconds(env),
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSlackBotToken
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ALL7GG6U.js";
|
|
4
4
|
import {
|
|
5
5
|
isSlackConversationId,
|
|
6
6
|
isSlackTeamId,
|
|
7
|
-
logWarn
|
|
7
|
+
logWarn,
|
|
8
|
+
setSpanAttributes,
|
|
9
|
+
setSpanStatus,
|
|
10
|
+
withSpan
|
|
8
11
|
} from "./chunk-KF7522P3.js";
|
|
9
12
|
|
|
10
13
|
// src/chat/slack/client.ts
|
|
@@ -142,6 +145,9 @@ function mapSlackError(error) {
|
|
|
142
145
|
if (apiError === "not_in_channel") {
|
|
143
146
|
return new SlackActionError(message, "not_in_channel", baseOptions);
|
|
144
147
|
}
|
|
148
|
+
if (apiError === "restricted_action_read_only_channel") {
|
|
149
|
+
return new SlackActionError(message, "read_only_channel", baseOptions);
|
|
150
|
+
}
|
|
145
151
|
if (apiError === "already_reacted") {
|
|
146
152
|
return new SlackActionError(message, "already_reacted", baseOptions);
|
|
147
153
|
}
|
|
@@ -182,15 +188,56 @@ function sleep(ms) {
|
|
|
182
188
|
}
|
|
183
189
|
async function withSlackRetries(task, maxAttempts = 3, context = {}) {
|
|
184
190
|
let attempt = 0;
|
|
191
|
+
const action = context.action ?? "unknown";
|
|
185
192
|
while (attempt < maxAttempts) {
|
|
186
193
|
attempt += 1;
|
|
194
|
+
const attemptNumber = attempt;
|
|
187
195
|
try {
|
|
188
|
-
return await
|
|
196
|
+
return await withSpan(
|
|
197
|
+
`POST slack.com/api/${action}`,
|
|
198
|
+
"http.client",
|
|
199
|
+
{},
|
|
200
|
+
async () => {
|
|
201
|
+
try {
|
|
202
|
+
return await task();
|
|
203
|
+
} catch (error) {
|
|
204
|
+
const mapped = mapSlackError(error);
|
|
205
|
+
const errorAttrs = {
|
|
206
|
+
"error.type": mapped.code
|
|
207
|
+
};
|
|
208
|
+
if (mapped.apiError) {
|
|
209
|
+
errorAttrs["app.slack.api_error_code"] = mapped.apiError;
|
|
210
|
+
}
|
|
211
|
+
if (mapped.code === "rate_limited") {
|
|
212
|
+
errorAttrs["http.response.status_code"] = 429;
|
|
213
|
+
if (mapped.retryAfterSeconds) {
|
|
214
|
+
errorAttrs["app.slack.retry_after_ms"] = mapped.retryAfterSeconds * 1e3;
|
|
215
|
+
}
|
|
216
|
+
} else if (mapped.statusCode != null) {
|
|
217
|
+
errorAttrs["http.response.status_code"] = mapped.statusCode;
|
|
218
|
+
}
|
|
219
|
+
setSpanAttributes(errorAttrs);
|
|
220
|
+
setSpanStatus("error");
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"http.request.method": "POST",
|
|
226
|
+
"server.address": "slack.com",
|
|
227
|
+
"url.scheme": "https",
|
|
228
|
+
"url.path": `/api/${action}`,
|
|
229
|
+
"app.slack.method": action,
|
|
230
|
+
"app.retry.max_attempts": maxAttempts,
|
|
231
|
+
...attemptNumber > 1 ? { "http.resend_count": attemptNumber - 1 } : {},
|
|
232
|
+
...context.attributes ?? {},
|
|
233
|
+
...context.spanAttributes ?? {}
|
|
234
|
+
}
|
|
235
|
+
);
|
|
189
236
|
} catch (error) {
|
|
190
237
|
const mapped = mapSlackError(error);
|
|
191
238
|
const isRetryable = mapped.code === "rate_limited";
|
|
192
239
|
const baseLogAttributes = {
|
|
193
|
-
"app.slack.action":
|
|
240
|
+
"app.slack.action": action,
|
|
194
241
|
"app.slack.error_code": mapped.code,
|
|
195
242
|
...mapped.apiError ? { "app.slack.api_error": mapped.apiError } : {},
|
|
196
243
|
...mapped.detail ? { "app.slack.detail": mapped.detail } : {},
|
|
@@ -253,7 +300,12 @@ async function getFilePermalink(fileId) {
|
|
|
253
300
|
const response = await withSlackRetries(
|
|
254
301
|
() => client2.files.info({
|
|
255
302
|
file: fileId
|
|
256
|
-
})
|
|
303
|
+
}),
|
|
304
|
+
3,
|
|
305
|
+
{
|
|
306
|
+
action: "files.info",
|
|
307
|
+
spanAttributes: { "app.slack.file_id": fileId }
|
|
308
|
+
}
|
|
257
309
|
);
|
|
258
310
|
return response.file?.permalink;
|
|
259
311
|
}
|
|
@@ -265,15 +317,31 @@ async function downloadPrivateSlackFile(url) {
|
|
|
265
317
|
"missing_token"
|
|
266
318
|
);
|
|
267
319
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
320
|
+
return withSpan(
|
|
321
|
+
"GET files.slack.com",
|
|
322
|
+
"http.client",
|
|
323
|
+
{},
|
|
324
|
+
async () => {
|
|
325
|
+
const response = await fetch(url, {
|
|
326
|
+
headers: {
|
|
327
|
+
Authorization: `Bearer ${token}`
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
setSpanAttributes({ "http.response.status_code": response.status });
|
|
331
|
+
if (!response.ok) {
|
|
332
|
+
setSpanAttributes({ "error.type": String(response.status) });
|
|
333
|
+
setSpanStatus("error");
|
|
334
|
+
throw new Error(`Slack file download failed: ${response.status}`);
|
|
335
|
+
}
|
|
336
|
+
return Buffer.from(await response.arrayBuffer());
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"http.request.method": "GET",
|
|
340
|
+
"server.address": "files.slack.com",
|
|
341
|
+
"url.scheme": "https",
|
|
342
|
+
"app.slack.method": "files.download"
|
|
271
343
|
}
|
|
272
|
-
|
|
273
|
-
if (!response.ok) {
|
|
274
|
-
throw new Error(`Slack file download failed: ${response.status}`);
|
|
275
|
-
}
|
|
276
|
-
return Buffer.from(await response.arrayBuffer());
|
|
344
|
+
);
|
|
277
345
|
}
|
|
278
346
|
|
|
279
347
|
// src/chat/destination.ts
|