@geminixiang/mikan 1.0.0-beta.62 → 1.0.0-beta.64
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 +26 -0
- package/README.md +13 -13
- package/dist/adapters/commands/sandbox.d.ts.map +1 -1
- package/dist/adapters/commands/sandbox.js +30 -53
- package/dist/adapters/commands/sandbox.js.map +1 -1
- package/dist/adapters/commands/types.d.ts +3 -3
- package/dist/adapters/commands/types.d.ts.map +1 -1
- package/dist/adapters/commands/types.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +8 -0
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +32 -2
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/web/admin/portal.d.ts.map +1 -1
- package/dist/adapters/web/admin/portal.js +26 -121
- package/dist/adapters/web/admin/portal.js.map +1 -1
- package/dist/cli/boot.js +1 -1
- package/dist/cli/boot.js.map +1 -1
- package/dist/cli/office.d.ts.map +1 -1
- package/dist/cli/office.js +33 -1
- package/dist/cli/office.js.map +1 -1
- package/dist/config.d.ts +26 -11
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +103 -61
- package/dist/config.js.map +1 -1
- package/dist/harness/execution-resolver.d.ts.map +1 -1
- package/dist/harness/execution-resolver.js +1 -1
- package/dist/harness/execution-resolver.js.map +1 -1
- package/dist/harness/prompt.d.ts.map +1 -1
- package/dist/harness/prompt.js +17 -21
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +2 -2
- package/dist/harness/runner.js.map +1 -1
- package/dist/office/projection.d.ts +19 -2
- package/dist/office/projection.d.ts.map +1 -1
- package/dist/office/projection.js +67 -99
- package/dist/office/projection.js.map +1 -1
- package/dist/office/types.d.ts +7 -8
- package/dist/office/types.d.ts.map +1 -1
- package/dist/office/types.js.map +1 -1
- package/dist/sandbox/index.d.ts +9 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +16 -12
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/provisioner.d.ts +8 -0
- package/dist/sandbox/provisioner.d.ts.map +1 -1
- package/dist/sandbox/provisioner.js +36 -0
- package/dist/sandbox/provisioner.js.map +1 -1
- package/dist/settings-mutation.d.ts +5 -29
- package/dist/settings-mutation.d.ts.map +1 -1
- package/dist/settings-mutation.js +7 -13
- package/dist/settings-mutation.js.map +1 -1
- package/dist/types.d.ts +2 -32
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,32 @@ any release.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.0.0-beta.64]
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- The retired door-policy keys (`sandbox.image.workspaceMount`, `sandbox.workspace`) are dropped at load time and no longer appear in the resolved sandbox settings or the Admin office view.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `mikan office migrate-door-policy` removes the retired keys from the global and every registered office settings file, preserving all other settings and leaving files without them untouched. Only an explicit shared-support `private` visibility is carried into `office.visibility`; `full`, `isolated`, and legacy `private` are removed without deriving any grant.
|
|
21
|
+
|
|
22
|
+
## [1.0.0-beta.63]
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Office data policy is now a single **visibility** derived from the Slack conversation type (ADR 0008): public channels are public offices; private channels, DMs, group DMs, externally shared channels, and unknown kinds are private. Every office receives the same projection — its own directory read-write, every other public office read-only under `/workspace/public/<office key>`, and shared `MEMORY.md`/`skills/` read-write for public offices or read-only for private ones. Nothing mounts the workspace root.
|
|
27
|
+
- The five-way door policy (`isolated`, `trusted/shared-support`, `trusted/full`, …) is retired. Legacy settings still parse but no longer change the projection; offices that still declare `full` are reported once per process. The Admin "door policy" control and `/pi-sandbox door` are replaced by one switch that can narrow a public channel to private (`/pi-sandbox visibility <private|default>`); nothing can widen beyond what Slack allows.
|
|
28
|
+
- Backends without a managed projection (`host`, `container:*`, `cloudflare:*`) now serve private offices with a one-time logged warning instead of refusing, since they are trusted deployments by definition.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- The Slack adapter records channel kinds for every registered office from the channel list loaded at startup, so offices created before kinds were recorded keep their public status without waiting for a new message.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- Recreating a sandbox container after a visibility change removes empty `/workspace/public/<key>` mountpoints that `docker commit` carried over from the previous mount set.
|
|
37
|
+
|
|
12
38
|
## [1.0.0-beta.62]
|
|
13
39
|
|
|
14
40
|
### Changed
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ mikan keeps the chat record, agent session, and execution runtime separate:
|
|
|
31
31
|
|
|
32
32
|
- **Multi-platform** — Slack, Telegram, Discord, and GitHub adapters
|
|
33
33
|
- **Concurrent conversations** — Slack threads, Discord replies/threads, and Telegram reply chains run as independent sessions
|
|
34
|
-
- **Conversation offices** — one office directory and one sandbox runtime per conversation, with
|
|
34
|
+
- **Conversation offices** — one office directory and one sandbox runtime per conversation, with public/private visibility derived from the platform conversation type
|
|
35
35
|
- **Sandbox execution** — host, shared container, per-conversation managed container, or Cloudflare bridge (experimental)
|
|
36
36
|
- **Credential vaults** — `/login` stores credentials under `--state-dir` and injects env into sandbox runs
|
|
37
37
|
- **Web session viewer** — read-only web view of the current session via `session` / `/session`
|
|
@@ -127,24 +127,24 @@ Slack threads, Discord replies/threads, and Telegram reply chains are mapped to
|
|
|
127
127
|
| `image:<image>` | Auto-provision one Docker container and one vault per conversation office |
|
|
128
128
|
| `cloudflare:<sandbox-id>` | Cloudflare Worker bridge (experimental; no auto workspace sync) |
|
|
129
129
|
|
|
130
|
-
Each office
|
|
130
|
+
Each office is **public** or **private**, following the Slack conversation type: public channels are public — every other office can read them (read-only, under `/workspace/public/`) and they may write the shared `MEMORY.md` and `skills/`. Private channels, DMs, group DMs, and externally shared channels are private — visible only to themselves, reading shared knowledge and public offices without writing back. Unknown conversation kinds are private. The admin portal or `/pi-sandbox visibility private` can narrow a public channel; nothing can widen beyond Slack. Visibility governs data access only; execution isolation is unaffected.
|
|
131
131
|
|
|
132
|
-
Only `image:*`
|
|
132
|
+
Only `image:*` enforces visibility. `host`, `container:*`, and `cloudflare:*` share one filesystem and are trusted deployments; a private office there is served with a logged warning.
|
|
133
133
|
|
|
134
134
|
For routing, mounts, vault behavior, managed container details, and Cloudflare notes, see [src/content/docs/sandbox.mdx](src/content/docs/sandbox.mdx).
|
|
135
135
|
|
|
136
136
|
## Chat commands
|
|
137
137
|
|
|
138
|
-
| Command | Purpose
|
|
139
|
-
| ------------------------------------------------ |
|
|
140
|
-
| `/login` / `/pi-login` | Store API keys or run built-in OAuth flows
|
|
141
|
-
| `session` / `/session` | Open a read-only web view of the current session
|
|
142
|
-
| `/new` / `/pi-new` | Reset the current session
|
|
143
|
-
| `/model` / `/pi-model provider/model[:thinking]` | Switch the LLM for the current conversation
|
|
144
|
-
| `/sandbox` / `/pi-sandbox [boost\|
|
|
145
|
-
| `/pi-auto-reply <on\|off>` | Toggle mention-free replies for the current Slack channel
|
|
146
|
-
| `/admin` / `/pi-admin` | Open the admin portal
|
|
147
|
-
| `stop` / `/stop` | Stop the current run (works on every platform)
|
|
138
|
+
| Command | Purpose |
|
|
139
|
+
| ------------------------------------------------ | -------------------------------------------------------------- |
|
|
140
|
+
| `/login` / `/pi-login` | Store API keys or run built-in OAuth flows |
|
|
141
|
+
| `session` / `/session` | Open a read-only web view of the current session |
|
|
142
|
+
| `/new` / `/pi-new` | Reset the current session |
|
|
143
|
+
| `/model` / `/pi-model provider/model[:thinking]` | Switch the LLM for the current conversation |
|
|
144
|
+
| `/sandbox` / `/pi-sandbox [boost\|visibility …]` | Show sandbox status, boost limits, or narrow office visibility |
|
|
145
|
+
| `/pi-auto-reply <on\|off>` | Toggle mention-free replies for the current Slack channel |
|
|
146
|
+
| `/admin` / `/pi-admin` | Open the admin portal |
|
|
147
|
+
| `stop` / `/stop` | Stop the current run (works on every platform) |
|
|
148
148
|
|
|
149
149
|
`session` is the only command accepted without a leading slash. See [src/content/docs/commands.mdx](src/content/docs/commands.mdx) for the full command reference and web session viewer setup.
|
|
150
150
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/sandbox.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/sandbox.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGvF,YAAY,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIvD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAe7E;AAED,qBAAa,qBAAsB,YAAW,cAAc;IACpD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBzD;CACF"}
|
|
@@ -1,32 +1,8 @@
|
|
|
1
1
|
import { resolveWorkspaceProjection } from "../../office/projection.js";
|
|
2
2
|
import { runtimeResourceKey } from "../../sandbox/identity.js";
|
|
3
|
-
import {
|
|
3
|
+
import { applyOfficeVisibility } from "../../settings-mutation.js";
|
|
4
4
|
import { slashForms, matchCommand } from "./manifest.js";
|
|
5
5
|
import { replySummary } from "./utils.js";
|
|
6
|
-
/**
|
|
7
|
-
* Word → selection; null clears the override, undefined is an unknown word.
|
|
8
|
-
* `shared`/`shared-support` default to public visibility (today's read-write
|
|
9
|
-
* behavior, unchanged); `shared-private` reads workspace memory but cannot
|
|
10
|
-
* write it, so information can flow in without leaking back out.
|
|
11
|
-
*/
|
|
12
|
-
function doorChoiceFromWord(word) {
|
|
13
|
-
switch (word) {
|
|
14
|
-
case "default":
|
|
15
|
-
return null;
|
|
16
|
-
case "isolated":
|
|
17
|
-
return { doorPolicy: "isolated" };
|
|
18
|
-
case "shared":
|
|
19
|
-
case "shared-support":
|
|
20
|
-
case "shared-public":
|
|
21
|
-
return { doorPolicy: "trusted", layout: "shared-support", visibility: "public" };
|
|
22
|
-
case "shared-private":
|
|
23
|
-
return { doorPolicy: "trusted", layout: "shared-support", visibility: "private" };
|
|
24
|
-
case "full":
|
|
25
|
-
return { doorPolicy: "trusted", layout: "full" };
|
|
26
|
-
default:
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
6
|
const SANDBOX_COMMANDS = slashForms("sandbox");
|
|
31
7
|
export function parseSandboxCommand(text) {
|
|
32
8
|
const matched = matchCommand(text, SANDBOX_COMMANDS, { stripMention: true });
|
|
@@ -36,10 +12,10 @@ export function parseSandboxCommand(text) {
|
|
|
36
12
|
if (action === "boost" && matched.args.length === 1) {
|
|
37
13
|
return { action };
|
|
38
14
|
}
|
|
39
|
-
if (action === "
|
|
15
|
+
if (action === "visibility" && matched.args.length <= 2) {
|
|
40
16
|
return {
|
|
41
17
|
action,
|
|
42
|
-
...(matched.args.length === 2 ? {
|
|
18
|
+
...(matched.args.length === 2 ? { visibility: matched.args[1]?.toLowerCase() } : {}),
|
|
43
19
|
};
|
|
44
20
|
}
|
|
45
21
|
return {};
|
|
@@ -63,8 +39,8 @@ export class SandboxCommandHandler {
|
|
|
63
39
|
if (parsed.action === "boost") {
|
|
64
40
|
await handleBoost(context, controller, containerKey);
|
|
65
41
|
}
|
|
66
|
-
else if (parsed.action === "
|
|
67
|
-
await
|
|
42
|
+
else if (parsed.action === "visibility") {
|
|
43
|
+
await handleVisibility(context, parsed);
|
|
68
44
|
}
|
|
69
45
|
else {
|
|
70
46
|
await showSandboxStatus(context, controller, containerKey);
|
|
@@ -88,41 +64,44 @@ async function handleBoost(context, controller, containerKey) {
|
|
|
88
64
|
"boost 會在此 sandbox runtime 關閉後結束。",
|
|
89
65
|
]);
|
|
90
66
|
}
|
|
91
|
-
|
|
67
|
+
function describeVisibility(projection) {
|
|
68
|
+
const source = projection.source === "override"
|
|
69
|
+
? "admin 覆寫"
|
|
70
|
+
: projection.source === "platform"
|
|
71
|
+
? "依平台頻道類型"
|
|
72
|
+
: "頻道類型未知,預設 private";
|
|
73
|
+
return `${projection.visibility}(${source})`;
|
|
74
|
+
}
|
|
75
|
+
async function handleVisibility(context, parsed) {
|
|
92
76
|
const office = context.services.workspace.office(context.address);
|
|
93
77
|
const projection = resolveWorkspaceProjection(office);
|
|
94
|
-
if (parsed.
|
|
95
|
-
await replySummary(context, "
|
|
96
|
-
`Current: ${projection
|
|
78
|
+
if (parsed.visibility === undefined) {
|
|
79
|
+
await replySummary(context, "Office Visibility", [
|
|
80
|
+
`Current: ${describeVisibility(projection)}`,
|
|
97
81
|
"",
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"-
|
|
102
|
-
"- `shared`:強制共用 MEMORY.md / skills / events(可讀寫)",
|
|
103
|
-
"- `shared-private`:同 shared,但共用 MEMORY.md 只讀不可寫",
|
|
104
|
-
"- `full`:強制掛載整個 workspace(全開)",
|
|
82
|
+
"public:其他 office 可唯讀這個 office,且可寫入共用 MEMORY.md / skills。",
|
|
83
|
+
"private:只有自己看得到,共用 MEMORY.md / skills 唯讀。",
|
|
84
|
+
"預設跟隨 Slack 頻道類型;private 頻道與 DM 一律 private。",
|
|
85
|
+
"admin 覆寫:`/pi-sandbox visibility <private|default>`(只能把 public 頻道改為 private)",
|
|
105
86
|
]);
|
|
106
87
|
return;
|
|
107
88
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
`未知的 door policy:\`${parsed.doorPolicy}\``,
|
|
112
|
-
"可用值:`default`、`isolated`、`shared`、`shared-private`、`full`",
|
|
89
|
+
if (parsed.visibility !== "private" && parsed.visibility !== "default") {
|
|
90
|
+
await replySummary(context, "Office Visibility", [
|
|
91
|
+
`未知的值:\`${parsed.visibility}\`。可用值:\`private\`、\`default\``,
|
|
113
92
|
]);
|
|
114
93
|
return;
|
|
115
94
|
}
|
|
116
|
-
const result =
|
|
95
|
+
const result = applyOfficeVisibility(context.services.runtime, office, parsed.visibility === "private" ? "private" : null);
|
|
117
96
|
if (!result.ok) {
|
|
118
|
-
await replySummary(context, "
|
|
119
|
-
"目前有工作正在執行,無法切換
|
|
97
|
+
await replySummary(context, "Office Visibility", [
|
|
98
|
+
"目前有工作正在執行,無法切換 visibility。等執行結束後再試一次。",
|
|
120
99
|
]);
|
|
121
100
|
return;
|
|
122
101
|
}
|
|
123
102
|
const updated = resolveWorkspaceProjection(office);
|
|
124
|
-
await replySummary(context, "
|
|
125
|
-
`
|
|
103
|
+
await replySummary(context, "Office Visibility", [
|
|
104
|
+
`Visibility 已更新。Current: ${describeVisibility(updated)}`,
|
|
126
105
|
"下一則訊息時會以新的掛載重建 sandbox 容器;容器內容會保留。",
|
|
127
106
|
]);
|
|
128
107
|
}
|
|
@@ -134,9 +113,7 @@ async function showSandboxStatus(context, controller, containerKey) {
|
|
|
134
113
|
await replySummary(context, "Sandbox", [
|
|
135
114
|
`Current: ${formatLimits(status.limits)}`,
|
|
136
115
|
`Status: ${status.boosted ? "boosted" : "default"}`,
|
|
137
|
-
`
|
|
138
|
-
`Workspace layout: ${projection.layout}`,
|
|
139
|
-
`Workspace visibility: ${projection.visibility}`,
|
|
116
|
+
`Office visibility: ${describeVisibility(projection)}`,
|
|
140
117
|
"",
|
|
141
118
|
`Default: ${formatLimits(defaultLimits)}`,
|
|
142
119
|
boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../src/adapters/commands/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["import type { WorkspacePolicyChoice } from \"../../config.js\";\nimport { resolveWorkspaceProjection } from \"../../office/projection.js\";\nimport { runtimeResourceKey } from \"../../sandbox/identity.js\";\nimport { applyConversationWorkspacePolicy } from \"../../settings-mutation.js\";\nimport { slashForms, matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler, ParsedSandboxCommand } from \"./types.js\";\nimport { replySummary } from \"./utils.js\";\n\n/**\n * Word → selection; null clears the override, undefined is an unknown word.\n * `shared`/`shared-support` default to public visibility (today's read-write\n * behavior, unchanged); `shared-private` reads workspace memory but cannot\n * write it, so information can flow in without leaking back out.\n */\nfunction doorChoiceFromWord(word: string): WorkspacePolicyChoice | null | undefined {\n switch (word) {\n case \"default\":\n return null;\n case \"isolated\":\n return { doorPolicy: \"isolated\" };\n case \"shared\":\n case \"shared-support\":\n case \"shared-public\":\n return { doorPolicy: \"trusted\", layout: \"shared-support\", visibility: \"public\" };\n case \"shared-private\":\n return { doorPolicy: \"trusted\", layout: \"shared-support\", visibility: \"private\" };\n case \"full\":\n return { doorPolicy: \"trusted\", layout: \"full\" };\n default:\n return undefined;\n }\n}\n\nexport type { ParsedSandboxCommand } from \"./types.js\";\n\nconst SANDBOX_COMMANDS = slashForms(\"sandbox\");\n\nexport function parseSandboxCommand(text: string): ParsedSandboxCommand | null {\n const matched = matchCommand(text, SANDBOX_COMMANDS, { stripMention: true });\n if (!matched) return null;\n\n const action = matched.args[0]?.toLowerCase();\n if (action === \"boost\" && matched.args.length === 1) {\n return { action };\n }\n if (action === \"door\" && matched.args.length <= 2) {\n return {\n action,\n ...(matched.args.length === 2 ? { doorPolicy: matched.args[1]?.toLowerCase() } : {}),\n };\n }\n return {};\n}\n\nexport class SandboxCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseSandboxCommand(context.commandText);\n if (!parsed) return false;\n\n if (context.services.sandbox.type !== \"image\" || !context.services.resourceController) {\n await replySummary(context, \"Sandbox\", [\n \"`/pi-sandbox` 目前只支援 `image:*` managed sandbox。\",\n ]);\n return true;\n }\n\n const controller = context.services.resourceController;\n const containerKey = runtimeResourceKey(context.services.sandbox, {\n userId: context.platformUserId,\n address: context.address,\n });\n if (parsed.action === \"boost\") {\n await handleBoost(context, controller, containerKey);\n } else if (parsed.action === \"door\") {\n await handleDoor(context, parsed);\n } else {\n await showSandboxStatus(context, controller, containerKey);\n }\n return true;\n }\n}\n\nasync function handleBoost(\n context: CommandContext,\n controller: NonNullable<CommandContext[\"services\"][\"resourceController\"]>,\n containerKey: string,\n): Promise<void> {\n const boostLimits = controller.getBoostLimits();\n if (!boostLimits?.cpus && !boostLimits?.memory) {\n await replySummary(context, \"Sandbox Boost\", [\n \"此 mikan instance 尚未設定 sandbox boost 規格。\",\n \"請先在全域 settings.json 設定 `sandbox.boost`。\",\n ]);\n return;\n }\n\n const status = await controller.boost(containerKey);\n await replySummary(context, \"Sandbox Boost\", [\n \"已暫時提升此 conversation 的 sandbox 規格。\",\n `Current: ${formatLimits(status.limits)}`,\n \"boost 會在此 sandbox runtime 關閉後結束。\",\n ]);\n}\n\nasync function handleDoor(context: CommandContext, parsed: ParsedSandboxCommand): Promise<void> {\n const office = context.services.workspace.office(context.address);\n const projection = resolveWorkspaceProjection(office);\n if (parsed.doorPolicy === undefined) {\n await replySummary(context, \"Sandbox Door\", [\n `Current: ${projection.doorPolicy} / ${projection.layout} / ${projection.visibility}`,\n \"\",\n \"預設不需要設定:共享範圍直接跟隨平台頻道屬性(public 頻道共享、private 頻道只讀、DM 隔離)。\",\n \"以下為 admin 覆寫選項:`/pi-sandbox door <default|isolated|shared|shared-private|full>`\",\n \"- `default`:清除覆寫,回到自動判定\",\n \"- `isolated`:強制只掛載自己辦公室\",\n \"- `shared`:強制共用 MEMORY.md / skills / events(可讀寫)\",\n \"- `shared-private`:同 shared,但共用 MEMORY.md 只讀不可寫\",\n \"- `full`:強制掛載整個 workspace(全開)\",\n ]);\n return;\n }\n\n const choice = doorChoiceFromWord(parsed.doorPolicy);\n if (choice === undefined) {\n await replySummary(context, \"Sandbox Door\", [\n `未知的 door policy:\\`${parsed.doorPolicy}\\``,\n \"可用值:`default`、`isolated`、`shared`、`shared-private`、`full`\",\n ]);\n return;\n }\n\n const result = applyConversationWorkspacePolicy(context.services.runtime, office, choice);\n if (!result.ok) {\n await replySummary(context, \"Sandbox Door\", [\n \"目前有工作正在執行,無法切換 door policy。等執行結束後再試一次。\",\n ]);\n return;\n }\n\n const updated = resolveWorkspaceProjection(office);\n await replySummary(context, \"Sandbox Door\", [\n `Door policy 已更新。Effective: ${updated.doorPolicy} / ${updated.layout} / ${updated.visibility}`,\n \"下一則訊息時會以新的掛載重建 sandbox 容器;容器內容會保留。\",\n ]);\n}\n\nasync function showSandboxStatus(\n context: CommandContext,\n controller: NonNullable<CommandContext[\"services\"][\"resourceController\"]>,\n containerKey: string,\n): Promise<void> {\n const status = controller.getLimitStatus(containerKey);\n const defaultLimits = controller.getDefaultLimits();\n const boostLimits = controller.getBoostLimits();\n const projection = resolveWorkspaceProjection(context.services.workspace.office(context.address));\n await replySummary(\n context,\n \"Sandbox\",\n [\n `Current: ${formatLimits(status.limits)}`,\n `Status: ${status.boosted ? \"boosted\" : \"default\"}`,\n `Workspace policy: ${projection.doorPolicy}`,\n `Workspace layout: ${projection.layout}`,\n `Workspace visibility: ${projection.visibility}`,\n \"\",\n `Default: ${formatLimits(defaultLimits)}`,\n boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,\n ].filter((line): line is string => line !== undefined),\n );\n}\n\nfunction formatLimits(limits: { cpus?: string; memory?: string } | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../src/adapters/commands/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveWorkspaceProjection } from \"../../office/projection.js\";\nimport { runtimeResourceKey } from \"../../sandbox/identity.js\";\nimport { applyOfficeVisibility } from \"../../settings-mutation.js\";\nimport { slashForms, matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler, ParsedSandboxCommand } from \"./types.js\";\nimport { replySummary } from \"./utils.js\";\n\nexport type { ParsedSandboxCommand } from \"./types.js\";\n\nconst SANDBOX_COMMANDS = slashForms(\"sandbox\");\n\nexport function parseSandboxCommand(text: string): ParsedSandboxCommand | null {\n const matched = matchCommand(text, SANDBOX_COMMANDS, { stripMention: true });\n if (!matched) return null;\n\n const action = matched.args[0]?.toLowerCase();\n if (action === \"boost\" && matched.args.length === 1) {\n return { action };\n }\n if (action === \"visibility\" && matched.args.length <= 2) {\n return {\n action,\n ...(matched.args.length === 2 ? { visibility: matched.args[1]?.toLowerCase() } : {}),\n };\n }\n return {};\n}\n\nexport class SandboxCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n const parsed = parseSandboxCommand(context.commandText);\n if (!parsed) return false;\n\n if (context.services.sandbox.type !== \"image\" || !context.services.resourceController) {\n await replySummary(context, \"Sandbox\", [\n \"`/pi-sandbox` 目前只支援 `image:*` managed sandbox。\",\n ]);\n return true;\n }\n\n const controller = context.services.resourceController;\n const containerKey = runtimeResourceKey(context.services.sandbox, {\n userId: context.platformUserId,\n address: context.address,\n });\n if (parsed.action === \"boost\") {\n await handleBoost(context, controller, containerKey);\n } else if (parsed.action === \"visibility\") {\n await handleVisibility(context, parsed);\n } else {\n await showSandboxStatus(context, controller, containerKey);\n }\n return true;\n }\n}\n\nasync function handleBoost(\n context: CommandContext,\n controller: NonNullable<CommandContext[\"services\"][\"resourceController\"]>,\n containerKey: string,\n): Promise<void> {\n const boostLimits = controller.getBoostLimits();\n if (!boostLimits?.cpus && !boostLimits?.memory) {\n await replySummary(context, \"Sandbox Boost\", [\n \"此 mikan instance 尚未設定 sandbox boost 規格。\",\n \"請先在全域 settings.json 設定 `sandbox.boost`。\",\n ]);\n return;\n }\n\n const status = await controller.boost(containerKey);\n await replySummary(context, \"Sandbox Boost\", [\n \"已暫時提升此 conversation 的 sandbox 規格。\",\n `Current: ${formatLimits(status.limits)}`,\n \"boost 會在此 sandbox runtime 關閉後結束。\",\n ]);\n}\n\nfunction describeVisibility(projection: ReturnType<typeof resolveWorkspaceProjection>): string {\n const source =\n projection.source === \"override\"\n ? \"admin 覆寫\"\n : projection.source === \"platform\"\n ? \"依平台頻道類型\"\n : \"頻道類型未知,預設 private\";\n return `${projection.visibility}(${source})`;\n}\n\nasync function handleVisibility(\n context: CommandContext,\n parsed: ParsedSandboxCommand,\n): Promise<void> {\n const office = context.services.workspace.office(context.address);\n const projection = resolveWorkspaceProjection(office);\n if (parsed.visibility === undefined) {\n await replySummary(context, \"Office Visibility\", [\n `Current: ${describeVisibility(projection)}`,\n \"\",\n \"public:其他 office 可唯讀這個 office,且可寫入共用 MEMORY.md / skills。\",\n \"private:只有自己看得到,共用 MEMORY.md / skills 唯讀。\",\n \"預設跟隨 Slack 頻道類型;private 頻道與 DM 一律 private。\",\n \"admin 覆寫:`/pi-sandbox visibility <private|default>`(只能把 public 頻道改為 private)\",\n ]);\n return;\n }\n if (parsed.visibility !== \"private\" && parsed.visibility !== \"default\") {\n await replySummary(context, \"Office Visibility\", [\n `未知的值:\\`${parsed.visibility}\\`。可用值:\\`private\\`、\\`default\\``,\n ]);\n return;\n }\n const result = applyOfficeVisibility(\n context.services.runtime,\n office,\n parsed.visibility === \"private\" ? \"private\" : null,\n );\n if (!result.ok) {\n await replySummary(context, \"Office Visibility\", [\n \"目前有工作正在執行,無法切換 visibility。等執行結束後再試一次。\",\n ]);\n return;\n }\n const updated = resolveWorkspaceProjection(office);\n await replySummary(context, \"Office Visibility\", [\n `Visibility 已更新。Current: ${describeVisibility(updated)}`,\n \"下一則訊息時會以新的掛載重建 sandbox 容器;容器內容會保留。\",\n ]);\n}\n\nasync function showSandboxStatus(\n context: CommandContext,\n controller: NonNullable<CommandContext[\"services\"][\"resourceController\"]>,\n containerKey: string,\n): Promise<void> {\n const status = controller.getLimitStatus(containerKey);\n const defaultLimits = controller.getDefaultLimits();\n const boostLimits = controller.getBoostLimits();\n const projection = resolveWorkspaceProjection(context.services.workspace.office(context.address));\n await replySummary(\n context,\n \"Sandbox\",\n [\n `Current: ${formatLimits(status.limits)}`,\n `Status: ${status.boosted ? \"boosted\" : \"default\"}`,\n `Office visibility: ${describeVisibility(projection)}`,\n \"\",\n `Default: ${formatLimits(defaultLimits)}`,\n boostLimits ? `Boost: ${formatLimits({ ...defaultLimits, ...boostLimits })}` : undefined,\n ].filter((line): line is string => line !== undefined),\n );\n}\n\nfunction formatLimits(limits: { cpus?: string; memory?: string } | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n"]}
|
|
@@ -126,9 +126,9 @@ export interface ParsedModelCommand {
|
|
|
126
126
|
error?: "invalid_spec";
|
|
127
127
|
}
|
|
128
128
|
export interface ParsedSandboxCommand {
|
|
129
|
-
action?: "boost" | "
|
|
130
|
-
/** Raw
|
|
131
|
-
|
|
129
|
+
action?: "boost" | "visibility";
|
|
130
|
+
/** Raw visibility argument; the handler validates it so typos get a usage reply. */
|
|
131
|
+
visibility?: string;
|
|
132
132
|
}
|
|
133
133
|
export type ParsedLoginCommand = {
|
|
134
134
|
action: "setup";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/adapters/commands/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport type {\n ConversationContext,\n HandleNewCommandOptions,\n MessagingBot,\n OfficeAddress,\n PlatformName,\n} from \"../../adapter.js\";\nimport type { Workspace } from \"../../office/index.js\";\nimport type { DockerContainerManager } from \"../../sandbox/provisioner.js\";\nimport type { SandboxConfig } from \"../../sandbox/index.js\";\nimport type { SandboxResourceController } from \"../../types.js\";\nimport type { VaultManager } from \"../../vault/index.js\";\nimport type { SessionViewTokenCreateOptions } from \"../web/session-view/types.js\";\n\ninterface CommandArgSpec {\n name: string;\n description: string;\n required: boolean;\n}\n\n/**\n * Data for the Slack adapter's generic slash route (how the synthesized\n * command event is built). All flags default to false.\n */\nexport interface SlackSlashRoute {\n /** Append the slash payload's free text to the synthesized command text. */\n includeText?: boolean;\n /** Scope the event to the invoking thread (thread-scoped session key). */\n thread?: boolean;\n /** Outside DMs, mark the event `private_command` so replies stay between the bot and the user. */\n privateCommand?: boolean;\n}\n\nexport interface CommandManifestEntry {\n /** Canonical name without slash, e.g. \"login\"; `/login` and `/pi-login` are derived. */\n name: string;\n description: string;\n /** Extra accepted spellings beyond `name` (e.g. \"autoreply\"); each also gets a `pi-` form. */\n aliases?: readonly string[];\n /** Optional single free-text argument for platforms that register typed args. */\n arg?: CommandArgSpec;\n /** Accepted without a leading slash. Per CONTEXT.md, only `session`. */\n bare?: boolean;\n /**\n * Routed by conversation intake as a magic word, never by a command\n * handler. Such an entry exists for platform registration/UI only.\n */\n magicWord?: boolean;\n /** Slack slash-command name, e.g. \"/pi-login\". Absent = not a Slack slash command. */\n slackCommand?: string;\n /** Slack routing data; absent for `new`, whose Slack dispatch is bespoke. */\n slackRoute?: SlackSlashRoute;\n /** Registered as a Discord application command. */\n discord?: boolean;\n /** Listed in Telegram's command menu; `description` overrides the shared one. */\n telegramMenu?: { description?: string };\n /**\n * Registered as a native grammY command handler, bypassing conversation\n * intake (no trigger/busy policy). Other menu commands reach the runtime\n * through the catch-all message path.\n */\n telegramCommand?: boolean;\n}\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(options: SessionViewTokenCreateOptions): { token: string };\n}\n\nexport interface AdminTokenStoreLike {\n create(args: {\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n platformUserName?: string;\n }): { token: string };\n}\n\ninterface CommandRuntimeBridge {\n handleNewCommand(options: HandleNewCommandOptions): Promise<void>;\n switchConversationModel(address: OfficeAddress, provider: string, model: string): boolean;\n refreshConversationEnvironment(address: OfficeAddress): boolean;\n}\n\nexport interface CommandServices {\n workspace: Workspace;\n runtime?: CommandRuntimeBridge;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n provisioner?: DockerContainerManager;\n resourceController?: SandboxResourceController;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n adminTokenStore: AdminTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: MessagingBot;\n responder: ConversationContext[\"responder\"];\n platform: PlatformName;\n address: OfficeAddress;\n platformUserId: string;\n platformUserName?: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n\n/** Minimal model lookup contract used by the model command. */\nexport interface ModelRegistry {\n find(provider: string, modelId: string): Model<Api> | undefined;\n}\n\n// ── command-specific parsed types ────────────────────────────────────────────\n\nexport interface ParsedModelCommand {\n provider?: string;\n /** Model id without a thinking-level suffix, when one was supplied. */\n model?: string;\n /** Complete model id candidate, including a possible colon suffix. */\n modelCandidate?: string;\n /** Suffix kept for handler-side validation after exact model lookup. */\n thinkingLevelCandidate?: string;\n thinkingLevel?: ThinkingLevel;\n error?: \"invalid_spec\";\n}\n\nexport interface ParsedSandboxCommand {\n action?: \"boost\" | \"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/adapters/commands/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport type {\n ConversationContext,\n HandleNewCommandOptions,\n MessagingBot,\n OfficeAddress,\n PlatformName,\n} from \"../../adapter.js\";\nimport type { Workspace } from \"../../office/index.js\";\nimport type { DockerContainerManager } from \"../../sandbox/provisioner.js\";\nimport type { SandboxConfig } from \"../../sandbox/index.js\";\nimport type { SandboxResourceController } from \"../../types.js\";\nimport type { VaultManager } from \"../../vault/index.js\";\nimport type { SessionViewTokenCreateOptions } from \"../web/session-view/types.js\";\n\ninterface CommandArgSpec {\n name: string;\n description: string;\n required: boolean;\n}\n\n/**\n * Data for the Slack adapter's generic slash route (how the synthesized\n * command event is built). All flags default to false.\n */\nexport interface SlackSlashRoute {\n /** Append the slash payload's free text to the synthesized command text. */\n includeText?: boolean;\n /** Scope the event to the invoking thread (thread-scoped session key). */\n thread?: boolean;\n /** Outside DMs, mark the event `private_command` so replies stay between the bot and the user. */\n privateCommand?: boolean;\n}\n\nexport interface CommandManifestEntry {\n /** Canonical name without slash, e.g. \"login\"; `/login` and `/pi-login` are derived. */\n name: string;\n description: string;\n /** Extra accepted spellings beyond `name` (e.g. \"autoreply\"); each also gets a `pi-` form. */\n aliases?: readonly string[];\n /** Optional single free-text argument for platforms that register typed args. */\n arg?: CommandArgSpec;\n /** Accepted without a leading slash. Per CONTEXT.md, only `session`. */\n bare?: boolean;\n /**\n * Routed by conversation intake as a magic word, never by a command\n * handler. Such an entry exists for platform registration/UI only.\n */\n magicWord?: boolean;\n /** Slack slash-command name, e.g. \"/pi-login\". Absent = not a Slack slash command. */\n slackCommand?: string;\n /** Slack routing data; absent for `new`, whose Slack dispatch is bespoke. */\n slackRoute?: SlackSlashRoute;\n /** Registered as a Discord application command. */\n discord?: boolean;\n /** Listed in Telegram's command menu; `description` overrides the shared one. */\n telegramMenu?: { description?: string };\n /**\n * Registered as a native grammY command handler, bypassing conversation\n * intake (no trigger/busy policy). Other menu commands reach the runtime\n * through the catch-all message path.\n */\n telegramCommand?: boolean;\n}\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface SessionViewTokenStoreLike {\n create(options: SessionViewTokenCreateOptions): { token: string };\n}\n\nexport interface AdminTokenStoreLike {\n create(args: {\n platform: PlatformName;\n platformUserId: string;\n conversationId: string;\n platformUserName?: string;\n }): { token: string };\n}\n\ninterface CommandRuntimeBridge {\n handleNewCommand(options: HandleNewCommandOptions): Promise<void>;\n switchConversationModel(address: OfficeAddress, provider: string, model: string): boolean;\n refreshConversationEnvironment(address: OfficeAddress): boolean;\n}\n\nexport interface CommandServices {\n workspace: Workspace;\n runtime?: CommandRuntimeBridge;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n provisioner?: DockerContainerManager;\n resourceController?: SandboxResourceController;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n adminTokenStore: AdminTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: MessagingBot;\n responder: ConversationContext[\"responder\"];\n platform: PlatformName;\n address: OfficeAddress;\n platformUserId: string;\n platformUserName?: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n\n/** Minimal model lookup contract used by the model command. */\nexport interface ModelRegistry {\n find(provider: string, modelId: string): Model<Api> | undefined;\n}\n\n// ── command-specific parsed types ────────────────────────────────────────────\n\nexport interface ParsedModelCommand {\n provider?: string;\n /** Model id without a thinking-level suffix, when one was supplied. */\n model?: string;\n /** Complete model id candidate, including a possible colon suffix. */\n modelCandidate?: string;\n /** Suffix kept for handler-side validation after exact model lookup. */\n thinkingLevelCandidate?: string;\n thinkingLevel?: ThinkingLevel;\n error?: \"invalid_spec\";\n}\n\nexport interface ParsedSandboxCommand {\n action?: \"boost\" | \"visibility\";\n /** Raw visibility argument; the handler validates it so typos get a usage reply. */\n visibility?: string;\n}\n\nexport type ParsedLoginCommand =\n | { action: \"setup\" }\n | { action: \"shared_create\" | \"shared_update\" | \"shared_delete\"; name: string }\n | { action: \"shared_list\" }\n | { action: \"copy_shared\"; name: string };\n"]}
|
|
@@ -155,6 +155,14 @@ export declare class SlackMessagingBot implements MessagingBot {
|
|
|
155
155
|
* so the projection keeps its isolated default).
|
|
156
156
|
*/
|
|
157
157
|
private channelKindFor;
|
|
158
|
+
/**
|
|
159
|
+
* Office visibility follows the recorded channel kind, and an unrecorded
|
|
160
|
+
* kind fails closed to private (ADR 0008). Offices created before kinds
|
|
161
|
+
* were recorded would therefore lose public status until their next
|
|
162
|
+
* message; the channel list loaded at startup already knows every kind, so
|
|
163
|
+
* record it for all registered Slack offices now. Metadata only.
|
|
164
|
+
*/
|
|
165
|
+
private backfillChannelKinds;
|
|
158
166
|
private processSlackMessageIntake;
|
|
159
167
|
private appendRunningTasks;
|
|
160
168
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/bot.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,YAAY,EAEjB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAI1B,KAAK,aAAa,EAElB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,
|
|
1
|
+
{"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/bot.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,YAAY,EAEjB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAI1B,KAAK,aAAa,EAElB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAA8C,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGnG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,KAAK,EAGV,YAAY,EAEZ,SAAS,EACV,MAAM,YAAY,CAAC;AAiBpB,OAAO,EASL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AA4ExB,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAwB,GAAG,MAAM,EAAE,CAOrF;AAuDD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM5D;AAMD,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAoBtE,qBAAa,iBAAkB,YAAW,YAAY;IACpD,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,SAAS,CAAY;IAE7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAY;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoC;IAClE,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,SAAS,CAAuB;IAExC,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,QAAQ,CAAmC;IACnD,gFAAgF;IAChF,OAAO,CAAC,gBAAgB,CAAiC;IACzD;;;;;OAKG;IACH,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,cAAc,CAA+B;IAErD,gDAAgD;IAChD,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,aAAa;IAoGrB,YACE,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,SAAS,CAAA;KAAE,EAiBrE;IAED,iBAAiB,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAEjD;IAMK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAyB3B;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ1B;IAED,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAE7C;IAED,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEtD;IAED,WAAW,IAAI,SAAS,EAAE,CAEzB;IAED,cAAc,IAAI,YAAY,EAAE,CAE/B;IAED,OAAO,CAAC,eAAe;IASvB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAIjB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUnF;IAEK,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYlF;IAEK,aAAa,CACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAUf;IAEK,mBAAmB,CACvB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAEf;IAEK,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAWjB;IAEK,mBAAmB,CACvB,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,GACf,OAAO,CAAC,IAAI,CAAC,CAIf;IAEK,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErF;IAEK,qBAAqB,CACzB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;KAAE,GACtC,OAAO,CAAC,IAAI,CAAC,CAUf;IAEK,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5D;IAED;;;;;;OAMG;IACG,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,sBAAsB,EAAE,CAAC,CA8CnC;IAED,oEAAoE;IAC9D,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAQ7C;IAEK,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW5E;IAED;;;;OAIG;IACH,qBAAqB,IAAI,OAAO,CAE/B;IAED;;;;;;;;;;;;;OAaG;IACG,kBAAkB,CACtB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC,CAgBjB;IAED,+DAA+D;IACzD,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlF;YAEa,iBAAiB;IAYzB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlE;IAEK,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI9D;IAMD,sEAAsE;IAChE,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBzF;IAED,uDAAuD;IACjD,4BAA4B,CAChC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,eAAe,EAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CAUf;IAED,mFAAmF;IAC7E,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQvF;IAED,kFAAkF;IAClF,OAAO,CAAC,YAAY;IAUpB;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAWpC;;;;;OAKG;IACH,OAAO,CAAC,8BAA8B;IA2BhC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnF;IAEK,kBAAkB,CACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EAAE,GACf,OAAO,CAAC,MAAM,CAAC,CAEjB;IAEK,UAAU,CACd,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAYf;IAED,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAE9C;IAED,cAAc,CACZ,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,IAAI,CAWN;IAED,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAkB3E;IAED,gBAAgB,IAAI,aAAa,CAiBhC;IAMD;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CA4E9C;IAMD,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,qBAAqB;IAO7B;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;IAWtB;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAkB5B,OAAO,CAAC,yBAAyB;IAmDjC,OAAO,CAAC,kBAAkB;IA4D1B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA+C3B,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,qBAAqB;IAqE7B,OAAO,CAAC,sBAAsB;IA6D9B;;;;;OAKG;YACW,iBAAiB;IAyB/B,OAAO,CAAC,kBAAkB;YA8CZ,gBAAgB;IAkE9B,OAAO,CAAC,iBAAiB;YAmCX,iBAAiB;YAsCjB,kBAAkB;YAoGlB,kBAAkB;IAyChC,OAAO,CAAC,mBAAmB;YA0Bb,iBAAiB;IAmD/B,OAAO,CAAC,sBAAsB;IAkF9B;;;;OAIG;YACW,kBAAkB;IA+BhC,6EAA6E;YAC/D,iBAAiB;IAmB/B;;OAEG;YACW,cAAc;YA4Bd,8BAA8B;YAwC9B,qBAAqB;IAqBnC,OAAO,CAAC,qBAAqB;YAcf,eAAe;YAuEf,mBAAmB;YAsCnB,UAAU;YA0BV,aAAa;CAmD5B"}
|
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
4
4
|
import { readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { basename, join } from "node:path";
|
|
6
6
|
import { createConversationEvent, createConversationMessage, } from "../../adapter.js";
|
|
7
|
-
import { createOfficeAddress } from "../../office/index.js";
|
|
7
|
+
import { createOfficeAddress, listRegisteredOffices } from "../../office/index.js";
|
|
8
8
|
import { COMMAND_MANIFEST } from "../commands/manifest.js";
|
|
9
9
|
import { slackConversationAutoReplyEnabled, resolveConversationSettings } from "../../config.js";
|
|
10
10
|
import * as log from "../../log.js";
|
|
@@ -12,7 +12,7 @@ import { isRecord, readTextFileIfExists } from "../../file-guards.js";
|
|
|
12
12
|
import { PRODUCT_NAME, formatForceStopped } from "../../platform-messages.js";
|
|
13
13
|
import { appendBotResponseLog, appendChannelLog, MessagingEventQueue, MessagingIntakeTracker, saveIncomingAttachments, withRetry, } from "../shared.js";
|
|
14
14
|
import { matchMagicWord, processMessageIntake } from "../intake.js";
|
|
15
|
-
import { recordPlatformChannelKind } from "../../office/projection.js";
|
|
15
|
+
import { readPlatformChannelKind, recordPlatformChannelKind, } from "../../office/projection.js";
|
|
16
16
|
import { AssistantThreadRegistry, handleAgentContextChanged, handleAgentDmOpened, handleAssistantThreadStarted, titleAssistantThread, } from "./assistant.js";
|
|
17
17
|
import { createSlackAdapters } from "./context.js";
|
|
18
18
|
import { hasMaterializedChatSession, registerThreadSession, } from "../../sessions/chat-history-sync.js";
|
|
@@ -267,6 +267,7 @@ export class SlackMessagingBot {
|
|
|
267
267
|
if (this.stopped)
|
|
268
268
|
return;
|
|
269
269
|
log.logInfo(`Loaded ${this.channels.size} channels, ${this.users.size} users`);
|
|
270
|
+
this.backfillChannelKinds();
|
|
270
271
|
// Record startup time before opening the socket. Slack may replay older events;
|
|
271
272
|
// those should be logged but not processed. Backfill runs in the background up
|
|
272
273
|
// to this timestamp so startup is not blocked by one history call per channel.
|
|
@@ -822,6 +823,35 @@ export class SlackMessagingBot {
|
|
|
822
823
|
return "public_channel";
|
|
823
824
|
return undefined;
|
|
824
825
|
}
|
|
826
|
+
/**
|
|
827
|
+
* Office visibility follows the recorded channel kind, and an unrecorded
|
|
828
|
+
* kind fails closed to private (ADR 0008). Offices created before kinds
|
|
829
|
+
* were recorded would therefore lose public status until their next
|
|
830
|
+
* message; the channel list loaded at startup already knows every kind, so
|
|
831
|
+
* record it for all registered Slack offices now. Metadata only.
|
|
832
|
+
*/
|
|
833
|
+
backfillChannelKinds() {
|
|
834
|
+
let recorded = 0;
|
|
835
|
+
for (const record of listRegisteredOffices(this.workspace.stateDir)) {
|
|
836
|
+
if (record.platform !== "slack")
|
|
837
|
+
continue;
|
|
838
|
+
const kind = this.channelKindFor(record.conversationId);
|
|
839
|
+
if (!kind)
|
|
840
|
+
continue;
|
|
841
|
+
try {
|
|
842
|
+
const office = this.workspace.office(record);
|
|
843
|
+
if (readPlatformChannelKind(office) === kind)
|
|
844
|
+
continue;
|
|
845
|
+
recordPlatformChannelKind(office, kind);
|
|
846
|
+
recorded++;
|
|
847
|
+
}
|
|
848
|
+
catch (err) {
|
|
849
|
+
log.logWarning("Failed to backfill Slack channel kind", String(err));
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
if (recorded > 0)
|
|
853
|
+
log.logInfo(`Recorded channel kind for ${recorded} Slack offices`);
|
|
854
|
+
}
|
|
825
855
|
processSlackMessageIntake(options) {
|
|
826
856
|
const kind = this.channelKindFor(options.event.conversationId);
|
|
827
857
|
if (kind) {
|