@gitterm/bot 0.0.0-stage → 0.1.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/README.md CHANGED
@@ -1,3 +1,84 @@
1
- # Temporary Holding Version
1
+ # @gitterm/bot
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The engine behind [`@gitterm/slack-bot`](https://www.npmjs.com/package/@gitterm/slack-bot) and [`@gitterm/discord-bot`](https://www.npmjs.com/package/@gitterm/discord-bot).
4
+ It turns chat messages into [GitTerm](https://gitterm.dev) agent runs with one opinionated model:
5
+
6
+ - **One sandbox per bot, tenant, channel and repository**, tagged for safe recovery. It pauses when
7
+ idle and is woken by the next message.
8
+ - **One OpenCode session per chat thread.** Follow-ups continue the session; messages posted in
9
+ between travel along as quoted context; images are attached once.
10
+ - **Questions and tool approvals become chat prompts** with buttons and typed replies.
11
+ - **Restarts reattach** to runs that were in flight.
12
+
13
+ Use it directly to put the same agent behind another chat platform. You write a `ChatAdapter`;
14
+ the engine owns sandboxes, sessions, runs, prompts, and recovery.
15
+
16
+ ```ts
17
+ import { createBot, type ChatAdapter } from "@gitterm/bot";
18
+
19
+ const adapter: ChatAdapter = {
20
+ platform: "matrix",
21
+ displayName: "Matrix",
22
+ async start(events) {
23
+ // Connect, then report every message in a thread with events.message({...}).
24
+ // Buttons call events.answer(promptId, answer, user).
25
+ return { scope: "<installation id>" };
26
+ },
27
+ async stop() {},
28
+ async history(thread, after) {
29
+ /* the thread's messages, oldest first */ return [];
30
+ },
31
+ async post(thread, text) {
32
+ /* post a status line, return its id */ return "id";
33
+ },
34
+ async edit(thread, messageId, text) {},
35
+ async remove(thread, messageId) {},
36
+ async reply(thread, markdown, footer) {
37
+ /* split with splitMessage() */
38
+ },
39
+ async ask(thread, prompt) {
40
+ /* render a question or permission prompt */ return "id";
41
+ },
42
+ async settle(thread, messageId, prompt, outcome) {
43
+ /* remove its controls */
44
+ },
45
+ };
46
+
47
+ await createBot({ adapter, repo: "https://github.com/acme/app" }).start();
48
+ ```
49
+
50
+ `createBot` takes the same options as the platform bots (`repo`, `channels`, `connections`,
51
+ `model`, `instructions`, `workspace`, `stateFile`, timeouts); see the
52
+ [Slack bot README](https://www.npmjs.com/package/@gitterm/slack-bot#configuration). `splitMessage`, `tablesToCode`, and
53
+ `interpretTypedAnswer` are exported for adapters.
54
+
55
+ The engine reports a message to the agent only when `mentioned` is true. Other thread replies
56
+ answer an open prompt when they come from the person who asked. Every message id must be unique
57
+ within its thread, and `history(thread, after)` must return only messages after `after`.
58
+
59
+ Two adapter methods are optional: `mark(message, state)` reacts to a request (👀, then ✅ or ❌), and
60
+ `indicate(thread, status)` shows a native "is working…" indicator instead of a status message
61
+ (`""` hides it; resolve `false` to fall back to status messages).
62
+
63
+ ## Security and operations
64
+
65
+ - **A saved bot's token is the bot, not you.** It reaches only the workspaces it created and only
66
+ its saved repository, model and tools. Call `withSavedConfig()` before `createBot()`: if the
67
+ saved settings can't be loaded, startup fails, and local settings can narrow its users and
68
+ channels but never widen them.
69
+ - **Sandboxes are per bot, channel and repository.** Private and public channels, and Discord
70
+ servers, never share one; `shareChannels: true` opts trusted channels into one. A direct
71
+ conversation gets its own, terminated after seven days (`directRetentionMs`).
72
+ - **Nothing personal is inherited.** Bot sandboxes get none of your saved environment variables or
73
+ agent config, and only the chosen model's credential.
74
+ - **Settings changes need `reset`.** A sandbox made with other settings is kept (it may hold work)
75
+ until someone runs `reset` in that channel. Reset doesn't revoke credentials it already received;
76
+ revoke those upstream.
77
+ - **Threads run side by side** in their sandbox, each its own session. The agent is told to check
78
+ for other sessions' work before switching branches.
79
+ - **`stop` and the deadline cover the whole request**, from waking the sandbox to the run itself,
80
+ and the server cancels a run at its deadline even if the bot is gone. Approvals go to the
81
+ requester unless you set `approvers`; "always allow" is off unless `allowAlways` is on.
82
+ - **One process per saved bot.** A second one waits for the first to stop (up to 90 seconds after a
83
+ crash); a process that loses its claim stops with exit code 1. Keep the state file on persistent
84
+ storage so restarts pick up runs in flight.
package/dist/bot.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { Bot, BotOptions } from "./types.js";
2
+ /**
3
+ * A chat bot backed by GitTerm sandboxes: one sandbox per repository that pauses when idle and
4
+ * wakes on the next message, and one OpenCode session per chat thread.
5
+ */
6
+ export declare function createBot(options: BotOptions): Bot;
@@ -0,0 +1,7 @@
1
+ /** Stream platform attachments with a deadline and actual-byte bound; never forward credentials
2
+ * to redirects or to a URL outside the platform's documented file hosts. */
3
+ export declare function downloadImage(url: string, options: {
4
+ hosts: string[];
5
+ headers?: Record<string, string>;
6
+ signal?: AbortSignal;
7
+ }): Promise<string>;
package/dist/env.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import type { BotOptions } from "./types.js";
2
+ type Env = Record<string, string | undefined>;
3
+ /**
4
+ * Bot settings from the environment, shared by the Slack and Discord command lines. GitTerm
5
+ * credentials are read by the SDK itself (`GITTERM_API_TOKEN`, `GITTERM_SERVER_URL`, or the
6
+ * `gitterm login` config).
7
+ */
8
+ export declare function botOptionsFromEnv(env?: Env): Omit<BotOptions, "adapter">;
9
+ /**
10
+ * The shared command-line entry: loads `.env` from the working directory and returns the bot
11
+ * options. `--repo`, `--model`, and `--instructions-file` override their environment variables.
12
+ */
13
+ export declare function cliOptions(argv?: string[]): {
14
+ command: string | undefined;
15
+ options: Omit<BotOptions, "adapter">;
16
+ };
17
+ /** The environment variables every bot command line reads, for its usage text. */
18
+ export declare const BOT_ENV_HELP = " Saved policy loads at startup; local settings cannot broaden its users, guests, or channels.\n\n GITTERM_API_TOKEN The bot's GitTerm token (dashboard \u2192 Bots creates one)\n GITTERM_SERVER_URL Self-hosted GitTerm API URL (default: hosted)\n GITTERM_BOT_REPO Repository for every channel: https://github.com/acme/app[#branch]\n GITTERM_BOT_REPOS More repositories people can name in a thread: url,url\n GITTERM_BOT_CHANNELS Per-channel repositories: <channel id>=<url>,\u2026\n GITTERM_BOT_MODEL provider/model, e.g. anthropic/claude-sonnet-5-5\n GITTERM_BOT_MODEL_CREDENTIAL Saved credential label (default: the provider's default)\n GITTERM_BOT_MODEL_API_KEY Or a model API key for this bot only\n GITTERM_BOT_GITHUB_TOKEN Your own GitHub token instead of a GitTerm GitHub connection\n GITTERM_BOT_CONNECTIONS Tools besides GitHub, by name: Linear,Sentry\n GITTERM_BOT_ALLOWED_USERS Only these user ids may use the bot: U012,U034\n GITTERM_BOT_ID Names an unsaved bot's sandboxes (saved bots use their own id)\n GITTERM_BOT_ALLOWED_CHANNELS Only these channel ids, whatever the repository routing\n GITTERM_BOT_APPROVERS Permission approver ids (default: requester only)\n GITTERM_BOT_ALLOW_ALWAYS true enables persistent approvals (default: disabled)\n GITTERM_BOT_SHARE_CHANNELS true opts equally trusted channels into a shared sandbox\n GITTERM_BOT_MAX_PENDING[_PER_USER] Queue bounds (defaults: 20 total, 3 per user)\n GITTERM_BOT_ALLOW_GUESTS true lets guests and external people use the bot\n GITTERM_BOT_INSTRUCTIONS[_FILE] What the agent should know and how to behave\n GITTERM_BOT_SETUP Command run before the agent starts, e.g. \"pnpm install\"\n GITTERM_BOT_SANDBOX_ENV_FILE dotenv file of variables for the sandbox (test credentials)\n GITTERM_BOT_PROVIDER Compute provider for new sandboxes (railway, e2b, \u2026)\n GITTERM_BOT_STATE_FILE, GITTERM_BOT_RUN_TIMEOUT_MINUTES";
19
+ /** Whether the options name at least one repository the bot can work on. */
20
+ export declare const hasRepository: (options: Omit<BotOptions, "adapter">) => boolean;
21
+ export {};
@@ -0,0 +1,8 @@
1
+ export { createBot } from "./bot.js";
2
+ export { downloadImage } from "./download.js";
3
+ export { withSavedConfig } from "./saved.js";
4
+ export { BOT_ENV_HELP, botOptionsFromEnv, cliOptions, hasRepository } from "./env.js";
5
+ export { BOT_TOKEN_SCOPES } from "./preflight.js";
6
+ export { splitMessage, tablesToCode } from "./markdown.js";
7
+ export { interpretTypedAnswer } from "./prompt.js";
8
+ export type { Bot, BotLogger, BotOptions, ChatAdapter, ChatAnswer, ChatEvents, ChatFile, ChatMessage, ChatPrompt, ChatThread, ChatUser, HistoryMessage, ModelChoice, RepoTarget, WorkspaceOverrides, } from "./types.js";