pi-roundtable 0.1.0 → 0.2.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 +148 -0
- package/README.md +10 -6
- package/README.zh-TW.md +144 -0
- 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 +11 -3
- 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
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,153 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.2.1] - 2026-10-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- A Traditional Chinese README, `README.zh-TW.md`, linked from the English one.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- `bin` is `src/cli/roundtable.mjs`, without the leading `./`: the npm that publishes from CI warned that it removed the `./` path, though 0.2.0's registry entry kept the `roundtable` command. An export test refuses a `bin` path that starts with a dot.
|
|
17
|
+
|
|
18
|
+
## [0.2.0] - 2026-10-01
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Main entry contracts and operational errors: `Admission`, `AgentTurnScope`, `AttachmentRef`, `BackgroundTurn`, `ChannelKey`, `ConversationPort`, `DefineOverrides`, `DrainOptions`, `EventSink`, `HoldCheck`, `HoldContext`, `HttpRoute`, `ImageDrawer`, `InboundMessage`, `Judge`, `JudgeError`, `JudgeModel`, `LinkedSessions`, `ListenerAddress`, `ListenerConfig`, `Locale`, `LogEntry`, `LogFn`, `Logger`, `MigrationError`, `Pronouns`, `ProviderError`, `Providers`, `QueuePort`, `ReferenceImage`, `ResolvedProviders`, `ScheduledOutcome`, `SessionContext`, `SessionPlan`, `SessionToolSnapshot`, `ThinkingLevel`, `TierConfig`, `ToolSelection`, `ToolTierTable`, `ToolTiers`, `TransientTask`.
|
|
23
|
+
- Testing fixtures: `OWNER_SPEAKER`, `TEST_GUILD`, `TestStore`, `describeDb`, `fakeThreads`, `openTestStore`, `silentLogger`, `testDatabaseUrl`, `useTestLocale`.
|
|
24
|
+
- The unstable-before-1.0 `pi-roundtable/kit` entry for owner commands, claims, and naming existing core services.
|
|
25
|
+
Its helpers and their supporting data types are `AUTO_THINKING`, `AgentCategory`, `AgentChannelLookup`, `AgentChannels`, `AgentError`, `AgentModels`, `AgentOps`, `AgentPost`, `AgentTurnRunner`, `Approval`, `AskOption`, `AssistantLike`, `Backlog`, `CategoryLayout`, `ChannelMessage`, `ChannelQueue`, `ChatSurface`, `ChoiceAnswer`, `ChoiceQuestion`, `DashboardBoard`, `DelegationWorker`, `DispatchThread`, `DispatchThreads`, `DispatchThreadsOptions`, `GroupMessage`, `ModelRef`, `OutboundReply`, `OwnerAnswer`, `OwnerIdentity`, `OwnerNotifier`, `OwnerPrompts`, `OwnerQuestion`, `ScoreQuestion`, `SpeakerFacts`, `SpeakerPolicy`, `THINKING_LEVELS`, `ThinkingPicker`, `ThreadHost`, `YesNoQuestion`, `attachmentsOf`, `channelSegment`, `discordKey`, `formatModelRef`, `headline`, `lastAssistant`, `outcome`, `ownerAttachmentDir`, `parseModelRef`, `quietLinks`, `settleTurn`, `splitReply`, `textOf`, `thinkingLabel`, `toolError`, `toolText`, `withAttachmentsBlock`, `withReference`.
|
|
26
|
+
- The migration ledger: a migration's `runs` is `"once"` (the default) or `"every-boot"`, and the host records each `once` migration in the table `roundtable_migrations` under the id `<plugin>/<name>`, created by the host itself. A `once` migration runs in its own transaction under an advisory lock with its ledger row written in that transaction, so a failed migration records nothing and two hosts starting together apply it once; an `every-boot` migration runs each start, unrecorded, and must be idempotent, as every migration had to be. A start logs one `migrations` line (ids applied now, number skipped, number every-boot), a migration that fails is named by its id, and `roundtable doctor` runs the same runner in a rolled-back transaction. `migrateDatabase(url, plugins)` and `MigrationReport` are in the main entry: they run plugins' migrations over a URL with the ledger and close their connection, for a test or a script.
|
|
27
|
+
- `Logger` is core's own structural interface (`debug`, `info`, `warn`, `error`, `fatal` as `LogFn`, and `child(fields)`), which a pino logger satisfies as it is, and `LogEntry` (one line as pino writes it) is in the main entry. Every plugin's `context.logger` is a child that adds `plugin: <name>` to its lines, and the error report head shows the plugin next to `app` and `module`; the fingerprint of an error is unchanged.
|
|
28
|
+
- `DefineOverrides.errorSink(entry)`: a plain function that also receives every `error` and `fatal` line of the logger `defineRoundtable` builds, after the ops agent's report (`config.ops`). It must not throw. A logger passed as `DefineOverrides.logger` is the caller's own, and forwards its error lines itself.
|
|
29
|
+
- The `requiredTools` contribution: the tool names startup refuses to run without, besides those each session tool requires, merged over plugins with each name once (`LinkedSessions.requiredTools`). A plugin that owns the owner's conversations contributes it with the `"owner"` persona.
|
|
30
|
+
- Test fidelity: `testHost` with `TestHost` and `TestHostOptions` boots the built-in plugins and yours over the test database with Discord and the runtime standing in, and returns the probe's `context`, the `conversations`, the slash commands `added` and `composed()`, the session tools of an owner or agent scope, a real `sessionContext`, and `stop()`; `sessionTools` lists the tools of any session extension, including one that also registers commands or event handlers. `testPlugin` gains the `holds` result (the plugin's hold rules chained as the host links them), the `owner` and `forwardJoinMs` options, the real `BACKGROUND_TURNS` by default, the real confirmation judge as `AGENTS.approvals` once `AGENTS` is given and a judge provider is, and the agent server's own claim when `AGENTS` is given a `team`. `holdChain` is in `pi-roundtable/kit`, and `useEagerCatalog` and `eagerText` are in `pi-roundtable/testing`: the probe that proves a value was not built from the catalog before the host applied its environment.
|
|
31
|
+
- Socket permissions: a unix-socket `ListenerConfig` takes `mode` (the socket file's permission bits), and the configuration's `http.socketMode` sets it for the `public` listener. The default is `0o660`, so only the socket's owner and group connect; a proxy that runs as a user outside that group needs `0o666` set explicitly.
|
|
32
|
+
- Route errors: a route whose handler throws or rejects answers `500 Internal Server Error` with that fixed body and the listener keeps serving. The host logs one error line with the route's `name` and `listener`, never the request URL, since paths may hold tokens. The listeners' servers also answer errors raised outside a route with the same 500.
|
|
33
|
+
- Declaration-signature and type-leak baseline checks, alongside parser-based runtime/type export snapshots.
|
|
34
|
+
- A guide entry index, context service types, database/locale fixture notes, and tested consumer compiler requirements.
|
|
35
|
+
- `PluginContext.env` (`HostEnv`: `locale`, `timeZone`, `now()`) and the `environment` host option (`HostEnvironment`: locale, time zone, assistant name, root command, Pi agent directory), so a plugin reads its own host's time zone and locale.
|
|
36
|
+
|
|
37
|
+
- Neutral hooks and keys, replacing the built-in-shaped ones (see Removed): `Service.startInBackground`, the `serviceStarted` event with `ServiceStartedEvent` and `ServiceStartOutcome`, `ChannelClaim.stop`, the agent server's names `AGENT_SERVER_PLUGIN`, `AGENT_TEAM_SERVICE` and `AGENT_SERVER_PRIORITY`, `parseChannelKey` and `channelKey` (`channelKey(surface, id)`) for `<surface>:<id>` channel keys, and `ConversationKind`, which is any string.
|
|
38
|
+
|
|
39
|
+
- The chat-surface slot: a plugin contributes `surfaces`, each a `ChatSurface` for the channel keys of one prefix (`surface`, `start(deliver)`, `sendReply`, and optionally `stop`, `startTyping`, `showStop`, `react`, `unreact`, `prompts`), and calls them through `PluginContext.surfaces`, a `SurfacePort` that picks the surface by a key's prefix (`of`, `sendReply`, `startTyping`, `showStop`, `react`, `unreact`, `prompts`; calls during setup throw `NotLinkedError`).
|
|
40
|
+
The host starts each surface as a service named `surface:<prefix>` before its plugin's own services, refuses two surfaces with one prefix, and logs and drops a message delivered under another prefix.
|
|
41
|
+
The main entry now also names `OutboundReply`, `OwnerPrompts`, `Approval`, `AskOption`, `OwnerAnswer`, and `OwnerQuestion`, which a surface's methods use; they moved there from `pi-roundtable/kit`, together with `ChatSurface`.
|
|
42
|
+
`examples/fake-surface.ts` is an in-memory surface for tests and a model for a real one.
|
|
43
|
+
- The agent server asks the owner for approvals and `ask_user` answers through `context.surfaces.prompts`, so they work in a channel of any surface that has `prompts`; before, only Discord's channels had them.
|
|
44
|
+
- The runtime slot: `Providers.runtime` takes a `RuntimeFactory` (`(deps: RuntimeDeps) => AgentRuntime`), and a plugin that fills it replaces the whole conversation runtime (agent turns, the owner's conversations, steering, held actions, transcripts) in place of Pi. The agent server builds the Pi runtime only when no plugin fills the slot, so a host that fills none is unchanged. `RuntimeDeps` hands the factory `logger`, `env`, `owner`, `sessions()`, `toolTiers`, `prompts`, `agents` (`AgentSessions`), `confirmations` (`HeldActionStore`), and `judge`.
|
|
45
|
+
`AgentRuntime` (the main entry's now, with `ContextUse`) has the methods the agent team calls besides the turn: `heldActions`, and the optional `contextUsage`, `preflight`, and `dispose`; the agent server's preflight and its `runtime` service call the optional ones. The unknown-slot refusal names `runtime` among the valid slots.
|
|
46
|
+
`AgentRunError` is exported, `TurnResult.error` is an `Error`, and the turn types a runtime author needs moved to the main entry from the kit: `AgentRuntime`, `AgentSessions`, `AttachmentFailure`, `ContextUse`, `HeldCall`, `LoadedSkill`, `ModelImage`, `PendingConfirmation`, `StoredAttachment`, `TranscriptEntry`, `TurnAttachments`, `TurnRequest`, `TurnResult`, and `TurnSelection`.
|
|
47
|
+
- Personas: a plugin contributes `personas` (`Persona`: `{ kind, prompt() }`), the system prompt of every non-agent conversation of one kind, and `LinkedSessions.persona(kind)` looks one up. `TurnRequest.kind` names the conversation's kind (`"owner"` when absent), and the Pi runtime picks the persona when it makes the session. Two personas of one kind, and the reserved kind `"agent"`, are refused when the host starts, naming the plugins; a turn of a kind with no persona is refused naming `personas` instead of running with the owner's prompt; `"owner"` keeps the host's `ownerSessions.persona`, and a contributed `"owner"` persona is refused on a host that sets one.
|
|
48
|
+
- `context.turns` (`ConversationTurns`, with `ConversationTurnInput`): `run({ channel, kind, text, speaker, attachments?, selection?, steerable?, interactive?, confirmed?, reply? })` runs one turn of a conversation a claim owns, over the runtime and the surfaces: it shows typing and the stop control, emits `turnStarted` and `turnEnded`, settles a thrown runtime into a failed result, and posts the answer or a failure or stopped notice through the surface unless `reply` is given. Calls during `setup` reject with `NotLinkedError`.
|
|
49
|
+
- `testPlugin` options `surfaces` (injected chat surfaces), `core` (`TestCore`: the parts of `context.core` the plugin reads; an ungiven member throws naming the option), `conversations` (overrides), and `turns`, and the result fields `conversations`, `turns`, `surfaces`, and `runtime` (the runtime the plugin's `runtime` provider built from stand-in dependencies). `runTool(name, args, { speaker, channel })` runs in the given channel. `conversations` now routes to the claims of the plugin under test, so a message a surface delivers reaches them.
|
|
50
|
+
- `examples/echo-runtime.ts` (a plugin that fills the `runtime` slot) and `examples/study-room.ts` (a conversation kind with its own persona, run through `context.turns`), each with a test that uses only the public entries.
|
|
51
|
+
- `activeToolsExtension` in `pi-roundtable/kit`, taking a function that returns the tool names: the extension that pins a session's active tools before every run, the one the core places last in each session, for a runtime or worker of your own.
|
|
52
|
+
- Background targets: a plugin contributes `backgroundTargets` (`BackgroundTarget`: `name`, `label(locale)`, and optional `schedules` and `delegation` limits), which say whose turn a schedule or a delegated task is and how much may be scheduled or delegated for it. `ConversationPort.target(name)` reads one when it is used, the router skips a turn whose target no plugin contributes (`no plugin contributes the background target "<name>"`, recorded as the schedule's last status, never falling back to another target), and two targets of one name are refused at start, naming both plugins. The agent server contributes `OWNER_TARGET` (name `"owner"`, exported from the main entry), and its claim answers only that target. `examples/support-desk.ts` is a plugin with a target of its own, with a test.
|
|
53
|
+
- Keyed services: a plugin provides a service other plugins read, and a plugin replaces a built-in one.
|
|
54
|
+
`serviceKey` (`serviceKey(id)`) makes a typed name, `ServiceKey`, and `PluginContext.services` (`Services`) has `get(KEY)`, `find(KEY)` and `provide(KEY, value)`; `RoundtablePlugin` has `provides` (the keys its setup provides, which the host checks are provided once setup returns) and `replaces` (the built-in services it takes over: the host drops the plugin that provides them and sets the replacement up where it stood).
|
|
55
|
+
`get` of a key not provided yet names the key and the plugin to register first, `find` is `undefined` when no registered plugin declares it and throws when one declares it but has not set up yet, `provide` is refused for a key the plugin does not declare, after setup, and twice, and the host refuses two plugins that declare one key, a replaced key nobody else provides, two replacements of one key, a replacement that does not list the key in `provides`, and a partial replacement, where the dropped plugin also provides a key the replacement does not replace.
|
|
56
|
+
The built-in plugins provide `AGENTS` (`AgentServer`: `team`, `directory`, `runtime`, `approvals`, `avatars`), `SKILLS` (`SkillRegistry`), `SCHEDULES` (`ScheduleStore`), `MEMORY` (`MemoryStore`), `BACKGROUND_TURNS` (`BackgroundTurns`) and `DELEGATION` (`Delegator`), each a port interface that an object with the same methods satisfies, so a stranger can provide or fake one without a class.
|
|
57
|
+
The main entry also names the ports' parts and data types: `AgentChange`, `AgentDirectory`, `AgentServer`, `AgentTeam`, `AvatarMode`, `AvatarStudio`, `DelegationRequest`, `Memory`, `MemoryKind`, `MEMORY_KINDS`, `PromptMemory`, `SpeakerMemory`, and, moved from the kit, `Agent`, `AgentGroup`, `AgentStatus`, `GroupStatus`, `NewSchedule`, `Recurrence`, `ResolvedSkill`, `Schedule`, `ScheduleChange`, `SkillCatalogEntry`, `SkillSet`, `SkillSource`, `TeamAgentStatus`, `TeamStatus`, `ThinkingSetting`, `Weekday`; `TIERS` is exported (frozen), so `Tier` names a public value.
|
|
58
|
+
The Discord connection's key is in the Discord entry: `DISCORD` and `DiscordServices` (`connection`, `commands`, `guard`, `threads`) are in `pi-roundtable/discord`.
|
|
59
|
+
`examples/shared-services.ts` provides, reads and replaces a service, with a test.
|
|
60
|
+
- `testPlugin` option `services` (a list of `servicePair(KEY, { ... })`, replacing `core`; an ungiven member throws naming the option, a service not given reads as absent to `find` and `get` says to give it), and the testing entry exports `servicePair`, `ServicePair` and `FakeThreadHost`.
|
|
61
|
+
- `DelegationJob` and `DelegationOutcome` (in the main entry), the job a `Delegator` runs and how it ended.
|
|
62
|
+
|
|
63
|
+
- The unstable-before-1.0 `pi-roundtable/discord` entry, the one built on discord.js types (a test checks that the main and kit declarations reach none; `pi-roundtable/testing` names a few, through `testHost`'s composed commands: `ComposedCommands`, `CommandGuard`, `InteractionModule` and `RootOption`): `DISCORD` (`DiscordServices`: `connection`, `commands`, `guard`, `threads`), `DiscordConnection`, `CommandRegistrar`, `CommandGuard`, `CommandGuardOptions`, `commandGuard`, `composeCommands`, `ComposedCommands`, `InteractionContribution`, `InteractionModule`, `RootOption`, `CommandRoot`, `ownerRootCommand`, `ownerCommandModule`, `OwnerCommandHandlers`, `groupOption`, the panel helpers `OwnerFacingError`, `PanelContent`, `ephemeralPanel`, `ownerPanel`, `ownerPanels`, `plain`, `replyWithPanels`, the agent panel `agentPanel`, `AgentPanel`, `AgentPanelMessage`, `AgentPanelOptions`, and the channel-operation tables `CHANNEL_OPERATIONS`, `CHANNEL_TOOLS`, `ChannelExecutor`, `ChannelInfo`, `ChannelOperation`, `ChannelTool`, `ChannelToolError`, `DISCORD_ADMIN_TOOLS`, `ManagedChannel`, `OPERATION_PERMISSIONS`, `OwnerOperations`, `fetchManagedChannel`, `isChannelOperation`, `operationLabel`, `parseChannelTool`.
|
|
64
|
+
A plugin adds slash commands from `setup` with `context.services.get(DISCORD).commands.add({ module, rootOptions })`. The Discord plugin collects them and composes them under the root command in its preflight, so a duplicate command name, a module that registers the root, or a subcommand added twice stops the start before any service starts; `commands.add` after that preflight throws a `PluginError`. `CommandGuard.isOwner` takes anything with `user.id`, so a test needs no cast.
|
|
65
|
+
A plugin that only reads a service in `setup` and returns `{}` (such as one that only adds commands) is no longer refused as adding nothing.
|
|
66
|
+
- `agentPanel({ guard, agents, skills? })`: the agent's profile panel with its buttons and form, owning its custom-id prefixes (`roundtable:agent:` and `roundtable:agent-modal:`, unchanged, so panels already posted keep working), the owner check, and the deferred reply of the form.
|
|
67
|
+
- `discord.refusalHint` in the configuration (`DiscordOptions.refusalHint`): text appended as it is to the refusal a non-owner gets from the root command. The core's Chinese refusal no longer carries a sentence about one host's dice commands; a host that wants such a sentence passes it.
|
|
68
|
+
- The agent server publishes the avatar studio as `AgentServer.avatars` and builds it itself (from `config.http.publicUrl` and `config.avatar`).
|
|
69
|
+
- Testing: `fakeDiscord` and `FakeDiscord`, the `DISCORD` service for a plugin that adds slash commands (records `commands.add`, gives a guard, composes the tree).
|
|
70
|
+
|
|
71
|
+
- **The internal sweep (the last of the unreleased public-surface work):** what the host repository's own code imported from its local `pi-roundtable/internal` mapping has a public home or left the core, so the core's public entries are what a stranger uses to build the same things.
|
|
72
|
+
The main entry names `ConfigError`, `DelegationError`, `MemoryError` and `ScheduleError`, the errors the public ports throw, `NO_ATTACHMENTS` beside `TurnAttachments`, and `THE_SPEAKER` beside `Speaker`; the testing entry names `TestLocale`.
|
|
73
|
+
The kit gains the helpers for running a Pi session of your own: `mcpExtension` and `VirtualServer`, `mcpAdapterExtension` and `McpEndpoint`, `readAttachmentExtension`, `promptSlot` and `PromptSlot`, `workTimeout`, `approvalCard`, `canonicalJson`, `runWorkerTask`, `archiveSessions`, the host-shell `SHELL_TOOLS` and `shellHoldRule`, the tool helpers `textToolsExtension`, `requiredString`, `stringList`, `TextToolDef` and `ToolInput`, and `channelQueue` (a queue of your own, apart from the host's).
|
|
74
|
+
It also has the pieces for mirroring a built-in tool in a worker that cannot reach the host: `DELEGATE_TOOL`, `DELEGATE_TOOL_SPEC`, `SCHEDULE_TOOLS`, `isScheduleTool`, `callScheduleTool`, `scheduleToolSpecs`, and the types `ScheduleToolContext`, `ScheduleToolName`, `ScheduleToolSpec` and `ScheduleToolWording`.
|
|
75
|
+
The effort policy is a public knob: `effortJudge` (with `EffortBrief`, `EffortJudgeOptions`, `EffortLevel`, `EffortPicker`, `PreviousTurn` and `JUDGE_WORK`), which picks a turn's thinking level from a message.
|
|
76
|
+
Also in the kit: `thinkingLine` and `zonedStamp` (presentation), `checkRepoName`, `SKILL_LIST_TOOL` and `skillListExtension` (skills), and `searchTerms` (memory).
|
|
77
|
+
|
|
78
|
+
### Removed
|
|
79
|
+
|
|
80
|
+
**Breaking for 0.1.0, so this is 0.2.0.**
|
|
81
|
+
A plugin that still has one of these fields is refused when it is defined or when the host starts, with an error that names the replacement.
|
|
82
|
+
|
|
83
|
+
- `RoundtablePlugin.useCommands(composed)`: the composed slash commands go to the Discord plugin's surface, not to a plugin. Add commands from `setup` with `context.services.get(DISCORD).commands.add(...)` (see the slash-command entry below).
|
|
84
|
+
- `RoundtablePlugin.agentServer()` and the `agentServer(outcome)` event handler, with `AgentServerOutcome`: give a service `startInBackground` and hear how it ended in `serviceStarted`, which names the plugin and service (`AGENT_SERVER_PLUGIN`, `AGENT_TEAM_SERVICE`).
|
|
85
|
+
The host runs every service's background start after all `start`s and the HTTP listeners, without holding up the boot; a failure is logged and heard as `failed`, and does not stop the other services.
|
|
86
|
+
- `RoundtablePlugin.stopTurn(channel)`: put `stop(channel)` on the `ChannelClaim` that owns the channel. The router asks only the owning claim, and a claim without `stop` means `false`.
|
|
87
|
+
- `PluginContext.core` and its types `CoreAccess`, `CoreServices`, `CoreStores`, `CoreDiscord` and `CoreAgents`: read a service with `context.services.get(KEY)` (`AGENTS`, `SKILLS`, `SCHEDULES`, `MEMORY`, `BACKGROUND_TURNS`, `DELEGATION`, and the Discord entry's `DISCORD`) and provide one with `services.provide`.
|
|
88
|
+
Reading `context.core` throws a `PluginError` that names `context.services`, at the plugin's first read, instead of returning `undefined`.
|
|
89
|
+
The stores bag is gone: each store belongs to the plugin that owns its table, and the held-action, skill and agent stores are not published (the agent server reads them; `AGENTS.directory` is the read-only half of the agents store).
|
|
90
|
+
- The `testPlugin` option `core` and its type `TestCore`: use `services` with `servicePair`.
|
|
91
|
+
- Kit: the class types of the built-in services (`AgentStore`, `AgentTeam`, `AvatarStudio`, `BackgroundTurns`, `ConfirmationJudge`, `Delegator`, `OwnerMemoryStore`, `PendingConfirmationStore`, `PiAgentRuntime`, `ScheduleStore`, `SkillRegistry`, `SkillStore`), replaced by the main entry's port interfaces where a plugin needs them, and the `*Options` types of those classes (`AgentTeamOptions`, `AvatarStudioOptions`, `BackgroundTurnsOptions`, `ConfirmationJudgeOptions`, `DelegatorOptions`, `PiAgentRuntimeOptions`, `SkillRegistryOptions`, `TeamTurnsOptions`), with `Detached`, `SkillEntry` and `SkillGroup`.
|
|
92
|
+
- **Renamed, for the unreleased types:** `OwnerMemoryStore`, `OwnerMemory`, `OwnerMemoryKind`, `OwnerPromptMemory` and `OWNER_MEMORY_KINDS` are `MemoryStore` (with `forSpeaker`, returning `SpeakerMemory`), `Memory`, `MemoryKind`, `PromptMemory` and `MEMORY_KINDS`; the tables and the tools' names do not change.
|
|
93
|
+
- Kit: `conversationKind()` and its closed `ConversationKind` (`"owner" | "party" | "agent"`); a conversation kind is any string that a claim's `startFresh` returns, and a plugin narrows the kinds of its own claims itself. `SessionContext.kind` is a `string`.
|
|
94
|
+
- Kit: `channelKey(channelId)` is `discordKey(channelId)`, because the main entry's `channelKey` builds a key on any surface.
|
|
95
|
+
|
|
96
|
+
- Addons: memory, skills, and Discord administration are built-in plugins of their own (`memory`, `skills`, `discord-admin`) that `defineRoundtable` adds unless the configuration switches them off with `memory: false`, `skills: false`, or `discord: { admin: false }`; all three are on by default. A switched-off addon contributes no tools, no prompt block, and no service, and its tables and rows are left untouched: `services.find(SKILLS)` and `find(MEMORY)` are `undefined`, the agents carry no skills, `agent_get` has no skills line, `agent_create` leaves out its `skills` parameter and refuses a call that passes some with an error saying skills are off, and `services.get` of an addon key fails naming the switch.
|
|
97
|
+
- `Contribution.toolTiers` (`Record<string, Tier>`): a plugin names the tier of each raw session tool it registers, as `defineTool` already does for its own; the operator's setting still wins and two plugins naming one tool are refused.
|
|
98
|
+
- `serviceKey(id, { absent })` and `ServiceKey.absent`: what a read of a service nobody provides says to fix it; `MEMORY` and `SKILLS` name their switches.
|
|
99
|
+
- `SkillRegistry.attach(agent, add, remove)`, which the agent team uses to give a new agent its skills.
|
|
100
|
+
- **Breaking (slash commands move under the Discord surface):** `Contribution.interactions`, `RoundtableOptions.commands`, `ChatSurface.useCommands` and the host's composition of commands are gone; the main entry no longer names `CommandRoot`, `ComposedInteractions`, `InteractionContribution`, `InteractionModule` or `RootOption` (they are in `pi-roundtable/discord`). A plugin that returns `interactions` from `setup`, a surface that has `useCommands`, and a host given `commands` are each refused with an error that names `context.services.get(DISCORD).commands.add(...)`, as `RoundtablePlugin.useCommands` is.
|
|
101
|
+
- **Breaking for the unreleased types:** the kit's Discord names moved to `pi-roundtable/discord` or were dropped: `DISCORD` and `DiscordServices` (now `connection`, `commands`, `guard`, `threads`; the owner's cards and the avatar studio are no longer published), `OwnerGuard` (now `CommandGuard`, a port), `ChannelExecutor`, `ChannelInfo`, `ChannelOperation`, `OwnerCommandHandlers`, `PanelContent`, `OwnerFacingError`, `groupOption`, `ownerCommandModule`, `ownerPanel`, `ownerPanels`, `ephemeralPanel`, `replyWithPanels` and `plain` moved; `DiscordSurface`, `DiscordSurfaceOptions`, `OwnerCards`, `OwnerCardsOptions`, `CardChannel`, `CardMessage` and `CardPayload` are gone (the owner's cards stay private to the Discord plugin and answer held actions through `context.surfaces.prompts`).
|
|
102
|
+
- `DiscordSurface.useCommands` is `setCommands` and is not published. `DiscordConnection` returns the public `AgentChannels`, `DashboardBoard`, `ThreadHost` and `OwnerOperations` (was `OwnerDiscord`) instead of the Discord classes, which removes five leaks from the type-leak baseline (now `ErrorReporter` only).
|
|
103
|
+
- `DISCORD_OWNER_TOOLS` is `DISCORD_ADMIN_TOOLS`.
|
|
104
|
+
|
|
105
|
+
### Changed
|
|
106
|
+
|
|
107
|
+
- **Breaking for the unreleased types:** `DefineOverrides` has five fields: `modelRuntime`, `logger`, `errorSink`, `listeners` and `aborted`. `errorReporter` is gone (set `config.ops` for the ops agent's reports, and `errorSink` for your own), `options` is gone (`options.listeners` is `listeners`, which adds listeners to the one `config.http` names, and `options.aborted` is `aborted`; the other host options come from the config), `modules.agentChannelOf` is gone (the modules read `AgentTeam.channelOf` from `AGENTS` when a tool runs), and `agentServer.ownerSessions` is gone (contribute `personas: [{ kind: "owner", prompt }]` and `requiredTools` from the plugin that owns the owner's conversations; the agent server no longer refuses a contributed `"owner"` persona). With no `"owner"` persona the owner's conversations start with an empty system prompt, as before.
|
|
108
|
+
- **Breaking for the unreleased types:** `AgentServerOptions`, `ModulesOptions`, `ErrorReporter`, `ErrorReportDelivery` and `ErrorReporterOptions` are no longer public: they were named by `DefineOverrides` only, and the public type-leak baseline is now empty. `Logger` is no longer pino's type, and `LogEntry` moved from `pi-roundtable/kit` to the main entry.
|
|
109
|
+
- **Breaking for the unreleased types:** migration names are unique inside a plugin, not across plugins (the ledger id is `<plugin>/<name>`), and `MigrationError.migration` and the start's error name that id. The same name in two plugins is allowed; a repeated id is still refused.
|
|
110
|
+
- The upgrade adds the ledger to a database that already ran the migrations without special handling: no id is recorded, so each `once` migration runs once more, as every start did before, and is recorded. **Rollback note:** a build from before the ledger ignores the table and runs every migration at every start; rolling forward again finds its ids recorded, so a `once` migration that fixes rows (for example one that gives rows written before speakers or guilds existed their owner or guild) does not run again over rows an older build wrote in between. Mark such a migration `runs: "every-boot"` if a rollback must converge.
|
|
111
|
+
- The host repository's tests use `migrateDatabase` where they migrated by hand, and its two import scripts read the ledger instead of migrating.
|
|
112
|
+
- **Breaking for the unreleased types:** the skills are provided by the new `skills` plugin, not by the agent server. the agent server takes no skills option (the paths are the `skills` config key's), the agent server provides only `AGENTS`, and the agent team reads the registry with `find(SKILLS)`, so the skill tools are the `skills` plugin's agent session tools (`skill-tools`) and its `agentSelection`, no longer part of `agent-tools`. `memoryPlugin` takes the `owner` and adds the `owner-memory` session tool, and `modulesPlugin` no longer adds that tool or `discord-admin`, which is the new plugin's.
|
|
113
|
+
- **Breaking for the unreleased types:** the core's tier table (`CORE_TOOL_TIERS`) names only `ask_user`, `compact_session` and `read_attachment`; the agent, schedule, delegation, web, skill and memory tools are declared by the plugin that adds them, with the same tiers. The Discord tools stay with the owner, as before.
|
|
114
|
+
- The order of the core's migrations is now memory, schedules, skills, held actions, agents, because the skills plugin sits before the agent server that reads its registry; all of them run before any setup and none touches another's tables. The stored data, the tool set, and each tool's tier under the default configuration are unchanged (a test records them for the owner's session, an agent's, and a group seat).
|
|
115
|
+
- The legacy skill kind `UPDATE` is no longer in the core's skill migrations; a host that still has skills stored under that kind converts them in a migration of its own, after the core's.
|
|
116
|
+
- **Breaking for a plugin that named it:** the built-in plugin list of `defineRoundtable` gains `discord-admin` (after `modules`) and `skills` (after it, before `agent-server`).
|
|
117
|
+
|
|
118
|
+
- **Breaking (the unreleased `InboundMessage` fields, and the second parameter's name of the published `ChannelClaim.owns`):** `InboundMessage` has neutral fields in place of Discord's. `guildId` is `space`, the server or workspace the channel belongs to; `webhookId` and `ownWebhook` are `integration?: { id, own }`, for a post by an integration such as a Discord webhook and whether it is the assistant's own voice; and `forwarded.channelMention` (`"<#id>"`) is `forwarded.source`, the forwarded-from channel's key. `ChannelClaim.owns(channel, guildId)` is `owns(channel, space)`; the parameter is positional, so callers do not change.
|
|
119
|
+
- **Breaking for the unreleased types (and the published `TurnEvent`):** `TurnEvent.agent` is optional and `TurnEvent` has `kind` (`"agent"` for an agent's turn, else the kind of a turn run through `context.turns`), so `turnStarted` and `turnEnded` also report those turns. `CoreAgents.runtime` is typed `AgentRuntime`, not the concrete `PiAgentRuntime` (the Pi runtime when no plugin fills the slot). `SessionContext.kind` is the turn's kind for a non-agent session, not always `"owner"`.
|
|
120
|
+
- `testPlugin`'s errors say what is possible inside it: `core service <name> is not provided yet` tells you to pass it in the `core` option instead of registering a built-in plugin, and a `conversations` call made from a handler no longer says it is "not during setup", since the harness links them.
|
|
121
|
+
- `DiscordSurface` implements `ChatSurface` (`surface = "discord"`, with `prompts`) and the Discord built-in contributes it: the `surface` service is now `surface:discord`, and a new `threads` service sweeps the dispatch threads once it has started.
|
|
122
|
+
- The agent server owns only `discord:` channel keys. A key of another surface, such as `mcp:<id>` on a host that claims those keys, whose id equals an agent's channel id was taken by the agent server at priority 100, because its ownership check sliced `discord:` off any key; it is now left to the claim that owns that surface.
|
|
123
|
+
`AGENT_SERVER_PRIORITY` (100) is exported and documented: a claim on `discord:` keys with a lower priority is beaten in the agents' channels and in the rest of the agent guild.
|
|
124
|
+
- The stop button core posts is answered by the Discord built-in (owner only, through `conversations.stop`), so it works on a host that does not add its own handler.
|
|
125
|
+
- An unknown provider slot passed to `providers` is refused, naming the valid slots, as an unknown part of `setup` already was.
|
|
126
|
+
- Trim source tests and test-only helpers from npm contents; retain example tests embedded by the guide, excluding the guide verifier itself.
|
|
127
|
+
- Freeze shared protocol constants and publish readonly types; process-wide locale and time-zone setters are not exported by main, kit, or testing.
|
|
128
|
+
- Keep production integration inspectors out of testing and host-assembly values out of the kit.
|
|
129
|
+
- Without an `images` provider, agents get an avatar generated from their display name and the assistant's icon (a colour from the display name and the initial of the agent's name, always a Latin letter or digit, in a 512 px PNG served by content hash), instead of all sharing the neutral one. They are no longer offered drawing: `agent_create` has no `avatar_prompt`, `agent_avatar` is not registered, agents' prompts and `agent_get` leave avatar prompts out, and the profile panel says no image provider is configured instead of offering to redraw. Startup makes the missing pictures and logs one info line, not a warning per agent on every boot. A host with an `images` provider behaves as before.
|
|
130
|
+
- `roundtable doctor` has an `image provider` check, after `plugins`: it states whether a plugin fills the `images` slot and, without one, how to provide it; it is never a failure.
|
|
131
|
+
- `PluginContext.providers` also has `filled`, the set of slots a plugin fills, so a default can be told from a provider without calling it.
|
|
132
|
+
- **Breaking for the unreleased types:** the core no longer knows owner profiles. A held action carries an opaque `selectionId` (the `id` of the `TurnSelection` whose turn held it) in place of `PendingConfirmation.profile`; the core stores it and the caller resolves it when the owner confirms. `ConfirmationGate.beginTurn(selectionId, confirmed, addressee?)` takes that id, and a call held outside a turn throws instead of being stamped `"general"`.
|
|
133
|
+
- Held actions are stored in a new table, `held_actions(channel_key, selection_id, held_at, calls)`, which the core creates on every boot. A host upgraded from an earlier build keeps its old `pending_confirmations` table, which the core no longer reads; copy its rows across (`selection_id` takes the old `profile` value) in a migration of your own, after the core's.
|
|
134
|
+
- **Breaking for a plugin that named it:** the core extension `profile-tools` is named `active-tools` (a plugin may not take either name); `CoreExtensions.profileTools` is `activeTools`.
|
|
135
|
+
- Log and error wording: the stale-session reason `connectors changed` is `session tools changed`, the startup error `profile tools are not registered` is `required tools are not registered`, `profile tools missing; running without them` is `selected tools missing; running without them`, and the turn log's `profile` key is `selection`.
|
|
136
|
+
- **Breaking for the unreleased types:** the core no longer knows party channels. `BackgroundTurn.mode` (`"owner" | "party"`) is `BackgroundTurn.target`, a `string` naming a contributed `BackgroundTarget`; `Schedule.mode` and `NewSchedule.mode` are `target`, and `DelegationJob.mode` is `target`. The `schedules.mode` column keeps its name and its values (`"owner"`, `"party"`), which are now the target names, so no stored schedule is rewritten. The schedule tools' limits come from `ScheduleToolContext.target` (replacing `mode` and `SCHEDULE_LIMITS`), the delegator's running limit from `DelegatorOptions.targets(name)` (replacing the built-in table), and a target without `schedules` or `delegation` refuses both. The agent server's claim used to skip only `"party"` turns and now answers only `"owner"`, so a turn for any other target never reaches the owner's tools. The `/<root> schedule` list names a schedule's target by its label, or by its name when no plugin contributes it.
|
|
137
|
+
- The built-in plugin `stores` is split by feature, so one store can be replaced: `memory` (migrations and `MEMORY`) and `schedule-store` (migrations and `SCHEDULES`) come first, and the agent server now owns the tables it reads: held actions, and agents and groups; the skills have their own addon.
|
|
138
|
+
The tables and the tools are unchanged.
|
|
139
|
+
- The default delegation worker is `WebResearchWorker` (it was named for one model), in `web-research-worker.ts`; it is not exported.
|
|
140
|
+
- **Breaking for the unreleased types:** `scheduleToolSpecs({ locale, timeZone })` takes the locale and the time zone its descriptions are written in, and `zonedStamp(date, timeZone)` (the kit's, a stamp in the zone you give) takes the zone, instead of both reading the process-wide setting. A worker of your own no longer sets the process's locale and time zone to get its tool descriptions in the wording and zone it wants. `useTestLocale({ locale, timeZone, assistant, root })` takes the same, with the neutral UTC and English defaults when called with nothing.
|
|
141
|
+
- The transitional local `pi-roundtable/internal` mapping is removed: the tsconfig path, `src/internal/`, and its guard test are gone, and a boundary test fails on any import of it and if the directory returns. It was never an npm export or part of the tarball, so a package consumer sees no change. `CardCadence`, `Expression`, `CardRenderError`, `packageDir`, `serveUnix` and the environment-variable parsers for tier members and tool tiers are no longer the core's: they moved into the host repository's own code, and the core does not export them.
|
|
142
|
+
|
|
143
|
+
### Fixed
|
|
144
|
+
|
|
145
|
+
- `defineRoundtable` no longer sets the process's locale, time zone, or `PI_CODING_AGENT_DIR`; `Roundtable.run()` applies the host's `environment`, so defining a second host no longer changes the first's.
|
|
146
|
+
- The root command `defineRoundtable` gives the host is built when `run()` applies the environment, so its description is in the host's language; `RoundtableOptions.commands.root` may be a `CommandRoot` or a function that returns one.
|
|
147
|
+
- One host runs per process: a second `run()` is refused with the fix. A failed `run()` stops what it started, closes the pool, and rethrows, and the same host may retry (tool tiers are declared idempotently per plugin).
|
|
148
|
+
- The shutdown drain waits on the host's own channel queue, not only on services, and the Discord built-in no longer contributes a `channel-queue` service.
|
|
149
|
+
- `shutdown()` runs once however many signals arrive and returns the exit code (non-zero when something did not stop or the boot had failed); only `listen()` exits the process, and the command line exits non-zero on a failed start.
|
|
150
|
+
- Importing `pi-roundtable/testing` in CI without a database URL no longer throws; repository CI requires its PostgreSQL URL in the workflow instead.
|
|
151
|
+
- Record the already-published main/testing API and CLI under 0.1.0 rather than presenting them as unreleased additions.
|
|
152
|
+
|
|
153
|
+
## [0.1.0]
|
|
154
|
+
|
|
8
155
|
### Added
|
|
9
156
|
|
|
10
157
|
- Main entry exports: `AgentSeed`, `ChannelClaim`, `Contribution`, `DefinedRoundtable`, `EventHandlers`, `HoldRule`, `InteractionContribution`, `Migration`, `NotLinkedError`, `PluginContext`, `PluginError`, `PromptSection`, `PromptTurn`, `Roundtable`, `RoundtableConfig`, `RoundtableOptions`, `RoundtablePlugin`, `Service`, `SessionTool`, `Speaker`, `Tier`, `ToolContribution`, `ToolRefusal`, `ToolSpec`, `ToolTurn`, `TurnEndEvent`, `TurnEvent`, `definePlugin`, `defineRoundtable`, `defineTool`.
|
|
@@ -26,3 +173,4 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
26
173
|
|
|
27
174
|
- `testPlugin`'s `contribution` includes the `agentSelection` a plugin adds; it was left out.
|
|
28
175
|
- A setup that throws a `NotLinkedError` reports its reason without a doubled period.
|
|
176
|
+
- `pi-web-access` (0.35.0) is a dependency: the built-in delegation worker loads its Pi extension by path, and a project without it failed at the `modules` plugin's setup with `Cannot find module 'pi-web-access/package.json'`.
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# pi-roundtable
|
|
2
2
|
|
|
3
|
+
English | [Traditional Chinese](README.zh-TW.md)
|
|
4
|
+
|
|
3
5
|
A Discord agent server on [Pi](https://github.com/earendil-works/pi).
|
|
4
6
|
You get a team of AI agents in one Discord server: each agent owns a channel and a conversation, they share tools and memory, and you extend the bot with plugins written in TypeScript.
|
|
5
7
|
|
|
@@ -36,7 +38,7 @@ It refuses to write anything when Bun is missing or too old, or when a file it w
|
|
|
36
38
|
Bun loads `.env` by itself, and `.gitignore` keeps it out of Git.
|
|
37
39
|
|
|
38
40
|
| Variable | What it is |
|
|
39
|
-
|
|
41
|
+
| --- | --- |
|
|
40
42
|
| `DISCORD_TOKEN` | The bot's token, from the application's Bot page |
|
|
41
43
|
| `DISCORD_GUILD_ID`, `DISCORD_ENTRY_CHANNEL_ID` | The server and the channel where the coordinating agent lives (turn on Developer Mode, then right-click to copy ids) |
|
|
42
44
|
| `OWNER_ID`, `OWNER_NAME` | You: the one person who can change everything |
|
|
@@ -52,11 +54,12 @@ Bun loads `.env` by itself, and `.gitignore` keeps it out of Git.
|
|
|
52
54
|
2. `.env` has a value for every variable `.env.example` lists.
|
|
53
55
|
3. `roundtable.config.ts` against its schema, naming the failing key.
|
|
54
56
|
4. Every plugin loads, and no two share a name.
|
|
55
|
-
5.
|
|
56
|
-
6.
|
|
57
|
+
5. Whether a plugin fills the `images` slot. Without one is not a failure: agents get avatars generated from their display names.
|
|
58
|
+
6. PostgreSQL is reachable and migratable.
|
|
59
|
+
7. The Discord token is valid, the bot is in your server, the Message Content intent is on, and the bot has the permissions it needs in the entry channel (including Pin Messages).
|
|
57
60
|
When the bot is not in the server, the fix is an invitation link that asks for exactly those permissions.
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
8. The model login exists.
|
|
62
|
+
9. `PUBLIC_URL` is a well-formed address; with `--reachable` it also has to answer, which is only true while the bot runs.
|
|
60
63
|
|
|
61
64
|
It exits non-zero on any failure and changes nothing it checked.
|
|
62
65
|
A fresh project fails only on the credentials you have not entered yet, and says which.
|
|
@@ -108,6 +111,7 @@ test("hello greets", async () => {
|
|
|
108
111
|
|
|
109
112
|
The [plugin guide](docs/plugins.md) explains every part a plugin can add (tools, prompt sections, agents, events, services, migrations, providers, slash commands, HTTP routes, and more), the order things start and stop in, and every startup error with its fix.
|
|
110
113
|
Its examples live in [`examples/`](examples), and the test suite runs each of them.
|
|
114
|
+
`pi-roundtable/kit` supplies claim, tool and presentation helpers and type-only names for the context’s existing services, and `pi-roundtable/discord` supplies the slash-command registrar, owner-command and panel helpers, and the agent panel, and is the entry that names discord.js types (`pi-roundtable/testing` names a few, through `testHost`'s composed commands); both are unstable before 1.0 and not covered by semver.
|
|
111
115
|
|
|
112
116
|
## Settings
|
|
113
117
|
|
|
@@ -128,7 +132,7 @@ export default {
|
|
|
128
132
|
## Commands
|
|
129
133
|
|
|
130
134
|
| Command | What it does |
|
|
131
|
-
|
|
135
|
+
| --- | --- |
|
|
132
136
|
| `roundtable init [dir]` | Creates a project in `dir` (default: the current directory) |
|
|
133
137
|
| `roundtable doctor [--reachable]` | Checks the setup and says how to fix what is wrong |
|
|
134
138
|
| `roundtable start` | Runs the checks that need no network, then the bot |
|
package/README.zh-TW.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# pi-roundtable
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 繁體中文
|
|
4
|
+
|
|
5
|
+
一個建立在 [Pi](https://github.com/earendil-works/pi) 上的 Discord 智慧體(agent)伺服器。
|
|
6
|
+
你會在一個 Discord 伺服器裡得到一組 AI 智慧體:每個智慧體擁有一個頻道和一段對話,彼此共用工具與記憶,你用 TypeScript 寫外掛(plugin)來擴充這個 bot。
|
|
7
|
+
|
|
8
|
+
- 為一位擁有者和一個 Discord 伺服器設計。你可以允許其他人與智慧體對話,但這樣的設定與風險由營運者自行承擔。
|
|
9
|
+
- 只支援 Bun。套件直接發佈 TypeScript 原始碼,不需要建置步驟。
|
|
10
|
+
- MIT 授權。
|
|
11
|
+
|
|
12
|
+
## 你需要準備
|
|
13
|
+
|
|
14
|
+
- [Bun](https://bun.sh/docs/installation) 1.3 以上。
|
|
15
|
+
- PostgreSQL。`init` 建立的專案附有 `docker-compose.yml`,可以直接啟動一個。
|
|
16
|
+
- 一個 Discord bot:一個含 bot 使用者、已開啟 Message Content intent,並已邀請進你的伺服器的應用程式。
|
|
17
|
+
- 模型登入:你選的模型的供應商 API key(`anthropic/...` 用 `ANTHROPIC_API_KEY`),或用 Pi 做過的登入。
|
|
18
|
+
- 一個能從網際網路連到這個行程的位址,例如通道(tunnel),因為 Discord 會從該位址取得智慧體的頭像。
|
|
19
|
+
|
|
20
|
+
## 五分鐘上手
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npx pi-roundtable init my-bot # 或:bunx pi-roundtable init my-bot
|
|
24
|
+
cd my-bot
|
|
25
|
+
bun install
|
|
26
|
+
docker compose up -d # PostgreSQL,與 .env.example 一致
|
|
27
|
+
cp .env.example .env # 然後填入內容
|
|
28
|
+
bunx roundtable doctor
|
|
29
|
+
bunx roundtable start
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`init` 會寫出一個可運作的專案,不會向你要任何祕密資訊。
|
|
33
|
+
Bun 不存在或版本太舊,或它要建立的檔案已經存在時,它不會寫入任何東西。
|
|
34
|
+
|
|
35
|
+
### `.env`
|
|
36
|
+
|
|
37
|
+
`.env.example` 說明了每個值的來源。
|
|
38
|
+
Bun 會自行載入 `.env`,`.gitignore` 也已讓它不進 Git。
|
|
39
|
+
|
|
40
|
+
| 變數 | 內容 |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `DISCORD_TOKEN` | bot 的 token,在應用程式的 Bot 頁面取得 |
|
|
43
|
+
| `DISCORD_GUILD_ID`、`DISCORD_ENTRY_CHANNEL_ID` | 伺服器,以及負責統籌的智慧體所在的頻道(開啟開發者模式後,按右鍵複製 id) |
|
|
44
|
+
| `OWNER_ID`、`OWNER_NAME` | 你自己:唯一能改動一切的人 |
|
|
45
|
+
| `DATABASE_URL` | PostgreSQL;預設值與 `docker-compose.yml` 一致 |
|
|
46
|
+
| `MODEL` | 智慧體使用的模型,格式為 `<provider>/<id>` |
|
|
47
|
+
| `PUBLIC_URL` | 能從網際網路連到這個行程的位址 |
|
|
48
|
+
|
|
49
|
+
### `doctor`
|
|
50
|
+
|
|
51
|
+
`bunx roundtable doctor` 依序檢查下列項目,逐項印出通過或失敗,並說明如何修正:
|
|
52
|
+
|
|
53
|
+
1. Bun 的版本。
|
|
54
|
+
2. `.env` 對 `.env.example` 列出的每個變數都有值。
|
|
55
|
+
3. `roundtable.config.ts` 符合其 schema,失敗時指出是哪個鍵。
|
|
56
|
+
4. 每個外掛都能載入,且沒有兩個外掛同名。
|
|
57
|
+
5. 是否有外掛填入 `images` 槽位。沒有並不算失敗:智慧體會用顯示名稱產生頭像。
|
|
58
|
+
6. PostgreSQL 連得上,且能執行 migration。
|
|
59
|
+
7. Discord token 有效、bot 已在你的伺服器裡、Message Content intent 已開啟,而且 bot 在入口頻道有它需要的權限(包含 Pin Messages)。
|
|
60
|
+
bot 不在伺服器裡時,修正方式是一個邀請連結,連結要求的正是這些權限。
|
|
61
|
+
8. 模型登入存在。
|
|
62
|
+
9. `PUBLIC_URL` 是格式正確的位址;加上 `--reachable` 時它還必須有回應,這只有在 bot 執行中才成立。
|
|
63
|
+
|
|
64
|
+
任何一項失敗,它就以非零狀態結束,並且不更動它檢查過的任何東西。
|
|
65
|
+
全新的專案只會因為你還沒填的憑證而失敗,而且會指出是哪些。
|
|
66
|
+
|
|
67
|
+
### `start`
|
|
68
|
+
|
|
69
|
+
`bunx roundtable start` 先執行不需要網路的檢查,其中任何一項失敗就停下來,並印出與 `doctor` 相同的訊息;全部通過則啟動 bot。
|
|
70
|
+
啟動後,`agents.ts` 裡的智慧體都有了自己的頻道,`/roundtable help` 會開啟控制面板。
|
|
71
|
+
收到 `SIGTERM` 或 `SIGINT` 時,它會先讓進行中的工作完成再停止。
|
|
72
|
+
|
|
73
|
+
## 外掛
|
|
74
|
+
|
|
75
|
+
`roundtable add plugin <name>` 會建立 `plugins/<name>.ts` 和它的測試,並把它列進 `roundtable.config.ts`。
|
|
76
|
+
外掛是一個有名稱和 `setup` 函式的物件,`setup` 回傳它要新增的東西;下面這個外掛給每個智慧體一個工具:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { definePlugin, defineTool } from "pi-roundtable";
|
|
80
|
+
import { Type } from "typebox";
|
|
81
|
+
|
|
82
|
+
export const hello = definePlugin({
|
|
83
|
+
name: "hello",
|
|
84
|
+
setup: () => ({
|
|
85
|
+
tools: [
|
|
86
|
+
defineTool({
|
|
87
|
+
name: "hello_greet",
|
|
88
|
+
description: "Greet someone by name. Call it when asked to say hello.",
|
|
89
|
+
parameters: Type.Object({ who: Type.String() }),
|
|
90
|
+
minTier: "member",
|
|
91
|
+
run: ({ who }) => `Hello, ${who}!`,
|
|
92
|
+
}),
|
|
93
|
+
],
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
測試時不需要 Discord 或 PostgreSQL:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { expect, test } from "bun:test";
|
|
102
|
+
import { testPlugin } from "pi-roundtable/testing";
|
|
103
|
+
import { hello } from "./hello.ts";
|
|
104
|
+
|
|
105
|
+
test("hello greets", async () => {
|
|
106
|
+
const harness = await testPlugin(hello);
|
|
107
|
+
expect(await harness.runTool("hello_greet", { who: "Ada" })).toBe("Hello, Ada!");
|
|
108
|
+
await harness.stop();
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
[外掛指南](docs/plugins.md)(英文)說明外掛能新增的每個部分(工具、提示詞區段、智慧體、事件、服務、migration、provider、斜線指令、HTTP 路由等等)、啟動與停止的順序,以及每一種啟動錯誤和它的修正方式。
|
|
113
|
+
指南裡的範例放在 [`examples/`](examples),測試套件會執行每一個範例。
|
|
114
|
+
`pi-roundtable/kit` 提供頻道認領(claim)、工具與呈現用的輔助函式,以及 context 現有服務的純型別名稱;`pi-roundtable/discord` 提供斜線指令註冊器、擁有者指令與面板的輔助函式,以及智慧體面板,是會用到 discord.js 型別的入口(`pi-roundtable/testing` 也透過 `testHost` 組合出的指令用到少數幾個)。這兩個入口在 1.0 之前都不穩定,不受語意化版本(semver)保證。
|
|
115
|
+
|
|
116
|
+
## 設定
|
|
117
|
+
|
|
118
|
+
`roundtable.config.ts` 放設定和外掛清單。
|
|
119
|
+
未知的鍵會報錯,並指出最接近的已知鍵。
|
|
120
|
+
|
|
121
|
+
bot 在 Discord 裡顯示的文字語言由 `locale` 設定決定:預設是 `en`,也可以是 `zh-TW`。
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
export default {
|
|
125
|
+
// ...
|
|
126
|
+
locale: "zh-TW",
|
|
127
|
+
timeZone: "Europe/Berlin", // 排程與時間戳記使用的時區;預設為 UTC
|
|
128
|
+
plugins: [hello],
|
|
129
|
+
} satisfies RoundtableConfig;
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 指令
|
|
133
|
+
|
|
134
|
+
| 指令 | 作用 |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `roundtable init [dir]` | 在 `dir`(預設為目前目錄)建立專案 |
|
|
137
|
+
| `roundtable doctor [--reachable]` | 檢查設定,並說明如何修正有問題的地方 |
|
|
138
|
+
| `roundtable start` | 先執行不需要網路的檢查,再啟動 bot |
|
|
139
|
+
| `roundtable add plugin <name>` | 新增 `plugins/<name>.ts` 和它的測試,並列進設定 |
|
|
140
|
+
|
|
141
|
+
## 變更與授權
|
|
142
|
+
|
|
143
|
+
[CHANGELOG.md](CHANGELOG.md)(英文)列出套件匯出名稱的每一項變更。
|
|
144
|
+
[MIT](LICENSE)。
|