managed-deepagents 0.10.0-dev.16 → 0.10.0-dev.18
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/README.md +809 -71
- package/dist/channels/actor.d.ts +43 -0
- package/dist/channels/actor.d.ts.map +1 -0
- package/dist/channels/actor.js +2 -0
- package/dist/channels/actor.js.map +1 -0
- package/dist/channels/assistant-text.d.ts +29 -0
- package/dist/channels/assistant-text.d.ts.map +1 -1
- package/dist/channels/assistant-text.js +72 -1
- package/dist/channels/assistant-text.js.map +1 -1
- package/dist/channels/binding.d.ts +20 -1
- package/dist/channels/binding.d.ts.map +1 -1
- package/dist/channels/binding.js +39 -10
- package/dist/channels/binding.js.map +1 -1
- package/dist/channels/delivery.d.ts +21 -0
- package/dist/channels/delivery.d.ts.map +1 -0
- package/dist/channels/delivery.js +196 -0
- package/dist/channels/delivery.js.map +1 -0
- package/dist/channels/github/connections.d.ts.map +1 -1
- package/dist/channels/github/connections.js +2 -2
- package/dist/channels/github/connections.js.map +1 -1
- package/dist/channels/github/index.d.ts +1 -8
- package/dist/channels/github/index.d.ts.map +1 -1
- package/dist/channels/github/index.js +46 -139
- package/dist/channels/github/index.js.map +1 -1
- package/dist/channels/github/mentions.d.ts +12 -0
- package/dist/channels/github/mentions.d.ts.map +1 -0
- package/dist/channels/github/mentions.js +185 -0
- package/dist/channels/github/mentions.js.map +1 -0
- package/dist/channels/github/schema.d.ts +271 -0
- package/dist/channels/github/schema.d.ts.map +1 -1
- package/dist/channels/github/schema.js +34 -3
- package/dist/channels/github/schema.js.map +1 -1
- package/dist/channels/github/types.d.ts +57 -4
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/http/endpoint.d.ts +95 -0
- package/dist/channels/http/endpoint.d.ts.map +1 -0
- package/dist/channels/http/endpoint.js +276 -0
- package/dist/channels/http/endpoint.js.map +1 -0
- package/dist/channels/http/http.d.ts.map +1 -1
- package/dist/channels/http/http.js +322 -86
- package/dist/channels/http/http.js.map +1 -1
- package/dist/channels/http/index.d.ts +1 -1
- package/dist/channels/http/index.d.ts.map +1 -1
- package/dist/channels/http/index.js +10 -16
- package/dist/channels/http/index.js.map +1 -1
- package/dist/channels/http/ingress.d.ts +40 -5
- package/dist/channels/http/ingress.d.ts.map +1 -1
- package/dist/channels/http/ingress.js +63 -40
- package/dist/channels/http/ingress.js.map +1 -1
- package/dist/channels/http/provider.d.ts +119 -0
- package/dist/channels/http/provider.d.ts.map +1 -0
- package/dist/channels/http/provider.js +227 -0
- package/dist/channels/http/provider.js.map +1 -0
- package/dist/channels/http/studio.d.ts +24 -0
- package/dist/channels/http/studio.d.ts.map +1 -0
- package/dist/channels/http/studio.js +95 -0
- package/dist/channels/http/studio.js.map +1 -0
- package/dist/channels/http/types.d.ts +107 -7
- package/dist/channels/http/types.d.ts.map +1 -1
- package/dist/channels/linear/connections.d.ts +11 -0
- package/dist/channels/linear/connections.d.ts.map +1 -0
- package/dist/channels/linear/connections.js +14 -0
- package/dist/channels/linear/connections.js.map +1 -0
- package/dist/channels/linear/graphql.d.ts +57 -0
- package/dist/channels/linear/graphql.d.ts.map +1 -0
- package/dist/channels/linear/graphql.js +167 -0
- package/dist/channels/linear/graphql.js.map +1 -0
- package/dist/channels/linear/index.d.ts +9 -0
- package/dist/channels/linear/index.d.ts.map +1 -0
- package/dist/channels/linear/index.js +489 -0
- package/dist/channels/linear/index.js.map +1 -0
- package/dist/channels/linear/schema.d.ts +1050 -0
- package/dist/channels/linear/schema.d.ts.map +1 -0
- package/dist/channels/linear/schema.js +255 -0
- package/dist/channels/linear/schema.js.map +1 -0
- package/dist/channels/linear/types.d.ts +162 -0
- package/dist/channels/linear/types.d.ts.map +1 -0
- package/dist/channels/linear/types.js +2 -0
- package/dist/channels/linear/types.js.map +1 -0
- package/dist/channels/message-schema.d.ts +2 -0
- package/dist/channels/message-schema.d.ts.map +1 -1
- package/dist/channels/message-schema.js +4 -0
- package/dist/channels/message-schema.js.map +1 -1
- package/dist/channels/messaging-registry.d.ts +16 -0
- package/dist/channels/messaging-registry.d.ts.map +1 -1
- package/dist/channels/messaging-registry.js +29 -0
- package/dist/channels/messaging-registry.js.map +1 -1
- package/dist/channels/reserved-connections.d.ts +2 -2
- package/dist/channels/reserved-connections.d.ts.map +1 -1
- package/dist/channels/reserved-connections.js +36 -9
- package/dist/channels/reserved-connections.js.map +1 -1
- package/dist/channels/run-input.d.ts +35 -2
- package/dist/channels/run-input.d.ts.map +1 -1
- package/dist/channels/run-input.js +74 -14
- package/dist/channels/run-input.js.map +1 -1
- package/dist/channels/runtime.d.ts +1 -1
- package/dist/channels/runtime.d.ts.map +1 -1
- package/dist/channels/runtime.js +1 -1
- package/dist/channels/runtime.js.map +1 -1
- package/dist/channels/schedule-delivery.d.ts +1 -1
- package/dist/channels/schedule-delivery.d.ts.map +1 -1
- package/dist/channels/schedule-delivery.js +9 -20
- package/dist/channels/schedule-delivery.js.map +1 -1
- package/dist/channels/slack/api.d.ts +2 -0
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +6 -0
- package/dist/channels/slack/api.js.map +1 -1
- package/dist/channels/slack/connections.d.ts +6 -0
- package/dist/channels/slack/connections.d.ts.map +1 -0
- package/dist/channels/slack/connections.js +6 -0
- package/dist/channels/slack/connections.js.map +1 -0
- package/dist/channels/slack/events.d.ts +272 -0
- package/dist/channels/slack/events.d.ts.map +1 -0
- package/dist/channels/slack/events.js +235 -0
- package/dist/channels/slack/events.js.map +1 -0
- package/dist/channels/slack/files.d.ts.map +1 -1
- package/dist/channels/slack/files.js +2 -3
- package/dist/channels/slack/files.js.map +1 -1
- package/dist/channels/slack/index.d.ts +76 -2
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +177 -6
- package/dist/channels/slack/index.js.map +1 -1
- package/dist/channels/slack/message-content.d.ts.map +1 -1
- package/dist/channels/slack/message-content.js +23 -28
- package/dist/channels/slack/message-content.js.map +1 -1
- package/dist/channels/slack/status.d.ts +76 -0
- package/dist/channels/slack/status.d.ts.map +1 -0
- package/dist/channels/slack/status.js +427 -0
- package/dist/channels/slack/status.js.map +1 -0
- package/dist/channels/source-providers.js +1 -1
- package/dist/channels/source-providers.js.map +1 -1
- package/dist/channels/trigger/client.d.ts.map +1 -1
- package/dist/channels/trigger/client.js +4 -9
- package/dist/channels/trigger/client.js.map +1 -1
- package/dist/channels/trigger/http.d.ts +114 -3
- package/dist/channels/trigger/http.d.ts.map +1 -1
- package/dist/channels/trigger/http.js +310 -71
- package/dist/channels/trigger/http.js.map +1 -1
- package/dist/channels/trigger/ingress.d.ts +38 -1
- package/dist/channels/trigger/ingress.d.ts.map +1 -1
- package/dist/channels/trigger/ingress.js +47 -1
- package/dist/channels/trigger/ingress.js.map +1 -1
- package/dist/channels/trigger/types.d.ts +82 -3
- package/dist/channels/trigger/types.d.ts.map +1 -1
- package/dist/channels/trigger/types.js +36 -0
- package/dist/channels/trigger/types.js.map +1 -1
- package/dist/channels/types.d.ts +19 -3
- package/dist/channels/types.d.ts.map +1 -1
- package/dist/channels/types.js +2 -0
- package/dist/channels/types.js.map +1 -1
- package/dist/connections.d.ts +5 -0
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +14 -1
- package/dist/connections.js.map +1 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/ingress/runs.d.ts +32 -1
- package/dist/ingress/runs.d.ts.map +1 -1
- package/dist/ingress/runs.js +85 -5
- package/dist/ingress/runs.js.map +1 -1
- package/dist/managed-composition.d.ts.map +1 -1
- package/dist/managed-composition.js +8 -13
- package/dist/managed-composition.js.map +1 -1
- package/dist/namespaces.d.ts +15 -21
- package/dist/namespaces.d.ts.map +1 -1
- package/dist/namespaces.js +2 -29
- package/dist/namespaces.js.map +1 -1
- package/dist/runtime/auth.d.ts +1 -2
- package/dist/runtime/auth.d.ts.map +1 -1
- package/dist/runtime/auth.js +94 -48
- package/dist/runtime/auth.js.map +1 -1
- package/dist/runtime/channel-icon.d.ts +22 -0
- package/dist/runtime/channel-icon.d.ts.map +1 -0
- package/dist/runtime/channel-icon.js +70 -0
- package/dist/runtime/channel-icon.js.map +1 -0
- package/dist/runtime/channel.d.ts +6 -0
- package/dist/runtime/channel.d.ts.map +1 -1
- package/dist/runtime/channel.js.map +1 -1
- package/dist/runtime/identity-http.d.ts +2 -0
- package/dist/runtime/identity-http.d.ts.map +1 -1
- package/dist/runtime/identity-http.js +35 -0
- package/dist/runtime/identity-http.js.map +1 -1
- package/dist/runtime/identity-runtime.d.ts +4 -9
- package/dist/runtime/identity-runtime.d.ts.map +1 -1
- package/dist/runtime/identity-runtime.js +12 -25
- package/dist/runtime/identity-runtime.js.map +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +32 -21
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/managed-thread.d.ts +60 -0
- package/dist/runtime/managed-thread.d.ts.map +1 -0
- package/dist/runtime/managed-thread.js +76 -0
- package/dist/runtime/managed-thread.js.map +1 -0
- package/dist/runtime/managed-tools.d.ts +10 -2
- package/dist/runtime/managed-tools.d.ts.map +1 -1
- package/dist/runtime/managed-tools.js +10 -8
- package/dist/runtime/managed-tools.js.map +1 -1
- package/dist/runtime/memory/policy.d.ts.map +1 -1
- package/dist/runtime/memory/policy.js +1 -3
- package/dist/runtime/memory/policy.js.map +1 -1
- package/dist/runtime/run-input.d.ts +10 -0
- package/dist/runtime/run-input.d.ts.map +1 -1
- package/dist/runtime/run-input.js +48 -2
- package/dist/runtime/run-input.js.map +1 -1
- package/dist/runtime/sandbox-manager.d.ts.map +1 -1
- package/dist/runtime/sandbox-manager.js +1 -1
- package/dist/runtime/sandbox-manager.js.map +1 -1
- package/dist/runtime/schedule-auth.d.ts +17 -0
- package/dist/runtime/schedule-auth.d.ts.map +1 -1
- package/dist/runtime/schedule-auth.js +39 -1
- package/dist/runtime/schedule-auth.js.map +1 -1
- package/dist/runtime/thread-record.d.ts +81 -0
- package/dist/runtime/thread-record.d.ts.map +1 -0
- package/dist/runtime/thread-record.js +205 -0
- package/dist/runtime/thread-record.js.map +1 -0
- package/dist/runtime/types.d.ts +5 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime-schedules.d.ts +13 -0
- package/dist/runtime-schedules.d.ts.map +1 -1
- package/dist/runtime-schedules.js +83 -20
- package/dist/runtime-schedules.js.map +1 -1
- package/dist/sandbox.d.ts +0 -6
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +3 -7
- package/dist/sandbox.js.map +1 -1
- package/dist/schedule-channel.d.ts +5 -2
- package/dist/schedule-channel.d.ts.map +1 -1
- package/dist/schedule-channel.js +13 -12
- package/dist/schedule-channel.js.map +1 -1
- package/dist/values.d.ts +15 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +31 -0
- package/dist/values.js.map +1 -0
- package/package.json +7 -7
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { JsonValue } from "../types.js";
|
|
2
|
+
import { type ChannelBinding } from "../binding.js";
|
|
2
3
|
import { type ReactionChoice } from "../reactions.js";
|
|
3
4
|
import { type ObservedIngressRun, type PendingInterrupt } from "./ingress.js";
|
|
4
|
-
import type
|
|
5
|
+
import { type MultitaskStrategy, type StartedIngressRun } from "../../ingress/runs.js";
|
|
6
|
+
import { type NormalizedRunInput } from "../run-input.js";
|
|
7
|
+
import type { HttpChannelAdapter } from "../http/types.js";
|
|
5
8
|
import { type TriggerClientLike } from "./client.js";
|
|
6
9
|
import { type IdentityContextVerifyOptions } from "./identity-context.js";
|
|
7
|
-
import { type TriggerChannelMessageEvent, type TriggerChannelResume } from "./types.js";
|
|
10
|
+
import { type TriggerChannelMessageEvent, type TriggerChannelUserPromptResponseEvent, type TriggerChannelResume, type TriggerChannelRuntimeStatus } from "./types.js";
|
|
11
|
+
import type { ChannelActor } from "../actor.js";
|
|
8
12
|
export { TRIGGER_SERVICE_KEY_HEADER } from "./types.js";
|
|
9
13
|
export declare const ASSISTANT_ID_ENV = "MDA_ASSISTANT_ID";
|
|
10
14
|
type StartAgent = (input: {
|
|
@@ -16,6 +20,11 @@ type StartAgent = (input: {
|
|
|
16
20
|
sourceProvider: string;
|
|
17
21
|
sourceThreadKey: string;
|
|
18
22
|
userId: string;
|
|
23
|
+
/** Other state keys to start with. */
|
|
24
|
+
state?: Record<string, unknown>;
|
|
25
|
+
metadata?: Record<string, unknown>;
|
|
26
|
+
webhook?: string;
|
|
27
|
+
multitaskStrategy?: MultitaskStrategy;
|
|
19
28
|
}) => Promise<StartedIngressRun>;
|
|
20
29
|
type ObserveAgent = (input: {
|
|
21
30
|
threadId: string;
|
|
@@ -40,6 +49,53 @@ type ThreadInterrupts = (input: {
|
|
|
40
49
|
sourceThreadKey: string;
|
|
41
50
|
userId: string;
|
|
42
51
|
}) => Promise<PendingInterrupt[]>;
|
|
52
|
+
type NewerRun = (input: {
|
|
53
|
+
threadId: string;
|
|
54
|
+
runId: string;
|
|
55
|
+
sourceProvider: string;
|
|
56
|
+
sourceThreadKey: string;
|
|
57
|
+
userId: string;
|
|
58
|
+
}) => Promise<boolean>;
|
|
59
|
+
type ThreadExists = (input: {
|
|
60
|
+
threadId: string;
|
|
61
|
+
sourceProvider: string;
|
|
62
|
+
sourceThreadKey: string;
|
|
63
|
+
userId: string;
|
|
64
|
+
}) => Promise<boolean>;
|
|
65
|
+
/** The thread a message event belongs to, before any run exists. */
|
|
66
|
+
export interface TriggerChannelThread {
|
|
67
|
+
id: string;
|
|
68
|
+
sourceThreadKey: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* What a message event starts: the run input a provider's handler returned
|
|
72
|
+
* (`input`), no run (`skipped`), or nothing because the handler failed
|
|
73
|
+
* (`failed`).
|
|
74
|
+
*/
|
|
75
|
+
export type TriggerMessageDecision = {
|
|
76
|
+
type: "input";
|
|
77
|
+
input: NormalizedRunInput;
|
|
78
|
+
} | {
|
|
79
|
+
type: "skipped";
|
|
80
|
+
} | {
|
|
81
|
+
type: "failed";
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* What a provider decides a message starts: at once, or `background` for a
|
|
85
|
+
* decision that may take longer than Trigger Server waits, such as an event
|
|
86
|
+
* endpoint's. Trigger Server then gets its answer first, and a failure is
|
|
87
|
+
* only logged.
|
|
88
|
+
*/
|
|
89
|
+
export type TriggerMessageRoute = TriggerMessageDecision | {
|
|
90
|
+
type: "background";
|
|
91
|
+
decide: () => Promise<TriggerMessageDecision>;
|
|
92
|
+
};
|
|
93
|
+
type CancelThreadRuns = (input: {
|
|
94
|
+
threadId: string;
|
|
95
|
+
sourceProvider: string;
|
|
96
|
+
sourceThreadKey: string;
|
|
97
|
+
userId: string;
|
|
98
|
+
}) => Promise<number>;
|
|
43
99
|
export interface TriggerChannelHttpOptions {
|
|
44
100
|
sourceProvider: string;
|
|
45
101
|
channelName: string;
|
|
@@ -49,15 +105,70 @@ export interface TriggerChannelHttpOptions {
|
|
|
49
105
|
startAgent?: StartAgent;
|
|
50
106
|
resumeAgent?: ResumeAgent;
|
|
51
107
|
threadInterrupts?: ThreadInterrupts;
|
|
108
|
+
threadExists?: ThreadExists;
|
|
109
|
+
/** Whether a newer run replaced a run on its thread; for tests. */
|
|
110
|
+
newerRun?: NewerRun;
|
|
52
111
|
observeAgent?: ObserveAgent;
|
|
112
|
+
cancelRuns?: CancelThreadRuns;
|
|
53
113
|
triggerClient?: TriggerClientLike;
|
|
54
114
|
/** How to react to the message that starts a run. Defaults to `eyes`. */
|
|
55
115
|
reactions?: ReactionChoice;
|
|
116
|
+
/**
|
|
117
|
+
* The status a run's `run.started` shows in place of Trigger Server's
|
|
118
|
+
* working phrases, and how.
|
|
119
|
+
*/
|
|
120
|
+
startedStatus?: TriggerChannelRuntimeStatus;
|
|
56
121
|
/** Provider hook for a message event's run input; defaults to `input.messages`. */
|
|
57
122
|
messageInput?: (event: TriggerChannelMessageEvent) => Record<string, unknown>[];
|
|
123
|
+
/**
|
|
124
|
+
* Decide what a message event starts, before its run; `undefined` starts
|
|
125
|
+
* it with `messageInput`'s messages. Interrupt resumes never ask.
|
|
126
|
+
*/
|
|
127
|
+
decideMessage?: (event: TriggerChannelMessageEvent, thread: TriggerChannelThread,
|
|
128
|
+
/** Who sent it, when Trigger Server names them. */
|
|
129
|
+
actor?: ChannelActor) => Promise<TriggerMessageRoute | undefined>;
|
|
130
|
+
/**
|
|
131
|
+
* Decide what a prompt response that answers no interrupt starts, as
|
|
132
|
+
* `decideMessage` does a message; `undefined` starts a run with no new
|
|
133
|
+
* messages, which reads the response from `runtime.channel.event`.
|
|
134
|
+
*/
|
|
135
|
+
decideInteraction?: (event: TriggerChannelUserPromptResponseEvent, thread: TriggerChannelThread,
|
|
136
|
+
/** Who responded, when Trigger Server names them. */
|
|
137
|
+
actor?: ChannelActor) => Promise<TriggerMessageRoute | undefined>;
|
|
138
|
+
/**
|
|
139
|
+
* Whether a message may only continue a thread the agent already ran on:
|
|
140
|
+
* then, on a thread no run ever started on, it starts nothing, and
|
|
141
|
+
* `decideMessage` isn't asked.
|
|
142
|
+
*/
|
|
143
|
+
onlyContinuesThread?: (event: TriggerChannelMessageEvent) => Promise<boolean>;
|
|
144
|
+
/**
|
|
145
|
+
* What a run a message or prompt response starts does to a run already
|
|
146
|
+
* active on its thread; `enqueue` when unset.
|
|
147
|
+
*/
|
|
148
|
+
multitaskStrategy?: MultitaskStrategy;
|
|
149
|
+
/**
|
|
150
|
+
* A message, or a prompt response that answers no interrupt, is done, as
|
|
151
|
+
* `channels.http`'s `onFinish` hears it: the run it started ended (`run`), it started none (`skipped`: `ignored` or
|
|
152
|
+
* `rejected`), its run couldn't start after Trigger Server had its answer
|
|
153
|
+
* (`failed: start`), or MDA lost track of the run (`failed: observe`). The
|
|
154
|
+
* delivery's `rawEvent` is the {@link TriggerFinishedEvent}. Called once,
|
|
155
|
+
* after the reply and lifecycle callbacks, in the background; failures are
|
|
156
|
+
* logged. A message whose run couldn't start before Trigger Server's answer
|
|
157
|
+
* gets an error instead, and Trigger Server may deliver it again.
|
|
158
|
+
*/
|
|
159
|
+
onFinish?: HttpChannelAdapter["onFinish"];
|
|
58
160
|
/** Override identity-context JWKS verify (unit tests). */
|
|
59
161
|
identityContext?: IdentityContextVerifyOptions;
|
|
60
162
|
}
|
|
61
163
|
/** Validate one Trigger message, start its run, and enqueue observation. */
|
|
62
164
|
export declare function handleTriggerChannelEventRequest(request: Request, options: TriggerChannelHttpOptions): Promise<Response>;
|
|
165
|
+
/**
|
|
166
|
+
* What a finished message's `rawEvent` holds: the Trigger delivery, without
|
|
167
|
+
* its identity token.
|
|
168
|
+
*/
|
|
169
|
+
export type TriggerFinishedEvent = {
|
|
170
|
+
delivery: JsonValue;
|
|
171
|
+
/** Who sent it, as Trigger Server named them. */
|
|
172
|
+
actor?: ChannelActor;
|
|
173
|
+
};
|
|
63
174
|
//# sourceMappingURL=http.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/channels/trigger/http.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/channels/trigger/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { INTERRUPT } from "@langchain/langgraph";
|
|
2
|
-
import {
|
|
2
|
+
import { bindingWithMessages } from "../binding.js";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { v5 as uuidv5 } from "uuid";
|
|
5
5
|
import { missingWorkspaceConnectionSlug } from "../../agent-auth.js";
|
|
6
|
-
import { extractAssistantText } from "../assistant-text.js";
|
|
7
6
|
import { DEFAULT_REACTION_EMOJI, resolveReactionEmoji, } from "../reactions.js";
|
|
8
7
|
import { logger } from "../../runtime/log.js";
|
|
9
8
|
import { INTERRUPT_CORRELATION_KEY } from "../../run-context.js";
|
|
10
|
-
import { ingressThreadInterrupts, observeIngressAgent, resumeIngressAgent, startIngressAgent, } from "./ingress.js";
|
|
9
|
+
import { cancelIngressThreadRuns, ingressThreadExists, ingressThreadInterrupts, newerIngressRun, observeIngressAgent, resumeIngressAgent, startIngressAgent, } from "./ingress.js";
|
|
11
10
|
import { scheduleIngressWork } from "../../ingress/index.js";
|
|
11
|
+
import { lookupStatus, rejectedAsBusy, } from "../../ingress/runs.js";
|
|
12
|
+
import { runStartOptions } from "../run-input.js";
|
|
12
13
|
import { createTriggerClient, LANGSMITH_MDA_TRIGGER_SERVER_URL_ENV, TriggerClientError, } from "./client.js";
|
|
13
14
|
import { IdentityContextUnavailableError, InvalidIdentityContextError, principalIdFromIdentityContext, } from "./identity-context.js";
|
|
14
15
|
import { resolveTracerSessionId } from "./tracing-project.js";
|
|
15
|
-
import { parseTriggerChannelEvent, triggerChannelBinding, TRIGGER_SERVICE_KEY_HEADER, normalizedUserPromptResponse, } from "./types.js";
|
|
16
|
+
import { parseTriggerChannelEvent, triggerChannelBinding, TRIGGER_SERVICE_KEY_HEADER, normalizedUserPromptResponse, triggerEventActor, } from "./types.js";
|
|
17
|
+
import { isRecord, nonblankString } from "../../values.js";
|
|
16
18
|
export { TRIGGER_SERVICE_KEY_HEADER } from "./types.js";
|
|
17
19
|
export const ASSISTANT_ID_ENV = "MDA_ASSISTANT_ID";
|
|
18
20
|
// Raised when the thread cannot say which interrupt an answer belongs to. An
|
|
@@ -67,7 +69,15 @@ export async function handleTriggerChannelEventRequest(request, options) {
|
|
|
67
69
|
}
|
|
68
70
|
throw error;
|
|
69
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Who the delivery came from, as Trigger Server names them; never logged.
|
|
74
|
+
* Only as trusted as the service key, which is checked for presence alone.
|
|
75
|
+
*/
|
|
76
|
+
const actor = triggerEventActor(event);
|
|
70
77
|
logger.info(`Channel incoming event (type=${event.type}, event_id=${logger.logIdentifier(event.event_id)}, delivery_id=${logger.logIdentifier(event.delivery_id)}, channel_id=${logger.logIdentifier(event.channel_id)})`);
|
|
78
|
+
if (event.type === "run_cancel") {
|
|
79
|
+
return await cancelEventRuns(event, userId, options);
|
|
80
|
+
}
|
|
71
81
|
let client = options.triggerClient;
|
|
72
82
|
if (!client) {
|
|
73
83
|
try {
|
|
@@ -91,6 +101,56 @@ export async function handleTriggerChannelEventRequest(request, options) {
|
|
|
91
101
|
logger.error(`${ASSISTANT_ID_ENV} is not configured`);
|
|
92
102
|
return Response.json({ error: "channel runtime is not configured" }, { status: 500 });
|
|
93
103
|
}
|
|
104
|
+
const observe = (started) => observeAcceptedRun({
|
|
105
|
+
event,
|
|
106
|
+
started,
|
|
107
|
+
client,
|
|
108
|
+
observeAgent: options.observeAgent,
|
|
109
|
+
sourceProvider: options.sourceProvider,
|
|
110
|
+
sourceThreadKey: event.source_thread_key,
|
|
111
|
+
userId,
|
|
112
|
+
reactions: options.reactions ?? DEFAULT_REACTION_EMOJI,
|
|
113
|
+
...(options.startedStatus && { startedStatus: options.startedStatus }),
|
|
114
|
+
multitaskStrategy: options.multitaskStrategy,
|
|
115
|
+
newerRun: options.newerRun,
|
|
116
|
+
});
|
|
117
|
+
const thread = {
|
|
118
|
+
id: triggerThreadId(event.source_thread_key),
|
|
119
|
+
sourceThreadKey: event.source_thread_key,
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Whether the delivery starts a turn of its own, which finishes: a message,
|
|
123
|
+
* or a prompt response that answers no interrupt. Resumes don't.
|
|
124
|
+
*/
|
|
125
|
+
let startsTurn = event.type === "message";
|
|
126
|
+
/** Tell the provider a turn is done, once; a failure is logged. */
|
|
127
|
+
const finish = async (finished) => {
|
|
128
|
+
if (!startsTurn || !options.onFinish)
|
|
129
|
+
return;
|
|
130
|
+
const { identity_context: _token, ...message } = event;
|
|
131
|
+
const raw = {
|
|
132
|
+
delivery: message,
|
|
133
|
+
// Strings only, so JSON as the delivery's `rawEvent` is.
|
|
134
|
+
...(actor && { actor: { ...actor } }),
|
|
135
|
+
};
|
|
136
|
+
try {
|
|
137
|
+
await options.onFinish(triggerTarget(event), {
|
|
138
|
+
threadId: thread.id,
|
|
139
|
+
rawEvent: raw,
|
|
140
|
+
...finished,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
logger.error(`channel finish notice failed (${logger.safeErrorLabel(error)})`);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const finishLater = (finished) => {
|
|
148
|
+
if (options.onFinish)
|
|
149
|
+
scheduleIngressWork(() => finish(finished), {
|
|
150
|
+
label: "Channel finish notice",
|
|
151
|
+
schedule: options.schedule,
|
|
152
|
+
});
|
|
153
|
+
};
|
|
94
154
|
let started;
|
|
95
155
|
try {
|
|
96
156
|
const common = {
|
|
@@ -102,13 +162,87 @@ export async function handleTriggerChannelEventRequest(request, options) {
|
|
|
102
162
|
context: {},
|
|
103
163
|
binding: triggerChannelBinding(event, options.sourceProvider, options.channelName),
|
|
104
164
|
};
|
|
165
|
+
/**
|
|
166
|
+
* Start the delivery's run as `route` decides: at once, in the
|
|
167
|
+
* background, or not at all. The answer for Trigger Server when it has
|
|
168
|
+
* one already, otherwise the run it started.
|
|
169
|
+
*/
|
|
170
|
+
const decideAndStart = async (route, undecided) => {
|
|
171
|
+
/** Start the run; `undefined` when `reject` left the thread to its run. */
|
|
172
|
+
const startRun = async (input) => {
|
|
173
|
+
const strategy = options.multitaskStrategy;
|
|
174
|
+
try {
|
|
175
|
+
return await (options.startAgent ?? startIngressAgent)({
|
|
176
|
+
...common,
|
|
177
|
+
...(input
|
|
178
|
+
? decidedRun(input, common.binding)
|
|
179
|
+
: { messages: undecided() }),
|
|
180
|
+
...(strategy && { multitaskStrategy: strategy }),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
// A run already active on the thread keeps it under `reject`.
|
|
185
|
+
if (rejectedAsBusy(strategy, error)) {
|
|
186
|
+
logger.info(`channel ${event.type} rejected: its thread is busy (delivery_id=${logger.logIdentifier(event.delivery_id)})`);
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
if (route?.type === "background") {
|
|
193
|
+
// Trigger Server only waits briefly, so it hears first; a failure
|
|
194
|
+
// from here on is logged, and nothing redelivers the event.
|
|
195
|
+
scheduleIngressWork(async () => {
|
|
196
|
+
const decided = await route.decide();
|
|
197
|
+
if (decided.type !== "input") {
|
|
198
|
+
logNoRun(event, decided);
|
|
199
|
+
return finish(skipped("ignored"));
|
|
200
|
+
}
|
|
201
|
+
let run;
|
|
202
|
+
try {
|
|
203
|
+
run = await startRun(decided.input);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
logger.error(`channel run could not be started (${logger.safeErrorLabel(error)})`);
|
|
207
|
+
return finish({
|
|
208
|
+
type: "failed",
|
|
209
|
+
reason: "start",
|
|
210
|
+
interrupts: [],
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
await finish(run ? await observe(run) : skipped("rejected"));
|
|
214
|
+
}, { label: "Channel event decision", schedule: options.schedule });
|
|
215
|
+
return acceptedResponse(event);
|
|
216
|
+
}
|
|
217
|
+
if (route?.type === "skipped") {
|
|
218
|
+
logNoRun(event, route);
|
|
219
|
+
finishLater(skipped("ignored"));
|
|
220
|
+
return acceptedResponse(event);
|
|
221
|
+
}
|
|
222
|
+
// Trigger Server may deliver it again, which finishes on its own.
|
|
223
|
+
if (route?.type === "failed")
|
|
224
|
+
return startFailedResponse();
|
|
225
|
+
const run = await startRun(route?.input);
|
|
226
|
+
if (!run) {
|
|
227
|
+
finishLater(skipped("rejected"));
|
|
228
|
+
return acceptedResponse(event);
|
|
229
|
+
}
|
|
230
|
+
return run;
|
|
231
|
+
};
|
|
105
232
|
switch (event.type) {
|
|
106
|
-
case "message":
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
233
|
+
case "message": {
|
|
234
|
+
const message = event;
|
|
235
|
+
if (!(await mayStartIn(common, message, options))) {
|
|
236
|
+
logger.info(`channel message started no run: the agent never ran on its thread (delivery_id=${logger.logIdentifier(message.delivery_id)})`);
|
|
237
|
+
finishLater(skipped("ignored"));
|
|
238
|
+
return acceptedResponse(message);
|
|
239
|
+
}
|
|
240
|
+
const outcome = await decideAndStart(await options.decideMessage?.(message, thread, actor), () => options.messageInput?.(message) ?? message.input.messages);
|
|
241
|
+
if (outcome instanceof Response)
|
|
242
|
+
return outcome;
|
|
243
|
+
started = outcome;
|
|
111
244
|
break;
|
|
245
|
+
}
|
|
112
246
|
case "interrupt_resume":
|
|
113
247
|
started = await (options.resumeAgent ?? resumeIngressAgent)({
|
|
114
248
|
...common,
|
|
@@ -125,16 +259,19 @@ export async function handleTriggerChannelEventRequest(request, options) {
|
|
|
125
259
|
correlationId: normalized.correlation_id,
|
|
126
260
|
threadInterrupts: options.threadInterrupts,
|
|
127
261
|
});
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
262
|
+
if (interruptId !== undefined) {
|
|
263
|
+
started = await (options.resumeAgent ?? resumeIngressAgent)({
|
|
264
|
+
...common,
|
|
265
|
+
resume: { [interruptId]: normalized },
|
|
266
|
+
});
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
// A fresh turn, which the provider may decide, and which finishes.
|
|
270
|
+
startsTurn = true;
|
|
271
|
+
const outcome = await decideAndStart(await options.decideInteraction?.(event, thread, actor), () => []);
|
|
272
|
+
if (outcome instanceof Response)
|
|
273
|
+
return outcome;
|
|
274
|
+
started = outcome;
|
|
138
275
|
break;
|
|
139
276
|
}
|
|
140
277
|
default: {
|
|
@@ -150,23 +287,82 @@ export async function handleTriggerChannelEventRequest(request, options) {
|
|
|
150
287
|
return acceptedResponse(event);
|
|
151
288
|
}
|
|
152
289
|
logger.error(`channel run could not be started (${logger.safeErrorLabel(error)})`);
|
|
153
|
-
return
|
|
290
|
+
return startFailedResponse();
|
|
154
291
|
}
|
|
155
|
-
scheduleIngressWork(() =>
|
|
156
|
-
event,
|
|
157
|
-
started,
|
|
158
|
-
client,
|
|
159
|
-
observeAgent: options.observeAgent,
|
|
160
|
-
sourceProvider: options.sourceProvider,
|
|
161
|
-
sourceThreadKey: event.source_thread_key,
|
|
162
|
-
userId,
|
|
163
|
-
reactions: options.reactions ?? DEFAULT_REACTION_EMOJI,
|
|
164
|
-
}), {
|
|
292
|
+
scheduleIngressWork(async () => finish(await observe(started)), {
|
|
165
293
|
label: "Channel run observation",
|
|
166
294
|
schedule: options.schedule,
|
|
167
295
|
});
|
|
168
296
|
return acceptedResponse(event);
|
|
169
297
|
}
|
|
298
|
+
/** Where a delivery's replies go, as the channel binding names it. */
|
|
299
|
+
function triggerTarget(event) {
|
|
300
|
+
return {
|
|
301
|
+
channel_id: event.channel_id,
|
|
302
|
+
address: event.address,
|
|
303
|
+
source_thread_key: event.source_thread_key,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function skipped(reason) {
|
|
307
|
+
return { type: "skipped", reason, interrupts: [] };
|
|
308
|
+
}
|
|
309
|
+
/** Log an event that started no run; a failed handler logged its own error. */
|
|
310
|
+
function logNoRun(event, decision) {
|
|
311
|
+
logger.info(`channel ${event.type} started no run (${decision.type === "failed" ? "its handler failed, " : ""}delivery_id=${logger.logIdentifier(event.delivery_id)})`);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* A decided run's start options. Its messages replace the delivery's in the
|
|
315
|
+
* binding, as the run's own input; its context sits beside the binding.
|
|
316
|
+
*/
|
|
317
|
+
function decidedRun(input, binding) {
|
|
318
|
+
return {
|
|
319
|
+
...runStartOptions(input),
|
|
320
|
+
binding: bindingWithMessages(binding, input.messages),
|
|
321
|
+
context: input.context ?? {},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function startFailedResponse() {
|
|
325
|
+
return Response.json({ error: "channel run could not be started" }, { status: 500 });
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Stop the source thread's runs; each run's observer reports `run.cancelled`.
|
|
329
|
+
*
|
|
330
|
+
* Runs are cancelled as the thread's channel principal, so the managed owner
|
|
331
|
+
* filter confines the cancel to this source conversation.
|
|
332
|
+
*/
|
|
333
|
+
async function cancelEventRuns(event, userId, options) {
|
|
334
|
+
let cancelled;
|
|
335
|
+
try {
|
|
336
|
+
cancelled = await (options.cancelRuns ?? cancelIngressThreadRuns)({
|
|
337
|
+
threadId: triggerThreadId(event.source_thread_key),
|
|
338
|
+
sourceProvider: options.sourceProvider,
|
|
339
|
+
sourceThreadKey: event.source_thread_key,
|
|
340
|
+
userId,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
logger.error(`channel runs could not be cancelled (${logger.safeErrorLabel(error)})`);
|
|
345
|
+
return Response.json({ error: "channel runs could not be cancelled" }, { status: 500 });
|
|
346
|
+
}
|
|
347
|
+
logger.info(`Channel runs cancelled (count=${cancelled}, delivery_id=${logger.logIdentifier(event.delivery_id)})`);
|
|
348
|
+
return acceptedResponse(event);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Whether a message may start a run on its thread: always, unless it may only
|
|
352
|
+
* continue a thread the agent already ran on and none ran there. A failed
|
|
353
|
+
* lookup is logged and lets it start, as before the check.
|
|
354
|
+
*/
|
|
355
|
+
async function mayStartIn(common, event, options) {
|
|
356
|
+
if (!(await options.onlyContinuesThread?.(event)))
|
|
357
|
+
return true;
|
|
358
|
+
try {
|
|
359
|
+
return await (options.threadExists ?? ingressThreadExists)(common);
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
logger.warn(`channel thread lookup failed; the message starts as before (${logger.safeErrorLabel(error)})`);
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
170
366
|
function acceptedResponse(event) {
|
|
171
367
|
return Response.json({ status: "accepted", delivery_id: event.delivery_id }, { status: 202 });
|
|
172
368
|
}
|
|
@@ -194,27 +390,63 @@ async function interruptAnsweredBy(input) {
|
|
|
194
390
|
}
|
|
195
391
|
return undefined;
|
|
196
392
|
}
|
|
197
|
-
|
|
393
|
+
// A tool-call review is answered from its own prompt (`interrupt_resume`):
|
|
394
|
+
// a click on one of the agent's buttons, such as a rating, is no decision.
|
|
395
|
+
const answerable = pending.filter((candidate) => !isToolCallReview(candidate.value));
|
|
396
|
+
if (answerable.length > 1) {
|
|
198
397
|
// Interrupt ids carry no ordering, so without a correlation id there is
|
|
199
398
|
// nothing to choose by.
|
|
200
|
-
throw new AmbiguousInterruptError(`${
|
|
399
|
+
throw new AmbiguousInterruptError(`${answerable.length} interrupts pending`);
|
|
201
400
|
}
|
|
202
|
-
|
|
401
|
+
if (answerable.length === 0 && pending.length > 0) {
|
|
402
|
+
// Starting a turn here would abandon the review.
|
|
403
|
+
throw new AmbiguousInterruptError("the thread waits on a tool-call review, which a click doesn't answer");
|
|
404
|
+
}
|
|
405
|
+
return answerable[0]?.id;
|
|
406
|
+
}
|
|
407
|
+
/** Whether an interrupt is a tool-call review, as `humanInTheLoopMiddleware` raises one. */
|
|
408
|
+
function isToolCallReview(value) {
|
|
409
|
+
return isRecord(value) && Array.isArray(value.action_requests);
|
|
203
410
|
}
|
|
204
411
|
function interruptCorrelationId(value) {
|
|
205
412
|
if (!isRecord(value))
|
|
206
413
|
return undefined;
|
|
207
|
-
|
|
208
|
-
return typeof id === "string" && id.trim() ? id : undefined;
|
|
414
|
+
return nonblankString(value[INTERRUPT_CORRELATION_KEY]);
|
|
209
415
|
}
|
|
210
416
|
async function observeAcceptedRun(input) {
|
|
211
417
|
const { event, started, client } = input;
|
|
212
|
-
const
|
|
418
|
+
const run = {
|
|
419
|
+
threadId: started.threadId,
|
|
420
|
+
runId: started.runId,
|
|
421
|
+
sourceProvider: input.sourceProvider,
|
|
422
|
+
sourceThreadKey: input.sourceThreadKey,
|
|
423
|
+
userId: input.userId,
|
|
424
|
+
};
|
|
425
|
+
// Only these strategies let a newer message cancel a run.
|
|
426
|
+
const replaceable = input.multitaskStrategy === "interrupt" ||
|
|
427
|
+
input.multitaskStrategy === "rollback";
|
|
428
|
+
/**
|
|
429
|
+
* A newer message's run took the thread over; it reports to Slack, so this
|
|
430
|
+
* run says nothing that would stop or fail the thread's status. It ended
|
|
431
|
+
* `interrupted`, as the HTTP channel reports a replaced run.
|
|
432
|
+
*/
|
|
433
|
+
const replaced = () => {
|
|
434
|
+
logger.info(`channel run was replaced by a newer message (delivery_id=${logger.logIdentifier(event.delivery_id)}, run_id=${logger.logIdentifier(started.runId)})`);
|
|
435
|
+
return {
|
|
436
|
+
type: "run",
|
|
437
|
+
run: { id: started.runId, status: "interrupted" },
|
|
438
|
+
interrupts: [],
|
|
439
|
+
};
|
|
440
|
+
};
|
|
441
|
+
const lifecycle = async (type, message, status) => {
|
|
213
442
|
logger.info(`Channel outgoing event (type=${type}, delivery_id=${logger.logIdentifier(event.delivery_id)}, channel_id=${logger.logIdentifier(event.channel_id)}, run_id=${logger.logIdentifier(started.runId)})`);
|
|
214
|
-
const request =
|
|
443
|
+
const request = {
|
|
444
|
+
...runtimeEvent(type, event, started, message, await resolveTracerSessionId()),
|
|
445
|
+
...status,
|
|
446
|
+
};
|
|
215
447
|
return bestEffort(client.postRuntimeEvent(event.channel_id, request));
|
|
216
448
|
};
|
|
217
|
-
await lifecycle("run.started");
|
|
449
|
+
await lifecycle("run.started", undefined, input.startedStatus);
|
|
218
450
|
// Not awaited here, so the reaction never delays the reply. The `finally`
|
|
219
451
|
// settles it so it cannot outlive this work item.
|
|
220
452
|
const reaction = reactToTriggerMessage({
|
|
@@ -226,46 +458,42 @@ async function observeAcceptedRun(input) {
|
|
|
226
458
|
try {
|
|
227
459
|
let observed;
|
|
228
460
|
try {
|
|
229
|
-
observed = await (input.observeAgent ?? observeIngressAgent)(
|
|
230
|
-
threadId: started.threadId,
|
|
231
|
-
runId: started.runId,
|
|
232
|
-
sourceProvider: input.sourceProvider,
|
|
233
|
-
sourceThreadKey: input.sourceThreadKey,
|
|
234
|
-
userId: input.userId,
|
|
235
|
-
});
|
|
461
|
+
observed = await (input.observeAgent ?? observeIngressAgent)(run);
|
|
236
462
|
}
|
|
237
463
|
catch (error) {
|
|
238
|
-
|
|
239
|
-
|
|
464
|
+
// `rollback` cancels, then deletes, the run a newer message replaced.
|
|
465
|
+
if (replaceable && lookupStatus(error) === 404)
|
|
466
|
+
return replaced();
|
|
467
|
+
await lifecycle("run.failed", missingConnectionFailureMessage(error));
|
|
468
|
+
return { type: "failed", reason: "observe", interrupts: [] };
|
|
469
|
+
}
|
|
470
|
+
const ended = (interrupts = []) => ({
|
|
471
|
+
type: "run",
|
|
472
|
+
run: { id: started.runId, status: observed.status },
|
|
473
|
+
interrupts: interrupts,
|
|
474
|
+
});
|
|
475
|
+
// Stopped from Slack or Studio: the run ended `interrupted`, with nothing
|
|
476
|
+
// waiting on a decision.
|
|
477
|
+
if (isCancelledRun(observed)) {
|
|
478
|
+
if (replaceable && (await wasReplaced(run, input.newerRun)))
|
|
479
|
+
return replaced();
|
|
480
|
+
await lifecycle("run.cancelled");
|
|
481
|
+
return ended();
|
|
240
482
|
}
|
|
483
|
+
// The run reports its own pause and posts its own answer (../delivery.ts).
|
|
241
484
|
if (isInterruptedRun(observed)) {
|
|
242
|
-
|
|
485
|
+
const interrupts = interruptEntries(observed.values).filter((entry) => isRecord(entry) &&
|
|
243
486
|
typeof entry.id === "string" &&
|
|
244
487
|
entry.id.trim().length > 0 &&
|
|
245
|
-
"value" in entry)
|
|
246
|
-
return;
|
|
488
|
+
"value" in entry);
|
|
489
|
+
return ended(interrupts);
|
|
247
490
|
}
|
|
248
491
|
if (observed.status !== "success") {
|
|
249
492
|
await lifecycle("run.failed");
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
const text = extractAssistantText(isRecord(observed.values) ? observed.values : {});
|
|
253
|
-
if (text) {
|
|
254
|
-
logger.info(`Channel outgoing action (type=post, delivery_id=${logger.logIdentifier(event.delivery_id)}, channel_id=${logger.logIdentifier(event.channel_id)}, run_id=${logger.logIdentifier(started.runId)})`);
|
|
255
|
-
const action = await bestEffort(createTriggerTransport(client, { actionId: event.event_id }).post({
|
|
256
|
-
message: { type: "content", content: text },
|
|
257
|
-
target: {
|
|
258
|
-
channel_id: event.channel_id,
|
|
259
|
-
address: event.address,
|
|
260
|
-
source_thread_key: event.source_thread_key,
|
|
261
|
-
},
|
|
262
|
-
}));
|
|
263
|
-
if (action === undefined) {
|
|
264
|
-
await lifecycle("run.failed");
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
493
|
+
return ended();
|
|
267
494
|
}
|
|
268
495
|
await lifecycle("run.completed");
|
|
496
|
+
return ended();
|
|
269
497
|
}
|
|
270
498
|
finally {
|
|
271
499
|
await reaction;
|
|
@@ -303,7 +531,7 @@ async function reactToTriggerMessage(input) {
|
|
|
303
531
|
reaction: { emoji, message_id: id },
|
|
304
532
|
}));
|
|
305
533
|
}
|
|
306
|
-
function runtimeEvent(type, event, started,
|
|
534
|
+
function runtimeEvent(type, event, started, message, tracerSessionId) {
|
|
307
535
|
return {
|
|
308
536
|
id: randomUUID(),
|
|
309
537
|
type,
|
|
@@ -314,7 +542,6 @@ function runtimeEvent(type, event, started, interrupts, message, tracerSessionId
|
|
|
314
542
|
run_id: started.runId,
|
|
315
543
|
...(tracerSessionId ? { tracer_session_id: tracerSessionId } : {}),
|
|
316
544
|
...(message ? { message: { text: message } } : {}),
|
|
317
|
-
...(interrupts?.length ? { interrupts } : {}),
|
|
318
545
|
};
|
|
319
546
|
}
|
|
320
547
|
function missingConnectionFailureMessage(error) {
|
|
@@ -350,8 +577,20 @@ async function bestEffort(promise) {
|
|
|
350
577
|
return undefined;
|
|
351
578
|
}
|
|
352
579
|
}
|
|
353
|
-
|
|
354
|
-
|
|
580
|
+
/** Whether a newer run replaced a cancelled one; a failed lookup says no. */
|
|
581
|
+
async function wasReplaced(run, newerRun = newerIngressRun) {
|
|
582
|
+
try {
|
|
583
|
+
return await newerRun(run);
|
|
584
|
+
}
|
|
585
|
+
catch (error) {
|
|
586
|
+
logger.warn(`channel run lookup failed; the run counts as stopped (${logger.safeErrorLabel(error)})`);
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function isCancelledRun(observed) {
|
|
591
|
+
// A cancelled run ends "interrupted" with nothing parked for a person to
|
|
592
|
+
// answer; reporting it as an interrupt would post an empty approval prompt.
|
|
593
|
+
return observed.status === "interrupted" && !hasInterrupt(observed.values);
|
|
355
594
|
}
|
|
356
595
|
function isInterruptedRun(observed) {
|
|
357
596
|
// Agent Server can report status "success" after join while values still
|