@geminixiang/mikan 1.0.0-beta.37 → 1.0.0-beta.38
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 +6 -0
- package/dist/agent.d.ts +0 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +29 -52
- package/dist/agent.js.map +1 -1
- package/dist/harness/skills.d.ts +8 -0
- package/dist/harness/skills.d.ts.map +1 -1
- package/dist/harness/skills.js +46 -4
- package/dist/harness/skills.js.map +1 -1
- package/dist/harness/types.d.ts +2 -0
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,12 @@ any release.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.0.0-beta.38]
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Align the conversation-skill symlink guard with what the host actually reads: symlinked entries are skipped per skill — with the skipped paths named in the host log and surfaced to the agent in its prompt — instead of one symlink anywhere disqualifying the whole tree, and symlinks inside vendored `node_modules` or dot directories (which the loader never reads) no longer disqualify anything. Offices whose skills vanished because a skill vendored npm dependencies (`.bin` symlinks) get them back.
|
|
17
|
+
|
|
12
18
|
## [1.0.0-beta.37]
|
|
13
19
|
|
|
14
20
|
### Fixed
|
package/dist/agent.d.ts
CHANGED
|
@@ -13,7 +13,6 @@ export declare function buildPromptPayload(message: ConversationMessage, workspa
|
|
|
13
13
|
userMessage: string;
|
|
14
14
|
imageAttachments: ImageContent[];
|
|
15
15
|
}>;
|
|
16
|
-
export declare function isSafePromptSkillTree(dir: string, conversationDir: string): boolean;
|
|
17
16
|
export declare function resolveTriggerAttribution(message: Pick<ConversationMessage, "id" | "text" | "userName">): string | undefined;
|
|
18
17
|
/**
|
|
19
18
|
* Instructions that vary per turn (event-trigger mode, response attribution).
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAiB,cAAc,EAAE,MAAM,YAAY,CAAC;AAIrF,OAAO,EAAY,KAAK,YAAY,EAAc,MAAM,uBAAuB,CAAC;AA6BhF,OAAO,KAAK,EAEV,mBAAmB,EAKpB,MAAM,cAAc,CAAC;AAiBtB,OAAO,EAKL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAiC5B,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,kBAAkB,GAC9B,MAAM,CAER;AAcD,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,kBAAkB,GAC9B,MAAM,CA2BR;AAgGD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,mBAAmB,EAC5B,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,kBAAkB,EAChC,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GACxD,OAAO,CAAC;IACT,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,YAAY,EAAE,CAAC;CAClC,CAAC,CAcD;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAiB,cAAc,EAAE,MAAM,YAAY,CAAC;AAIrF,OAAO,EAAY,KAAK,YAAY,EAAc,MAAM,uBAAuB,CAAC;AA6BhF,OAAO,KAAK,EAEV,mBAAmB,EAKpB,MAAM,cAAc,CAAC;AAiBtB,OAAO,EAKL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAiC5B,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,kBAAkB,GAC9B,MAAM,CAER;AAcD,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,kBAAkB,GAC9B,MAAM,CA2BR;AAgGD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,mBAAmB,EAC5B,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,kBAAkB,EAChC,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GACxD,OAAO,CAAC;IACT,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,YAAY,EAAE,CAAC;CAClC,CAAC,CAcD;AA8KD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,IAAI,GAAG,MAAM,GAAG,UAAU,CAAC,GAC7D,MAAM,GAAG,SAAS,CAOpB;AAyMD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,OAAO,EACvB,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,YAAY,EAAE,MAAM,GACnB,MAAM,CAkBR;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAYR;AA80CD;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC,CAsYxF"}
|
package/dist/agent.js
CHANGED
|
@@ -8,7 +8,7 @@ import { runSubagent } from "./harness/subagent-runner.js";
|
|
|
8
8
|
import { loadSubagentProfiles } from "./harness/subagent-profiles.js";
|
|
9
9
|
import { mergeSubagentProgress, parseSubagentProgressSnapshot, renderSubagentDashboard, settleSubagentProgress, } from "./subagent-progress.js";
|
|
10
10
|
import { createHash } from "crypto";
|
|
11
|
-
import { existsSync, lstatSync
|
|
11
|
+
import { existsSync, lstatSync } from "fs";
|
|
12
12
|
import { chmod, mkdtemp, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
13
13
|
import { tmpdir } from "os";
|
|
14
14
|
import { basename, isAbsolute, join, posix, relative, resolve, sep } from "path";
|
|
@@ -242,54 +242,28 @@ function loadMikanSkills(address, conversationDir, workspacePath, projection) {
|
|
|
242
242
|
}
|
|
243
243
|
// Load conversation-specific skills (override workspace skills on collision).
|
|
244
244
|
// Host prompt construction must never follow an agent-created symlink out of
|
|
245
|
-
// the conversation office
|
|
245
|
+
// the conversation office, so this load rejects symlinks per entry — on
|
|
246
|
+
// exactly the paths it reads. Vendored node_modules and dot directories are
|
|
247
|
+
// never read and never disqualify anything.
|
|
246
248
|
const conversationSkillsDir = projection.promptSources.conversationSkillsDir;
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
else if (existsSync(conversationSkillsDir)) {
|
|
256
|
-
log.logWarning("Ignoring unsafe conversation skill tree", conversationSkillsDir);
|
|
257
|
-
}
|
|
258
|
-
return Array.from(skillMap.values());
|
|
259
|
-
}
|
|
260
|
-
export function isSafePromptSkillTree(dir, conversationDir) {
|
|
261
|
-
if (!existsSync(dir))
|
|
262
|
-
return true;
|
|
263
|
-
if (dir !== conversationDir && !dir.startsWith(`${conversationDir}${sep}`))
|
|
264
|
-
return false;
|
|
265
|
-
const stack = [dir];
|
|
266
|
-
while (stack.length > 0) {
|
|
267
|
-
const current = stack.pop();
|
|
268
|
-
let stats;
|
|
269
|
-
try {
|
|
270
|
-
stats = lstatSync(current);
|
|
271
|
-
}
|
|
272
|
-
catch {
|
|
273
|
-
return false;
|
|
274
|
-
}
|
|
275
|
-
if (stats.isSymbolicLink())
|
|
276
|
-
return false;
|
|
277
|
-
if (!stats.isDirectory())
|
|
249
|
+
const conversationSkills = loadSkillsFromDir({
|
|
250
|
+
dir: conversationSkillsDir,
|
|
251
|
+
source: "channel",
|
|
252
|
+
rejectSymlinks: true,
|
|
253
|
+
});
|
|
254
|
+
const skippedSkillLinks = [];
|
|
255
|
+
for (const diagnostic of conversationSkills.diagnostics) {
|
|
256
|
+
if (diagnostic.code !== "symlink")
|
|
278
257
|
continue;
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
for (const entry of entries) {
|
|
287
|
-
if (entry.isSymbolicLink())
|
|
288
|
-
return false;
|
|
289
|
-
stack.push(join(current, entry.name));
|
|
290
|
-
}
|
|
258
|
+
log.logWarning("Skipping conversation skill entry (symlink)", diagnostic.path);
|
|
259
|
+
skippedSkillLinks.push(translatePath(diagnostic.path));
|
|
260
|
+
}
|
|
261
|
+
for (const skill of conversationSkills.skills) {
|
|
262
|
+
skill.filePath = translatePath(skill.filePath);
|
|
263
|
+
skill.baseDir = translatePath(skill.baseDir);
|
|
264
|
+
skillMap.set(skill.name, skill);
|
|
291
265
|
}
|
|
292
|
-
return
|
|
266
|
+
return { skills: Array.from(skillMap.values()), skippedSkillLinks };
|
|
293
267
|
}
|
|
294
268
|
function buildRuntimePaths(runtimeWorkspaceRoot, address) {
|
|
295
269
|
const workspaceRoot = runtimeWorkspaceRoot.replace(/\/+$/, "") || "/";
|
|
@@ -344,7 +318,7 @@ export function resolveTriggerAttribution(message) {
|
|
|
344
318
|
return `@${message.userName}`;
|
|
345
319
|
return undefined;
|
|
346
320
|
}
|
|
347
|
-
function buildSystemPrompt(workspacePath, address, conversationKind, currentUserId, memory, sandboxConfig, platform, skills, projection) {
|
|
321
|
+
function buildSystemPrompt(workspacePath, address, conversationKind, currentUserId, memory, sandboxConfig, platform, skills, projection, skippedSkillLinks = []) {
|
|
348
322
|
const { workspaceRoot, conversationPath, scratchPath } = buildRuntimePaths(workspacePath, address);
|
|
349
323
|
const sandboxType = sandboxConfig.type;
|
|
350
324
|
const isContainerLike = sandboxType === "container" || sandboxType === "image";
|
|
@@ -450,7 +424,9 @@ Scripts are in: {baseDir}/
|
|
|
450
424
|
\`name\` and \`description\` are required. Use \`{baseDir}\` as placeholder for the skill's directory path.
|
|
451
425
|
|
|
452
426
|
### Available Skills
|
|
453
|
-
${skills.length > 0 ? formatSkillsForPrompt(skills) : "(no skills installed yet)"}
|
|
427
|
+
${skills.length > 0 ? formatSkillsForPrompt(skills) : "(no skills installed yet)"}${skippedSkillLinks.length > 0
|
|
428
|
+
? `\n\nNote: these skill entries were skipped because they are symlinks, which the host never follows when reading this conversation's skills: ${skippedSkillLinks.join(", ")}. Replace each with a real file or directory (for example \`cp -rL\`) to load it.`
|
|
429
|
+
: ""}
|
|
454
430
|
|
|
455
431
|
## Events
|
|
456
432
|
Use the \`event\` tool to schedule immediate, one-shot, or periodic follow-ups. It writes to the host-side mikan control plane and fills routing fields for the current conversation automatically.
|
|
@@ -1109,9 +1085,10 @@ async function prepareRunContext(params) {
|
|
|
1109
1085
|
reloadSessionMessages(session, conversationId);
|
|
1110
1086
|
const projection = resolveWorkspaceProjection(join(conversationDir, ".."), message.address);
|
|
1111
1087
|
const memory = await getMemory(projection);
|
|
1112
|
-
const
|
|
1088
|
+
const conversationSkillLoad = loadMikanSkills(message.address, conversationDir, pathContext.runtimeWorkspaceRoot, projection);
|
|
1089
|
+
const skills = mergeExtensionSkills(conversationSkillLoad.skills, params.extensionSkills ?? []);
|
|
1113
1090
|
const triggerAttribution = resolveTriggerAttribution(message);
|
|
1114
|
-
const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, message.address, message.conversationKind, message.userId, memory, executor.getSandboxConfig(), platform, skills, projection);
|
|
1091
|
+
const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, message.address, message.conversationKind, message.userId, memory, executor.getSandboxConfig(), platform, skills, projection, conversationSkillLoad.skippedSkillLinks);
|
|
1115
1092
|
session.agent.state.systemPrompt = systemPrompt;
|
|
1116
1093
|
// Cache diagnosis: a byte-stable system prompt is the precondition for
|
|
1117
1094
|
// provider prompt caching. If this hash changes between turns of one
|
|
@@ -1545,7 +1522,7 @@ export async function createRunner(options) {
|
|
|
1545
1522
|
});
|
|
1546
1523
|
// Initial system prompt (will be updated each run with fresh memory/channels/users/skills)
|
|
1547
1524
|
const memory = await getMemory(projection);
|
|
1548
|
-
const skills = loadMikanSkills(options.address, conversationDir, pathContext.runtimeWorkspaceRoot, projection);
|
|
1525
|
+
const { skills, skippedSkillLinks } = loadMikanSkills(options.address, conversationDir, pathContext.runtimeWorkspaceRoot, projection);
|
|
1549
1526
|
const emptyPlatform = {
|
|
1550
1527
|
name: "chat",
|
|
1551
1528
|
formattingGuide: "",
|
|
@@ -1553,7 +1530,7 @@ export async function createRunner(options) {
|
|
|
1553
1530
|
users: [],
|
|
1554
1531
|
trustModel: "membership",
|
|
1555
1532
|
};
|
|
1556
|
-
const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, options.address, "shared", undefined, memory, sandboxConfig, emptyPlatform, skills, projection);
|
|
1533
|
+
const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, options.address, "shared", undefined, memory, sandboxConfig, emptyPlatform, skills, projection, skippedSkillLinks);
|
|
1557
1534
|
// Create session manager and settings manager. Top-level/private sessions
|
|
1558
1535
|
// use the conversation's current pointer; scoped sessions use fixed files.
|
|
1559
1536
|
// Platform-specific scope behavior is resolved before runner creation.
|