-
Notifications
You must be signed in to change notification settings - Fork 114
Expand file tree
/
Copy pathchannel.tsx
More file actions
124 lines (112 loc) · 3.53 KB
/
Copy pathchannel.tsx
File metadata and controls
124 lines (112 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import {
createChannel,
type Channel,
type CreateChannelOptions,
} from "@copilotkit/channels";
import {
managedRunInput,
reportRecoverableError,
} from "./channel-helpers.js";
import { appCommands } from "./commands/index.js";
import { IssueCard, IssueList, PageList } from "./components/index.js";
import { appContext } from "./context/app-context.js";
import { ConfirmWrite } from "./human-in-the-loop/index.js";
import { parseConfirmWriteInterrupt } from "./interrupt.js";
import { FILE_ISSUE_CALLBACK, fileIssueSubmit } from "./modals/file-issue.js";
import { IncidentCard } from "./tools/showcase-tools.js";
import { RenderChart } from "./tools/render-chart.js";
import { appTools } from "./tools/index.js";
type ChannelAgent = NonNullable<CreateChannelOptions["agent"]>;
/** Build the managed OpenTag Channel; Intelligence owns its platform adapters. */
export function createOpenTagChannel(
name: string,
agent: ChannelAgent,
): Channel {
const channel = createChannel({
name,
agent,
identifyUser: "platform",
tools: appTools,
context: [...appContext],
commands: appCommands,
components: [
IssueCard,
IssueList,
PageList,
IncidentCard,
ConfirmWrite,
RenderChart,
],
});
const runAgentSafely: Parameters<typeof channel.onMessage>[0] = async ({
thread,
message,
}) => {
try {
await thread.runAgent(managedRunInput(message));
} catch (error) {
try {
await thread.post(
"Sorry — I hit an error handling that. Please try again.",
);
} catch (postError) {
throw new AggregateError(
[error, postError],
"The agent run and its user-facing error reply both failed",
);
}
// A failed turn is isolated from future turns. Once the user receives an
// explicit failure response, the Channel can safely remain available.
reportRecoverableError(error, {
operation: "run_agent",
recovery: "posted_user_facing_error",
});
}
};
channel.onMention(async ({ thread, message }) => {
await thread.subscribe();
await runAgentSafely({ thread, message });
});
channel.onMessage(async ({ thread, message }) => {
if (message.actor.kind === "bot" || message.actor.kind === "app") return;
if (await thread.isSubscribed()) {
await runAgentSafely({ thread, message });
}
});
channel.onModalSubmit(FILE_ISSUE_CALLBACK, fileIssueSubmit);
channel.onInterrupt("on_interrupt", async ({ payload, thread }) => {
const { args } = parseConfirmWriteInterrupt(payload);
await thread.post(
<ConfirmWrite
action={args.action}
fields={args.fields}
detail={args.detail ?? undefined}
attempt={args.attempt}
previousError={args.previous_error ?? undefined}
/>,
);
});
channel.onThreadStarted(async ({ thread, user }) => {
if (!user?.name) return;
try {
await thread.setSuggestedPrompts([
{
title: `Triage ${user.name}'s issues`,
message: "Triage my open issues",
},
{
title: "What shipped this week?",
message: "Summarize what shipped this week",
},
]);
} catch (error) {
// Suggested prompts are an optional affordance; their absence does not
// affect message delivery, agent execution, or later thread turns.
reportRecoverableError(error, {
operation: "set_suggested_prompts",
recovery: "continue_without_suggested_prompts",
});
}
});
return channel;
}