@geminixiang/mikan 1.0.0-beta.60 → 1.0.0-beta.62
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 +44 -0
- package/README.md +3 -1
- package/dist/adapters/intake.js +12 -2
- package/dist/adapters/intake.js.map +1 -1
- package/dist/adapters/progressive-renderer.d.ts.map +1 -1
- package/dist/adapters/progressive-renderer.js +4 -0
- package/dist/adapters/progressive-renderer.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +9 -3
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +145 -16
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/task-status.d.ts +7 -0
- package/dist/adapters/slack/task-status.d.ts.map +1 -0
- package/dist/adapters/slack/task-status.js +101 -0
- package/dist/adapters/slack/task-status.js.map +1 -0
- package/dist/adapters/web/admin/portal.d.ts +1 -1
- package/dist/adapters/web/admin/portal.d.ts.map +1 -1
- package/dist/adapters/web/admin/portal.js +33 -25
- package/dist/adapters/web/admin/portal.js.map +1 -1
- package/dist/adapters/web/admin/types.d.ts +6 -3
- package/dist/adapters/web/admin/types.d.ts.map +1 -1
- package/dist/adapters/web/admin/types.js.map +1 -1
- package/dist/adapters/web/server.d.ts +3 -0
- package/dist/adapters/web/server.d.ts.map +1 -1
- package/dist/adapters/web/server.js +4 -6
- package/dist/adapters/web/server.js.map +1 -1
- package/dist/adapters/web/session-view/portal.js +2 -1
- package/dist/adapters/web/session-view/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 +66 -0
- package/dist/cli/office.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -4
- package/dist/config.js.map +1 -1
- package/dist/env-manifest.js +3 -3
- package/dist/env-manifest.js.map +1 -1
- package/dist/events/index.d.ts +56 -34
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +114 -23
- package/dist/events/index.js.map +1 -1
- package/dist/events/scheduler.d.ts +34 -0
- package/dist/events/scheduler.d.ts.map +1 -0
- package/dist/events/scheduler.js +231 -0
- package/dist/events/scheduler.js.map +1 -0
- package/dist/harness/mcp.d.ts.map +1 -1
- package/dist/harness/mcp.js +1 -3
- package/dist/harness/mcp.js.map +1 -1
- package/dist/harness/open-connector.d.ts +21 -10
- package/dist/harness/open-connector.d.ts.map +1 -1
- package/dist/harness/open-connector.js +109 -135
- package/dist/harness/open-connector.js.map +1 -1
- package/dist/harness/presenter.d.ts +1 -0
- package/dist/harness/presenter.d.ts.map +1 -1
- package/dist/harness/presenter.js +37 -9
- package/dist/harness/presenter.js.map +1 -1
- package/dist/harness/prompt.d.ts.map +1 -1
- package/dist/harness/prompt.js +9 -4
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +81 -34
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session.d.ts +3 -0
- package/dist/harness/session.d.ts.map +1 -1
- package/dist/harness/session.js +21 -1
- package/dist/harness/session.js.map +1 -1
- package/dist/harness/tools/event.d.ts +1 -1
- package/dist/harness/tools/event.d.ts.map +1 -1
- package/dist/harness/tools/event.js +19 -22
- package/dist/harness/tools/event.js.map +1 -1
- package/dist/harness/tools/index.d.ts +4 -1
- package/dist/harness/tools/index.d.ts.map +1 -1
- package/dist/harness/tools/index.js +6 -3
- package/dist/harness/tools/index.js.map +1 -1
- package/dist/harness/tools/task.d.ts +20 -0
- package/dist/harness/tools/task.d.ts.map +1 -0
- package/dist/harness/tools/task.js +79 -0
- package/dist/harness/tools/task.js.map +1 -0
- package/dist/main.js +17 -9
- package/dist/main.js.map +1 -1
- package/dist/office/index.d.ts +3 -2
- package/dist/office/index.d.ts.map +1 -1
- package/dist/office/index.js +3 -3
- package/dist/office/index.js.map +1 -1
- package/dist/office/projection.d.ts.map +1 -1
- package/dist/office/projection.js +0 -2
- package/dist/office/projection.js.map +1 -1
- package/dist/office/types.d.ts +0 -2
- package/dist/office/types.d.ts.map +1 -1
- package/dist/office/types.js.map +1 -1
- package/dist/runtime/conversation-runtime.d.ts.map +1 -1
- package/dist/runtime/conversation-runtime.js +35 -13
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +6 -2
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sessions/chat-history-sync.js +8 -3
- package/dist/sessions/chat-history-sync.js.map +1 -1
- package/dist/sessions/session-store.d.ts +8 -0
- package/dist/sessions/session-store.d.ts.map +1 -1
- package/dist/sessions/session-store.js +46 -21
- package/dist/sessions/session-store.js.map +1 -1
- package/dist/types.d.ts +26 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -2
- package/dist/events/watcher.d.ts +0 -43
- package/dist/events/watcher.d.ts.map +0 -1
- package/dist/events/watcher.js +0 -395
- package/dist/events/watcher.js.map +0 -1
|
@@ -11,7 +11,7 @@ import * as log from "../../log.js";
|
|
|
11
11
|
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
|
-
import { processMessageIntake } from "../intake.js";
|
|
14
|
+
import { matchMagicWord, processMessageIntake } from "../intake.js";
|
|
15
15
|
import { recordPlatformChannelKind } from "../../office/projection.js";
|
|
16
16
|
import { AssistantThreadRegistry, handleAgentContextChanged, handleAgentDmOpened, handleAssistantThreadStarted, titleAssistantThread, } from "./assistant.js";
|
|
17
17
|
import { createSlackAdapters } from "./context.js";
|
|
@@ -21,6 +21,7 @@ import { isSlackThreadSessionKey, planSlackAdapterSession, planSlackEventAnchorR
|
|
|
21
21
|
import { reportUserFacingError } from "../../observability/index.js";
|
|
22
22
|
import { recordSlackUpdate } from "./update-diagnostics.js";
|
|
23
23
|
import { renderSlackBlocks, resolveSlackMentions } from "./blocks.js";
|
|
24
|
+
import { querySlackTasks, isTaskStatusQuestion, formatTaskStatus, readTaskRoots, } from "./task-status.js";
|
|
24
25
|
import { StreamStartLimiter } from "./stream-limits.js";
|
|
25
26
|
const SLACK_EVENT_ANCHOR_TEXT = "Working on it...";
|
|
26
27
|
function hasMessageContent(message, includeBlocks = true) {
|
|
@@ -130,9 +131,87 @@ export class SlackMessagingBot {
|
|
|
130
131
|
return (resolveConversationSettings(this.workspace.office(address)).slack?.replyMode ?? "top-level");
|
|
131
132
|
}
|
|
132
133
|
createContext(event) {
|
|
133
|
-
|
|
134
|
+
const context = createSlackAdapters(event, this, {
|
|
134
135
|
replyMode: this.resolveReplyMode(event.address),
|
|
135
136
|
});
|
|
137
|
+
if (event.conversationKind === "direct") {
|
|
138
|
+
context.responder.getTaskStatus = (key) => querySlackTasks(this.conversationDir(event.channel), event.channel, this.handler
|
|
139
|
+
.getRunningSessions()
|
|
140
|
+
.filter((s) => s.address.platform === "slack" && s.address.conversationId === event.channel), key);
|
|
141
|
+
}
|
|
142
|
+
if (event.conversationKind === "direct" &&
|
|
143
|
+
event.thread_ts &&
|
|
144
|
+
this.isTaskThread(event.channel, event.thread_ts)) {
|
|
145
|
+
let notified = false;
|
|
146
|
+
context.responder.notifyCompletion = async () => {
|
|
147
|
+
if (notified)
|
|
148
|
+
return;
|
|
149
|
+
const text = `<@${event.user}> 這一輪處理已結束,請查看上方結果。`;
|
|
150
|
+
const ts = await this.postInThread(event.channel, event.thread_ts, text);
|
|
151
|
+
notified = true;
|
|
152
|
+
this.logBotResponse(event.channel, text, ts, event.thread_ts);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (event.conversationKind === "direct" && !event.thread_ts) {
|
|
156
|
+
context.responder.startTask = async (message, task) => {
|
|
157
|
+
if (this.stopped)
|
|
158
|
+
throw new Error("Slack is shutting down; task was not started.");
|
|
159
|
+
await context.responder.deleteResponse();
|
|
160
|
+
const root = await this.postMessage(event.channel, message);
|
|
161
|
+
this.logToFile(event.channel, {
|
|
162
|
+
date: new Date().toISOString(),
|
|
163
|
+
ts: root,
|
|
164
|
+
user: "bot",
|
|
165
|
+
text: message,
|
|
166
|
+
isMessagingBot: true,
|
|
167
|
+
taskRoot: true,
|
|
168
|
+
});
|
|
169
|
+
const sessionKey = resolveSlackSessionKey(event.channel, root);
|
|
170
|
+
try {
|
|
171
|
+
registerThreadSession({
|
|
172
|
+
conversationDir: this.conversationDir(event.channel),
|
|
173
|
+
sessionKey,
|
|
174
|
+
});
|
|
175
|
+
const child = {
|
|
176
|
+
...event,
|
|
177
|
+
ts: `task:${root}`,
|
|
178
|
+
thread_ts: root,
|
|
179
|
+
sessionKey,
|
|
180
|
+
text: task,
|
|
181
|
+
};
|
|
182
|
+
if (!this.getQueue(sessionKey).enqueue(async () => {
|
|
183
|
+
try {
|
|
184
|
+
await this.handler.handleEvent({
|
|
185
|
+
...child,
|
|
186
|
+
attachments: child.attachments?.map((a) => ({
|
|
187
|
+
name: a.original,
|
|
188
|
+
localPath: a.localPath,
|
|
189
|
+
})),
|
|
190
|
+
}, this, this.createContext(child));
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
reportUserFacingError(error, {
|
|
194
|
+
domain: "mikan",
|
|
195
|
+
surface: "task_handoff",
|
|
196
|
+
operation: "start_task_run",
|
|
197
|
+
severity: "error",
|
|
198
|
+
platform: "slack",
|
|
199
|
+
context: { conversationId: event.channel, sessionKey, threadTs: root },
|
|
200
|
+
});
|
|
201
|
+
await this.postInThread(event.channel, root, "Task could not start. Please reply here to try again.");
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}))
|
|
205
|
+
throw new Error("Task queue is closed.");
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
await this.updateMessage(event.channel, root, `${message}\n\nTask could not start.`);
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
return sessionKey;
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return context;
|
|
136
215
|
}
|
|
137
216
|
constructor(handler, config) {
|
|
138
217
|
this.statusUpdates = new Map();
|
|
@@ -155,7 +234,7 @@ export class SlackMessagingBot {
|
|
|
155
234
|
this.stopped = false;
|
|
156
235
|
this.queues = new Map();
|
|
157
236
|
this.intake = new MessagingIntakeTracker("Slack");
|
|
158
|
-
this.
|
|
237
|
+
this.eventScheduler = null;
|
|
159
238
|
this.handler = handler;
|
|
160
239
|
this.workspace = config.workspace;
|
|
161
240
|
this.botToken = config.botToken;
|
|
@@ -172,8 +251,8 @@ export class SlackMessagingBot {
|
|
|
172
251
|
rejectRateLimitedCalls: true,
|
|
173
252
|
});
|
|
174
253
|
}
|
|
175
|
-
|
|
176
|
-
this.
|
|
254
|
+
setEventScheduler(scheduler) {
|
|
255
|
+
this.eventScheduler = scheduler;
|
|
177
256
|
}
|
|
178
257
|
// ==========================================================================
|
|
179
258
|
// Public API
|
|
@@ -710,6 +789,9 @@ export class SlackMessagingBot {
|
|
|
710
789
|
}
|
|
711
790
|
return this.hasKnownThreadSession(conversationId, sessionKey) ? sessionKey : conversationId;
|
|
712
791
|
}
|
|
792
|
+
isTaskThread(channel, root) {
|
|
793
|
+
return readTaskRoots(this.conversationDir(channel)).has(root);
|
|
794
|
+
}
|
|
713
795
|
hasKnownThreadSession(conversationId, sessionKey) {
|
|
714
796
|
return hasMaterializedChatSession({
|
|
715
797
|
conversationDir: this.conversationDir(conversationId),
|
|
@@ -840,8 +922,14 @@ export class SlackMessagingBot {
|
|
|
840
922
|
}
|
|
841
923
|
}
|
|
842
924
|
}
|
|
843
|
-
|
|
844
|
-
|
|
925
|
+
/**
|
|
926
|
+
* Scheduled jobs of the opener's own DM office only: the App Home is a
|
|
927
|
+
* personal surface, so other offices' schedules are not listed here.
|
|
928
|
+
*/
|
|
929
|
+
appendScheduledJobs(blocks, dmChannelId) {
|
|
930
|
+
const periodicEvents = dmChannelId && this.eventScheduler
|
|
931
|
+
? this.eventScheduler.periodicEvents(createOfficeAddress("slack", dmChannelId))
|
|
932
|
+
: [];
|
|
845
933
|
blocks.push({ type: "divider" }, {
|
|
846
934
|
type: "header",
|
|
847
935
|
text: {
|
|
@@ -878,7 +966,7 @@ export class SlackMessagingBot {
|
|
|
878
966
|
});
|
|
879
967
|
}
|
|
880
968
|
}
|
|
881
|
-
buildHomeView() {
|
|
969
|
+
buildHomeView(dmChannelId) {
|
|
882
970
|
const blocks = [
|
|
883
971
|
{
|
|
884
972
|
type: "section",
|
|
@@ -894,7 +982,7 @@ export class SlackMessagingBot {
|
|
|
894
982
|
},
|
|
895
983
|
];
|
|
896
984
|
this.appendRunningTasks(blocks);
|
|
897
|
-
this.appendScheduledJobs(blocks);
|
|
985
|
+
this.appendScheduledJobs(blocks, dmChannelId);
|
|
898
986
|
blocks.push({ type: "divider" }, {
|
|
899
987
|
type: "context",
|
|
900
988
|
elements: [
|
|
@@ -1135,6 +1223,39 @@ export class SlackMessagingBot {
|
|
|
1135
1223
|
ack();
|
|
1136
1224
|
return false;
|
|
1137
1225
|
}
|
|
1226
|
+
async deliverTaskUpdate(event, attachmentsPromise) {
|
|
1227
|
+
if (matchMagicWord(event.text) === "stop" || event.text.startsWith("/"))
|
|
1228
|
+
return false;
|
|
1229
|
+
event.attachments = await attachmentsPromise;
|
|
1230
|
+
const context = this.createContext(event);
|
|
1231
|
+
const post = (text) => event.thread_ts
|
|
1232
|
+
? this.postInThread(event.channel, event.thread_ts, text)
|
|
1233
|
+
: this.postMessage(event.channel, text);
|
|
1234
|
+
try {
|
|
1235
|
+
if (!event.attachments?.length && isTaskStatusQuestion(event.text)) {
|
|
1236
|
+
const tasks = await context.responder.getTaskStatus(event.thread_ts ? event.sessionKey : undefined);
|
|
1237
|
+
if (!tasks.length)
|
|
1238
|
+
return false;
|
|
1239
|
+
const active = tasks.filter((t) => t.status === "running" || t.status === "stopping");
|
|
1240
|
+
const selection = event.thread_ts ? tasks : active.length ? active : tasks.slice(0, 1);
|
|
1241
|
+
const text = selection.length > 1
|
|
1242
|
+
? "目前有多個任務在處理,請到你想查詢的任務對話串詢問,避免弄錯。"
|
|
1243
|
+
: formatTaskStatus(selection);
|
|
1244
|
+
const ts = await post(text);
|
|
1245
|
+
this.logBotResponse(event.channel, text, ts, event.thread_ts);
|
|
1246
|
+
return true;
|
|
1247
|
+
}
|
|
1248
|
+
if (event.thread_ts && (await this.handler.steer?.(context.message))) {
|
|
1249
|
+
await post("收到補充,會在下一個處理步驟納入;目前的操作不會立即中斷。");
|
|
1250
|
+
return true;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
catch (error) {
|
|
1254
|
+
await post(error instanceof Error ? error.message : "Could not deliver task update.");
|
|
1255
|
+
return true;
|
|
1256
|
+
}
|
|
1257
|
+
return false;
|
|
1258
|
+
}
|
|
1138
1259
|
async handleMessageEvent({ event, ack, }) {
|
|
1139
1260
|
const e = event;
|
|
1140
1261
|
if (!this.admitHumanMessage(e, ack))
|
|
@@ -1182,7 +1303,7 @@ export class SlackMessagingBot {
|
|
|
1182
1303
|
ack();
|
|
1183
1304
|
return;
|
|
1184
1305
|
}
|
|
1185
|
-
if (!isDM && isThreadReply) {
|
|
1306
|
+
if (!isDM && isThreadReply && matchMagicWord(slackEvent.text) !== "stop") {
|
|
1186
1307
|
void attachmentsPromise.catch((err) => {
|
|
1187
1308
|
log.logWarning("Failed to log Slack message", String(err));
|
|
1188
1309
|
});
|
|
@@ -1191,6 +1312,13 @@ export class SlackMessagingBot {
|
|
|
1191
1312
|
}
|
|
1192
1313
|
const activeSessionKey = slackEvent.sessionKey ?? resolveSlackSessionKey(e.channel, e.thread_ts);
|
|
1193
1314
|
slackEvent.sessionKey = activeSessionKey;
|
|
1315
|
+
const taskThread = isDM && !!e.thread_ts && this.isTaskThread(e.channel, e.thread_ts);
|
|
1316
|
+
const taskControl = taskThread || (isDM && !e.thread_ts && isTaskStatusQuestion(slackEvent.text));
|
|
1317
|
+
if (taskControl) {
|
|
1318
|
+
ack();
|
|
1319
|
+
if (await this.deliverTaskUpdate(slackEvent, attachmentsPromise))
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1194
1322
|
const autoReply = !isDM && slackConversationAutoReplyEnabled(this.workspace.office(slackEvent.address));
|
|
1195
1323
|
const intake = this.processSlackMessageIntake({
|
|
1196
1324
|
event: slackEvent,
|
|
@@ -1199,7 +1327,8 @@ export class SlackMessagingBot {
|
|
|
1199
1327
|
addressed: isDM || autoReply,
|
|
1200
1328
|
magicWordAddressed: isDM,
|
|
1201
1329
|
});
|
|
1202
|
-
|
|
1330
|
+
if (!taskControl)
|
|
1331
|
+
ack();
|
|
1203
1332
|
await intake;
|
|
1204
1333
|
}
|
|
1205
1334
|
async handleSlashCommand({ body, ack, }) {
|
|
@@ -1246,7 +1375,7 @@ export class SlackMessagingBot {
|
|
|
1246
1375
|
this.webClient.views
|
|
1247
1376
|
.publish({
|
|
1248
1377
|
user_id: e.user,
|
|
1249
|
-
view: this.buildHomeView(),
|
|
1378
|
+
view: this.buildHomeView(e.channel),
|
|
1250
1379
|
})
|
|
1251
1380
|
.catch((err) => {
|
|
1252
1381
|
log.logWarning(`Failed to publish App Home view`, String(err));
|
|
@@ -1277,11 +1406,11 @@ export class SlackMessagingBot {
|
|
|
1277
1406
|
await this.postMessage(channelId, formatForceStopped("slack", userId ?? "unknown"));
|
|
1278
1407
|
// Refresh home tab
|
|
1279
1408
|
if (userId) {
|
|
1280
|
-
this.
|
|
1281
|
-
.publish({
|
|
1409
|
+
this.openDirectConversation(userId)
|
|
1410
|
+
.then((dmChannelId) => this.webClient.views.publish({
|
|
1282
1411
|
user_id: userId,
|
|
1283
|
-
view: this.buildHomeView(),
|
|
1284
|
-
})
|
|
1412
|
+
view: this.buildHomeView(dmChannelId),
|
|
1413
|
+
}))
|
|
1285
1414
|
.catch((err) => {
|
|
1286
1415
|
log.logWarning(`Failed to refresh App Home view`, String(err));
|
|
1287
1416
|
});
|