@geminixiang/mikan 1.0.0-beta.11 → 1.0.0-beta.13
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 +45 -0
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +1 -0
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +1 -0
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/github/bot.d.ts +10 -1
- package/dist/adapters/github/bot.d.ts.map +1 -1
- package/dist/adapters/github/bot.js +34 -0
- package/dist/adapters/github/bot.js.map +1 -1
- package/dist/adapters/github/client.d.ts +6 -0
- package/dist/adapters/github/client.d.ts.map +1 -1
- package/dist/adapters/github/client.js +22 -0
- package/dist/adapters/github/client.js.map +1 -1
- package/dist/adapters/github/context.d.ts.map +1 -1
- package/dist/adapters/github/context.js +3 -1
- package/dist/adapters/github/context.js.map +1 -1
- package/dist/adapters/github/tool-pack.d.ts +12 -0
- package/dist/adapters/github/tool-pack.d.ts.map +1 -0
- package/dist/adapters/github/tool-pack.js +30 -0
- package/dist/adapters/github/tool-pack.js.map +1 -0
- package/dist/adapters/github/types.d.ts +55 -1
- package/dist/adapters/github/types.d.ts.map +1 -1
- package/dist/adapters/github/types.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +1 -0
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +1 -0
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +1 -0
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +1 -0
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/agent.d.ts +16 -11
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +220 -225
- package/dist/agent.js.map +1 -1
- package/dist/cli/download.d.ts.map +1 -0
- package/dist/cli/download.js.map +1 -0
- package/dist/commands/model.d.ts.map +1 -1
- package/dist/commands/model.js +1 -0
- package/dist/commands/model.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +8 -12
- package/dist/execution-resolver.js.map +1 -1
- package/dist/harness/models.d.ts +5 -0
- package/dist/harness/models.d.ts.map +1 -1
- package/dist/harness/models.js +10 -0
- package/dist/harness/models.js.map +1 -1
- package/dist/main.js +18 -6
- package/dist/main.js.map +1 -1
- package/dist/runtime/conversation-runtime.js +1 -1
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +9 -3
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/tools/github-checks.d.ts +9 -4
- package/dist/tools/github-checks.d.ts.map +1 -1
- package/dist/tools/github-checks.js +43 -12
- package/dist/tools/github-checks.js.map +1 -1
- package/dist/tools/github-pr.d.ts +1 -1
- package/dist/tools/github-pr.d.ts.map +1 -1
- package/dist/tools/github-pr.js.map +1 -1
- package/dist/tools/index.d.ts +10 -4
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +12 -9
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/types.d.ts +28 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/trigger.d.ts.map +1 -1
- package/dist/trigger.js +1 -2
- package/dist/trigger.js.map +1 -1
- package/dist/types.d.ts +18 -35
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault/policy.d.ts +20 -0
- package/dist/vault/policy.d.ts.map +1 -0
- package/dist/vault/policy.js +20 -0
- package/dist/vault/policy.js.map +1 -0
- package/dist/web/admin/portal.js +4 -4
- package/dist/web/admin/portal.js.map +1 -1
- package/dist/web/login/portal.js +1 -1
- package/dist/web/login/portal.js.map +1 -1
- package/dist/web/portal-shell.d.ts +4 -0
- package/dist/web/portal-shell.d.ts.map +1 -0
- package/dist/{portal-shell.js → web/portal-shell.js} +2 -2
- package/dist/web/portal-shell.js.map +1 -0
- package/dist/web/session-view/portal.js +1 -1
- package/dist/web/session-view/portal.js.map +1 -1
- package/package.json +4 -4
- package/dist/download.d.ts.map +0 -1
- package/dist/download.js.map +0 -1
- package/dist/model-registry.d.ts +0 -4
- package/dist/model-registry.d.ts.map +0 -1
- package/dist/model-registry.js +0 -7
- package/dist/model-registry.js.map +0 -1
- package/dist/portal-shell.d.ts +0 -4
- package/dist/portal-shell.d.ts.map +0 -1
- package/dist/portal-shell.js.map +0 -1
- /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
- /package/dist/{download.js → cli/download.js} +0 -0
package/dist/agent.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent runner: prompt construction, path translation, run lifecycle, and createRunner.
|
|
3
|
+
* Single module — do not split helpers that only this file uses.
|
|
4
|
+
*/
|
|
1
5
|
import { DEFAULT_EVENT_BUDGET, defaultExtensionDirs, formatSkillsForPrompt, loadExtensions, loadSkillsFromDir, MikanAgentSession, MikanModels, } from "./harness/index.js";
|
|
2
6
|
import { createHash } from "crypto";
|
|
3
7
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -7,18 +11,31 @@ import { resolveConversationSettings } from "./config.js";
|
|
|
7
11
|
import { readEnv } from "./utils/env.js";
|
|
8
12
|
import { ActorExecutionResolver } from "./execution-resolver.js";
|
|
9
13
|
import * as log from "./log.js";
|
|
10
|
-
import { reportUserFacingError } from "./observability/sentry.js";
|
|
11
14
|
import { createExecutor, } from "./sandbox/index.js";
|
|
12
15
|
import { createMountedRuntimePathContext } from "./sandbox/path-context.js";
|
|
13
|
-
import { addLifecycleBreadcrumb, metricAttributes, updateActiveSpanAttribution, } from "./observability/sentry.js";
|
|
16
|
+
import { addLifecycleBreadcrumb, metricAttributes, reportUserFacingError, updateActiveSpanAttribution, } from "./observability/sentry.js";
|
|
14
17
|
import { AgentMemoryFileManager } from "./sessions/agent-memory-file-manager.js";
|
|
15
18
|
import { extractSessionUuid, openManagedSession, } from "./sessions/store.js";
|
|
16
19
|
import { HostEventStore } from "./tools/event.js";
|
|
17
20
|
import { createMikanTools } from "./tools/index.js";
|
|
18
21
|
import * as Sentry from "@sentry/node";
|
|
19
22
|
import { formatLocalTimestamp } from "./utils/date.js";
|
|
20
|
-
import { resolveConfiguredModel } from "./model-registry.js";
|
|
21
23
|
import { emitAgentEvent } from "./agent-events.js";
|
|
24
|
+
export function getUnresolvedSandboxPathContext(sandboxConfig, hostWorkspaceRoot) {
|
|
25
|
+
if (sandboxConfig.type === "image") {
|
|
26
|
+
return createMountedRuntimePathContext(hostWorkspaceRoot, "/workspace");
|
|
27
|
+
}
|
|
28
|
+
return createExecutor(sandboxConfig).getPathContext(hostWorkspaceRoot);
|
|
29
|
+
}
|
|
30
|
+
export function translateRuntimePathToHost(runtimePath, pathContext) {
|
|
31
|
+
return pathContext.runtimeToHostPath?.(runtimePath) ?? runtimePath;
|
|
32
|
+
}
|
|
33
|
+
export function translateAttachPathToHost(filePath, pathContext) {
|
|
34
|
+
const runtimePath = posix.isAbsolute(filePath)
|
|
35
|
+
? filePath
|
|
36
|
+
: posix.join(pathContext.runtimeWorkspaceRoot, filePath);
|
|
37
|
+
return translateRuntimePathToHost(runtimePath, pathContext);
|
|
38
|
+
}
|
|
22
39
|
const IMAGE_MIME_TYPES = {
|
|
23
40
|
jpg: "image/jpeg",
|
|
24
41
|
jpeg: "image/jpeg",
|
|
@@ -29,12 +46,52 @@ const IMAGE_MIME_TYPES = {
|
|
|
29
46
|
function getImageMimeType(filename) {
|
|
30
47
|
return IMAGE_MIME_TYPES[filename.toLowerCase().split(".").pop() || ""];
|
|
31
48
|
}
|
|
32
|
-
function
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
function formatTimestampedUserMessage(message) {
|
|
50
|
+
const timestamp = formatLocalTimestamp(new Date());
|
|
51
|
+
const threadContext = message.threadTs ? ` [in-thread:${message.threadTs}]` : "";
|
|
52
|
+
return `[${timestamp}] [${message.userName || "unknown"}]${threadContext}: ${message.text}`;
|
|
53
|
+
}
|
|
54
|
+
function collectMessageAttachments(message, workspacePath, pathContext) {
|
|
55
|
+
const imageAttachments = [];
|
|
56
|
+
const nonImagePaths = [];
|
|
57
|
+
for (const attachment of message.attachments || []) {
|
|
58
|
+
const runtimePath = `${workspacePath}/${attachment.localPath}`;
|
|
59
|
+
const hostPath = pathContext?.runtimeToHostPath?.(runtimePath) ?? runtimePath;
|
|
60
|
+
const mimeType = getImageMimeType(attachment.localPath);
|
|
61
|
+
if (mimeType && existsSync(hostPath)) {
|
|
62
|
+
try {
|
|
63
|
+
imageAttachments.push({
|
|
64
|
+
type: "image",
|
|
65
|
+
mimeType,
|
|
66
|
+
data: readFileSync(hostPath).toString("base64"),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
nonImagePaths.push(runtimePath);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
nonImagePaths.push(runtimePath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { imageAttachments, nonImagePaths };
|
|
78
|
+
}
|
|
79
|
+
export function buildPromptPayload(message, workspacePath, pathContext) {
|
|
80
|
+
let userMessage = formatTimestampedUserMessage(message);
|
|
81
|
+
const { imageAttachments, nonImagePaths } = collectMessageAttachments(message, workspacePath, pathContext);
|
|
82
|
+
if (nonImagePaths.length > 0) {
|
|
83
|
+
userMessage += `\n\n<slack_attachments>\n${nonImagePaths.join("\n")}\n</slack_attachments>`;
|
|
84
|
+
}
|
|
85
|
+
return { userMessage, imageAttachments };
|
|
86
|
+
}
|
|
87
|
+
async function writePromptDebugContext(conversationDir, systemPrompt, session, userMessage, imageAttachmentCount) {
|
|
88
|
+
const debugContext = {
|
|
89
|
+
systemPrompt,
|
|
90
|
+
messages: session.messages,
|
|
91
|
+
newUserMessage: userMessage,
|
|
92
|
+
imageAttachmentCount,
|
|
93
|
+
};
|
|
94
|
+
await writeFile(join(conversationDir, "last_prompt.jsonl"), JSON.stringify(debugContext, null, 2));
|
|
38
95
|
}
|
|
39
96
|
async function getMemory(conversationDir) {
|
|
40
97
|
const parts = [];
|
|
@@ -329,82 +386,21 @@ Do not add this to \`[SILENT]\` responses.`);
|
|
|
329
386
|
}
|
|
330
387
|
return parts.join("\n\n");
|
|
331
388
|
}
|
|
332
|
-
export function
|
|
333
|
-
if (
|
|
334
|
-
return
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
: createExecutor(sandboxConfig);
|
|
347
|
-
const executor = {
|
|
348
|
-
exec(command, options) {
|
|
349
|
-
return activeExecutor.exec(command, options);
|
|
350
|
-
},
|
|
351
|
-
getWorkspacePath(hostPath) {
|
|
352
|
-
return activeExecutor.getWorkspacePath(hostPath);
|
|
353
|
-
},
|
|
354
|
-
getSandboxConfig() {
|
|
355
|
-
return activeExecutor.getSandboxConfig();
|
|
356
|
-
},
|
|
357
|
-
getPathContext(hostWorkspaceRoot) {
|
|
358
|
-
return activeExecutor.getPathContext(hostWorkspaceRoot);
|
|
359
|
-
},
|
|
360
|
-
};
|
|
361
|
-
return {
|
|
362
|
-
executionResolver,
|
|
363
|
-
executor,
|
|
364
|
-
getPathContext: () => executor.getPathContext(hostWorkspacePath),
|
|
365
|
-
async resolveExecutorForRun(context) {
|
|
366
|
-
if (!executionResolver)
|
|
367
|
-
return;
|
|
368
|
-
activeExecutor = await executionResolver.resolve(context);
|
|
369
|
-
},
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Extension host services over mikan's runtime infrastructure: schedules
|
|
374
|
-
* become event files under `<workspaceDir>/events` (picked up live by
|
|
375
|
-
* EventsWatcher), secrets come from `vaults/extensions/<slug>/env`, and
|
|
376
|
-
* notify posts through the platform bots when main.ts provides a notifier.
|
|
377
|
-
*/
|
|
378
|
-
function buildExtensionHostServices(params) {
|
|
379
|
-
const { workspaceDir, vaultManager, platformNotifier, platformReactor } = params;
|
|
380
|
-
const eventStore = HostEventStore.fromWorkspaceDir(workspaceDir);
|
|
381
|
-
return {
|
|
382
|
-
stateDir: readEnv("STATE_DIR"),
|
|
383
|
-
scheduleStore: {
|
|
384
|
-
write: async (filename, payload) => {
|
|
385
|
-
// Event files tolerate a missing platform (single-platform default),
|
|
386
|
-
// so the harness payload is a valid event payload as written.
|
|
387
|
-
await eventStore.write(filename, payload);
|
|
388
|
-
},
|
|
389
|
-
delete: async (filename) => (await eventStore.delete(filename)).deleted,
|
|
390
|
-
list: async () => (await eventStore.list()).map((entry) => ({
|
|
391
|
-
filename: entry.filename,
|
|
392
|
-
payload: entry.payload,
|
|
393
|
-
})),
|
|
394
|
-
},
|
|
395
|
-
...(platformNotifier ? { postMessage: platformNotifier } : {}),
|
|
396
|
-
...(platformReactor ? { addReaction: platformReactor } : {}),
|
|
397
|
-
...(vaultManager
|
|
398
|
-
? {
|
|
399
|
-
resolveSecrets: (slug) => vaultManager.resolve(`extensions/${slug}`)?.env ?? {},
|
|
400
|
-
}
|
|
401
|
-
: {}),
|
|
402
|
-
};
|
|
389
|
+
export function appendTriggerAttribution(text, triggerAttribution, sessionLink) {
|
|
390
|
+
if (!triggerAttribution)
|
|
391
|
+
return text;
|
|
392
|
+
const trimmed = text.trimEnd();
|
|
393
|
+
const legacySuffix = `_Triggered by ${triggerAttribution}_`;
|
|
394
|
+
// Slack mrkdwn italics cannot span a URL — a `_..._` wrapping a link renders
|
|
395
|
+
// the underscores literally. Keep the session link outside the italic span.
|
|
396
|
+
const suffix = sessionLink ? `${legacySuffix} · session: ${sessionLink}` : legacySuffix;
|
|
397
|
+
if (trimmed.endsWith(suffix))
|
|
398
|
+
return text;
|
|
399
|
+
const body = trimmed.endsWith(legacySuffix)
|
|
400
|
+
? trimmed.slice(0, -legacySuffix.length).trimEnd()
|
|
401
|
+
: trimmed;
|
|
402
|
+
return `${body}\n\n${suffix}`;
|
|
403
403
|
}
|
|
404
|
-
/**
|
|
405
|
-
* Local (workspace/conversation) skills override extension skills on name
|
|
406
|
-
* collision: an admin's on-disk skill always beats an extension's.
|
|
407
|
-
*/
|
|
408
404
|
function mergeExtensionSkills(local, extension) {
|
|
409
405
|
if (extension.length === 0)
|
|
410
406
|
return local;
|
|
@@ -415,42 +411,6 @@ function mergeExtensionSkills(local, extension) {
|
|
|
415
411
|
byName.set(skill.name, skill);
|
|
416
412
|
return [...byName.values()];
|
|
417
413
|
}
|
|
418
|
-
async function createConfiguredAgentSession(params) {
|
|
419
|
-
const { conversationId, workspaceDir, systemPrompt, model, thinkingLevel, tools, sessionStore, models, vaultManager, platformNotifier, platformReactor, } = params;
|
|
420
|
-
// Host-only dirs under the state dir: extension code runs in the mikan
|
|
421
|
-
// process, so it must never load from workspace paths — those are mounted
|
|
422
|
-
// into sandbox containers and agent-writable (sandbox escape otherwise).
|
|
423
|
-
const extensionsResult = await loadExtensions({
|
|
424
|
-
dirs: defaultExtensionDirs(conversationId, readEnv("STATE_DIR")),
|
|
425
|
-
context: { conversationId, workspaceDir, model, thinkingLevel },
|
|
426
|
-
services: buildExtensionHostServices({
|
|
427
|
-
workspaceDir,
|
|
428
|
-
vaultManager,
|
|
429
|
-
platformNotifier,
|
|
430
|
-
platformReactor,
|
|
431
|
-
}),
|
|
432
|
-
});
|
|
433
|
-
for (const err of extensionsResult.errors) {
|
|
434
|
-
log.logWarning(`[${conversationId}] Extension load error: ${err.path}`, err.error);
|
|
435
|
-
}
|
|
436
|
-
if (extensionsResult.extensions.length > 0) {
|
|
437
|
-
log.logInfo(`[${conversationId}] Loaded ${extensionsResult.extensions.length} extension(s): ${extensionsResult.extensions.map((extension) => extension.name).join(", ")}`);
|
|
438
|
-
}
|
|
439
|
-
const session = new MikanAgentSession({
|
|
440
|
-
systemPrompt,
|
|
441
|
-
model,
|
|
442
|
-
thinkingLevel,
|
|
443
|
-
tools,
|
|
444
|
-
models,
|
|
445
|
-
sessionStore,
|
|
446
|
-
extensions: extensionsResult.registry,
|
|
447
|
-
});
|
|
448
|
-
const reloaded = session.reloadFromSession();
|
|
449
|
-
if (reloaded > 0) {
|
|
450
|
-
log.logInfo(`[${conversationId}] Reloaded ${reloaded} messages from session context`);
|
|
451
|
-
}
|
|
452
|
-
return { session, extensionSkills: extensionsResult.skills };
|
|
453
|
-
}
|
|
454
414
|
function createEmptyUsageTotals() {
|
|
455
415
|
return {
|
|
456
416
|
input: 0,
|
|
@@ -521,53 +481,6 @@ function createRunQueue(responder, runState) {
|
|
|
521
481
|
wait: () => queueChain,
|
|
522
482
|
};
|
|
523
483
|
}
|
|
524
|
-
function formatTimestampedUserMessage(message) {
|
|
525
|
-
const timestamp = formatLocalTimestamp(new Date());
|
|
526
|
-
const threadContext = message.threadTs ? ` [in-thread:${message.threadTs}]` : "";
|
|
527
|
-
return `[${timestamp}] [${message.userName || "unknown"}]${threadContext}: ${message.text}`;
|
|
528
|
-
}
|
|
529
|
-
function collectMessageAttachments(message, workspacePath, pathContext) {
|
|
530
|
-
const imageAttachments = [];
|
|
531
|
-
const nonImagePaths = [];
|
|
532
|
-
for (const attachment of message.attachments || []) {
|
|
533
|
-
const runtimePath = `${workspacePath}/${attachment.localPath}`;
|
|
534
|
-
const hostPath = pathContext?.runtimeToHostPath?.(runtimePath) ?? runtimePath;
|
|
535
|
-
const mimeType = getImageMimeType(attachment.localPath);
|
|
536
|
-
if (mimeType && existsSync(hostPath)) {
|
|
537
|
-
try {
|
|
538
|
-
imageAttachments.push({
|
|
539
|
-
type: "image",
|
|
540
|
-
mimeType,
|
|
541
|
-
data: readFileSync(hostPath).toString("base64"),
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
catch {
|
|
545
|
-
nonImagePaths.push(runtimePath);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
else {
|
|
549
|
-
nonImagePaths.push(runtimePath);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
return { imageAttachments, nonImagePaths };
|
|
553
|
-
}
|
|
554
|
-
export function buildPromptPayload(message, workspacePath, pathContext) {
|
|
555
|
-
let userMessage = formatTimestampedUserMessage(message);
|
|
556
|
-
const { imageAttachments, nonImagePaths } = collectMessageAttachments(message, workspacePath, pathContext);
|
|
557
|
-
if (nonImagePaths.length > 0) {
|
|
558
|
-
userMessage += `\n\n<slack_attachments>\n${nonImagePaths.join("\n")}\n</slack_attachments>`;
|
|
559
|
-
}
|
|
560
|
-
return { userMessage, imageAttachments };
|
|
561
|
-
}
|
|
562
|
-
async function writePromptDebugContext(conversationDir, systemPrompt, session, userMessage, imageAttachmentCount) {
|
|
563
|
-
const debugContext = {
|
|
564
|
-
systemPrompt,
|
|
565
|
-
messages: session.messages,
|
|
566
|
-
newUserMessage: userMessage,
|
|
567
|
-
imageAttachmentCount,
|
|
568
|
-
};
|
|
569
|
-
await writeFile(join(conversationDir, "last_prompt.jsonl"), JSON.stringify(debugContext, null, 2));
|
|
570
|
-
}
|
|
571
484
|
function getFinalAssistantText(session) {
|
|
572
485
|
const lastAssistant = session.messages.findLast((message) => message.role === "assistant");
|
|
573
486
|
return (lastAssistant?.content
|
|
@@ -575,21 +488,6 @@ function getFinalAssistantText(session) {
|
|
|
575
488
|
.map((content) => content.text)
|
|
576
489
|
.join("\n") || "");
|
|
577
490
|
}
|
|
578
|
-
export function appendTriggerAttribution(text, triggerAttribution, sessionLink) {
|
|
579
|
-
if (!triggerAttribution)
|
|
580
|
-
return text;
|
|
581
|
-
const trimmed = text.trimEnd();
|
|
582
|
-
const legacySuffix = `_Triggered by ${triggerAttribution}_`;
|
|
583
|
-
// Slack mrkdwn italics cannot span a URL — a `_..._` wrapping a link renders
|
|
584
|
-
// the underscores literally. Keep the session link outside the italic span.
|
|
585
|
-
const suffix = sessionLink ? `${legacySuffix} · session: ${sessionLink}` : legacySuffix;
|
|
586
|
-
if (trimmed.endsWith(suffix))
|
|
587
|
-
return text;
|
|
588
|
-
const body = trimmed.endsWith(legacySuffix)
|
|
589
|
-
? trimmed.slice(0, -legacySuffix.length).trimEnd()
|
|
590
|
-
: trimmed;
|
|
591
|
-
return `${body}\n\n${suffix}`;
|
|
592
|
-
}
|
|
593
491
|
function isEventTriggerAttribution(triggerAttribution) {
|
|
594
492
|
return triggerAttribution?.startsWith("[event:") === true;
|
|
595
493
|
}
|
|
@@ -745,6 +643,136 @@ async function reportUsageSummary(ctx) {
|
|
|
745
643
|
await waitForQueue();
|
|
746
644
|
}
|
|
747
645
|
}
|
|
646
|
+
function extractToolResultText(result) {
|
|
647
|
+
if (typeof result === "string") {
|
|
648
|
+
return result;
|
|
649
|
+
}
|
|
650
|
+
if (result &&
|
|
651
|
+
typeof result === "object" &&
|
|
652
|
+
"content" in result &&
|
|
653
|
+
Array.isArray(result.content)) {
|
|
654
|
+
const content = result.content;
|
|
655
|
+
const textParts = [];
|
|
656
|
+
for (const part of content) {
|
|
657
|
+
if (part.type === "text" && part.text) {
|
|
658
|
+
textParts.push(part.text);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (textParts.length > 0) {
|
|
662
|
+
return textParts.join("\n");
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return JSON.stringify(result);
|
|
666
|
+
}
|
|
667
|
+
function buildThreadSessionName(message) {
|
|
668
|
+
const text = message?.text?.trim();
|
|
669
|
+
if (!text)
|
|
670
|
+
return undefined;
|
|
671
|
+
const userLabel = message?.userName || message?.user || "unknown";
|
|
672
|
+
return `[${userLabel}]: ${text}`;
|
|
673
|
+
}
|
|
674
|
+
function createRunnerExecutionContext(sandboxConfig, vaultManager, provisioner, workspaceDir, hostWorkspacePath) {
|
|
675
|
+
const executionResolver = vaultManager && sandboxConfig.type !== "host"
|
|
676
|
+
? new ActorExecutionResolver(sandboxConfig, vaultManager, provisioner, workspaceDir)
|
|
677
|
+
: undefined;
|
|
678
|
+
// activeExecutor is replaced at the start of each run() call when executionResolver
|
|
679
|
+
// is present, so the stable `executor` wrapper always delegates to the latest resolved value.
|
|
680
|
+
let activeExecutor = executionResolver !== undefined
|
|
681
|
+
? createExecutor({ type: "host" })
|
|
682
|
+
: createExecutor(sandboxConfig);
|
|
683
|
+
const executor = {
|
|
684
|
+
exec(command, options) {
|
|
685
|
+
return activeExecutor.exec(command, options);
|
|
686
|
+
},
|
|
687
|
+
getWorkspacePath(hostPath) {
|
|
688
|
+
return activeExecutor.getWorkspacePath(hostPath);
|
|
689
|
+
},
|
|
690
|
+
getSandboxConfig() {
|
|
691
|
+
return activeExecutor.getSandboxConfig();
|
|
692
|
+
},
|
|
693
|
+
getPathContext(hostWorkspaceRoot) {
|
|
694
|
+
return activeExecutor.getPathContext(hostWorkspaceRoot);
|
|
695
|
+
},
|
|
696
|
+
};
|
|
697
|
+
return {
|
|
698
|
+
executionResolver,
|
|
699
|
+
executor,
|
|
700
|
+
getPathContext: () => executor.getPathContext(hostWorkspacePath),
|
|
701
|
+
async resolveExecutorForRun(context) {
|
|
702
|
+
if (!executionResolver)
|
|
703
|
+
return;
|
|
704
|
+
activeExecutor = await executionResolver.resolve(context);
|
|
705
|
+
},
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Extension host services over mikan's runtime infrastructure: schedules
|
|
710
|
+
* become event files under `<workspaceDir>/events` (picked up live by
|
|
711
|
+
* EventsWatcher), secrets come from `vaults/extensions/<slug>/env`, and
|
|
712
|
+
* notify posts through the platform bots when main.ts provides a notifier.
|
|
713
|
+
*/
|
|
714
|
+
function buildExtensionHostServices(params) {
|
|
715
|
+
const { workspaceDir, vaultManager, platformNotifier, platformReactor } = params;
|
|
716
|
+
const eventStore = HostEventStore.fromWorkspaceDir(workspaceDir);
|
|
717
|
+
return {
|
|
718
|
+
stateDir: readEnv("STATE_DIR"),
|
|
719
|
+
scheduleStore: {
|
|
720
|
+
write: async (filename, payload) => {
|
|
721
|
+
// Event files tolerate a missing platform (single-platform default),
|
|
722
|
+
// so the harness payload is a valid event payload as written.
|
|
723
|
+
await eventStore.write(filename, payload);
|
|
724
|
+
},
|
|
725
|
+
delete: async (filename) => (await eventStore.delete(filename)).deleted,
|
|
726
|
+
list: async () => (await eventStore.list()).map((entry) => ({
|
|
727
|
+
filename: entry.filename,
|
|
728
|
+
payload: entry.payload,
|
|
729
|
+
})),
|
|
730
|
+
},
|
|
731
|
+
...(platformNotifier ? { postMessage: platformNotifier } : {}),
|
|
732
|
+
...(platformReactor ? { addReaction: platformReactor } : {}),
|
|
733
|
+
...(vaultManager
|
|
734
|
+
? {
|
|
735
|
+
resolveSecrets: (slug) => vaultManager.resolve(`extensions/${slug}`)?.env ?? {},
|
|
736
|
+
}
|
|
737
|
+
: {}),
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
async function createConfiguredAgentSession(params) {
|
|
741
|
+
const { conversationId, workspaceDir, systemPrompt, model, thinkingLevel, tools, sessionStore, models, vaultManager, platformNotifier, platformReactor, } = params;
|
|
742
|
+
// Host-only dirs under the state dir: extension code runs in the mikan
|
|
743
|
+
// process, so it must never load from workspace paths — those are mounted
|
|
744
|
+
// into sandbox containers and agent-writable (sandbox escape otherwise).
|
|
745
|
+
const extensionsResult = await loadExtensions({
|
|
746
|
+
dirs: defaultExtensionDirs(conversationId, readEnv("STATE_DIR")),
|
|
747
|
+
context: { conversationId, workspaceDir, model, thinkingLevel },
|
|
748
|
+
services: buildExtensionHostServices({
|
|
749
|
+
workspaceDir,
|
|
750
|
+
vaultManager,
|
|
751
|
+
platformNotifier,
|
|
752
|
+
platformReactor,
|
|
753
|
+
}),
|
|
754
|
+
});
|
|
755
|
+
for (const err of extensionsResult.errors) {
|
|
756
|
+
log.logWarning(`[${conversationId}] Extension load error: ${err.path}`, err.error);
|
|
757
|
+
}
|
|
758
|
+
if (extensionsResult.extensions.length > 0) {
|
|
759
|
+
log.logInfo(`[${conversationId}] Loaded ${extensionsResult.extensions.length} extension(s): ${extensionsResult.extensions.map((extension) => extension.name).join(", ")}`);
|
|
760
|
+
}
|
|
761
|
+
const session = new MikanAgentSession({
|
|
762
|
+
systemPrompt,
|
|
763
|
+
model,
|
|
764
|
+
thinkingLevel,
|
|
765
|
+
tools,
|
|
766
|
+
models,
|
|
767
|
+
sessionStore,
|
|
768
|
+
extensions: extensionsResult.registry,
|
|
769
|
+
});
|
|
770
|
+
const reloaded = session.reloadFromSession();
|
|
771
|
+
if (reloaded > 0) {
|
|
772
|
+
log.logInfo(`[${conversationId}] Reloaded ${reloaded} messages from session context`);
|
|
773
|
+
}
|
|
774
|
+
return { session, extensionSkills: extensionsResult.skills };
|
|
775
|
+
}
|
|
748
776
|
function reloadSessionMessages(session, conversationId) {
|
|
749
777
|
const reloaded = session.reloadFromSession();
|
|
750
778
|
if (reloaded > 0) {
|
|
@@ -752,7 +780,7 @@ function reloadSessionMessages(session, conversationId) {
|
|
|
752
780
|
}
|
|
753
781
|
}
|
|
754
782
|
async function prepareRunContext(params) {
|
|
755
|
-
const { message, responder, platform, conversationId, conversationDir, sessionUuid, runState, executor, executionResolver, resolveExecutorForRun, getPathContext, session, setEventContext, setSandboxContext, setUploadFunction, setReactFunction, } = params;
|
|
783
|
+
const { message, responder, platform, conversationId, conversationDir, sessionUuid, runState, executor, executionResolver, resolveExecutorForRun, getPathContext, session, setEventContext, setSandboxContext, setUploadFunction, setReactFunction, bindPlatformToolPacks, } = params;
|
|
756
784
|
let pathContext = params.pathContext;
|
|
757
785
|
const sessionConversation = message.sessionKey.split(":")[0];
|
|
758
786
|
await mkdir(join(conversationDir, "scratch"), { recursive: true });
|
|
@@ -761,6 +789,7 @@ async function prepareRunContext(params) {
|
|
|
761
789
|
platform: platform.name,
|
|
762
790
|
userId: message.userId,
|
|
763
791
|
conversationId,
|
|
792
|
+
trustModel: platform.trustModel,
|
|
764
793
|
});
|
|
765
794
|
pathContext = getPathContext();
|
|
766
795
|
}
|
|
@@ -794,12 +823,9 @@ async function prepareRunContext(params) {
|
|
|
794
823
|
// (interactive turns on platforms with reaction support); otherwise unset
|
|
795
824
|
// so the tool reports it is unavailable rather than silently no-op'ing.
|
|
796
825
|
setReactFunction(responder.react ? async (emoji) => responder.react(emoji) : null);
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
|
|
800
|
-
const githubOps = platform.name === "github" ? platformGithubOps : undefined;
|
|
801
|
-
setGithubPrFunction(githubOps ? (request) => githubOps.pushAndCreatePr(conversationId, request) : null);
|
|
802
|
-
setGithubChecksFunction(githubOps ? (branch) => githubOps.getChecks(conversationId, branch) : null);
|
|
826
|
+
// Platform capability packs (e.g. GitHub PR/CI) enable themselves per run;
|
|
827
|
+
// core does not branch on platform name here.
|
|
828
|
+
bindPlatformToolPacks({ conversationId, platformName: platform.name });
|
|
803
829
|
resetRunState(runState, responder, sessionConversation, message.userName, sessionUuid, triggerAttribution);
|
|
804
830
|
const runQueue = createRunQueue(responder, runState);
|
|
805
831
|
runState.queue = runQueue.queue;
|
|
@@ -1087,27 +1113,6 @@ function attachSessionEventHandlers(params) {
|
|
|
1087
1113
|
}
|
|
1088
1114
|
});
|
|
1089
1115
|
}
|
|
1090
|
-
function extractToolResultText(result) {
|
|
1091
|
-
if (typeof result === "string") {
|
|
1092
|
-
return result;
|
|
1093
|
-
}
|
|
1094
|
-
if (result &&
|
|
1095
|
-
typeof result === "object" &&
|
|
1096
|
-
"content" in result &&
|
|
1097
|
-
Array.isArray(result.content)) {
|
|
1098
|
-
const content = result.content;
|
|
1099
|
-
const textParts = [];
|
|
1100
|
-
for (const part of content) {
|
|
1101
|
-
if (part.type === "text" && part.text) {
|
|
1102
|
-
textParts.push(part.text);
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
if (textParts.length > 0) {
|
|
1106
|
-
return textParts.join("\n");
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
return JSON.stringify(result);
|
|
1110
|
-
}
|
|
1111
1116
|
/**
|
|
1112
1117
|
* Create a new PiAgentWrapper for a channel.
|
|
1113
1118
|
* Sets up the session and subscribes to events once.
|
|
@@ -1115,18 +1120,18 @@ function extractToolResultText(result) {
|
|
|
1115
1120
|
* Runner caching is handled by the caller (channelStates in main.ts).
|
|
1116
1121
|
* This is a stateless factory function.
|
|
1117
1122
|
*/
|
|
1118
|
-
export async function createRunner(sandboxConfig, sessionKey, conversationId, conversationDir, workspaceDir, sessionScope, vaultManager, provisioner, sessionView, platformNotifier, platformReactor,
|
|
1123
|
+
export async function createRunner(sandboxConfig, sessionKey, conversationId, conversationDir, workspaceDir, sessionScope, vaultManager, provisioner, sessionView, platformNotifier, platformReactor, platformToolPackFactories) {
|
|
1119
1124
|
const agentConfig = resolveConversationSettings(conversationDir);
|
|
1120
1125
|
const workspaceBase = join(conversationDir, "..");
|
|
1121
1126
|
const { executionResolver, executor, getPathContext, resolveExecutorForRun } = createRunnerExecutionContext(sandboxConfig, vaultManager, provisioner, workspaceDir, workspaceBase);
|
|
1122
1127
|
let pathContext = getUnresolvedSandboxPathContext(sandboxConfig, workspaceBase);
|
|
1123
1128
|
// Create tools (per-runner, with per-runner upload function setter)
|
|
1124
|
-
const { tools, setUploadFunction, setReactFunction,
|
|
1129
|
+
const { tools, setUploadFunction, setReactFunction, bindPlatformToolPacks, setEventContext, setSandboxContext, } = createMikanTools(executor, workspaceDir, { sandbox: sandboxConfig, provisioner }, platformToolPackFactories ?? []);
|
|
1125
1130
|
const modelRegistry = MikanModels.create();
|
|
1126
1131
|
if (modelRegistry.getError()) {
|
|
1127
1132
|
log.logWarning("models.json load error", modelRegistry.getError());
|
|
1128
1133
|
}
|
|
1129
|
-
const model =
|
|
1134
|
+
const model = modelRegistry.resolve(agentConfig.provider, agentConfig.model);
|
|
1130
1135
|
// Initial system prompt (will be updated each run with fresh memory/channels/users/skills)
|
|
1131
1136
|
const memory = await getMemory(conversationDir);
|
|
1132
1137
|
const skills = loadMikanSkills(conversationDir, pathContext.runtimeWorkspaceRoot);
|
|
@@ -1135,6 +1140,7 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
|
|
|
1135
1140
|
formattingGuide: "",
|
|
1136
1141
|
channels: [],
|
|
1137
1142
|
users: [],
|
|
1143
|
+
trustModel: "membership",
|
|
1138
1144
|
};
|
|
1139
1145
|
const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, conversationId, "shared", undefined, memory, sandboxConfig, emptyPlatform, skills);
|
|
1140
1146
|
// Create session manager and settings manager. Top-level/private sessions
|
|
@@ -1193,9 +1199,7 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
|
|
|
1193
1199
|
setSandboxContext,
|
|
1194
1200
|
setUploadFunction,
|
|
1195
1201
|
setReactFunction,
|
|
1196
|
-
|
|
1197
|
-
setGithubChecksFunction,
|
|
1198
|
-
platformGithubOps,
|
|
1202
|
+
bindPlatformToolPacks,
|
|
1199
1203
|
pathContext,
|
|
1200
1204
|
});
|
|
1201
1205
|
pathContext = prepared.pathContext;
|
|
@@ -1288,13 +1292,4 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
|
|
|
1288
1292
|
},
|
|
1289
1293
|
};
|
|
1290
1294
|
}
|
|
1291
|
-
export function translateRuntimePathToHost(runtimePath, pathContext) {
|
|
1292
|
-
return pathContext.runtimeToHostPath?.(runtimePath) ?? runtimePath;
|
|
1293
|
-
}
|
|
1294
|
-
export function translateAttachPathToHost(filePath, pathContext) {
|
|
1295
|
-
const runtimePath = posix.isAbsolute(filePath)
|
|
1296
|
-
? filePath
|
|
1297
|
-
: posix.join(pathContext.runtimeWorkspaceRoot, filePath);
|
|
1298
|
-
return translateRuntimePathToHost(runtimePath, pathContext);
|
|
1299
|
-
}
|
|
1300
1295
|
//# sourceMappingURL=agent.js.map
|