@geminixiang/mikan 1.0.0-beta.12 → 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 +26 -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 +6 -2
- package/dist/adapters/github/bot.d.ts.map +1 -1
- package/dist/adapters/github/bot.js +23 -2
- package/dist/adapters/github/bot.js.map +1 -1
- package/dist/adapters/github/context.d.ts.map +1 -1
- package/dist/adapters/github/context.js +1 -0
- 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 +52 -0
- 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 -230
- 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 -7
- 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 +1 -1
- package/dist/tools/github-checks.d.ts.map +1 -1
- package/dist/tools/github-checks.js +15 -4
- 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 -5
- 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 -39
- 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,17 +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
|
|
803
|
-
? {
|
|
804
|
-
getChecks: (branch) => githubOps.getChecks(conversationId, branch),
|
|
805
|
-
getJobLog: (jobId) => githubOps.getJobLog(conversationId, jobId),
|
|
806
|
-
}
|
|
807
|
-
: 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 });
|
|
808
829
|
resetRunState(runState, responder, sessionConversation, message.userName, sessionUuid, triggerAttribution);
|
|
809
830
|
const runQueue = createRunQueue(responder, runState);
|
|
810
831
|
runState.queue = runQueue.queue;
|
|
@@ -1092,27 +1113,6 @@ function attachSessionEventHandlers(params) {
|
|
|
1092
1113
|
}
|
|
1093
1114
|
});
|
|
1094
1115
|
}
|
|
1095
|
-
function extractToolResultText(result) {
|
|
1096
|
-
if (typeof result === "string") {
|
|
1097
|
-
return result;
|
|
1098
|
-
}
|
|
1099
|
-
if (result &&
|
|
1100
|
-
typeof result === "object" &&
|
|
1101
|
-
"content" in result &&
|
|
1102
|
-
Array.isArray(result.content)) {
|
|
1103
|
-
const content = result.content;
|
|
1104
|
-
const textParts = [];
|
|
1105
|
-
for (const part of content) {
|
|
1106
|
-
if (part.type === "text" && part.text) {
|
|
1107
|
-
textParts.push(part.text);
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
if (textParts.length > 0) {
|
|
1111
|
-
return textParts.join("\n");
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
return JSON.stringify(result);
|
|
1115
|
-
}
|
|
1116
1116
|
/**
|
|
1117
1117
|
* Create a new PiAgentWrapper for a channel.
|
|
1118
1118
|
* Sets up the session and subscribes to events once.
|
|
@@ -1120,18 +1120,18 @@ function extractToolResultText(result) {
|
|
|
1120
1120
|
* Runner caching is handled by the caller (channelStates in main.ts).
|
|
1121
1121
|
* This is a stateless factory function.
|
|
1122
1122
|
*/
|
|
1123
|
-
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) {
|
|
1124
1124
|
const agentConfig = resolveConversationSettings(conversationDir);
|
|
1125
1125
|
const workspaceBase = join(conversationDir, "..");
|
|
1126
1126
|
const { executionResolver, executor, getPathContext, resolveExecutorForRun } = createRunnerExecutionContext(sandboxConfig, vaultManager, provisioner, workspaceDir, workspaceBase);
|
|
1127
1127
|
let pathContext = getUnresolvedSandboxPathContext(sandboxConfig, workspaceBase);
|
|
1128
1128
|
// Create tools (per-runner, with per-runner upload function setter)
|
|
1129
|
-
const { tools, setUploadFunction, setReactFunction,
|
|
1129
|
+
const { tools, setUploadFunction, setReactFunction, bindPlatformToolPacks, setEventContext, setSandboxContext, } = createMikanTools(executor, workspaceDir, { sandbox: sandboxConfig, provisioner }, platformToolPackFactories ?? []);
|
|
1130
1130
|
const modelRegistry = MikanModels.create();
|
|
1131
1131
|
if (modelRegistry.getError()) {
|
|
1132
1132
|
log.logWarning("models.json load error", modelRegistry.getError());
|
|
1133
1133
|
}
|
|
1134
|
-
const model =
|
|
1134
|
+
const model = modelRegistry.resolve(agentConfig.provider, agentConfig.model);
|
|
1135
1135
|
// Initial system prompt (will be updated each run with fresh memory/channels/users/skills)
|
|
1136
1136
|
const memory = await getMemory(conversationDir);
|
|
1137
1137
|
const skills = loadMikanSkills(conversationDir, pathContext.runtimeWorkspaceRoot);
|
|
@@ -1140,6 +1140,7 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
|
|
|
1140
1140
|
formattingGuide: "",
|
|
1141
1141
|
channels: [],
|
|
1142
1142
|
users: [],
|
|
1143
|
+
trustModel: "membership",
|
|
1143
1144
|
};
|
|
1144
1145
|
const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, conversationId, "shared", undefined, memory, sandboxConfig, emptyPlatform, skills);
|
|
1145
1146
|
// Create session manager and settings manager. Top-level/private sessions
|
|
@@ -1198,9 +1199,7 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
|
|
|
1198
1199
|
setSandboxContext,
|
|
1199
1200
|
setUploadFunction,
|
|
1200
1201
|
setReactFunction,
|
|
1201
|
-
|
|
1202
|
-
setGithubChecksFunction,
|
|
1203
|
-
platformGithubOps,
|
|
1202
|
+
bindPlatformToolPacks,
|
|
1204
1203
|
pathContext,
|
|
1205
1204
|
});
|
|
1206
1205
|
pathContext = prepared.pathContext;
|
|
@@ -1293,13 +1292,4 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
|
|
|
1293
1292
|
},
|
|
1294
1293
|
};
|
|
1295
1294
|
}
|
|
1296
|
-
export function translateRuntimePathToHost(runtimePath, pathContext) {
|
|
1297
|
-
return pathContext.runtimeToHostPath?.(runtimePath) ?? runtimePath;
|
|
1298
|
-
}
|
|
1299
|
-
export function translateAttachPathToHost(filePath, pathContext) {
|
|
1300
|
-
const runtimePath = posix.isAbsolute(filePath)
|
|
1301
|
-
? filePath
|
|
1302
|
-
: posix.join(pathContext.runtimeWorkspaceRoot, filePath);
|
|
1303
|
-
return translateRuntimePathToHost(runtimePath, pathContext);
|
|
1304
|
-
}
|
|
1305
1295
|
//# sourceMappingURL=agent.js.map
|