@geminixiang/mikan 1.0.0-beta.36 → 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 +14 -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/commands/sandbox.js +1 -1
- package/dist/commands/sandbox.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/dist/provisioner.d.ts +18 -0
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +77 -11
- package/dist/provisioner.js.map +1 -1
- package/dist/web/admin/portal.js +2 -2
- package/dist/web/admin/portal.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,20 @@ 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
|
+
|
|
18
|
+
## [1.0.0-beta.37]
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Stop discarding preserved container contents on mount drift: a drifted managed container is now committed and re-created from its snapshot with the desired mounts — writable layer intact — instead of being rebuilt from the base image. This closes the gap that recreated layout-migrated containers from scratch on their first message after upgrade (stored mount signatures went stale as shared sources changed overnight). `MIKAN_SKIP_CONTAINER_PRESERVATION=1` restores plain recreation.
|
|
23
|
+
- Stop treating ordinary directory activity as mount drift: directory mount sources are now fingerprinted by identity (a replacement directory is drift) instead of size and mtime, so event-file churn and children being created no longer force container rebuilds. File sources keep content fingerprints — atomic replacement leaves a stale inode in the container and must recreate it.
|
|
24
|
+
- Door-policy changes now preserve container contents through the rebuild; the portal and `/pi-sandbox door` copy no longer warn about a reset.
|
|
25
|
+
|
|
12
26
|
## [1.0.0-beta.36]
|
|
13
27
|
|
|
14
28
|
### Added
|
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.
|