@jungjaehoon/mama-os 0.9.2 β 0.9.4
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 +41 -7
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +2 -3
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/claude-cli-wrapper.d.ts +4 -4
- package/dist/agent/claude-cli-wrapper.d.ts.map +1 -1
- package/dist/agent/claude-cli-wrapper.js +17 -5
- package/dist/agent/claude-cli-wrapper.js.map +1 -1
- package/dist/agent/claude-client.js +3 -3
- package/dist/agent/claude-client.js.map +1 -1
- package/dist/agent/codex-mcp-process.d.ts +10 -0
- package/dist/agent/codex-mcp-process.d.ts.map +1 -1
- package/dist/agent/codex-mcp-process.js +226 -58
- package/dist/agent/codex-mcp-process.js.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts +15 -1
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +37 -3
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +1 -0
- package/dist/agent/persistent-cli-process.d.ts +2 -0
- package/dist/agent/persistent-cli-process.d.ts.map +1 -1
- package/dist/agent/persistent-cli-process.js +15 -0
- package/dist/agent/persistent-cli-process.js.map +1 -1
- package/dist/agent/types.d.ts +3 -3
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/api/graph-api.d.ts.map +1 -1
- package/dist/api/graph-api.js +31 -5
- package/dist/api/graph-api.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +91 -6
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +7 -1
- package/dist/cli/commands/stop.d.ts.map +1 -1
- package/dist/cli/commands/stop.js +49 -0
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/config/config-manager.d.ts.map +1 -1
- package/dist/cli/config/config-manager.js +60 -15
- package/dist/cli/config/config-manager.js.map +1 -1
- package/dist/cli/config/types.d.ts +19 -5
- package/dist/cli/config/types.d.ts.map +1 -1
- package/dist/cli/config/types.js +3 -3
- package/dist/cli/config/types.js.map +1 -1
- package/dist/gateways/image-analyzer.js +1 -1
- package/dist/gateways/image-analyzer.js.map +1 -1
- package/dist/gateways/slack.d.ts.map +1 -1
- package/dist/gateways/slack.js +8 -19
- package/dist/gateways/slack.js.map +1 -1
- package/dist/multi-agent/agent-process-manager.d.ts +15 -1
- package/dist/multi-agent/agent-process-manager.d.ts.map +1 -1
- package/dist/multi-agent/agent-process-manager.js +121 -22
- package/dist/multi-agent/agent-process-manager.js.map +1 -1
- package/dist/multi-agent/background-task-manager.d.ts +2 -2
- package/dist/multi-agent/background-task-manager.js +2 -2
- package/dist/multi-agent/bmad-templates.d.ts +67 -0
- package/dist/multi-agent/bmad-templates.d.ts.map +1 -0
- package/dist/multi-agent/bmad-templates.js +248 -0
- package/dist/multi-agent/bmad-templates.js.map +1 -0
- package/dist/multi-agent/council-engine.d.ts +60 -0
- package/dist/multi-agent/council-engine.d.ts.map +1 -0
- package/dist/multi-agent/council-engine.js +284 -0
- package/dist/multi-agent/council-engine.js.map +1 -0
- package/dist/multi-agent/multi-agent-base.d.ts +18 -9
- package/dist/multi-agent/multi-agent-base.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-base.js +116 -33
- package/dist/multi-agent/multi-agent-base.js.map +1 -1
- package/dist/multi-agent/multi-agent-discord.d.ts +3 -35
- package/dist/multi-agent/multi-agent-discord.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-discord.js +81 -302
- package/dist/multi-agent/multi-agent-discord.js.map +1 -1
- package/dist/multi-agent/multi-agent-slack.d.ts +2 -25
- package/dist/multi-agent/multi-agent-slack.d.ts.map +1 -1
- package/dist/multi-agent/multi-agent-slack.js +173 -253
- package/dist/multi-agent/multi-agent-slack.js.map +1 -1
- package/dist/multi-agent/runtime-process.d.ts +3 -0
- package/dist/multi-agent/runtime-process.d.ts.map +1 -1
- package/dist/multi-agent/runtime-process.js +4 -0
- package/dist/multi-agent/runtime-process.js.map +1 -1
- package/dist/multi-agent/shared-context.d.ts.map +1 -1
- package/dist/multi-agent/shared-context.js +4 -4
- package/dist/multi-agent/shared-context.js.map +1 -1
- package/dist/multi-agent/system-reminder.d.ts +1 -1
- package/dist/multi-agent/system-reminder.js +1 -1
- package/dist/multi-agent/types.d.ts +31 -15
- package/dist/multi-agent/types.d.ts.map +1 -1
- package/dist/multi-agent/types.js +1 -3
- package/dist/multi-agent/types.js.map +1 -1
- package/dist/multi-agent/ultrawork-state.d.ts +57 -0
- package/dist/multi-agent/ultrawork-state.d.ts.map +1 -0
- package/dist/multi-agent/ultrawork-state.js +191 -0
- package/dist/multi-agent/ultrawork-state.js.map +1 -0
- package/dist/multi-agent/ultrawork.d.ts +37 -19
- package/dist/multi-agent/ultrawork.d.ts.map +1 -1
- package/dist/multi-agent/ultrawork.js +587 -41
- package/dist/multi-agent/ultrawork.js.map +1 -1
- package/dist/multi-agent/workflow-engine.d.ts +7 -0
- package/dist/multi-agent/workflow-engine.d.ts.map +1 -1
- package/dist/multi-agent/workflow-engine.js +238 -33
- package/dist/multi-agent/workflow-engine.js.map +1 -1
- package/dist/multi-agent/workflow-types.d.ts +74 -1
- package/dist/multi-agent/workflow-types.d.ts.map +1 -1
- package/dist/onboarding/complete-autonomous-prompt.d.ts +1 -1
- package/dist/onboarding/complete-autonomous-prompt.d.ts.map +1 -1
- package/dist/onboarding/complete-autonomous-prompt.js +27 -10
- package/dist/onboarding/complete-autonomous-prompt.js.map +1 -1
- package/dist/onboarding/phase-7-integrations.d.ts.map +1 -1
- package/dist/onboarding/phase-7-integrations.js +23 -3
- package/dist/onboarding/phase-7-integrations.js.map +1 -1
- package/dist/onboarding/phase-9-finalization.d.ts.map +1 -1
- package/dist/onboarding/phase-9-finalization.js +33 -0
- package/dist/onboarding/phase-9-finalization.js.map +1 -1
- package/dist/setup/setup-prompt.d.ts +1 -1
- package/dist/setup/setup-prompt.d.ts.map +1 -1
- package/dist/setup/setup-prompt.js +1 -1
- package/package.json +1 -1
- package/public/viewer/js/modules/settings.js +110 -15
- package/public/viewer/js/utils/format.js +10 -7
- package/public/viewer/src/modules/settings.ts +133 -16
- package/public/viewer/src/utils/api.ts +2 -1
- package/public/viewer/src/utils/format.ts +10 -7
- package/public/viewer/viewer.html +1 -0
- package/templates/bmad/LICENSE +28 -0
- package/templates/bmad/architecture.md +343 -0
- package/templates/bmad/bmm-workflow-status.template.yaml +66 -0
- package/templates/bmad/prd.md +198 -0
- package/templates/bmad/product-brief.md +149 -0
- package/templates/bmad/sprint-status.template.yaml +35 -0
- package/templates/bmad/tech-spec.md +151 -0
- package/templates/personas/architect.md +70 -0
- package/templates/personas/conductor.md +373 -0
- package/templates/personas/developer.md +20 -7
- package/templates/personas/pm.md +49 -33
- package/templates/personas/reviewer.md +18 -5
- package/dist/multi-agent/pr-review-poller.d.ts +0 -197
- package/dist/multi-agent/pr-review-poller.d.ts.map +0 -1
- package/dist/multi-agent/pr-review-poller.js +0 -972
- package/dist/multi-agent/pr-review-poller.js.map +0 -1
- package/templates/personas/sisyphus-builtin-en.md +0 -161
- package/templates/personas/sisyphus.md +0 -218
|
@@ -14,9 +14,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
14
14
|
exports.MultiAgentSlackHandler = void 0;
|
|
15
15
|
const slack_multi_bot_manager_js_1 = require("./slack-multi-bot-manager.js");
|
|
16
16
|
const message_splitter_js_1 = require("../gateways/message-splitter.js");
|
|
17
|
-
const pr_review_poller_js_1 = require("./pr-review-poller.js");
|
|
18
17
|
const delegation_format_validator_js_1 = require("./delegation-format-validator.js");
|
|
19
18
|
const log_sanitizer_js_1 = require("../utils/log-sanitizer.js");
|
|
19
|
+
const channel_history_js_1 = require("../gateways/channel-history.js");
|
|
20
20
|
const multi_agent_base_js_1 = require("./multi-agent-base.js");
|
|
21
21
|
/** Heartbeat interval for status polling (60 seconds) */
|
|
22
22
|
const HEARTBEAT_INTERVAL_MS = 60 * 1000;
|
|
@@ -41,10 +41,10 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
41
41
|
heartbeatChannelId = null;
|
|
42
42
|
/** Heartbeat polling interval handle */
|
|
43
43
|
heartbeatInterval;
|
|
44
|
-
/** PR poller summaries by channel for LEAD wake-up context. */
|
|
45
|
-
prPollerSummaries = new Map();
|
|
46
44
|
/** Interval handle for periodic cleanup */
|
|
47
45
|
mentionCleanupInterval;
|
|
46
|
+
/** Tracks the process used for history seeding per agent:channel */
|
|
47
|
+
historySeedProcess = new Map();
|
|
48
48
|
constructor(config, processOptions = {}, runtimeOptions = {}) {
|
|
49
49
|
super(config, processOptions, runtimeOptions);
|
|
50
50
|
this.multiBotManager = new slack_multi_bot_manager_js_1.SlackMultiBotManager(config);
|
|
@@ -83,7 +83,7 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
83
83
|
const userId = match[1];
|
|
84
84
|
const agentId = this.multiBotManager.resolveAgentIdFromUserId(userId);
|
|
85
85
|
if (agentId) {
|
|
86
|
-
// Resolve 'main' to the actual agent ID (e.g., '
|
|
86
|
+
// Resolve 'main' to the actual agent ID (e.g., 'conductor')
|
|
87
87
|
const resolvedId = agentId === 'main' ? (this.multiBotManager.getMainBotAgentId() ?? agentId) : agentId;
|
|
88
88
|
agentIds.push(resolvedId);
|
|
89
89
|
}
|
|
@@ -96,6 +96,7 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
96
96
|
clearInterval(this.mentionCleanupInterval);
|
|
97
97
|
this.mentionCleanupInterval = undefined;
|
|
98
98
|
}
|
|
99
|
+
this.historySeedProcess.clear();
|
|
99
100
|
await this.multiBotManager.stopAll();
|
|
100
101
|
}
|
|
101
102
|
/**
|
|
@@ -185,131 +186,6 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
185
186
|
this.processManager.setMentionDelegation(true);
|
|
186
187
|
this.logger.log(`[MultiAgentSlack] Mention delegation enabled with ${botUserIdMap.size} bot IDs`);
|
|
187
188
|
}
|
|
188
|
-
// Configure PR poller wiring (if enabled)
|
|
189
|
-
this.configurePrPoller();
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Configure PR Review Poller callbacks.
|
|
193
|
-
* Extracted so it can be called from both initializeMultiBots() and updateConfig()
|
|
194
|
-
* to enable hot-reload of PR poller.
|
|
195
|
-
*/
|
|
196
|
-
configurePrPoller() {
|
|
197
|
-
if (!this.isPrReviewPollingEnabled()) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (this.config.mention_delegation) {
|
|
201
|
-
const botUserIdMap = this.multiBotManager.getBotUserIdMap();
|
|
202
|
-
const orchestratorId = this.config.default_agent || 'sisyphus';
|
|
203
|
-
const orchestratorUserId = botUserIdMap.get(orchestratorId);
|
|
204
|
-
if (orchestratorUserId) {
|
|
205
|
-
this.prReviewPoller.setTargetAgentUserId(orchestratorUserId);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// PR Review Poller: wake up LEAD with compact summaries from new review items.
|
|
209
|
-
this.prReviewPoller.setMessageSender(async () => { });
|
|
210
|
-
this.prReviewPoller.setOnBatchItem(async (channelId, summary, item) => {
|
|
211
|
-
const compact = this.compactPrReviewBatchSummary(summary);
|
|
212
|
-
const bucket = this.prPollerSummaries.get(channelId) ?? [];
|
|
213
|
-
if (!compact || bucket.some((candidate) => candidate.id === item.id)) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const merged = { ...item, summary: compact };
|
|
217
|
-
this.prPollerSummaries.set(channelId, [...bucket, merged].slice(-12));
|
|
218
|
-
});
|
|
219
|
-
this.prReviewPoller.setOnBatchComplete(async (channelId, digest) => {
|
|
220
|
-
const items = (digest?.items && digest.items.length > 0
|
|
221
|
-
? digest.items
|
|
222
|
-
: this.prPollerSummaries.get(channelId)) ?? [];
|
|
223
|
-
const count = items.length;
|
|
224
|
-
if (count === 0) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
const actionableItems = digest?.newItems ?? items.filter((item) => !item.isReminder);
|
|
228
|
-
if (actionableItems.length === 0) {
|
|
229
|
-
this.prPollerSummaries.delete(channelId);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
const reminderCount = (digest?.reminderItems ?? items.filter((item) => item.isReminder))
|
|
233
|
-
.length;
|
|
234
|
-
this.prPollerSummaries.delete(channelId);
|
|
235
|
-
const sessions = this.prReviewPoller.getSessionDetails();
|
|
236
|
-
const session = sessions.find((s) => s.channelId === channelId);
|
|
237
|
-
if (!session) {
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
const prLabel = `${session.owner}/${session.repo}#${session.prNumber}`;
|
|
241
|
-
const summaryLines = items
|
|
242
|
-
.map((item, idx) => `- ${idx + 1}. [${item.severity}] ${item.summary}`)
|
|
243
|
-
.join('\n');
|
|
244
|
-
const promptSummary = `\n${summaryLines}`;
|
|
245
|
-
const reminderSuffix = reminderCount > 0 ? `\nπ Reminders: ${reminderCount} item(s).` : '';
|
|
246
|
-
if (this.mainWebClient) {
|
|
247
|
-
await this.mainWebClient.chat.postMessage({
|
|
248
|
-
channel: channelId,
|
|
249
|
-
text: `π PR ${prLabel} β ${count} new review item(s)\n\n${promptSummary}${reminderSuffix}`,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
this.orchestrator.resetChain(channelId);
|
|
253
|
-
const defaultAgentId = this.config.default_agent || 'sisyphus';
|
|
254
|
-
const basePrompt = `π PR Review follow-up (${count} new item(s))\n` +
|
|
255
|
-
`Target: ${prLabel}\n` +
|
|
256
|
-
`Workspace: \`${session.workspaceDir}\`\n` +
|
|
257
|
-
`Items:\n${promptSummary}${reminderSuffix}`;
|
|
258
|
-
this.messageQueue.enqueue(defaultAgentId, {
|
|
259
|
-
prompt: `${basePrompt}\n\nClassify severity, implement fixable items, and coordinate with Dev/Reviewer.`,
|
|
260
|
-
channelId,
|
|
261
|
-
source: 'slack',
|
|
262
|
-
enqueuedAt: Date.now(),
|
|
263
|
-
context: { channelId, userId: 'pr-poller' },
|
|
264
|
-
});
|
|
265
|
-
this.logger.info(`[MultiAgentSlack] PR Poller -> LEAD wake-up (${count} items)`);
|
|
266
|
-
this.tryDrainNow(defaultAgentId, 'slack', channelId).catch(() => { });
|
|
267
|
-
const helperAgents = this.resolvePrReviewAssistants(defaultAgentId);
|
|
268
|
-
for (const helperAgentId of helperAgents) {
|
|
269
|
-
const helperPrompt = helperAgentId === 'developer'
|
|
270
|
-
? `${basePrompt}\n\nImplement available fixes in the workspace and request Reviewer verification when changes are made.`
|
|
271
|
-
: `${basePrompt}\n\nInspect the comments for correctness issues and provide reviewer guidance.`;
|
|
272
|
-
this.messageQueue.enqueue(helperAgentId, {
|
|
273
|
-
prompt: helperPrompt,
|
|
274
|
-
channelId,
|
|
275
|
-
source: 'slack',
|
|
276
|
-
enqueuedAt: Date.now(),
|
|
277
|
-
context: { channelId, userId: 'pr-poller' },
|
|
278
|
-
});
|
|
279
|
-
this.logger.info(`[MultiAgentSlack] PR Poller -> ${helperAgentId} wake-up (${count} items)`);
|
|
280
|
-
this.tryDrainNow(helperAgentId, 'slack', channelId).catch(() => { });
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
this.logger.log('[MultiAgentSlack] PR Poller summaries now feed LEAD wake-up');
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* Keep PR poller summaries short and remove duplicates.
|
|
287
|
-
*/
|
|
288
|
-
compactPrReviewBatchSummary(summary) {
|
|
289
|
-
const compact = summary.replace(/[\r\n]+/g, ' ').trim();
|
|
290
|
-
const max = 230;
|
|
291
|
-
if (!compact) {
|
|
292
|
-
return '';
|
|
293
|
-
}
|
|
294
|
-
return compact.length > max ? `${compact.slice(0, max)}β¦` : compact;
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Resolve helper agents for PR review workflow in addition to default lead.
|
|
298
|
-
*/
|
|
299
|
-
resolvePrReviewAssistants(defaultAgentId) {
|
|
300
|
-
const candidateIds = ['developer', 'reviewer', 'dev', 'lead'];
|
|
301
|
-
const helperAgents = [];
|
|
302
|
-
for (const id of candidateIds) {
|
|
303
|
-
const cfg = this.config.agents[id];
|
|
304
|
-
if (!cfg || id === defaultAgentId) {
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
if (cfg.enabled === false) {
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
helperAgents.push(id);
|
|
311
|
-
}
|
|
312
|
-
return [...new Set(helperAgents)];
|
|
313
189
|
}
|
|
314
190
|
/**
|
|
315
191
|
* Set main Slack WebClient (for heartbeat status messages)
|
|
@@ -322,12 +198,6 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
322
198
|
await client.chat.postMessage({ channel: channelId, text: chunk });
|
|
323
199
|
}
|
|
324
200
|
}, 'slack');
|
|
325
|
-
// Only set PR poller sender if not already configured (e.g., by reviewer bot)
|
|
326
|
-
if (this.isPrReviewPollingEnabled() && !this.prReviewPoller.hasMessageSender?.()) {
|
|
327
|
-
this.prReviewPoller.setMessageSender(async (channelId, text) => {
|
|
328
|
-
await client.chat.postMessage({ channel: channelId, text });
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
201
|
}
|
|
332
202
|
/**
|
|
333
203
|
* Set main bot's user ID (call when Slack connects via auth.test)
|
|
@@ -354,66 +224,6 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
354
224
|
this.config = config;
|
|
355
225
|
this.orchestrator.updateConfig(config);
|
|
356
226
|
this.processManager.updateConfig(config);
|
|
357
|
-
if (this.isPrReviewPollingEnabled()) {
|
|
358
|
-
// Re-wire PR poller callbacks on hot-reload enable
|
|
359
|
-
this.configurePrPoller();
|
|
360
|
-
}
|
|
361
|
-
else {
|
|
362
|
-
this.prReviewPoller.stopAll();
|
|
363
|
-
this.prPollerSummaries.clear();
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Handle PR review polling commands from human messages.
|
|
368
|
-
* Returns true if the message was a PR command (consumed), false otherwise.
|
|
369
|
-
*
|
|
370
|
-
* Start: message contains a GitHub PR URL (auto-detect)
|
|
371
|
-
* Stop: message contains "pr stop", "stop polling"
|
|
372
|
-
*/
|
|
373
|
-
async handlePRCommand(channelId, content) {
|
|
374
|
-
if (!this.isPrReviewPollingEnabled()) {
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
if (!this.mainWebClient) {
|
|
378
|
-
return false;
|
|
379
|
-
}
|
|
380
|
-
const contentLower = content.toLowerCase();
|
|
381
|
-
// Stop commands
|
|
382
|
-
const stopPatterns = ['pr μ€μ§', 'pr stop', 'ν΄λ§ μ€μ§', 'stop polling', 'pr μ’
λ£', 'stop pr'];
|
|
383
|
-
if (stopPatterns.some((p) => contentLower.includes(p))) {
|
|
384
|
-
const sessions = this.prReviewPoller.getActiveSessions();
|
|
385
|
-
if (sessions.length === 0) {
|
|
386
|
-
await this.mainWebClient.chat.postMessage({
|
|
387
|
-
channel: channelId,
|
|
388
|
-
text: 'π No active PR polling sessions.',
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
this.prReviewPoller.stopAll();
|
|
393
|
-
await this.mainWebClient.chat.postMessage({
|
|
394
|
-
channel: channelId,
|
|
395
|
-
text: `βΉοΈ PR review polling stopped: ${sessions.join(', ')}`,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
return true;
|
|
399
|
-
}
|
|
400
|
-
// Start: detect PR URL in message
|
|
401
|
-
const prUrls = pr_review_poller_js_1.PRReviewPoller.extractPRUrls(content);
|
|
402
|
-
if (prUrls.length > 0) {
|
|
403
|
-
for (const prUrl of prUrls) {
|
|
404
|
-
const started = await this.prReviewPoller.startPolling(prUrl, channelId);
|
|
405
|
-
if (started) {
|
|
406
|
-
const parsed = this.prReviewPoller.parsePRUrl(prUrl);
|
|
407
|
-
const key = parsed ? `${parsed.owner}/${parsed.repo}#${parsed.prNumber}` : prUrl;
|
|
408
|
-
await this.mainWebClient.chat.postMessage({
|
|
409
|
-
channel: channelId,
|
|
410
|
-
text: `π *PR Review Poller started* -- ${key}\nPolling for new review comments every 60 seconds. Type "PR stop" to stop.`,
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return true;
|
|
415
|
-
}
|
|
416
|
-
return false;
|
|
417
227
|
}
|
|
418
228
|
/**
|
|
419
229
|
* Handle a Slack message with multi-agent logic
|
|
@@ -481,29 +291,38 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
481
291
|
// Build context for this agent
|
|
482
292
|
const agentContext = this.sharedContext.buildContextForAgent(context.channelId, agentId, 5);
|
|
483
293
|
// Build full prompt with context.
|
|
484
|
-
// NOTE: Do NOT inject historyContext into persistent processes -- the CLI process
|
|
485
|
-
// already retains conversation memory across turns. Injecting historyContext causes
|
|
486
|
-
// duplicate messages in Claude's context, making old messages appear "just conversed"
|
|
487
|
-
// and creating cross-agent context confusion.
|
|
488
|
-
// Only inject agentContext (other agents' messages) for inter-agent awareness.
|
|
489
294
|
let fullPrompt = cleanMessage;
|
|
490
|
-
if (agentContext) {
|
|
491
|
-
fullPrompt = `${agentContext}\n\n${fullPrompt}`;
|
|
492
|
-
}
|
|
493
|
-
// Inject agent availability status and active work (Phase 2 + 3)
|
|
494
|
-
const agentStatus = this.buildAgentStatusSection(agentId);
|
|
495
|
-
const workSection = this.workTracker.buildWorkSection(agentId);
|
|
496
|
-
const dynamicContext = [agentStatus, workSection].filter(Boolean).join('\n');
|
|
497
|
-
if (dynamicContext) {
|
|
498
|
-
fullPrompt = `${dynamicContext}\n\n${fullPrompt}`;
|
|
499
|
-
}
|
|
500
|
-
this.logger.log(`[MultiAgentSlack] Processing agent ${agentId}, prompt length: ${fullPrompt.length}`);
|
|
501
295
|
// Track work start (completed in finally block)
|
|
502
296
|
this.workTracker.startWork(agentId, context.channelId, cleanMessage);
|
|
503
297
|
let process = null;
|
|
504
298
|
try {
|
|
505
299
|
// Get or create process for this agent in this channel
|
|
506
300
|
process = await this.processManager.getProcess('slack', context.channelId, agentId);
|
|
301
|
+
// Inject channel history when process is new/replaced for this agent:channel.
|
|
302
|
+
// After process restart the CLI has no prior memory, so history must be re-seeded.
|
|
303
|
+
const sessionKey = `${agentId}:${context.channelId}`;
|
|
304
|
+
const needsHistorySeed = this.historySeedProcess.get(sessionKey) !== process;
|
|
305
|
+
if (needsHistorySeed) {
|
|
306
|
+
const channelHistory = (0, channel_history_js_1.getChannelHistory)();
|
|
307
|
+
const displayName = agent.display_name || agentId;
|
|
308
|
+
const historyContext = channelHistory.formatForContext(context.channelId, context.messageId, displayName);
|
|
309
|
+
if (historyContext) {
|
|
310
|
+
fullPrompt = `${historyContext}\n\n${fullPrompt}`;
|
|
311
|
+
}
|
|
312
|
+
this.historySeedProcess.set(sessionKey, process);
|
|
313
|
+
}
|
|
314
|
+
if (agentContext) {
|
|
315
|
+
fullPrompt = `${agentContext}\n\n${fullPrompt}`;
|
|
316
|
+
}
|
|
317
|
+
// Inject agent availability status and active work (Phase 2 + 3)
|
|
318
|
+
const agentStatus = this.buildAgentStatusSection(agentId);
|
|
319
|
+
const workSection = this.workTracker.buildWorkSection(agentId);
|
|
320
|
+
const channelInfo = `## Current Channel\nPlatform: Slack\nchannel_id: ${context.channelId}\nUse **slack_send** to send messages/files to this channel.`;
|
|
321
|
+
const dynamicContext = [agentStatus, workSection, channelInfo].filter(Boolean).join('\n');
|
|
322
|
+
if (dynamicContext) {
|
|
323
|
+
fullPrompt = `${dynamicContext}\n\n${fullPrompt}`;
|
|
324
|
+
}
|
|
325
|
+
this.logger.log(`[MultiAgentSlack] Processing agent ${agentId}, prompt length: ${fullPrompt.length}`);
|
|
507
326
|
// Send message and get response (with timeout, properly cleaned up)
|
|
508
327
|
let timeoutHandle;
|
|
509
328
|
let result;
|
|
@@ -525,21 +344,42 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
525
344
|
}
|
|
526
345
|
let msg = '';
|
|
527
346
|
const modelTag = event.agentModel ? ` [${event.agentModel}]` : '';
|
|
347
|
+
const progress = event.totalSteps && event.completedSteps !== undefined
|
|
348
|
+
? ` [${event.completedSteps}/${event.totalSteps}]`
|
|
349
|
+
: '';
|
|
528
350
|
if (event.type === 'step-started') {
|
|
529
|
-
msg = ` ${event.agentDisplayName}${modelTag} μμ...`;
|
|
351
|
+
msg = ` ${event.agentDisplayName}${modelTag}${progress} μμ...`;
|
|
530
352
|
}
|
|
531
353
|
else if (event.type === 'step-completed') {
|
|
532
354
|
const sec = event.duration_ms ? Math.round(event.duration_ms / 1000) : 0;
|
|
533
|
-
|
|
355
|
+
const pct = event.totalSteps && event.completedSteps !== undefined
|
|
356
|
+
? ` (${Math.round((event.completedSteps / event.totalSteps) * 100)}%)`
|
|
357
|
+
: '';
|
|
358
|
+
msg = `${event.agentDisplayName}${modelTag} (${sec}s)${pct} μλ£`;
|
|
534
359
|
}
|
|
535
360
|
else if (event.type === 'step-failed') {
|
|
536
|
-
msg = `${event.agentDisplayName}${modelTag} β μ€ν¨: ${event.error?.substring(0, 100)}`;
|
|
361
|
+
msg = `${event.agentDisplayName}${modelTag}${progress} β μ€ν¨: ${event.error?.substring(0, 100)}`;
|
|
537
362
|
}
|
|
538
363
|
if (msg) {
|
|
539
364
|
this.mainWebClient.chat.postMessage({ channel: context.channelId, text: msg }).catch(() => { });
|
|
540
365
|
}
|
|
541
366
|
});
|
|
542
367
|
if (workflowResult) {
|
|
368
|
+
if (workflowResult.failed) {
|
|
369
|
+
// Workflow plan parsed but execution failed β feed error back to Conductor
|
|
370
|
+
this.logger.warn(`[MultiAgentSlack] Workflow failed: ${workflowResult.failed}, sending feedback to conductor`);
|
|
371
|
+
const feedback = `[SYSTEM] Your workflow_plan failed to execute.\nReason: ${workflowResult.failed}\nPlease adjust and retry, or respond without a workflow_plan.`;
|
|
372
|
+
const retryResult = await process.sendMessage(feedback);
|
|
373
|
+
const cleanedRetry = await this.executeTextToolCalls(retryResult.response);
|
|
374
|
+
const formattedResponse = this.formatAgentResponse(agent, cleanedRetry);
|
|
375
|
+
return {
|
|
376
|
+
agentId,
|
|
377
|
+
agent,
|
|
378
|
+
content: formattedResponse,
|
|
379
|
+
rawContent: cleanedRetry,
|
|
380
|
+
duration: result.duration_ms,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
543
383
|
const display = workflowResult.directMessage
|
|
544
384
|
? `${workflowResult.directMessage}\n\n${workflowResult.result}`
|
|
545
385
|
: workflowResult.result;
|
|
@@ -552,36 +392,103 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
552
392
|
duration: result.duration_ms,
|
|
553
393
|
};
|
|
554
394
|
}
|
|
395
|
+
// Check for council plan (after workflow, before tool calls)
|
|
396
|
+
const councilResult = await this.tryExecuteCouncil(result.response, context.channelId, 'slack', async (event) => {
|
|
397
|
+
if (!this.mainWebClient) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
let msg = '';
|
|
401
|
+
if (event.type === 'council-round-started') {
|
|
402
|
+
msg = `π£οΈ ${event.agentDisplayName} Round ${event.round} μμ...`;
|
|
403
|
+
}
|
|
404
|
+
else if (event.type === 'council-round-completed') {
|
|
405
|
+
const sec = event.duration_ms ? Math.round(event.duration_ms / 1000) : 0;
|
|
406
|
+
msg = `π£οΈ ${event.agentDisplayName} Round ${event.round} (${sec}s) μλ£`;
|
|
407
|
+
}
|
|
408
|
+
else if (event.type === 'council-round-failed') {
|
|
409
|
+
msg = `π£οΈ ${event.agentDisplayName} Round ${event.round} β μ€ν¨: ${event.error?.substring(0, 100)}`;
|
|
410
|
+
}
|
|
411
|
+
if (msg) {
|
|
412
|
+
try {
|
|
413
|
+
await this.mainWebClient.chat.postMessage({ channel: context.channelId, text: msg });
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
this.logger?.warn('[MultiAgentSlack] Failed to post council progress:', err);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
if (councilResult) {
|
|
421
|
+
const display = councilResult.directMessage
|
|
422
|
+
? `${councilResult.directMessage}\n\n${councilResult.result}`
|
|
423
|
+
: councilResult.result;
|
|
424
|
+
const formattedResponse = this.formatAgentResponse(agent, display);
|
|
425
|
+
return {
|
|
426
|
+
agentId,
|
|
427
|
+
agent,
|
|
428
|
+
content: formattedResponse,
|
|
429
|
+
rawContent: display,
|
|
430
|
+
duration: result.duration_ms,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
// Strip any workflow/council plan JSON that wasn't executed
|
|
434
|
+
// (prevents raw JSON from leaking to Slack when plan execution is skipped)
|
|
435
|
+
let responseForProcessing = result.response;
|
|
436
|
+
if (this.workflowEngine?.isEnabled()) {
|
|
437
|
+
responseForProcessing = this.workflowEngine.extractNonPlanContent(responseForProcessing);
|
|
438
|
+
}
|
|
439
|
+
if (this.councilEngine) {
|
|
440
|
+
responseForProcessing = this.councilEngine.extractNonPlanContent(responseForProcessing);
|
|
441
|
+
}
|
|
555
442
|
// Execute text-based gateway tool calls (```tool_call blocks in response)
|
|
556
|
-
const cleanedResponse = await this.executeTextToolCalls(
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
443
|
+
const cleanedResponse = await this.executeTextToolCalls(responseForProcessing);
|
|
444
|
+
// Parse all delegation commands (both sync and background)
|
|
445
|
+
const delegations = this.delegationManager.parseAllDelegations(agentId, cleanedResponse);
|
|
446
|
+
let displayResponse = cleanedResponse;
|
|
447
|
+
// Handle background delegations
|
|
448
|
+
const bgDelegations = delegations.filter((d) => d.background);
|
|
449
|
+
if (bgDelegations.length > 0) {
|
|
450
|
+
let submittedCount = 0;
|
|
451
|
+
for (const delegation of bgDelegations) {
|
|
452
|
+
const check = this.delegationManager.isDelegationAllowed(delegation.fromAgentId, delegation.toAgentId);
|
|
453
|
+
if (check.allowed) {
|
|
454
|
+
this.backgroundTaskManager.submit({
|
|
455
|
+
description: delegation.task.substring(0, 200),
|
|
456
|
+
prompt: delegation.task,
|
|
457
|
+
agentId: delegation.toAgentId,
|
|
458
|
+
requestedBy: agentId,
|
|
459
|
+
channelId: context.channelId,
|
|
460
|
+
source: 'slack',
|
|
461
|
+
});
|
|
462
|
+
this.logger.log(`[MultiAgentSlack] Background delegation: ${agentId} -> ${delegation.toAgentId} (async)`);
|
|
463
|
+
submittedCount++;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (submittedCount > 0) {
|
|
467
|
+
displayResponse =
|
|
468
|
+
bgDelegations[0].originalContent || `π ${submittedCount} background task(s) delegated`;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// Handle synchronous delegations via message queue
|
|
472
|
+
const syncDelegations = delegations.filter((d) => !d.background);
|
|
473
|
+
for (const delegation of syncDelegations) {
|
|
474
|
+
const check = this.delegationManager.isDelegationAllowed(delegation.fromAgentId, delegation.toAgentId);
|
|
560
475
|
if (check.allowed) {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
description: bgDelegation.task.substring(0, 200),
|
|
564
|
-
prompt: bgDelegation.task,
|
|
565
|
-
agentId: bgDelegation.toAgentId,
|
|
566
|
-
requestedBy: agentId,
|
|
476
|
+
this.messageQueue.enqueue(delegation.toAgentId, {
|
|
477
|
+
prompt: delegation.task,
|
|
567
478
|
channelId: context.channelId,
|
|
568
479
|
source: 'slack',
|
|
480
|
+
enqueuedAt: Date.now(),
|
|
481
|
+
context,
|
|
569
482
|
});
|
|
570
|
-
this.logger.log(`[MultiAgentSlack]
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
agentId,
|
|
576
|
-
agent,
|
|
577
|
-
content: formattedResponse,
|
|
578
|
-
rawContent: displayResponse,
|
|
579
|
-
duration: result.duration_ms,
|
|
580
|
-
};
|
|
483
|
+
this.logger.log(`[MultiAgentSlack] Sync delegation (queued): ${agentId} -> ${delegation.toAgentId}`);
|
|
484
|
+
this.tryDrainNow(delegation.toAgentId, 'slack', context.channelId).catch(() => { });
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
this.logger.log(`[MultiAgentSlack] Sync delegation denied: ${agentId} -> ${delegation.toAgentId}: ${check.reason}`);
|
|
581
488
|
}
|
|
582
489
|
}
|
|
583
490
|
// Format response with agent prefix
|
|
584
|
-
const formattedResponse = this.formatAgentResponse(agent,
|
|
491
|
+
const formattedResponse = this.formatAgentResponse(agent, displayResponse);
|
|
585
492
|
return {
|
|
586
493
|
agentId,
|
|
587
494
|
agent,
|
|
@@ -605,11 +512,33 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
605
512
|
context,
|
|
606
513
|
};
|
|
607
514
|
this.messageQueue.enqueue(agentId, queuedMessage);
|
|
515
|
+
const queueSize = this.messageQueue.getQueueSize(agentId);
|
|
516
|
+
const queueText = queueSize > 0
|
|
517
|
+
? `β οΈ ${agent.display_name}μ΄(κ°) νμ¬ μμ
μ€μ
λλ€. ${queueSize}κ°μ λ©μμ§κ° λκΈ°μ΄μ μμ΅λλ€.`
|
|
518
|
+
: `β οΈ ${agent.display_name}μ΄(κ°) νμ¬ μμ
μ€μ
λλ€. μμ²μ΄ λκΈ°μ΄μ λ±λ‘λμμ΅λλ€.`;
|
|
608
519
|
// Trigger immediate drain if process is idle or reaped
|
|
609
520
|
this.tryDrainNow(agentId, 'slack', context.channelId).catch(() => { });
|
|
610
|
-
return
|
|
521
|
+
return {
|
|
522
|
+
agentId,
|
|
523
|
+
agent,
|
|
524
|
+
content: this.formatAgentResponse(agent, queueText),
|
|
525
|
+
rawContent: queueText,
|
|
526
|
+
duration: 0,
|
|
527
|
+
};
|
|
611
528
|
}
|
|
612
|
-
|
|
529
|
+
const fallbackMessage = errMsg.toLowerCase().includes('timed out') || errMsg.toLowerCase().includes('timeout')
|
|
530
|
+
? `β οΈ ${agent.display_name} μλ΅μ΄ μκ° μ΄κ³Όλμ΄ μ²λ¦¬ κ²°κ³Όλ₯Ό λͺ» λ°μμ΅λλ€. μ μ ν λ€μ μλν΄ μ£ΌμΈμ.`
|
|
531
|
+
: `β οΈ ${agent.display_name} μ²λ¦¬ μ€ μ€λ₯κ° λ°μνμ΅λλ€: ${errMsg}`;
|
|
532
|
+
const fallbackRaw = errMsg.toLowerCase().includes('timed out') || errMsg.toLowerCase().includes('timeout')
|
|
533
|
+
? 'Response timed out'
|
|
534
|
+
: `Error: ${errMsg}`;
|
|
535
|
+
return {
|
|
536
|
+
agentId,
|
|
537
|
+
agent,
|
|
538
|
+
content: this.formatAgentResponse(agent, fallbackMessage),
|
|
539
|
+
rawContent: fallbackRaw,
|
|
540
|
+
duration: 0,
|
|
541
|
+
};
|
|
613
542
|
}
|
|
614
543
|
finally {
|
|
615
544
|
if (process) {
|
|
@@ -819,8 +748,9 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
819
748
|
for (const response of responses) {
|
|
820
749
|
// Filter out self-mentions to prevent routing an agent's response back to itself
|
|
821
750
|
const mentionedAgentIds = this.extractMentionedAgentIds(response.rawContent).filter((id) => id !== response.agentId);
|
|
822
|
-
if (mentionedAgentIds.length === 0)
|
|
751
|
+
if (mentionedAgentIds.length === 0) {
|
|
823
752
|
continue;
|
|
753
|
+
}
|
|
824
754
|
// Hard gate: block malformed delegations from can_delegate agents
|
|
825
755
|
const senderAgent = this.orchestrator.getAgent(response.agentId);
|
|
826
756
|
if (senderAgent?.can_delegate && (0, delegation_format_validator_js_1.isDelegationAttempt)(response.rawContent)) {
|
|
@@ -907,10 +837,7 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
907
837
|
return;
|
|
908
838
|
}
|
|
909
839
|
const agentStates = this.processManager.getAgentStates();
|
|
910
|
-
|
|
911
|
-
? this.prReviewPoller.getActiveSessions()
|
|
912
|
-
: [];
|
|
913
|
-
// Check if any agent is busy or PR polling is active
|
|
840
|
+
// Check if any agent is busy
|
|
914
841
|
let hasBusy = false;
|
|
915
842
|
for (const state of agentStates.values()) {
|
|
916
843
|
if (state === 'busy' || state === 'starting') {
|
|
@@ -918,8 +845,8 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
918
845
|
break;
|
|
919
846
|
}
|
|
920
847
|
}
|
|
921
|
-
// Silent when no agents are busy
|
|
922
|
-
if (!hasBusy
|
|
848
|
+
// Silent when no agents are busy
|
|
849
|
+
if (!hasBusy)
|
|
923
850
|
return;
|
|
924
851
|
// Build status line
|
|
925
852
|
const agentConfigs = this.config.agents;
|
|
@@ -936,11 +863,7 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
936
863
|
}
|
|
937
864
|
parts.push(entry);
|
|
938
865
|
}
|
|
939
|
-
|
|
940
|
-
// Append PR polling info
|
|
941
|
-
if (prSessions.length > 0) {
|
|
942
|
-
statusLine += ` | π PR: ${prSessions.join(', ')}`;
|
|
943
|
-
}
|
|
866
|
+
const statusLine = `β±οΈ *Agent Status* | ${parts.join(' | ')}`;
|
|
944
867
|
try {
|
|
945
868
|
await this.mainWebClient.chat.postMessage({
|
|
946
869
|
channel: this.heartbeatChannelId,
|
|
@@ -951,9 +874,6 @@ class MultiAgentSlackHandler extends multi_agent_base_js_1.MultiAgentHandlerBase
|
|
|
951
874
|
this.logger.error('[Heartbeat] Failed to post status:', err);
|
|
952
875
|
}
|
|
953
876
|
}
|
|
954
|
-
isPrReviewPollingEnabled() {
|
|
955
|
-
return this.config.pr_review_poller?.enabled === true;
|
|
956
|
-
}
|
|
957
877
|
/**
|
|
958
878
|
* Get status of all agent bots
|
|
959
879
|
*/
|