pi-roundtable 0.1.0 → 0.2.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/CHANGELOG.md +138 -0
- package/README.md +6 -4
- package/docs/plugins.md +1448 -66
- package/examples/echo-runtime.test.ts +122 -0
- package/examples/echo-runtime.ts +107 -0
- package/examples/events.test.ts +1 -0
- package/examples/events.ts +2 -2
- package/examples/fake-surface.test.ts +180 -0
- package/examples/fake-surface.ts +109 -0
- package/examples/interactions.test.ts +12 -3
- package/examples/interactions.ts +19 -19
- package/examples/shared-services.test.ts +74 -0
- package/examples/shared-services.ts +60 -0
- package/examples/study-room.test.ts +69 -0
- package/examples/study-room.ts +52 -0
- package/examples/support-desk.test.ts +34 -0
- package/examples/support-desk.ts +40 -0
- package/package.json +9 -2
- package/src/cli/checks/database.ts +11 -30
- package/src/cli/checks/images.ts +19 -0
- package/src/cli/doctor.ts +6 -0
- package/src/cli/main.ts +2 -1
- package/src/core/agents/agent-claim.ts +42 -16
- package/src/core/agents/agent-messages.ts +4 -4
- package/src/core/agents/agent-ports.ts +8 -21
- package/src/core/agents/agent-prompt.ts +3 -1
- package/src/core/agents/agent-store.ts +6 -4
- package/src/core/agents/agent-team-fixture.ts +389 -0
- package/src/core/agents/agent-team.ts +26 -25
- package/src/core/agents/agent-tools.ts +34 -15
- package/src/core/agents/avatar-studio.ts +40 -6
- package/src/core/agents/fallback-avatar.ts +85 -0
- package/src/core/agents/team-editing.ts +49 -10
- package/src/core/agents/team-keys.ts +22 -9
- package/src/core/agents/team-layout.ts +4 -3
- package/src/core/agents/team-lifecycle.ts +28 -2
- package/src/core/agents/team-options.ts +9 -5
- package/src/core/agents/team-status.ts +4 -4
- package/src/core/agents/team-text.ts +8 -6
- package/src/core/agents/team-turn-types.ts +4 -4
- package/src/core/agents/team-turns.ts +6 -5
- package/src/core/builtin/agent-server.ts +187 -121
- package/src/core/builtin/discord-admin.ts +41 -0
- package/src/core/builtin/discord.ts +65 -40
- package/src/core/builtin/modules.ts +67 -58
- package/src/core/builtin/session-tool.ts +24 -0
- package/src/core/builtin/skills.ts +72 -0
- package/src/core/builtin/stores.ts +55 -31
- package/src/core/config/config.ts +45 -4
- package/src/core/config/schema.ts +6 -0
- package/src/core/contract/channels.ts +54 -14
- package/src/core/contract/providers.ts +17 -0
- package/src/core/contract/runtime.ts +138 -0
- package/src/core/contract/services.ts +45 -0
- package/src/core/contract/surface.ts +88 -0
- package/src/core/db/migrations.ts +118 -3
- package/src/core/define-roundtable.ts +70 -46
- package/src/core/define.ts +2 -1
- package/src/core/discord/agent-commands.ts +39 -14
- package/src/core/discord/agent-panel.ts +66 -0
- package/src/core/discord/channel-executor.ts +6 -3
- package/src/core/discord/channel-operations.ts +14 -11
- package/src/core/discord/command-collection.ts +46 -0
- package/src/core/{registry/interactions.ts → discord/compose-commands.ts} +5 -5
- package/src/core/discord/connection.ts +35 -0
- package/src/core/discord/discord-surface.ts +32 -94
- package/src/core/discord/dispatch-threads.ts +2 -2
- package/src/core/discord/inbound-message.ts +99 -0
- package/src/core/discord/interaction-module.ts +57 -1
- package/src/core/discord/owner-cards.ts +4 -4
- package/src/core/discord/owner-command.ts +44 -11
- package/src/core/discord/owner-discord.ts +2 -2
- package/src/core/discord/schedule-commands.ts +21 -18
- package/src/core/discord/stop-button.ts +44 -1
- package/src/core/domain/attachment.ts +12 -8
- package/src/core/domain/conversation.ts +3 -4
- package/src/core/domain/owner-prompts.ts +5 -5
- package/src/core/domain/ports.ts +9 -53
- package/src/core/freeze.ts +9 -0
- package/src/core/host.ts +255 -82
- package/src/core/http/listeners.ts +55 -20
- package/src/core/i18n/agent-panel.ts +4 -0
- package/src/core/i18n/index.ts +12 -2
- package/src/core/i18n/owner.ts +7 -1
- package/src/core/i18n/schedules.ts +12 -6
- package/src/core/identity.ts +1 -1
- package/src/core/judging/effort-judge.ts +25 -3
- package/src/core/log.ts +21 -3
- package/src/core/models.ts +2 -2
- package/src/core/modules/background/background-turns.ts +6 -4
- package/src/core/modules/delegation/delegate.ts +3 -2
- package/src/core/modules/delegation/delegator.ts +15 -13
- package/src/core/modules/delegation/{sol-worker.ts → web-research-worker.ts} +6 -6
- package/src/core/modules/discord-admin/discord-admin.ts +6 -6
- package/src/core/modules/host-shell/shell-policy.ts +8 -3
- package/src/core/modules/memory/owner-memory-store.ts +27 -25
- package/src/core/modules/memory/owner-memory.ts +9 -13
- package/src/core/modules/schedules/recurrence.ts +5 -3
- package/src/core/modules/schedules/schedule-store.ts +11 -11
- package/src/core/modules/schedules/schedule-tools.ts +22 -20
- package/src/core/modules/schedules/scheduler.ts +7 -2
- package/src/core/modules/schedules/schedules.ts +9 -3
- package/src/core/modules/skills/skill-registry.ts +3 -2
- package/src/core/modules/skills/skill-store.ts +2 -15
- package/src/core/modules/skills/skill-tools.ts +10 -4
- package/src/core/ops/error-reporter.ts +1 -1
- package/src/core/plugin.ts +232 -28
- package/src/core/registry/contributions.ts +173 -14
- package/src/core/registry/providers.ts +35 -5
- package/src/core/registry/services.ts +229 -0
- package/src/core/routing/channel-queue.ts +5 -0
- package/src/core/routing/channel-router.ts +21 -7
- package/src/core/routing/conversation-turns.ts +139 -0
- package/src/core/routing/message-text.ts +9 -2
- package/src/core/routing/surface-port.ts +39 -0
- package/src/core/runtime/conversation-sessions.ts +4 -3
- package/src/core/runtime/extensions/confirmation-fixture.ts +11 -0
- package/src/core/runtime/extensions/confirmation-gate.ts +11 -9
- package/src/core/runtime/mcp.ts +1 -1
- package/src/core/runtime/pending-confirmation-store.ts +10 -12
- package/src/core/runtime/pi-agent-runtime.ts +9 -6
- package/src/core/runtime/prompt-slot.ts +8 -3
- package/src/core/runtime/runtime-types.ts +9 -35
- package/src/core/runtime/session-factory.ts +21 -5
- package/src/core/runtime/text-tools.ts +1 -3
- package/src/core/services.ts +238 -90
- package/src/core/sessions.ts +3 -2
- package/src/core/shared/{profile-tools.ts → active-tools.ts} +2 -2
- package/src/core/shared/delegate-tool.ts +4 -3
- package/src/core/shared/schedule-tools.ts +22 -13
- package/src/core/shared/session-messages.ts +1 -1
- package/src/core/speakers.ts +6 -38
- package/src/core/testing/database.ts +1 -7
- package/src/core/testing/eager-catalog.ts +30 -0
- package/src/core/testing/locale.ts +23 -5
- package/src/core/testing/modules.ts +105 -41
- package/src/core/testing/test-host.ts +279 -0
- package/src/core/testing/tool-set.ts +64 -0
- package/src/core/time.ts +6 -1
- package/src/core/tool-set.snapshot.json +288 -0
- package/src/core/tool-tiers.ts +17 -57
- package/src/discord/index.ts +64 -0
- package/src/index.ts +181 -10
- package/src/kit/channels.ts +14 -0
- package/src/kit/domain.ts +18 -0
- package/src/kit/holds.ts +4 -0
- package/src/kit/index.ts +129 -0
- package/src/kit/judging.ts +10 -0
- package/src/kit/memory.ts +3 -0
- package/src/kit/mirror.ts +19 -0
- package/src/kit/presentation.ts +7 -0
- package/src/kit/shell.ts +7 -0
- package/src/kit/skills.ts +8 -0
- package/src/kit/support.ts +21 -0
- package/src/kit/threads.ts +7 -0
- package/src/kit/tools.ts +14 -0
- package/src/kit/worker.ts +16 -0
- package/src/testing.ts +394 -34
- package/examples/guide.test.ts +0 -52
- package/src/cli/add-plugin.test.ts +0 -107
- package/src/cli/checks/basic.test.ts +0 -276
- package/src/cli/checks/database.test.ts +0 -117
- package/src/cli/checks/discord.test.ts +0 -200
- package/src/cli/cli.test.ts +0 -122
- package/src/cli/config-edit.test.ts +0 -130
- package/src/cli/doctor.test.ts +0 -184
- package/src/cli/init.test.ts +0 -127
- package/src/cli/size.test.ts +0 -9
- package/src/cli/templates.test.ts +0 -129
- package/src/cli/testing/fixtures.ts +0 -106
- package/src/core/agents/agent-claim.test.ts +0 -142
- package/src/core/agents/agent-dashboard.test.ts +0 -120
- package/src/core/agents/agent-guild.test.ts +0 -124
- package/src/core/agents/agent-store.test.ts +0 -205
- package/src/core/agents/avatar-studio.test.ts +0 -88
- package/src/core/agents/group-round.test.ts +0 -143
- package/src/core/agents/owner-identity.test.ts +0 -172
- package/src/core/agents/team-turns.test.ts +0 -278
- package/src/core/attachments/attachments.test.ts +0 -85
- package/src/core/boundary.test.ts +0 -45
- package/src/core/builtin/modules.test.ts +0 -127
- package/src/core/config/config.test.ts +0 -135
- package/src/core/contract/discord.ts +0 -33
- package/src/core/db/migrations.test.ts +0 -237
- package/src/core/define-roundtable.test.ts +0 -134
- package/src/core/define.test.ts +0 -144
- package/src/core/discord/agent-commands.test.ts +0 -58
- package/src/core/discord/agent-discord.test.ts +0 -70
- package/src/core/discord/dispatch-thread-host.test.ts +0 -114
- package/src/core/discord/dispatch-threads.test.ts +0 -94
- package/src/core/discord/owner-cards.test.ts +0 -435
- package/src/core/discord/owner-discord.test.ts +0 -325
- package/src/core/domain/expression.ts +0 -21
- package/src/core/domain/profile.ts +0 -32
- package/src/core/drain.test.ts +0 -46
- package/src/core/events.test.ts +0 -81
- package/src/core/holds.test.ts +0 -54
- package/src/core/host.test.ts +0 -536
- package/src/core/http/listeners.test.ts +0 -164
- package/src/core/i18n/i18n.test.ts +0 -139
- package/src/core/identity.test.ts +0 -18
- package/src/core/judging/effort-judge.test.ts +0 -112
- package/src/core/judging/model-judge.test.ts +0 -126
- package/src/core/modules/delegation/delegator.test.ts +0 -170
- package/src/core/modules/memory/owner-memory-store.test.ts +0 -244
- package/src/core/modules/schedules/schedule.test.ts +0 -358
- package/src/core/modules/skills/skill-kind.test.ts +0 -55
- package/src/core/modules/skills/skill-registry.test.ts +0 -376
- package/src/core/ops/error-reporter.test.ts +0 -283
- package/src/core/presentation/card-cadence.ts +0 -41
- package/src/core/presentation/presentation.test.ts +0 -166
- package/src/core/public-entry.test.ts +0 -59
- package/src/core/registry/contributions.test.ts +0 -215
- package/src/core/registry/interactions.test.ts +0 -87
- package/src/core/registry/providers.test.ts +0 -103
- package/src/core/routing/channel-queue.test.ts +0 -31
- package/src/core/routing/channel-router.test.ts +0 -326
- package/src/core/routing/conversation-kind.ts +0 -14
- package/src/core/routing/settle-turn.test.ts +0 -21
- package/src/core/runtime/compaction-tiers.test.ts +0 -227
- package/src/core/runtime/extensions/ask-user.test.ts +0 -114
- package/src/core/runtime/extensions/self-compact-guard.test.ts +0 -50
- package/src/core/runtime/pending-confirmation-store.test.ts +0 -52
- package/src/core/runtime/session-archive.test.ts +0 -20
- package/src/core/runtime/steerable-run.test.ts +0 -264
- package/src/core/runtime/text-tools.test.ts +0 -110
- package/src/core/runtime/turn-answer.test.ts +0 -72
- package/src/core/runtime/worker-task.test.ts +0 -82
- package/src/core/services.test.ts +0 -28
- package/src/core/sessions.test.ts +0 -85
- package/src/core/shared/unix-server.ts +0 -18
- package/src/core/size.test.ts +0 -9
- package/src/core/speakers.test.ts +0 -78
- package/src/core/testing/file-size.ts +0 -34
- package/src/core/time.test.ts +0 -166
- package/src/core/tool-tiers.test.ts +0 -117
- package/src/entries.test.ts +0 -112
- package/src/testing.test.ts +0 -156
|
@@ -2,31 +2,24 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { userInfo } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { ModelRegistry, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import {
|
|
6
|
-
type AgentServerOptions,
|
|
7
|
-
agentServerPlugin,
|
|
8
|
-
} from "./builtin/agent-server.ts";
|
|
5
|
+
import { agentServerPlugin } from "./builtin/agent-server.ts";
|
|
9
6
|
import { discordPlugin } from "./builtin/discord.ts";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
modulesPlugin,
|
|
13
|
-
schedulerPlugin,
|
|
14
|
-
} from "./builtin/modules.ts";
|
|
7
|
+
import { discordAdminPlugin } from "./builtin/discord-admin.ts";
|
|
8
|
+
import { modulesPlugin, schedulerPlugin } from "./builtin/modules.ts";
|
|
15
9
|
import { seedsPlugin } from "./builtin/seeds.ts";
|
|
16
|
-
import {
|
|
10
|
+
import { skillsPlugin } from "./builtin/skills.ts";
|
|
11
|
+
import { memoryPlugin, scheduleStorePlugin } from "./builtin/stores.ts";
|
|
17
12
|
import { type RoundtableConfig, resolveConfig } from "./config/config.ts";
|
|
18
|
-
import { ownerRootCommand } from "./discord/owner-command.ts";
|
|
19
13
|
import { ConfigError } from "./domain/errors.ts";
|
|
20
14
|
import { JudgeError } from "./errors.ts";
|
|
21
15
|
import type { RoundtableOptions } from "./host.ts";
|
|
22
|
-
import {
|
|
16
|
+
import type { ListenerConfig } from "./http/listeners.ts";
|
|
23
17
|
import { type JudgeModel, piJudgeModel } from "./judging/model-judge.ts";
|
|
24
|
-
import { createLogger, type Logger } from "./log.ts";
|
|
18
|
+
import { createLogger, type LogEntry, type Logger } from "./log.ts";
|
|
25
19
|
import { formatModelRef, type ModelRef } from "./models.ts";
|
|
26
20
|
import { ErrorReporter } from "./ops/error-reporter.ts";
|
|
27
21
|
import type { RoundtablePlugin } from "./plugin.ts";
|
|
28
22
|
import { speakerPolicy } from "./speakers.ts";
|
|
29
|
-
import { setTimeZone } from "./time.ts";
|
|
30
23
|
import { toolTiers } from "./tool-tiers.ts";
|
|
31
24
|
|
|
32
25
|
/** What `defineRoundtable` returns: give both to `new Roundtable(options, plugins)`. */
|
|
@@ -39,14 +32,21 @@ export interface DefinedRoundtable {
|
|
|
39
32
|
export interface DefineOverrides {
|
|
40
33
|
/** The model login the process shares; by default one is opened in the agent directory. */
|
|
41
34
|
modelRuntime?: ModelRuntime;
|
|
42
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Your own logger, when the process already has one. The host reports errors to `config.ops`'s
|
|
37
|
+
* agent and to `errorSink` from the logger it builds, so a logger given here forwards its own
|
|
38
|
+
* error lines to them if it should.
|
|
39
|
+
*/
|
|
43
40
|
logger?: Logger;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Also receives every `error` and `fatal` log line of the logger the host builds, after the ops
|
|
43
|
+
* agent's report. It must not throw.
|
|
44
|
+
*/
|
|
45
|
+
errorSink?: (entry: LogEntry) => void;
|
|
46
|
+
/** More HTTP listeners than the one `config.http` names, each with its own id. */
|
|
47
|
+
listeners?: readonly ListenerConfig[];
|
|
48
|
+
/** Receives the work a shutdown drain gave up on, before any service stops. */
|
|
49
|
+
aborted?: (left: string[]) => Promise<void>;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const ASSETS = join(import.meta.dir, "assets");
|
|
@@ -82,8 +82,9 @@ function judgeThrough(modelRuntime: ModelRuntime, ref: ModelRef): JudgeModel {
|
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
84
|
* The host's options and the plugin list of a configured bot: the built-in plugins in their
|
|
85
|
-
* fixed order, then the operator's, then the scheduler, so a due schedule fires only once
|
|
86
|
-
* everything it reaches runs.
|
|
85
|
+
* fixed order (each service one provides is read by the ones after it), then the operator's, then the scheduler, so a due schedule fires only once
|
|
86
|
+
* everything it reaches runs. The memory, Discord administration, and skills addons are left out
|
|
87
|
+
* when the configuration switches them off. Configuration mistakes stop here, naming the key and the fix.
|
|
87
88
|
*/
|
|
88
89
|
export async function defineRoundtable(
|
|
89
90
|
input: RoundtableConfig,
|
|
@@ -91,25 +92,27 @@ export async function defineRoundtable(
|
|
|
91
92
|
): Promise<DefinedRoundtable> {
|
|
92
93
|
const config = resolveConfig(input);
|
|
93
94
|
const { name, owner, discord } = config;
|
|
94
|
-
|
|
95
|
-
setTimeZone(config.timeZone);
|
|
96
|
-
// Pi packages such as pi-web-access read their config from the Pi agent directory.
|
|
97
|
-
process.env.PI_CODING_AGENT_DIR = config.agentDir;
|
|
95
|
+
// Nothing here touches the process: the host applies the environment when it runs.
|
|
98
96
|
const modelRuntime =
|
|
99
97
|
overrides.modelRuntime ??
|
|
100
98
|
(await ModelRuntime.create({
|
|
101
99
|
authPath: join(config.agentDir, "auth.json"),
|
|
100
|
+
modelsPath: join(config.agentDir, "models.json"),
|
|
102
101
|
}));
|
|
103
|
-
const errorReporter =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
: undefined);
|
|
102
|
+
const errorReporter = config.ops
|
|
103
|
+
? new ErrorReporter({ opsAgent: config.ops.agent, app: name })
|
|
104
|
+
: undefined;
|
|
105
|
+
const { errorSink } = overrides;
|
|
108
106
|
const logger =
|
|
109
107
|
overrides.logger ??
|
|
110
108
|
createLogger(
|
|
111
109
|
discord.rootCommand,
|
|
112
|
-
errorReporter
|
|
110
|
+
errorReporter || errorSink
|
|
111
|
+
? (entry) => {
|
|
112
|
+
errorReporter?.record(entry);
|
|
113
|
+
errorSink?.(entry);
|
|
114
|
+
}
|
|
115
|
+
: undefined,
|
|
113
116
|
);
|
|
114
117
|
const speakers = speakerPolicy({
|
|
115
118
|
owners: [owner.id],
|
|
@@ -125,12 +128,24 @@ export async function defineRoundtable(
|
|
|
125
128
|
return {
|
|
126
129
|
options: {
|
|
127
130
|
logger,
|
|
131
|
+
environment: {
|
|
132
|
+
locale: config.locale,
|
|
133
|
+
timeZone: config.timeZone,
|
|
134
|
+
assistant: name,
|
|
135
|
+
rootCommand: discord.rootCommand,
|
|
136
|
+
agentDir: config.agentDir,
|
|
137
|
+
},
|
|
128
138
|
database: { url: config.databaseUrl },
|
|
129
139
|
toolTiers: toolTiers(config.toolTiers),
|
|
130
|
-
commands: { root: ownerRootCommand(discord.rootCommand) },
|
|
131
140
|
listeners: [
|
|
132
141
|
config.http.socketPath
|
|
133
|
-
? {
|
|
142
|
+
? {
|
|
143
|
+
id: "public",
|
|
144
|
+
socketPath: config.http.socketPath,
|
|
145
|
+
...(config.http.socketMode === undefined
|
|
146
|
+
? {}
|
|
147
|
+
: { mode: config.http.socketMode }),
|
|
148
|
+
}
|
|
134
149
|
: {
|
|
135
150
|
id: "public",
|
|
136
151
|
port: config.http.port,
|
|
@@ -138,12 +153,14 @@ export async function defineRoundtable(
|
|
|
138
153
|
? { hostname: config.http.hostname }
|
|
139
154
|
: {}),
|
|
140
155
|
},
|
|
156
|
+
...(overrides.listeners ?? []),
|
|
141
157
|
],
|
|
142
158
|
judgeModel: judgeThrough(modelRuntime, config.judge.model),
|
|
143
|
-
...overrides.
|
|
159
|
+
...(overrides.aborted ? { aborted: overrides.aborted } : {}),
|
|
144
160
|
},
|
|
145
161
|
plugins: [
|
|
146
|
-
|
|
162
|
+
...(config.memory ? [memoryPlugin({ owner })] : []),
|
|
163
|
+
scheduleStorePlugin(),
|
|
147
164
|
discordPlugin({
|
|
148
165
|
token: discord.token,
|
|
149
166
|
ownerId: owner.id,
|
|
@@ -151,8 +168,9 @@ export async function defineRoundtable(
|
|
|
151
168
|
speakers,
|
|
152
169
|
rootCommand: discord.rootCommand,
|
|
153
170
|
dataDir: config.dataDir,
|
|
154
|
-
|
|
155
|
-
|
|
171
|
+
...(discord.refusalHint === undefined
|
|
172
|
+
? {}
|
|
173
|
+
: { refusalHint: discord.refusalHint }),
|
|
156
174
|
}),
|
|
157
175
|
modulesPlugin({
|
|
158
176
|
owner,
|
|
@@ -161,8 +179,18 @@ export async function defineRoundtable(
|
|
|
161
179
|
agentDir: config.agentDir,
|
|
162
180
|
dataDir: config.dataDir,
|
|
163
181
|
delegation: config.delegation,
|
|
164
|
-
...overrides.modules,
|
|
165
182
|
}),
|
|
183
|
+
...(discord.admin ? [discordAdminPlugin({ owner })] : []),
|
|
184
|
+
...(config.skills
|
|
185
|
+
? [
|
|
186
|
+
skillsPlugin({
|
|
187
|
+
guildId: discord.guild,
|
|
188
|
+
reposDir: config.skills.reposDir ?? join(config.dataDir, "repos"),
|
|
189
|
+
writtenDir: join(config.dataDir, "skills"),
|
|
190
|
+
builtinDir: config.skills.builtinDir ?? join(ASSETS, "skills"),
|
|
191
|
+
}),
|
|
192
|
+
]
|
|
193
|
+
: []),
|
|
166
194
|
agentServerPlugin({
|
|
167
195
|
guildId: discord.guild,
|
|
168
196
|
entryChannelId: discord.entryChannel,
|
|
@@ -178,14 +206,10 @@ export async function defineRoundtable(
|
|
|
178
206
|
workDir: config.workDir,
|
|
179
207
|
shellUser: userInfo().username,
|
|
180
208
|
prompts: { shared, guest },
|
|
181
|
-
skills: {
|
|
182
|
-
reposDir: config.skills.reposDir ?? join(config.dataDir, "repos"),
|
|
183
|
-
writtenDir: join(config.dataDir, "skills"),
|
|
184
|
-
builtinDir: config.skills.builtinDir ?? join(ASSETS, "skills"),
|
|
185
|
-
},
|
|
186
209
|
avatarListener: "public",
|
|
210
|
+
avatarUrl: config.http.publicUrl,
|
|
211
|
+
avatarReference: config.avatar ?? join(ASSETS, "neutral.png"),
|
|
187
212
|
...(errorReporter ? { errorReporter } : {}),
|
|
188
|
-
...overrides.agentServer,
|
|
189
213
|
}),
|
|
190
214
|
seedsPlugin(config.agents),
|
|
191
215
|
...config.plugins,
|
package/src/core/define.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Static, TObject } from "typebox";
|
|
2
2
|
import { PluginError } from "./errors.ts";
|
|
3
3
|
import type { HoldRule } from "./holds.ts";
|
|
4
|
-
import type
|
|
4
|
+
import { type RoundtablePlugin, refuseRemovedFields } from "./plugin.ts";
|
|
5
5
|
import type { AgentTurnScope, ChannelKey, SessionTool } from "./sessions.ts";
|
|
6
6
|
import { toolError, toolText } from "./shared/tool-result.ts";
|
|
7
7
|
import { type Speaker, TIERS, type Tier } from "./speakers.ts";
|
|
@@ -150,5 +150,6 @@ export function definePlugin(plugin: RoundtablePlugin): RoundtablePlugin {
|
|
|
150
150
|
throw new PluginError(
|
|
151
151
|
`plugin ${plugin.name}: setup is missing. Give the function that returns what the plugin adds.`,
|
|
152
152
|
);
|
|
153
|
+
refuseRemovedFields(plugin);
|
|
153
154
|
return plugin;
|
|
154
155
|
}
|
|
@@ -22,23 +22,26 @@ import {
|
|
|
22
22
|
} from "discord.js";
|
|
23
23
|
import {
|
|
24
24
|
type Agent,
|
|
25
|
-
type AgentStore,
|
|
26
25
|
MAX_AVATAR_PROMPT_CHARS,
|
|
27
26
|
MAX_PROMPT_CHARS,
|
|
28
27
|
} from "../agents/agent-store.ts";
|
|
29
|
-
import type { AgentTeam } from "../agents/agent-team.ts";
|
|
30
28
|
import type { AvatarMode } from "../agents/agent-tools.ts";
|
|
31
|
-
import type { AvatarStudio } from "../agents/avatar-studio.ts";
|
|
32
29
|
import { AgentError } from "../domain/errors.ts";
|
|
33
30
|
import { messages } from "../i18n/index.ts";
|
|
34
31
|
import { THINKING_LEVELS, thinkingLabel } from "../models.ts";
|
|
35
32
|
import {
|
|
36
33
|
BUILTIN_SKILLS,
|
|
37
|
-
type SkillRegistry,
|
|
38
34
|
type SkillSet,
|
|
39
35
|
} from "../modules/skills/skill-registry.ts";
|
|
36
|
+
import type {
|
|
37
|
+
AgentDirectory,
|
|
38
|
+
AgentTeam,
|
|
39
|
+
AvatarStudio,
|
|
40
|
+
SkillRegistry,
|
|
41
|
+
} from "../services.ts";
|
|
40
42
|
import { OwnerFacingError, ownerPanel } from "./owner-panel.ts";
|
|
41
43
|
|
|
44
|
+
/** The custom-id prefixes of the panel's buttons and forms; posted panels carry them, so they never change. */
|
|
42
45
|
export const AGENT_BUTTON_PREFIX = "roundtable:agent:";
|
|
43
46
|
export const AGENT_MODAL_PREFIX = "roundtable:agent-modal:";
|
|
44
47
|
/** A prompt this long goes out as a file, so the panel stays inside Discord's text limit. */
|
|
@@ -75,13 +78,22 @@ const ACTIONS: readonly Action[] = [
|
|
|
75
78
|
];
|
|
76
79
|
|
|
77
80
|
export interface AgentCommandsOptions {
|
|
78
|
-
store: Pick<
|
|
81
|
+
store: Pick<AgentDirectory, "agentByChannel" | "groupByChannel" | "agent">;
|
|
79
82
|
team: Pick<
|
|
80
83
|
AgentTeam,
|
|
81
84
|
"redrawAvatar" | "update" | "modelOf" | "usableModels" | "defaultModel"
|
|
82
85
|
>;
|
|
83
|
-
studio: Pick<AvatarStudio, "url">;
|
|
84
|
-
skills
|
|
86
|
+
studio: Pick<AvatarStudio, "url" | "canDraw">;
|
|
87
|
+
/** The skills an agent carries; without the skills addon the panel lists none. */
|
|
88
|
+
skills?: Pick<SkillRegistry, "carried">;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** An agent's panel as a message: one Components V2 container, with the prompt attached as a file when it is long. */
|
|
92
|
+
export interface AgentPanelMessage {
|
|
93
|
+
components: ContainerBuilder[];
|
|
94
|
+
flags: MessageFlags.IsComponentsV2;
|
|
95
|
+
allowedMentions: { parse: never[] };
|
|
96
|
+
files: AttachmentBuilder[];
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
/** The skills line of the panel: built-in ones marked, missing ones with their reason. */
|
|
@@ -107,7 +119,7 @@ export class AgentCommands {
|
|
|
107
119
|
}
|
|
108
120
|
|
|
109
121
|
/** The panel for the agent of a channel; an error when the channel has none. */
|
|
110
|
-
panel(channelId: string, note?: string) {
|
|
122
|
+
panel(channelId: string, note?: string): AgentPanelMessage {
|
|
111
123
|
const { store } = this.#options;
|
|
112
124
|
const agent = store.agentByChannel(channelId);
|
|
113
125
|
if (!agent)
|
|
@@ -119,8 +131,10 @@ export class AgentCommands {
|
|
|
119
131
|
return this.#panel(agent, note);
|
|
120
132
|
}
|
|
121
133
|
|
|
122
|
-
#panel(agent: Agent, note?: string) {
|
|
134
|
+
#panel(agent: Agent, note?: string): AgentPanelMessage {
|
|
123
135
|
const text = messages();
|
|
136
|
+
// Without an image provider nothing can be drawn, so the avatar buttons are not offered.
|
|
137
|
+
const drawing = this.#options.studio.canDraw;
|
|
124
138
|
const inline = agent.prompt.length <= INLINE_PROMPT_CHARS;
|
|
125
139
|
const { model, thinking } = this.#options.team.modelOf(agent.name);
|
|
126
140
|
const follows =
|
|
@@ -136,7 +150,12 @@ export class AgentCommands {
|
|
|
136
150
|
model,
|
|
137
151
|
thinking: thinkingLabel(thinking),
|
|
138
152
|
follows,
|
|
139
|
-
skills: skillsText(
|
|
153
|
+
skills: skillsText(
|
|
154
|
+
this.#options.skills?.carried(agent.name) ?? {
|
|
155
|
+
skills: [],
|
|
156
|
+
missing: [],
|
|
157
|
+
},
|
|
158
|
+
),
|
|
140
159
|
}),
|
|
141
160
|
),
|
|
142
161
|
)
|
|
@@ -149,7 +168,9 @@ export class AgentCommands {
|
|
|
149
168
|
)
|
|
150
169
|
.addTextDisplayComponents(
|
|
151
170
|
new TextDisplayBuilder().setContent(
|
|
152
|
-
|
|
171
|
+
drawing
|
|
172
|
+
? text.agentAvatarPromptText(agent.avatarPrompt)
|
|
173
|
+
: text.agentNoImageProvider,
|
|
153
174
|
),
|
|
154
175
|
new TextDisplayBuilder().setContent(
|
|
155
176
|
inline
|
|
@@ -177,9 +198,13 @@ export class AgentCommands {
|
|
|
177
198
|
.addActionRowComponents(
|
|
178
199
|
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
|
179
200
|
button("edit", text.agentEditPromptLabel, ButtonStyle.Primary),
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
201
|
+
...(drawing
|
|
202
|
+
? [
|
|
203
|
+
button("redraw", text.agentRedrawLabel),
|
|
204
|
+
button("newprompt", text.agentNewAvatarPromptLabel),
|
|
205
|
+
button("editavatar", text.agentEditAvatarLabel),
|
|
206
|
+
]
|
|
207
|
+
: []),
|
|
183
208
|
button("model", text.agentModelLabel),
|
|
184
209
|
),
|
|
185
210
|
)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type Interaction, MessageFlags } from "discord.js";
|
|
2
|
+
import type { AgentServer, SkillRegistry } from "../services.ts";
|
|
3
|
+
import {
|
|
4
|
+
AGENT_BUTTON_PREFIX,
|
|
5
|
+
AGENT_MODAL_PREFIX,
|
|
6
|
+
AgentCommands,
|
|
7
|
+
type AgentPanelMessage,
|
|
8
|
+
} from "./agent-commands.ts";
|
|
9
|
+
import type { CommandGuard } from "./interaction-module.ts";
|
|
10
|
+
|
|
11
|
+
export interface AgentPanelOptions {
|
|
12
|
+
/** Who may press the panel's buttons and submit its forms. */
|
|
13
|
+
guard: CommandGuard;
|
|
14
|
+
/** The agent server whose agents the panel shows and edits. */
|
|
15
|
+
agents: AgentServer;
|
|
16
|
+
/** The skills an agent carries, listed in the panel; without it the panel lists none. */
|
|
17
|
+
skills?: Pick<SkillRegistry, "carried">;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** An agent's panel: its prompt, picture, model, and skills, with buttons and forms to change them. */
|
|
21
|
+
export interface AgentPanel {
|
|
22
|
+
/**
|
|
23
|
+
* The panel of the agent whose channel this is. Throws OwnerFacingError, which a command run
|
|
24
|
+
* through the guard answers as a panel, when the channel belongs to no agent.
|
|
25
|
+
*/
|
|
26
|
+
open(channelId: string): AgentPanelMessage;
|
|
27
|
+
/**
|
|
28
|
+
* Answers the panel's own buttons and form, for the owner only and deferred as Discord needs;
|
|
29
|
+
* true when it handled the interaction. The custom ids are fixed, so panels already posted keep working.
|
|
30
|
+
*/
|
|
31
|
+
handles(interaction: Interaction): Promise<boolean>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Builds the agent panel of `/<root> profile`; the caller adds the subcommand and routes the panel's components here. */
|
|
35
|
+
export function agentPanel(options: AgentPanelOptions): AgentPanel {
|
|
36
|
+
const { guard, agents } = options;
|
|
37
|
+
const commands = new AgentCommands({
|
|
38
|
+
store: agents.directory,
|
|
39
|
+
team: agents.team,
|
|
40
|
+
studio: agents.avatars,
|
|
41
|
+
...(options.skills ? { skills: options.skills } : {}),
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
open: (channelId) => commands.panel(channelId),
|
|
45
|
+
async handles(interaction) {
|
|
46
|
+
if (
|
|
47
|
+
interaction.isButton() &&
|
|
48
|
+
interaction.customId.startsWith(AGENT_BUTTON_PREFIX)
|
|
49
|
+
) {
|
|
50
|
+
if (!guard.isOwner(interaction)) return true;
|
|
51
|
+
await guard.run(interaction, () => commands.button(interaction));
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (
|
|
55
|
+
interaction.isModalSubmit() &&
|
|
56
|
+
interaction.customId.startsWith(AGENT_MODAL_PREFIX)
|
|
57
|
+
) {
|
|
58
|
+
if (!guard.isOwner(interaction)) return true;
|
|
59
|
+
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
|
60
|
+
await guard.run(interaction, () => commands.submit(interaction));
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type PermissionResolvable,
|
|
12
12
|
type TextChannel,
|
|
13
13
|
} from "discord.js";
|
|
14
|
+
import { freeze } from "../freeze.ts";
|
|
14
15
|
import type { ChannelOperation } from "./channel-operations.ts";
|
|
15
16
|
import {
|
|
16
17
|
type ChannelExecutor,
|
|
@@ -18,7 +19,9 @@ import {
|
|
|
18
19
|
} from "./channel-operations.ts";
|
|
19
20
|
|
|
20
21
|
/** Discord permissions the bot (and the owner granting it) needs for each operation. */
|
|
21
|
-
export const OPERATION_PERMISSIONS:
|
|
22
|
+
export const OPERATION_PERMISSIONS: Readonly<
|
|
23
|
+
Record<ChannelOperation, readonly bigint[]>
|
|
24
|
+
> = freeze({
|
|
22
25
|
read: [
|
|
23
26
|
PermissionFlagsBits.ViewChannel,
|
|
24
27
|
PermissionFlagsBits.ReadMessageHistory,
|
|
@@ -47,7 +50,7 @@ export const OPERATION_PERMISSIONS: Record<ChannelOperation, bigint[]> = {
|
|
|
47
50
|
PermissionFlagsBits.ViewChannel,
|
|
48
51
|
PermissionFlagsBits.ManageRoles,
|
|
49
52
|
],
|
|
50
|
-
};
|
|
53
|
+
});
|
|
51
54
|
|
|
52
55
|
export type ManagedChannel = TextChannel | NewsChannel;
|
|
53
56
|
/** What the owner's own agent may also reach: threads of those channels. */
|
|
@@ -93,7 +96,7 @@ export function operationPermissions(
|
|
|
93
96
|
? PermissionFlagsBits.SendMessagesInThreads
|
|
94
97
|
: flag,
|
|
95
98
|
)
|
|
96
|
-
: needed;
|
|
99
|
+
: [...needed];
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
interface ChannelFile {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type TSchema, Type } from "typebox";
|
|
2
2
|
import Value from "typebox/value";
|
|
3
|
+
import { freeze } from "../freeze.ts";
|
|
3
4
|
import { type Messages, messages } from "../i18n/index.ts";
|
|
4
5
|
|
|
5
6
|
/** What a call may do in one channel, with the owner-facing label; grants and permission checks name these. */
|
|
6
|
-
export const CHANNEL_OPERATIONS = [
|
|
7
|
+
export const CHANNEL_OPERATIONS = Object.freeze([
|
|
7
8
|
"read",
|
|
8
9
|
"send",
|
|
9
10
|
"edit",
|
|
@@ -11,7 +12,7 @@ export const CHANNEL_OPERATIONS = [
|
|
|
11
12
|
"delete",
|
|
12
13
|
"channel",
|
|
13
14
|
"permissions",
|
|
14
|
-
] as const;
|
|
15
|
+
] as const);
|
|
15
16
|
export type ChannelOperation = (typeof CHANNEL_OPERATIONS)[number];
|
|
16
17
|
|
|
17
18
|
export const isChannelOperation = (value: string): value is ChannelOperation =>
|
|
@@ -37,7 +38,7 @@ const message = { channelId: id, messageId: id };
|
|
|
37
38
|
const strict = { additionalProperties: false } as const;
|
|
38
39
|
|
|
39
40
|
/** One upload may carry at most this much, in all files together. */
|
|
40
|
-
|
|
41
|
+
const CHANNEL_UPLOAD_MAX_BYTES = 8 * 1024 * 1024;
|
|
41
42
|
|
|
42
43
|
const file = Type.Object(
|
|
43
44
|
{
|
|
@@ -69,14 +70,16 @@ const files = Type.Optional(
|
|
|
69
70
|
}),
|
|
70
71
|
);
|
|
71
72
|
|
|
72
|
-
interface ChannelTool {
|
|
73
|
-
operation: ChannelOperation;
|
|
74
|
-
schema:
|
|
75
|
-
description: string;
|
|
73
|
+
export interface ChannelTool {
|
|
74
|
+
readonly operation: ChannelOperation;
|
|
75
|
+
readonly schema: Readonly<TSchema>;
|
|
76
|
+
readonly description: string;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
/**
|
|
79
|
-
export const CHANNEL_TOOLS: Record<string, ChannelTool
|
|
79
|
+
/** Discord tool descriptions and schemas, each gated by one channel operation. */
|
|
80
|
+
export const CHANNEL_TOOLS: Readonly<Record<string, ChannelTool>> = freeze<
|
|
81
|
+
Record<string, ChannelTool>
|
|
82
|
+
>({
|
|
80
83
|
discord_get_channel_info: {
|
|
81
84
|
operation: "read",
|
|
82
85
|
schema: Type.Object(channel, strict),
|
|
@@ -246,7 +249,7 @@ export const CHANNEL_TOOLS: Record<string, ChannelTool> = {
|
|
|
246
249
|
description:
|
|
247
250
|
"Remove a role's or member's permission overwrite in this channel.",
|
|
248
251
|
},
|
|
249
|
-
};
|
|
252
|
+
});
|
|
250
253
|
|
|
251
254
|
/** A tool call that cannot be run as asked; the code is safe to show the caller. */
|
|
252
255
|
export class ChannelToolError extends Error {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { PluginError } from "../errors.ts";
|
|
2
|
+
import { type ComposedCommands, composeCommands } from "./compose-commands.ts";
|
|
3
|
+
import type {
|
|
4
|
+
CommandRegistrar,
|
|
5
|
+
CommandRoot,
|
|
6
|
+
InteractionContribution,
|
|
7
|
+
} from "./interaction-module.ts";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The commands the plugins add while they set up, composed once under the root command. Adding
|
|
11
|
+
* after the composition is refused, because Discord registers the commands as it connects and a
|
|
12
|
+
* late one would never reach it.
|
|
13
|
+
*/
|
|
14
|
+
export class CommandCollection {
|
|
15
|
+
readonly #contributions: InteractionContribution[] = [];
|
|
16
|
+
#closed = false;
|
|
17
|
+
|
|
18
|
+
/** What plugins reach as `DISCORD.commands`. */
|
|
19
|
+
readonly registrar: CommandRegistrar = {
|
|
20
|
+
add: (contribution) => {
|
|
21
|
+
if (this.#closed)
|
|
22
|
+
throw new PluginError(
|
|
23
|
+
"commands can be added only while plugins set up: the Discord plugin composed them in its preflight, before any service started. Call commands.add from setup.",
|
|
24
|
+
);
|
|
25
|
+
if (
|
|
26
|
+
typeof contribution?.module?.commands !== "function" ||
|
|
27
|
+
typeof contribution.module.handle !== "function"
|
|
28
|
+
)
|
|
29
|
+
throw new PluginError(
|
|
30
|
+
"commands.add takes { module, rootOptions? }, where the module has commands() and handle(interaction).",
|
|
31
|
+
);
|
|
32
|
+
this.#contributions.push(contribution);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Every contribution added so far, in the order added. */
|
|
37
|
+
added(): readonly InteractionContribution[] {
|
|
38
|
+
return this.#contributions;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Composes every added contribution, in the order added; closes the collection to later additions. */
|
|
42
|
+
compose(root: CommandRoot): ComposedCommands {
|
|
43
|
+
this.#closed = true;
|
|
44
|
+
return composeCommands(root, this.#contributions);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -2,15 +2,15 @@ import {
|
|
|
2
2
|
type RESTPostAPIChatInputApplicationCommandsJSONBody,
|
|
3
3
|
SlashCommandBuilder,
|
|
4
4
|
} from "discord.js";
|
|
5
|
+
import { PluginError } from "../errors.ts";
|
|
5
6
|
import type {
|
|
6
7
|
CommandRoot,
|
|
7
8
|
InteractionContribution,
|
|
8
9
|
InteractionModule,
|
|
9
|
-
} from "
|
|
10
|
-
import { PluginError } from "../errors.ts";
|
|
10
|
+
} from "./interaction-module.ts";
|
|
11
11
|
|
|
12
12
|
/** Every command to register with Discord, and the modules that answer them in order. */
|
|
13
|
-
export interface
|
|
13
|
+
export interface ComposedCommands {
|
|
14
14
|
commands: RESTPostAPIChatInputApplicationCommandsJSONBody[];
|
|
15
15
|
modules: InteractionModule[];
|
|
16
16
|
}
|
|
@@ -20,10 +20,10 @@ export interface ComposedInteractions {
|
|
|
20
20
|
* the modules' own commands. Refuses a module that registers the root itself, and any command or
|
|
21
21
|
* subcommand name used twice.
|
|
22
22
|
*/
|
|
23
|
-
export function
|
|
23
|
+
export function composeCommands(
|
|
24
24
|
root: CommandRoot,
|
|
25
25
|
contributions: readonly InteractionContribution[],
|
|
26
|
-
):
|
|
26
|
+
): ComposedCommands {
|
|
27
27
|
const commands = contributions.flatMap(({ module }) => module.commands());
|
|
28
28
|
const names = new Set<string>();
|
|
29
29
|
for (const command of commands) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { AgentChannels, DashboardBoard } from "../agents/agent-ports.ts";
|
|
2
|
+
import type { ChannelKey } from "../domain/conversation.ts";
|
|
3
|
+
import type { OwnerOperations } from "../modules/discord-admin/discord-admin.ts";
|
|
4
|
+
import type { ChannelExecutor } from "./channel-operations.ts";
|
|
5
|
+
import type { ThreadHost } from "./dispatch-threads.ts";
|
|
6
|
+
|
|
7
|
+
/** What Discord calls a channel, for the web app's lists. */
|
|
8
|
+
export type ChannelInfo =
|
|
9
|
+
| { kind: "dm"; name: string }
|
|
10
|
+
| { kind: "guild"; name: string; guild: string; guildId: string };
|
|
11
|
+
|
|
12
|
+
/** The Discord connection as plugins use it, apart from the chat surface the host drives. */
|
|
13
|
+
export interface DiscordConnection {
|
|
14
|
+
/** The owner's direct-message channel. */
|
|
15
|
+
ownerChannel(): Promise<ChannelKey>;
|
|
16
|
+
/** Sends the owner a direct message. */
|
|
17
|
+
notifyOwner(text: string): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* What a channel is called: undefined when Discord no longer knows it; throws while the
|
|
20
|
+
* connection is not ready or Discord cannot be reached.
|
|
21
|
+
*/
|
|
22
|
+
channelInfo(channelId: string): Promise<ChannelInfo | undefined>;
|
|
23
|
+
/** Channel operations for outside agents; undefined until the connection is ready. */
|
|
24
|
+
channelExecutor(): ChannelExecutor | undefined;
|
|
25
|
+
/** Reports deleted server channels by id; register before the connection starts. */
|
|
26
|
+
onChannelDeleted(listener: (channelId: string) => void): void;
|
|
27
|
+
/** The agent server's channels and webhooks. */
|
|
28
|
+
agentChannels(guildId: string): AgentChannels;
|
|
29
|
+
/** The agent server's `#dashboard` and its pinned status message. */
|
|
30
|
+
agentDashboard(guildId: string): DashboardBoard;
|
|
31
|
+
/** Threads for background dispatches, in the channel that started each. */
|
|
32
|
+
threadHost(): ThreadHost;
|
|
33
|
+
/** Discord reading and management for the owner's agent; calls fail until the connection is ready. */
|
|
34
|
+
ownerOperations(): OwnerOperations;
|
|
35
|
+
}
|