pi-roundtable 0.22.5 → 0.23.1
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/CHANGELOG.md +61 -1
- package/README.md +35 -9
- package/README.zh-TW.md +51 -13
- package/docs/migrating-0.8.md +2 -2
- package/docs/plugins.md +351 -28
- package/examples/discord-adapter.ts +9 -1
- package/examples/seeds.test.ts +2 -2
- package/examples/seeds.ts +1 -2
- package/package.json +6 -2
- package/src/characters/catalog.ts +1055 -0
- package/src/characters/characters.css +68 -0
- package/src/characters/colors.ts +108 -0
- package/src/characters/compose.ts +281 -0
- package/src/characters/css.ts +63 -0
- package/src/characters/index.ts +33 -0
- package/src/characters/parse.ts +196 -0
- package/src/characters/parts-face.ts +354 -0
- package/src/characters/parts-hair.ts +56 -0
- package/src/characters/parts-hats.ts +57 -0
- package/src/characters/parts-outfits.ts +81 -0
- package/src/characters/parts-props.ts +137 -0
- package/src/characters/spec.ts +224 -0
- package/src/characters/suggest.ts +101 -0
- package/src/cli/checks/characters.ts +23 -0
- package/src/cli/checks/images.ts +5 -3
- package/src/cli/doctor.ts +2 -0
- package/src/core/agents/agent-claim.ts +30 -11
- package/src/core/agents/agent-dashboard.ts +1 -1
- package/src/core/agents/agent-messages.ts +43 -15
- package/src/core/agents/agent-prompt.ts +2 -4
- package/src/core/agents/agent-rules.ts +42 -3
- package/src/core/agents/agent-schema.ts +37 -15
- package/src/core/agents/agent-store.ts +57 -13
- package/src/core/agents/agent-team-fixture.ts +90 -14
- package/src/core/agents/agent-team.ts +83 -50
- package/src/core/agents/agent-tools.ts +94 -41
- package/src/core/agents/avatar-studio.ts +40 -4
- package/src/core/agents/character-png.ts +40 -0
- package/src/core/agents/group-messages.ts +7 -4
- package/src/core/agents/group-turns.ts +27 -14
- package/src/core/agents/team-editing.ts +99 -58
- package/src/core/agents/team-keys.ts +177 -11
- package/src/core/agents/team-lifecycle.ts +151 -84
- package/src/core/agents/team-options.ts +13 -6
- package/src/core/agents/team-status.ts +31 -13
- package/src/core/agents/team-text.ts +13 -3
- package/src/core/agents/team-turn-types.ts +25 -7
- package/src/core/agents/team-turns.ts +143 -28
- package/src/core/attachments/attachment-fetcher.ts +162 -64
- package/src/core/attachments/prompt-block.ts +7 -1
- package/src/core/attachments/tool-attachment.ts +20 -1
- package/src/core/attachments/turn-attachments.ts +18 -2
- package/src/core/builtin/agent-server.ts +103 -40
- package/src/core/builtin/discord.ts +4 -17
- package/src/core/builtin/modules.ts +6 -5
- package/src/core/builtin/threads.ts +37 -0
- package/src/core/config/agent-team.ts +43 -0
- package/src/core/config/config.ts +50 -59
- package/src/core/config/limits.ts +93 -0
- package/src/core/config/ops.ts +34 -0
- package/src/core/config/schema.ts +26 -0
- package/src/core/contract/channels.ts +20 -0
- package/src/core/contract/surface.ts +77 -7
- package/src/core/define-roundtable.ts +70 -42
- package/src/core/discord/agent-commands.ts +108 -86
- package/src/core/discord/agent-panel.ts +11 -2
- package/src/core/discord/connection.ts +1 -1
- package/src/core/discord/discord-surface.ts +10 -1
- package/src/core/discord/dispatch-thread-host.ts +1 -1
- package/src/core/domain/conversation.ts +24 -0
- package/src/core/holds.ts +221 -15
- package/src/core/host-registration.ts +2 -1
- package/src/core/http/listeners.ts +63 -3
- package/src/core/i18n/agent-panel.ts +14 -21
- package/src/core/interactions/prompts.ts +12 -0
- package/src/core/modes/autonomy.ts +23 -0
- package/src/core/modes/conversation-modes.ts +213 -0
- package/src/core/modes/modes-plugin.ts +24 -0
- package/src/core/modules/delegation/delegator.ts +4 -4
- package/src/core/modules/host-shell/shell-arithmetic.ts +81 -0
- package/src/core/modules/host-shell/shell-lexer-types.ts +53 -0
- package/src/core/modules/host-shell/shell-lexer.ts +493 -0
- package/src/core/modules/host-shell/shell-paths.ts +85 -4
- package/src/core/modules/host-shell/shell-policy.ts +341 -332
- package/src/core/modules/host-shell/shell-programs.ts +298 -0
- package/src/core/modules/host-shell/shell-quoting.ts +237 -0
- package/src/core/modules/host-shell/shell-scripts.ts +114 -0
- package/src/core/modules/host-shell/shell-wrappers.ts +104 -0
- package/src/core/modules/schedules/precheck-tools.ts +17 -0
- package/src/core/modules/schedules/schedules.ts +9 -3
- package/src/core/plugin.ts +8 -1
- package/src/core/presence/direct-channels.ts +30 -0
- package/src/core/presentation/reply-body.ts +25 -0
- package/src/core/registry/contributions.ts +31 -7
- package/src/core/registry/providers.ts +1 -1
- package/src/core/routing/conversation-turns.ts +2 -15
- package/src/core/routing/surface-port.ts +5 -1
- package/src/core/runtime/extensions/approval-card.ts +1 -5
- package/src/core/runtime/extensions/approval-fields.ts +3 -7
- package/src/core/runtime/extensions/confirmation-gate-extension.ts +13 -2
- package/src/core/runtime/extensions/confirmation-gate.ts +41 -40
- package/src/core/runtime/extensions/gate-refusals.ts +23 -0
- package/src/core/runtime/extensions/held-calls.ts +16 -1
- package/src/core/runtime/extensions/hold-describer.ts +58 -8
- package/src/core/runtime/extensions/hold-lookups.ts +19 -19
- package/src/core/runtime/extensions/yolo.ts +105 -0
- package/src/core/runtime/pi-agent-runtime.ts +3 -5
- package/src/core/runtime/runtime-plugin.ts +42 -1
- package/src/core/runtime/runtime-types.ts +11 -0
- package/src/core/runtime/turn-budget.ts +32 -5
- package/src/core/runtime/turn-limits.ts +14 -3
- package/src/core/services.ts +61 -9
- package/src/core/shared/attachment-reader.ts +6 -0
- package/src/core/shared/unix-server.ts +10 -1
- package/src/core/testing/modules.ts +12 -8
- package/src/core/testing/surface-threads.ts +58 -0
- package/src/core/testing/thread-host.ts +17 -5
- package/src/core/threads/dispatch-threads.ts +299 -0
- package/src/core/threads/host-threads.ts +19 -0
- package/src/core/tool-set.snapshot.json +5 -0
- package/src/index.ts +27 -1
- package/src/kit/characters.ts +5 -0
- package/src/kit/index.ts +2 -0
- package/src/kit/shell.ts +1 -0
- package/src/kit/support.ts +1 -1
- package/src/kit/threads.ts +1 -1
- package/src/testing.ts +4 -0
- package/templates/agents.ts +1 -1
- package/src/core/discord/dispatch-threads.ts +0 -194
|
@@ -15,17 +15,20 @@ import {
|
|
|
15
15
|
SeparatorBuilder,
|
|
16
16
|
SeparatorSpacingSize,
|
|
17
17
|
StringSelectMenuBuilder,
|
|
18
|
+
type StringSelectMenuInteraction,
|
|
18
19
|
StringSelectMenuOptionBuilder,
|
|
19
20
|
TextDisplayBuilder,
|
|
20
21
|
TextInputBuilder,
|
|
21
22
|
TextInputStyle,
|
|
22
23
|
} from "discord.js";
|
|
23
24
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
CATALOG,
|
|
26
|
+
type CharacterInput,
|
|
27
|
+
CLASS_IDS,
|
|
28
|
+
type PaletteName,
|
|
29
|
+
} from "../../characters/index.ts";
|
|
30
|
+
import { characterOf } from "../agents/agent-rules.ts";
|
|
31
|
+
import { type Agent, MAX_PROMPT_CHARS } from "../agents/agent-store.ts";
|
|
29
32
|
import { AgentError } from "../domain/errors.ts";
|
|
30
33
|
import { messages } from "../i18n/index.ts";
|
|
31
34
|
import { THINKING_LEVELS, thinkingLabel } from "../models.ts";
|
|
@@ -68,22 +71,18 @@ export function formModels(
|
|
|
68
71
|
: offered;
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
type Action = "edit" | "
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"newprompt",
|
|
76
|
-
"editavatar",
|
|
77
|
-
"model",
|
|
78
|
-
];
|
|
74
|
+
type Action = "edit" | "model";
|
|
75
|
+
/** The buttons of the panel; the look is chosen with the two selects, `class` and `color`. */
|
|
76
|
+
const ACTIONS: readonly Action[] = ["edit", "model"];
|
|
77
|
+
type LookAction = "class" | "color";
|
|
79
78
|
|
|
80
79
|
export interface AgentCommandsOptions {
|
|
81
80
|
store: Pick<AgentDirectory, "agentByChannel" | "groupByChannel" | "agent">;
|
|
82
81
|
team: Pick<
|
|
83
82
|
AgentTeam,
|
|
84
|
-
"
|
|
83
|
+
"dress" | "update" | "modelOf" | "usableModels" | "defaultModel"
|
|
85
84
|
>;
|
|
86
|
-
studio: Pick<AvatarStudio, "url"
|
|
85
|
+
studio: Pick<AvatarStudio, "url">;
|
|
87
86
|
/** The skills an agent carries; without the skills addon the panel lists none. */
|
|
88
87
|
skills?: Pick<SkillRegistry, "carried">;
|
|
89
88
|
}
|
|
@@ -133,8 +132,7 @@ export class AgentCommands {
|
|
|
133
132
|
|
|
134
133
|
#panel(agent: Agent, note?: string): AgentPanelMessage {
|
|
135
134
|
const text = messages();
|
|
136
|
-
|
|
137
|
-
const drawing = this.#options.studio.canDraw;
|
|
135
|
+
const look = characterOf(agent);
|
|
138
136
|
const inline = agent.prompt.length <= INLINE_PROMPT_CHARS;
|
|
139
137
|
const { model, thinking } = this.#options.team.modelOf(agent.name);
|
|
140
138
|
const follows =
|
|
@@ -168,9 +166,10 @@ export class AgentCommands {
|
|
|
168
166
|
)
|
|
169
167
|
.addTextDisplayComponents(
|
|
170
168
|
new TextDisplayBuilder().setContent(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
text.agentLook(
|
|
170
|
+
CATALOG.classes[look.class].label,
|
|
171
|
+
look.palette ?? look.colors.primary,
|
|
172
|
+
),
|
|
174
173
|
),
|
|
175
174
|
new TextDisplayBuilder().setContent(
|
|
176
175
|
inline
|
|
@@ -196,15 +195,14 @@ export class AgentCommands {
|
|
|
196
195
|
new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Small),
|
|
197
196
|
)
|
|
198
197
|
.addActionRowComponents(
|
|
198
|
+
new ActionRowBuilder<StringSelectMenuBuilder>()
|
|
199
|
+
.addComponents(this.#lookMenu("class", agent.name, look.class))
|
|
200
|
+
.toJSON(),
|
|
201
|
+
new ActionRowBuilder<StringSelectMenuBuilder>()
|
|
202
|
+
.addComponents(this.#lookMenu("color", agent.name, look.palette))
|
|
203
|
+
.toJSON(),
|
|
199
204
|
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
|
200
205
|
button("edit", text.agentEditPromptLabel, ButtonStyle.Primary),
|
|
201
|
-
...(drawing
|
|
202
|
-
? [
|
|
203
|
-
button("redraw", text.agentRedrawLabel),
|
|
204
|
-
button("newprompt", text.agentNewAvatarPromptLabel),
|
|
205
|
-
button("editavatar", text.agentEditAvatarLabel),
|
|
206
|
-
]
|
|
207
|
-
: []),
|
|
208
206
|
button("model", text.agentModelLabel),
|
|
209
207
|
),
|
|
210
208
|
)
|
|
@@ -227,23 +225,54 @@ export class AgentCommands {
|
|
|
227
225
|
};
|
|
228
226
|
}
|
|
229
227
|
|
|
230
|
-
/** A
|
|
228
|
+
/** A dropdown of the look: the 24 classes or the 8 colors, the agent's own chosen. */
|
|
229
|
+
#lookMenu(
|
|
230
|
+
action: LookAction,
|
|
231
|
+
name: string,
|
|
232
|
+
current: string | undefined,
|
|
233
|
+
): StringSelectMenuBuilder {
|
|
234
|
+
const text = messages();
|
|
235
|
+
const option = (label: string, value: string, description?: string) => {
|
|
236
|
+
const built = new StringSelectMenuOptionBuilder()
|
|
237
|
+
.setLabel(label)
|
|
238
|
+
.setValue(value)
|
|
239
|
+
.setDefault(value === current);
|
|
240
|
+
return description
|
|
241
|
+
? built.setDescription(description.slice(0, 100))
|
|
242
|
+
: built;
|
|
243
|
+
};
|
|
244
|
+
return new StringSelectMenuBuilder()
|
|
245
|
+
.setCustomId(`${AGENT_BUTTON_PREFIX}${action}:${name}`)
|
|
246
|
+
.setPlaceholder(
|
|
247
|
+
action === "class"
|
|
248
|
+
? text.agentClassPlaceholder
|
|
249
|
+
: text.agentColorPlaceholder,
|
|
250
|
+
)
|
|
251
|
+
.addOptions(
|
|
252
|
+
action === "class"
|
|
253
|
+
? CLASS_IDS.map((id) =>
|
|
254
|
+
option(CATALOG.classes[id].label, id, CATALOG.classes[id].jobs),
|
|
255
|
+
)
|
|
256
|
+
: (Object.keys(CATALOG.palettes) as PaletteName[]).map((id) =>
|
|
257
|
+
option(id.charAt(0).toUpperCase() + id.slice(1), id),
|
|
258
|
+
),
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** A panel button: a form opens. */
|
|
231
263
|
async button(interaction: ButtonInteraction): Promise<void> {
|
|
232
264
|
const [action, name] = interaction.customId
|
|
233
265
|
.slice(AGENT_BUTTON_PREFIX.length)
|
|
234
266
|
.split(":");
|
|
235
267
|
const agent = name ? this.#options.store.agent(name) : undefined;
|
|
236
|
-
if (!agent
|
|
237
|
-
|
|
238
|
-
if (action
|
|
239
|
-
|
|
240
|
-
await interaction.editReply(await this.#avatar(agent.name, "redraw"));
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
268
|
+
if (!agent) throw new OwnerFacingError(messages().agentNotFound);
|
|
269
|
+
// A panel posted before 0.23 still carries the redraw buttons.
|
|
270
|
+
if (!ACTIONS.includes(action as Action))
|
|
271
|
+
throw new OwnerFacingError(messages().agentPanelOutdated);
|
|
243
272
|
await interaction.showModal(
|
|
244
273
|
action === "model"
|
|
245
274
|
? await this.#modelModal(agent)
|
|
246
|
-
: this.#
|
|
275
|
+
: this.#editModal(agent),
|
|
247
276
|
);
|
|
248
277
|
}
|
|
249
278
|
|
|
@@ -292,7 +321,7 @@ export class AgentCommands {
|
|
|
292
321
|
);
|
|
293
322
|
}
|
|
294
323
|
|
|
295
|
-
#
|
|
324
|
+
#editModal(agent: Agent): ModalBuilder {
|
|
296
325
|
const field = (
|
|
297
326
|
id: string,
|
|
298
327
|
label: string,
|
|
@@ -308,50 +337,23 @@ export class AgentCommands {
|
|
|
308
337
|
if (value) input.setValue(value);
|
|
309
338
|
return new LabelBuilder().setLabel(label).setTextInputComponent(input);
|
|
310
339
|
};
|
|
311
|
-
|
|
312
|
-
`${AGENT_MODAL_PREFIX}
|
|
313
|
-
|
|
314
|
-
if (action === "edit")
|
|
315
|
-
return modal
|
|
316
|
-
.setTitle(
|
|
317
|
-
messages().agentEditModalTitle(agent.displayName).slice(0, 45),
|
|
318
|
-
)
|
|
319
|
-
.addLabelComponents(
|
|
320
|
-
field(
|
|
321
|
-
"display",
|
|
322
|
-
messages().agentDisplayNameLabel,
|
|
323
|
-
TextInputStyle.Short,
|
|
324
|
-
32,
|
|
325
|
-
agent.displayName,
|
|
326
|
-
),
|
|
327
|
-
field(
|
|
328
|
-
"prompt",
|
|
329
|
-
messages().agentPromptLabel,
|
|
330
|
-
TextInputStyle.Paragraph,
|
|
331
|
-
MAX_PROMPT_CHARS,
|
|
332
|
-
agent.prompt,
|
|
333
|
-
),
|
|
334
|
-
);
|
|
335
|
-
if (action === "newprompt")
|
|
336
|
-
return modal
|
|
337
|
-
.setTitle(messages().agentNewAvatarPromptLabel)
|
|
338
|
-
.addLabelComponents(
|
|
339
|
-
field(
|
|
340
|
-
"text",
|
|
341
|
-
messages().agentAvatarPromptLabel,
|
|
342
|
-
TextInputStyle.Paragraph,
|
|
343
|
-
MAX_AVATAR_PROMPT_CHARS,
|
|
344
|
-
agent.avatarPrompt,
|
|
345
|
-
),
|
|
346
|
-
);
|
|
347
|
-
return modal
|
|
348
|
-
.setTitle(messages().agentEditAvatarLabel)
|
|
340
|
+
return new ModalBuilder()
|
|
341
|
+
.setCustomId(`${AGENT_MODAL_PREFIX}edit:${agent.name}`)
|
|
342
|
+
.setTitle(messages().agentEditModalTitle(agent.displayName).slice(0, 45))
|
|
349
343
|
.addLabelComponents(
|
|
350
344
|
field(
|
|
351
|
-
"
|
|
352
|
-
messages().
|
|
345
|
+
"display",
|
|
346
|
+
messages().agentDisplayNameLabel,
|
|
347
|
+
TextInputStyle.Short,
|
|
348
|
+
32,
|
|
349
|
+
agent.displayName,
|
|
350
|
+
),
|
|
351
|
+
field(
|
|
352
|
+
"prompt",
|
|
353
|
+
messages().agentPromptLabel,
|
|
353
354
|
TextInputStyle.Paragraph,
|
|
354
|
-
|
|
355
|
+
MAX_PROMPT_CHARS,
|
|
356
|
+
agent.prompt,
|
|
355
357
|
),
|
|
356
358
|
);
|
|
357
359
|
}
|
|
@@ -383,23 +385,43 @@ export class AgentCommands {
|
|
|
383
385
|
await interaction.editReply(this.#panel(updated, messages().agentSaved));
|
|
384
386
|
return;
|
|
385
387
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
388
|
+
throw new OwnerFacingError(messages().agentPanelOutdated);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* A choice in one of the panel's two dropdowns, class or color; the reply is deferred by the
|
|
393
|
+
* caller. The choice goes through `dress`, which keeps the colors on a new class and the
|
|
394
|
+
* class on a new color.
|
|
395
|
+
*/
|
|
396
|
+
async select(interaction: StringSelectMenuInteraction): Promise<void> {
|
|
397
|
+
const [action, name] = interaction.customId
|
|
398
|
+
.slice(AGENT_BUTTON_PREFIX.length)
|
|
399
|
+
.split(":");
|
|
400
|
+
const chosen = interaction.values[0];
|
|
401
|
+
if (
|
|
402
|
+
!name ||
|
|
403
|
+
chosen === undefined ||
|
|
404
|
+
(action !== "class" && action !== "color")
|
|
405
|
+
)
|
|
406
|
+
throw new OwnerFacingError(messages().agentNotFound);
|
|
407
|
+
const input: CharacterInput =
|
|
408
|
+
action === "class"
|
|
409
|
+
? { class: chosen as CharacterInput["class"] }
|
|
410
|
+
: { palette: chosen as PaletteName };
|
|
411
|
+
await interaction.editReply(await this.#dress(name, input));
|
|
390
412
|
}
|
|
391
413
|
|
|
392
|
-
async #
|
|
414
|
+
async #dress(name: string, input: CharacterInput) {
|
|
393
415
|
try {
|
|
394
|
-
const agent = await this.#options.team.
|
|
395
|
-
return this.#panel(agent, messages().
|
|
416
|
+
const agent = await this.#options.team.dress(name, input);
|
|
417
|
+
return this.#panel(agent, messages().agentLookChanged);
|
|
396
418
|
} catch (error) {
|
|
397
419
|
if (!(error instanceof AgentError)) throw error;
|
|
398
420
|
const agent = this.#options.store.agent(name);
|
|
399
421
|
return agent
|
|
400
422
|
? this.#panel(
|
|
401
423
|
agent,
|
|
402
|
-
messages().
|
|
424
|
+
messages().agentLookFailed(error.message).slice(0, 300),
|
|
403
425
|
)
|
|
404
426
|
: ownerPanel({
|
|
405
427
|
title: messages().ownerFailedTitle,
|
|
@@ -17,7 +17,7 @@ export interface AgentPanelOptions {
|
|
|
17
17
|
skills?: Pick<SkillRegistry, "carried">;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/** An agent's panel: its prompt,
|
|
20
|
+
/** An agent's panel: its prompt, look, model, and skills, with dropdowns, buttons and forms to change them. */
|
|
21
21
|
export interface AgentPanel {
|
|
22
22
|
/**
|
|
23
23
|
* The panel of the agent whose channel this is. Throws OwnerFacingError, which a command run
|
|
@@ -25,7 +25,7 @@ export interface AgentPanel {
|
|
|
25
25
|
*/
|
|
26
26
|
open(channelId: string): AgentPanelMessage;
|
|
27
27
|
/**
|
|
28
|
-
* Answers the panel's own buttons and form, for the owner only and deferred as Discord needs;
|
|
28
|
+
* Answers the panel's own buttons, dropdowns and form, for the owner only and deferred as Discord needs;
|
|
29
29
|
* true when it handled the interaction. The custom ids are fixed, so panels already posted keep working.
|
|
30
30
|
*/
|
|
31
31
|
handles(interaction: Interaction): Promise<boolean>;
|
|
@@ -51,6 +51,15 @@ export function agentPanel(options: AgentPanelOptions): AgentPanel {
|
|
|
51
51
|
await guard.run(interaction, () => commands.button(interaction));
|
|
52
52
|
return true;
|
|
53
53
|
}
|
|
54
|
+
if (
|
|
55
|
+
interaction.isStringSelectMenu() &&
|
|
56
|
+
interaction.customId.startsWith(AGENT_BUTTON_PREFIX)
|
|
57
|
+
) {
|
|
58
|
+
if (!(await guard.allows(interaction))) return true;
|
|
59
|
+
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
|
60
|
+
await guard.run(interaction, () => commands.select(interaction));
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
54
63
|
if (
|
|
55
64
|
interaction.isModalSubmit() &&
|
|
56
65
|
interaction.customId.startsWith(AGENT_MODAL_PREFIX)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { AgentChannels, DashboardBoard } from "../agents/agent-ports.ts";
|
|
2
2
|
import type { ChannelKey } from "../domain/conversation.ts";
|
|
3
3
|
import type { OwnerOperations } from "../modules/discord-admin/discord-admin.ts";
|
|
4
|
+
import type { ThreadHost } from "../threads/dispatch-threads.ts";
|
|
4
5
|
import type { ChannelExecutor } from "./channel-operations.ts";
|
|
5
|
-
import type { ThreadHost } from "./dispatch-threads.ts";
|
|
6
6
|
|
|
7
7
|
/** What Discord calls a channel, for the web app's lists. */
|
|
8
8
|
export type ChannelInfo =
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type REST,
|
|
13
13
|
} from "discord.js";
|
|
14
14
|
import type { AgentChannels, DashboardBoard } from "../agents/agent-ports.ts";
|
|
15
|
-
import type { ChatSurface } from "../contract/surface.ts";
|
|
15
|
+
import type { ChatSurface, SurfaceThreads } from "../contract/surface.ts";
|
|
16
16
|
import type {
|
|
17
17
|
ChannelKey,
|
|
18
18
|
InboundMessage,
|
|
@@ -25,6 +25,7 @@ import type { PromptScope, Prompts } from "../interactions/prompts.ts";
|
|
|
25
25
|
import type { Logger } from "../log.ts";
|
|
26
26
|
import type { OwnerOperations } from "../modules/discord-admin/discord-admin.ts";
|
|
27
27
|
import { splitReply } from "../presentation/reply-splitter.ts";
|
|
28
|
+
import { threadsOfHost } from "../threads/host-threads.ts";
|
|
28
29
|
import { DiscordAgentChannels, DiscordDashboard } from "./agent-discord.ts";
|
|
29
30
|
import {
|
|
30
31
|
type ContextSourceMessage,
|
|
@@ -77,6 +78,14 @@ export class DiscordSurface
|
|
|
77
78
|
readonly surface = "discord";
|
|
78
79
|
readonly supportsFiles = true;
|
|
79
80
|
readonly formatting: string | undefined;
|
|
81
|
+
/** Dispatch threads, through the client as it is when each call is made. */
|
|
82
|
+
readonly threads: SurfaceThreads = threadsOfHost({
|
|
83
|
+
open: (parentId, name, line) =>
|
|
84
|
+
this.threadHost().open(parentId, name, line),
|
|
85
|
+
post: (threadId, text) => this.threadHost().post(threadId, text),
|
|
86
|
+
send: (threadId, text) => this.threadHost().send(threadId, text),
|
|
87
|
+
close: (threadId) => this.threadHost().close(threadId),
|
|
88
|
+
});
|
|
80
89
|
readonly #options: DiscordSurfaceOptions;
|
|
81
90
|
readonly #client = new Client({
|
|
82
91
|
intents: [
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "discord.js";
|
|
7
7
|
import type { InterimMessage } from "../domain/interim.ts";
|
|
8
8
|
import { splitReply } from "../presentation/reply-splitter.ts";
|
|
9
|
-
import type { ThreadHost } from "
|
|
9
|
+
import type { ThreadHost } from "../threads/dispatch-threads.ts";
|
|
10
10
|
|
|
11
11
|
/** Parents a dispatch thread may start in; DMs, threads, forums, and voice chats keep posting in place. */
|
|
12
12
|
const HOSTS: readonly ChannelType[] = [
|
|
@@ -26,6 +26,12 @@ export interface HeldCall {
|
|
|
26
26
|
* a restart honors them; a private conversation ignores them.
|
|
27
27
|
*/
|
|
28
28
|
approverIds?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* The hold classes of the rules that held it, as `<rule>.<class>` ids (see `HoldRule.holdClasses`),
|
|
31
|
+
* or a rule's bare name for a rule that reports none, kept with the call across a restart. Absent for a call held before 0.23 or by a check that
|
|
32
|
+
* has no classes: its classes are unknown, and yolo mode always asks a card for it.
|
|
33
|
+
*/
|
|
34
|
+
holdClasses?: string[];
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
export interface PendingConfirmation {
|
|
@@ -55,6 +61,17 @@ export type TurnResult =
|
|
|
55
61
|
| { ok: true; text: string; thinking?: string; files?: ReplyFile[] }
|
|
56
62
|
| { ok: false; error: Error; stopped?: true };
|
|
57
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Who a reply speaks as on a surface that shows authors, such as an agent in a room: the name to
|
|
66
|
+
* show and, for an agent of the agent team, its stable name.
|
|
67
|
+
*/
|
|
68
|
+
export interface PostAuthor {
|
|
69
|
+
/** The name to show, such as an agent's display name. */
|
|
70
|
+
name: string;
|
|
71
|
+
/** The agent's name, when an agent of the agent team speaks. */
|
|
72
|
+
agent?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
58
75
|
export interface OutboundReply {
|
|
59
76
|
/** The assistant's thinking for this turn, posted as a quiet line before the card. */
|
|
60
77
|
thinking?: string;
|
|
@@ -64,4 +81,11 @@ export interface OutboundReply {
|
|
|
64
81
|
chunks: string[];
|
|
65
82
|
/** Files the run produced, posted after the text. */
|
|
66
83
|
files?: ReplyFile[];
|
|
84
|
+
/**
|
|
85
|
+
* Who speaks, for a surface that shows authors, such as a room of an agent. Absent = the
|
|
86
|
+
* assistant. Discord posts agents through webhooks and ignores it.
|
|
87
|
+
*/
|
|
88
|
+
author?: PostAuthor;
|
|
89
|
+
/** Set only on the reply for a turn that ended in an error. A surface may show it; Discord ignores it. */
|
|
90
|
+
failed?: true;
|
|
67
91
|
}
|