8000 feat(commands): add /compact to summarize older messages on demand by geminixiang · Pull Request #164 · geminixiang/mikan · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ change in a minor release.

### Added

- `/compact [instructions]` (`/pi-compact` in Slack) summarizes a session's older messages with
pi-durable's compaction to free up context, in DMs and at a channel's top level, and reports the
context size before and after. Earlier messages stay in the session history. Existing Slack apps
must add `/pi-compact` to their slash commands; the example manifests include it.
- Codemode `store(key, value)` and `load(key)` keep JSON values across scripts in a conversation,
as in Pi, instead of only within one script. Values survive a restart, a thread started from a
run sees the values as of that run, and `/new` clears them. They are kept in a pi-durable
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ For routing, mounts, vault behavior, and managed container details, see [src/con
| `/login` / `/pi-login` | Store API keys or run built-in OAuth flows |
| `session` / `/session` | Open a read-only web view of the current session |
| `/new` / `/pi-new` | Reset the current session |
| `/compact` / `/pi-compact` | Summarize older messages to free up context |
| `/model` / `/pi-model provider/model[:thinking]` | Switch the LLM for the current conversation |
| `/sandbox` / `/pi-sandbox [boost\|visibility …]` | Show sandbox status, boost limits, or narrow office visibility |
| `/pi-auto-reply <on\|off\|jev>` | Set mention-free replies for the current Slack channel |
Expand Down
5 changes: 5 additions & 0 deletions deploy/examples/slack-agent-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"description": "start a new session",
"should_escape": false
},
{
"command": "/pi-compact",
"description": "summarize older messages to free up context",
"should_escape": false
},
{
"command": "/pi-model",
"description": "switch this conversation's LLM",
Expand Down
5 changes: 5 additions & 0 deletions deploy/examples/slack-app-manifest.e2e.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
"description": "new",
"should_escape": false
},
{
"command": "/pi-compact",
"description": "summarize older messages to free up context",
"should_escape": false
},
{
"command": "/pi-model",
"description": "model",
Expand Down
5 changes: 5 additions & 0 deletions deploy/examples/slack-app-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"description": "start a new session",
"should_escape": false
},
{
"command": "/pi-compact",
"description": "summarize older messages to free up context",
"should_escape": false
},
{
"command": "/pi-model",
"description": "switch this conversation's LLM",
Expand Down
42 changes: 42 additions & 0 deletions src/adapters/commands/compact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { slashForms, matchCommand } from "./manifest.js";
import type { CommandContext, CommandHandler } from "./types.js";
import { replySummary } from "./utils.js";
import { errorMessage } from "../../unknown-values.js";

const COMPACT_COMMANDS = slashForms("compact");
const TITLE = "Compact";

export class CompactCommandHandler implements CommandHandler {
async tryHandle(context: CommandContext): Promise<boolean> {
const matched = matchCommand(context.commandText, COMPACT_COMMANDS);
if (!matched) return false;

if (!context.services.runtime) {
await replySummary(context, TITLE, ["Compact is not configured correctly on the server."]);
return true;
}

const instructions = matched.args.join(" ").trim() || undefined;
try {
const outcome = await context.services.runtime.handleCompactCommand({
address: context.address,
sessionKey: context.sessionKey,
platform: context.messagingInfo,
instructions,
});
await replySummary(
context,
TITLE,
outcome.compacted
? [
`Context compacted: about ${outcome.tokensBefore.toLocaleString("en-US")} → ${outcome.tokensAfter.toLocaleString("en-US")} tokens.`,
"Earlier messages stay in the session history.",
]
: ["Nothing to compact yet; the conversation is still short."],
);
} catch (error) {
await replySummary(context, TITLE, [`Could not compact: ${errorMessage(error)}`]);
}
return true;
}
}
14 changes: 14 additions & 0 deletions src/adapters/commands/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,20 @@ export const COMMAND_MANIFEST: readonly CommandManifestEntry[] = [
telegramMenu: {},
telegramCommand: true,
},
{
name: "compact",
description: "Summarize older messages to free up context",
arg: {
name: "instructions",
description: "What the summary should keep",
required: false,
},
slackCommand: "/pi-compact",
slackRoute: { includeText: true },
discord: true,
telegramMenu: {},
telegramCommand: true,
},
{
name: "admin",
description: "Open the admin portal",
Expand Down
2 changes: 2 additions & 0 deletions src/adapters/commands/registry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AdminCommandHandler } from "./admin.js";
import { AutoReplyCommandHandler } from "./auto-reply.js";
import { CompactCommandHandler } from "./compact.js";
import { LoginCommandHandler } from "./login.js";
import { COMMAND_MANIFEST } from "./manifest.js";
import { ModelCommandHandler } from "./model.js";
Expand All @@ -16,6 +17,7 @@ const HANDLER_FACTORIES: Record<string, (modelRegistry: ModelRegistry) => Comman
model: (modelRegistry) => new ModelCommandHandler(modelRegistry),
sandbox: () => new SandboxCommandHandler(),
new: () => new NewCommandHandler(),
compact: () => new CompactCommandHandler(),
};

export function defaultCommandHandlers(modelRegistry: ModelRegistry): CommandHandler[] {
Expand Down
5 changes: 5 additions & 0 deletions src/adapters/commands/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import type { SandboxConfig } from "../../sandbox/types.js";
import type {
SandboxResourceController,
ConversationContext,
CompactOutcome,
HandleCompactCommandOptions,
HandleNewCommandOptions,
MessagingInfo,
MessagingBot,
OfficeAddress,
PlatformName,
Expand Down Expand Up @@ -59,6 +62,7 @@ export interface AdminTokenStoreLike {

interface CommandRuntimeBridge extends RunnerCacheControl {
handleNewCommand(options: HandleNewCommandOptions): Promise<void>;
handleCompactCommand(options: HandleCompactCommandOptions): Promise<CompactOutcome>;
}

export interface CommandServices {
Expand Down Expand Up @@ -86,6 +90,7 @@ export interface CommandContext {
sessionKey: string;
commandText: string;
privateConversation: boolean;
messagingInfo: MessagingInfo;
services: CommandServices;
}

Expand Down
31 changes: 16 additions & 15 deletions src/content/docs/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,20 @@ import { Aside, Badge, Card, CardGrid, Steps } from "@astrojs/starlight/componen

## Command quick reference

| Command | Purpose and scope |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `/login` / `/pi-login` | Open a 15-minute login/vault link. DM only. |
| `session` / `/session` / `/pi-session` | Open the current session viewer. DM only. |
| `/new` / `/pi-new` | Reset the current session. DM only. |
| `/model` / `/pi-model [provider/model[:thinking]]` | Show or switch the current conversation model. Thinking levels: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. |
| `stop` / `/stop` | Stop the matching active run. On Slack, prefer text form so thread-local routing stays accurate. |
| `/pi-auto-reply <on\|off\|jev>` | Reply to every message in the current Slack channel (`on`), only to mentions (`off`), or when Jev judges a message addresses mikan (`jev`). |
| `/admin` / `/pi-admin` | Open the 30-minute admin portal. |
| `/sandbox` / `/pi-sandbox` | Show the current sandbox limits plus this office's visibility. |
| `/pi-sandbox boost` | Apply configured boost limits until the managed sandbox runtime stops. |
| `/pi-sandbox visibility` | Report the office's effective visibility and where it came from. |
| `/pi-sandbox visibility <private\|default>` | Narrow this public channel to a private office, or clear that override. See [Office visibility](#office-visibility) below. |
| Command | Purpose and scope |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/login` / `/pi-login` | Open a 15-minute login/vault link. DM only. |
| `session` / `/session` / `/pi-session` | Open the current session viewer. DM only. |
| `/new` / `/pi-new` | Reset the current session. DM only. |
| `/compact [instructions]` / `/pi-compact` | Summarize older messages to free up context; DMs and channels. The instructions say what the summary keeps. Earlier messages stay in the session history. |
| `/model` / `/pi-model [provider/model[:thinking]]` | Show or switch the current conversation model. Thinking levels: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. |
| `stop` / `/stop` | Stop the matching active run. On Slack, prefer text form so thread-local routing stays accurate. |
| `/pi-auto-reply <on\|off\|jev>` | Reply to every message in the current Slack channel (`on`), only to mentions (`off`), or when Jev judges a message addresses mikan (`jev`). |
| `/admin` / `/pi-admin` | Open the 30-minute admin portal. |
| `/sandbox` / `/pi-sandbox` | Show the current sandbox limits plus this office's visibility. |
| `/pi-sandbox boost` | Apply configured boost limits until the managed sandbox runtime stops. |
| `/pi-sandbox visibility` | Report the office's effective visibility and where it came from. |
| `/pi-sandbox visibility <private\|default>` | Narrow this public channel to a private office, or clear that override. See [Office visibility](#office-visibility) below. |

`/pi-sandbox` applies to managed sandbox runtimes only: in `host` or `container:<name>` mode it replies that the command needs `image:*`.

Expand All @@ -55,8 +56,8 @@ The change takes effect on the next message, when the sandbox container is repla
are derived from it at startup, and Slack's routing recognizes the manifest's `/pi-*` names.
Because Discord rejects an option description longer than 100 characters, the manifest's argument
descriptions are length-checked by the command tests. Slack commands (`/pi-login`, `/pi-session`,
`/pi-new`, `/pi-model`, `/pi-sandbox`, `/pi-auto-reply`, and `/pi-admin`) must also be registered
in the Slack App manifest.
`/pi-new`, `/pi-compact`, `/pi-model`, `/pi-sandbox`, `/pi-auto-reply`, and `/pi-admin`) must also
be registered in the Slack App manifest.
</Aside>

## Shared vault commands
Expand Down
31 changes: 16 additions & 15 deletions src/content/docs/ja/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,20 @@ import { Aside, Badge, Card, CardGrid, Steps } from "@astrojs/starlight/componen

## コマンド早見表

| コマンド | 用途と scope |
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/login` / `/pi-login` | 15 分間有効な login/vault link を開きます。DM のみ。 |
| `session` / `/session` / `/pi-session` | 現在の session viewer を開きます。DM のみ。 |
| `/new` / `/pi-new` | 現在の session をリセットします。DM のみ。 |
| `/model` / `/pi-model [provider/model[:thinking]]` | 現在の conversation model を表示または切り替えます。Thinking levels:`off`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`。 |
| `stop` / `/stop` | 一致する実行中の run を停止します。Slack では thread-local routing を正確に保つため、テキスト形式を優先してください。 |
| `/pi-auto-reply <on\|off\|jev>` | 現在の Slack channel で、すべてのメッセージに応答する(`on`)、mention にだけ応答する(`off`)、または Jev がメッセージが mikan 宛てかを判断する(`jev`)。 |
| `/admin` / `/pi-admin` | 30 分間有効な admin portal を開きます。 |
| `/sandbox` / `/pi-sandbox` | 現在の sandbox の制限と、この office の visibility を表示します。 |
| `/pi-sandbox boost` | managed sandbox runtime が停止するまで、設定された boost limits を適用します。 |
| `/pi-sandbox visibility` | office の実効 visibility とその由来を表示します。 |
| `/pi-sandbox visibility <private\|default>` | この public channel を private office に狭めるか、その override を解除します。下の [Office visibility](#office-visibility) を参照。 |
| コマンド | 用途と scope |
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/login` / `/pi-login` | 15 分間有効な login/vault link を開きます。DM のみ。 |
| `session` / `/session` / `/pi-session` | 現在の session viewer を開きます。DM のみ。 |
| `/new` / `/pi-new` | 現在の session をリセットします。DM のみ。 |
| `/compact [instructions]` / `/pi-compact` | 古いメッセージを要約して context を空けます。DM とチャンネルで使えます。instructions で要約に残す内容を指定できます。以前のメッセージは session 履歴に残ります。 |
| `/model` / `/pi-model [provider/model[:thinking]]` | 現在の conversation model を表示または切り替えます。Thinking levels:`off`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`。 |
| `stop` / `/stop` | 一致する実行中の run を停止します。Slack では thread-local routing を正確に保つため、テキスト形式を優先してください。 |
| `/pi-auto-reply <on\|off\|jev>` | 現在の Slack channel で、すべてのメッセージに応答する(`on`)、mention にだけ応答する(`off`)、または Jev がメッセージが mikan 宛てかを判断する(`jev`)。 |
| `/admin` / `/pi-admin` | 30 分間有効な admin portal を開きます。 |
| `/sandbox` / `/pi-sandbox` | 現在の sandbox の制限と、この office の visibility を表示します。 |
| `/pi-sandbox boost` | managed sandbox runtime が停止するまで、設定された boost limits を適用します。 |
| `/pi-sandbox visibility` | office の実効 visibility とその由来を表示します。 |
| `/pi-sandbox visibility <private\|default>` | この public channel を private office に狭めるか、その override を解除します。下の [Office visibility](#office-visibility) を参照。 |

### Office visibility

Expand All @@ -53,8 +54,8 @@ visibility は Slack の conversation type に従います(ADR 0008)。publi
menu は 起動時にそこから導出されます。Slack の routing は manifest の `/pi-*`
名を認識します。Discord は 100 文字を超える option description を拒否するため、manifest の引数
description は command tests で長さが検証されます。Slack の `/pi-login`、`/pi-session`、
`/pi-new`、`/pi-model`、`/pi-sandbox`、`/pi-auto-reply`、`/pi-admin` は Slack App manifest
にも登録する必要があります。
`/pi-new`、`/pi-compact`、`/pi-model`、`/pi-sandbox`、`/pi-auto-reply`、`/pi-admin` は Slack App
manifest にも登録する必要があります。
</Aside>

## 共有 vault コマンド
Expand Down
Loading
Loading
0