@geminixiang/mikan 0.5.3 → 1.0.0-beta.1
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 +27 -0
- package/README.md +2 -2
- package/dist/adapter.d.ts +1 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/agent.d.ts +10 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +153 -95
- package/dist/agent.js.map +1 -1
- package/dist/commands/model.d.ts +2 -2
- package/dist/commands/model.d.ts.map +1 -1
- package/dist/commands/model.js.map +1 -1
- package/dist/commands/registry.d.ts.map +1 -1
- package/dist/commands/registry.js +2 -5
- package/dist/commands/registry.js.map +1 -1
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +83 -5
- package/dist/config.js.map +1 -1
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +2 -3
- package/dist/execution-resolver.js.map +1 -1
- package/dist/harness/auth.d.ts +16 -0
- package/dist/harness/auth.d.ts.map +1 -0
- package/dist/harness/auth.js +86 -0
- package/dist/harness/auth.js.map +1 -0
- package/dist/harness/extensions/loader.d.ts +43 -0
- package/dist/harness/extensions/loader.d.ts.map +1 -0
- package/dist/harness/extensions/loader.js +325 -0
- package/dist/harness/extensions/loader.js.map +1 -0
- package/dist/harness/extensions/registry.d.ts +23 -0
- package/dist/harness/extensions/registry.d.ts.map +1 -0
- package/dist/harness/extensions/registry.js +45 -0
- package/dist/harness/extensions/registry.js.map +1 -0
- package/dist/harness/extensions/types.d.ts +225 -0
- package/dist/harness/extensions/types.d.ts.map +1 -0
- package/dist/harness/extensions/types.js +2 -0
- package/dist/harness/extensions/types.js.map +1 -0
- package/dist/harness/http.d.ts +16 -0
- package/dist/harness/http.d.ts.map +1 -0
- package/dist/harness/http.js +71 -0
- package/dist/harness/http.js.map +1 -0
- package/dist/harness/index.d.ts +19 -0
- package/dist/harness/index.d.ts.map +1 -0
- package/dist/harness/index.js +18 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/models.d.ts +34 -0
- package/dist/harness/models.d.ts.map +1 -0
- package/dist/harness/models.js +248 -0
- package/dist/harness/models.js.map +1 -0
- package/dist/harness/runner.d.ts +124 -0
- package/dist/harness/runner.d.ts.map +1 -0
- package/dist/harness/runner.js +436 -0
- package/dist/harness/runner.js.map +1 -0
- package/dist/harness/session-store.d.ts +77 -0
- package/dist/harness/session-store.d.ts.map +1 -0
- package/dist/harness/session-store.js +248 -0
- package/dist/harness/session-store.js.map +1 -0
- package/dist/harness/settings.d.ts +47 -0
- package/dist/harness/settings.d.ts.map +1 -0
- package/dist/harness/settings.js +29 -0
- package/dist/harness/settings.js.map +1 -0
- package/dist/harness/skills.d.ts +54 -0
- package/dist/harness/skills.d.ts.map +1 -0
- package/dist/harness/skills.js +203 -0
- package/dist/harness/skills.js.map +1 -0
- package/dist/harness/types.d.ts +32 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +2 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/main.js +65 -3
- package/dist/main.js.map +1 -1
- package/dist/model-registry.d.ts +2 -2
- package/dist/model-registry.d.ts.map +1 -1
- package/dist/model-registry.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 +3 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/types.js.map +1 -1
- package/dist/sessions/agent-memory-file-manager.d.ts.map +1 -1
- package/dist/sessions/agent-memory-file-manager.js +8 -8
- package/dist/sessions/agent-memory-file-manager.js.map +1 -1
- package/dist/sessions/metadata.js +2 -2
- package/dist/sessions/metadata.js.map +1 -1
- package/dist/sessions/store.d.ts +3 -3
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +4 -4
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +1 -1
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/sessions/types.js.map +1 -1
- package/dist/trigger.d.ts.map +1 -1
- package/dist/trigger.js +5 -13
- package/dist/trigger.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vault/index.d.ts.map +1 -1
- package/dist/vault/index.js +7 -1
- package/dist/vault/index.js.map +1 -1
- package/dist/web/admin/portal.d.ts.map +1 -1
- package/dist/web/admin/portal.js +328 -9
- package/dist/web/admin/portal.js.map +1 -1
- package/dist/web/admin/provider-models.d.ts +2 -2
- package/dist/web/admin/provider-models.d.ts.map +1 -1
- package/dist/web/admin/provider-models.js.map +1 -1
- package/dist/web/session-view/service.js +5 -5
- package/dist/web/session-view/service.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,33 @@ any release.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [1.0.0-beta.1]
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Accept `maxOutputTokens` as an alias for `maxTokens` in custom model configuration.
|
|
17
|
+
- Restore npm release automation with compatible Node.js and npm versions.
|
|
18
|
+
|
|
19
|
+
### Tests
|
|
20
|
+
|
|
21
|
+
- Add coverage for production-shaped custom model configuration.
|
|
22
|
+
|
|
23
|
+
## [1.0.0-beta.0]
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Add mikan's native agent harness with model, authentication, session, compaction, and skill support.
|
|
28
|
+
- Add an extension system for hooks, schedules, notifications, data directories, secrets, and bundled skills, with an agent project manager example.
|
|
29
|
+
- Add per-conversation token usage bar charts to the admin portal.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- Replace the pi-coding-agent runtime and session integration with the native harness built on pi-agent-core and pi-ai.
|
|
34
|
+
|
|
35
|
+
### Tests
|
|
36
|
+
|
|
37
|
+
- Add coverage for harness authentication, extensions, HTTP helpers, runner, sessions, and skills.
|
|
38
|
+
|
|
12
39
|
## [0.5.3] - 2026-06-30
|
|
13
40
|
|
|
14
41
|
### Added
|
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@ mikan keeps the chat record, agent session, and execution runtime separate:
|
|
|
16
16
|

|
|
17
17
|
|
|
18
18
|
- **Chat / conversation data** is the platform-facing record: `log.jsonl`, attachments, and conversation files.
|
|
19
|
-
- **Session orchestration** turns platform events into agent runs, handles top-level/thread scopes, and persists
|
|
20
|
-
- **
|
|
19
|
+
- **Session orchestration** turns platform events into agent runs, handles top-level/thread scopes, and persists structured context under `sessions/*.jsonl`.
|
|
20
|
+
- **mikan agent harness** (`src/harness/`, built on pi-agent-core and pi-ai) runs the model loop, session persistence, compaction, and calls mikan tools.
|
|
21
21
|
- **Sandbox runtime** is where tool commands execute: host, Docker container/image, Firecracker, or Cloudflare bridge.
|
|
22
22
|
- **Vault** provides runtime credentials as env vars and mounted secret files.
|
|
23
23
|
|
package/dist/adapter.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { MessagingBot, ConversationContext, ConversationEvent, MessagingEventHandler, ChatAdapter, ConversationMessage, ConversationResponder, ChatToolResult, ConversationKind, MessagingInfo, PlatformName, RunningSession, } from "./types.js";
|
|
1
|
+
export type { MessagingBot, ConversationContext, ConversationEvent, MessagingEventHandler, ChatAdapter, ConversationMessage, ConversationResponder, ChatToolResult, ConversationKind, MessagingInfo, PlatformName, PlatformNotifier, RunningSession, } from "./types.js";
|
|
2
2
|
//# sourceMappingURL=adapter.d.ts.map
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,GACf,MAAM,YAAY,CAAC"}
|
package/dist/adapter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"","sourcesContent":["export type {\n MessagingBot,\n ConversationContext,\n ConversationEvent,\n MessagingEventHandler,\n ChatAdapter,\n ConversationMessage,\n ConversationResponder,\n ChatToolResult,\n ConversationKind,\n MessagingInfo,\n PlatformName,\n RunningSession,\n} from \"./types.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"","sourcesContent":["export type {\n MessagingBot,\n ConversationContext,\n ConversationEvent,\n MessagingEventHandler,\n ChatAdapter,\n ConversationMessage,\n ConversationResponder,\n ChatToolResult,\n ConversationKind,\n MessagingInfo,\n PlatformName,\n PlatformNotifier,\n RunningSession,\n} from \"./types.js\";\n"]}
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ImageContent } from "@earendil-works/pi-ai";
|
|
2
2
|
import type { ConversationMessage } from "./adapter.js";
|
|
3
|
+
import type { PlatformNotifier } from "./types.js";
|
|
3
4
|
import type { SessionViewTokenStoreLike } from "./commands/types.js";
|
|
4
5
|
import type { DockerContainerManager } from "./provisioner.js";
|
|
5
6
|
import { type RuntimePathContext, type SandboxConfig } from "./sandbox/index.js";
|
|
@@ -8,6 +9,14 @@ import { type ResolvedSessionScope } from "./sessions/store.js";
|
|
|
8
9
|
export type { PiAgentWrapper } from "./types.js";
|
|
9
10
|
import type { PiAgentWrapper } from "./types.js";
|
|
10
11
|
export declare function resolveTriggerAttribution(message: Pick<ConversationMessage, "id" | "text" | "userName">): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Instructions that vary per turn (event-trigger mode, response attribution).
|
|
14
|
+
* These are delivered with the user message rather than baked into the system
|
|
15
|
+
* prompt, so the system prompt stays cache-stable across a conversation's turns
|
|
16
|
+
* (in a multi-user channel the attribution line alone changes every turn).
|
|
17
|
+
* Returns an empty string when the turn needs no special framing.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildTurnInstructions(isEventTrigger: boolean, triggerAttribution: string | undefined, platformName: string): string;
|
|
11
20
|
export declare function getUnresolvedSandboxPathContext(sandboxConfig: SandboxConfig, hostWorkspaceRoot: string): RuntimePathContext;
|
|
12
21
|
export declare function buildPromptPayload(message: ConversationMessage, workspacePath: string, pathContext?: RuntimePathContext): {
|
|
13
22
|
userMessage: string;
|
|
@@ -24,7 +33,7 @@ export declare function appendTriggerAttribution(text: string, triggerAttributio
|
|
|
24
33
|
export declare function createRunner(sandboxConfig: SandboxConfig, sessionKey: string, conversationId: string, conversationDir: string, workspaceDir: string, sessionScope: ResolvedSessionScope, vaultManager?: VaultManager, provisioner?: DockerContainerManager, sessionView?: {
|
|
25
34
|
tokenStore: SessionViewTokenStoreLike;
|
|
26
35
|
portalBaseUrl?: string;
|
|
27
|
-
}): Promise<PiAgentWrapper>;
|
|
36
|
+
}, platformNotifier?: PlatformNotifier): Promise<PiAgentWrapper>;
|
|
28
37
|
export declare function translateRuntimePathToHost(runtimePath: string, pathContext: RuntimePathContext): string;
|
|
29
38
|
export declare function translateAttachPathToHost(filePath: string, pathContext: RuntimePathContext): string;
|
|
30
39
|
//# sourceMappingURL=agent.d.ts.map
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,YAAY,EAAc,MAAM,uBAAuB,CAAC;AAkBhF,OAAO,KAAK,EACV,mBAAmB,EAKpB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,KAAK,YAAY,EAAc,MAAM,uBAAuB,CAAC;AAkBhF,OAAO,KAAK,EACV,mBAAmB,EAKpB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAiC,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAMrE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAO5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAGL,KAAK,oBAAoB,EAE1B,MAAM,qBAAqB,CAAC;AAQ7B,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAyIjD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,IAAI,GAAG,MAAM,GAAG,UAAU,CAAC,GAC7D,MAAM,GAAG,SAAS,CAOpB;AA6KD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,OAAO,EACvB,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,YAAY,EAAE,MAAM,GACnB,MAAM,CAkBR;AAED,wBAAgB,+BAA+B,CAC7C,aAAa,EAAE,aAAa,EAC5B,iBAAiB,EAAE,MAAM,GACxB,kBAAkB,CAMpB;AAwUD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,mBAAmB,EAC5B,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,kBAAkB,GAC/B;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,YAAY,EAAE,CAAC;CAClC,CAaA;AA+BD,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAYR;AAmsBD;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,oBAAoB,EAClC,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,sBAAsB,EACpC,WAAW,CAAC,EAAE;IACZ,UAAU,EAAE,yBAAyB,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,EACD,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,OAAO,CAAC,cAAc,CAAC,CAyNzB;AAED,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,kBAAkB,GAC9B,MAAM,CAER;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,kBAAkB,GAC9B,MAAM,CAKR"}
|
package/dist/agent.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { DEFAULT_EVENT_BUDGET, defaultExtensionDirs, formatSkillsForPrompt, loadExtensions, loadSkillsFromDir, MikanAgentSession, MikanModels, } from "./harness/index.js";
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
3
|
import { existsSync, readFileSync } from "fs";
|
|
4
4
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
5
|
-
import { homedir } from "os";
|
|
6
5
|
import { join, posix } from "path";
|
|
7
6
|
import { resolveConversationSettings } from "./config.js";
|
|
7
|
+
import { readEnv } from "./utils/env.js";
|
|
8
8
|
import { ActorExecutionResolver } from "./execution-resolver.js";
|
|
9
9
|
import * as log from "./log.js";
|
|
10
10
|
import { reportUserFacingError } from "./observability/sentry.js";
|
|
@@ -13,6 +13,7 @@ import { createMountedRuntimePathContext } from "./sandbox/path-context.js";
|
|
|
13
13
|
import { addLifecycleBreadcrumb, metricAttributes, updateActiveSpanAttribution, } from "./observability/sentry.js";
|
|
14
14
|
import { AgentMemoryFileManager } from "./sessions/agent-memory-file-manager.js";
|
|
15
15
|
import { extractSessionUuid, openManagedSession, } from "./sessions/store.js";
|
|
16
|
+
import { HostEventStore } from "./tools/event.js";
|
|
16
17
|
import { createMikanTools } from "./tools/index.js";
|
|
17
18
|
import * as Sentry from "@sentry/node";
|
|
18
19
|
import { formatLocalTimestamp } from "./utils/date.js";
|
|
@@ -151,7 +152,7 @@ export function resolveTriggerAttribution(message) {
|
|
|
151
152
|
return `@${message.userName}`;
|
|
152
153
|
return undefined;
|
|
153
154
|
}
|
|
154
|
-
function buildSystemPrompt(workspacePath, conversationId, conversationKind, currentUserId, memory, sandboxConfig, platform, skills
|
|
155
|
+
function buildSystemPrompt(workspacePath, conversationId, conversationKind, currentUserId, memory, sandboxConfig, platform, skills) {
|
|
155
156
|
const { workspaceRoot, conversationPath, scratchPath } = buildRuntimePaths(workspacePath, conversationId);
|
|
156
157
|
const sandboxType = sandboxConfig.type;
|
|
157
158
|
const isContainerLike = sandboxType === "container" || sandboxType === "image";
|
|
@@ -165,25 +166,11 @@ function buildSystemPrompt(workspacePath, conversationId, conversationKind, curr
|
|
|
165
166
|
? platform.users.map((u) => `${u.id}\t@${u.userName}\t${u.displayName}`).join("\n")
|
|
166
167
|
: "(no users loaded)";
|
|
167
168
|
const envDescription = buildEnvDescription(sandboxType, workspaceRoot);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
- Complete the task directly. Avoid generic greetings, self-introductions, or boilerplate offers to help.
|
|
174
|
-
- For reminders/follow-ups, prefer a short direct response that sounds like a continuation of prior intent.
|
|
175
|
-
- If the event text includes tone, brevity, or language instructions, follow them literally.
|
|
176
|
-
`
|
|
177
|
-
: "";
|
|
178
|
-
const attributionInstructions = triggerAttribution
|
|
179
|
-
? `
|
|
180
|
-
## Attribution
|
|
181
|
-
Always end your final ${platform.name} response and any GitHub issue/PR comments or descriptions you write via tools with:
|
|
182
|
-
_Triggered by ${triggerAttribution}_
|
|
183
|
-
|
|
184
|
-
Do not add this to \`[SILENT]\` responses.
|
|
185
|
-
`
|
|
186
|
-
: "";
|
|
169
|
+
// Per-turn instructions (event-trigger mode, attribution) are delivered with
|
|
170
|
+
// the user message via buildTurnInstructions(), not baked in here, so this
|
|
171
|
+
// system prompt stays byte-stable across a conversation's turns and keeps the
|
|
172
|
+
// provider prompt cache warm (a changing system prefix invalidates the cache
|
|
173
|
+
// for the whole request, including the far larger conversation history).
|
|
187
174
|
const slackBlockKitInstructions = platform.name === "slack"
|
|
188
175
|
? `
|
|
189
176
|
## Slack Rendering
|
|
@@ -202,7 +189,6 @@ Do not add this to \`[SILENT]\` responses.
|
|
|
202
189
|
- Scoped/thread sessions use fixed files at \`${conversationPath}/sessions/<scope_id>.jsonl\` (for example \`${conversationPath}/sessions/1777386320.800769.jsonl\`).
|
|
203
190
|
- If a user asks about something that should exist in conversation history but is not found in the current context window, do not answer "I don't know" or "I don't have that". Instead, search the thread session, top-level session, and \`log.jsonl\` before responding.
|
|
204
191
|
- User messages include a \`[in-thread:TS]\` marker when sent from within a platform thread/reply (TS is the thread or parent message identifier). Without this marker, the message is a top-level conversation message.
|
|
205
|
-
${eventTriggerInstructions}
|
|
206
192
|
${platform.formattingGuide}${slackBlockKitInstructions}
|
|
207
193
|
|
|
208
194
|
## Platform IDs
|
|
@@ -290,7 +276,6 @@ Format: \`{"date":"...","ts":"...","user":"...","userName":"...","text":"...","i
|
|
|
290
276
|
The log contains user messages and your final responses (not tool calls/results).
|
|
291
277
|
Use \`log.jsonl\` for quick grep-style history. Use \`${conversationPath}/sessions/\` when you need structured turns, tool outputs, or thread/session lineage.
|
|
292
278
|
${isContainerLike || isFirecracker ? "Install jq: apt-get install jq" : ""}
|
|
293
|
-
${attributionInstructions}
|
|
294
279
|
\`\`\`bash
|
|
295
280
|
# Recent messages
|
|
296
281
|
tail -30 log.jsonl | jq -c '{date: .date[0:19], user: (.userName // .user), text}'
|
|
@@ -318,6 +303,32 @@ ls -1 sessions/
|
|
|
318
303
|
Each tool requires a "label" parameter (shown to user).
|
|
319
304
|
`;
|
|
320
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Instructions that vary per turn (event-trigger mode, response attribution).
|
|
308
|
+
* These are delivered with the user message rather than baked into the system
|
|
309
|
+
* prompt, so the system prompt stays cache-stable across a conversation's turns
|
|
310
|
+
* (in a multi-user channel the attribution line alone changes every turn).
|
|
311
|
+
* Returns an empty string when the turn needs no special framing.
|
|
312
|
+
*/
|
|
313
|
+
export function buildTurnInstructions(isEventTrigger, triggerAttribution, platformName) {
|
|
314
|
+
const parts = [];
|
|
315
|
+
if (isEventTrigger) {
|
|
316
|
+
parts.push(`## Event Trigger Mode
|
|
317
|
+
- You are handling a scheduled/background event, not opening a brand new chat with a stranger.
|
|
318
|
+
- Treat the incoming user message as a self-contained task prepared by an earlier run.
|
|
319
|
+
- Complete the task directly. Avoid generic greetings, self-introductions, or boilerplate offers to help.
|
|
320
|
+
- For reminders/follow-ups, prefer a short direct response that sounds like a continuation of prior intent.
|
|
321
|
+
- If the event text includes tone, brevity, or language instructions, follow them literally.`);
|
|
322
|
+
}
|
|
323
|
+
if (triggerAttribution) {
|
|
324
|
+
parts.push(`## Attribution
|
|
325
|
+
Always end your final ${platformName} response and any GitHub issue/PR comments or descriptions you write via tools with:
|
|
326
|
+
_Triggered by ${triggerAttribution}_
|
|
327
|
+
|
|
328
|
+
Do not add this to \`[SILENT]\` responses.`);
|
|
329
|
+
}
|
|
330
|
+
return parts.join("\n\n");
|
|
331
|
+
}
|
|
321
332
|
export function getUnresolvedSandboxPathContext(sandboxConfig, hostWorkspaceRoot) {
|
|
322
333
|
if (sandboxConfig.type === "image") {
|
|
323
334
|
return createMountedRuntimePathContext(hostWorkspaceRoot, "/workspace");
|
|
@@ -358,58 +369,81 @@ function createRunnerExecutionContext(sandboxConfig, vaultManager, provisioner,
|
|
|
358
369
|
},
|
|
359
370
|
};
|
|
360
371
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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 } = 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
|
+
})),
|
|
369
394
|
},
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
throw new Error(`No API key for provider "${provider}". Set the appropriate environment variable or configure via auth.json`);
|
|
395
|
+
...(platformNotifier ? { postMessage: platformNotifier } : {}),
|
|
396
|
+
...(vaultManager
|
|
397
|
+
? {
|
|
398
|
+
resolveSecrets: (slug) => vaultManager.resolve(`extensions/${slug}`)?.env ?? {},
|
|
375
399
|
}
|
|
376
|
-
|
|
377
|
-
|
|
400
|
+
: {}),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Local (workspace/conversation) skills override extension skills on name
|
|
405
|
+
* collision: an admin's on-disk skill always beats an extension's.
|
|
406
|
+
*/
|
|
407
|
+
function mergeExtensionSkills(local, extension) {
|
|
408
|
+
if (extension.length === 0)
|
|
409
|
+
return local;
|
|
410
|
+
const byName = new Map();
|
|
411
|
+
for (const skill of extension)
|
|
412
|
+
byName.set(skill.name, skill);
|
|
413
|
+
for (const skill of local)
|
|
414
|
+
byName.set(skill.name, skill);
|
|
415
|
+
return [...byName.values()];
|
|
416
|
+
}
|
|
417
|
+
async function createConfiguredAgentSession(params) {
|
|
418
|
+
const { conversationId, workspaceDir, systemPrompt, model, thinkingLevel, tools, sessionStore, models, vaultManager, platformNotifier, } = params;
|
|
419
|
+
// Host-only dirs under the state dir: extension code runs in the mikan
|
|
420
|
+
// process, so it must never load from workspace paths — those are mounted
|
|
421
|
+
// into sandbox containers and agent-writable (sandbox escape otherwise).
|
|
422
|
+
const extensionsResult = await loadExtensions({
|
|
423
|
+
dirs: defaultExtensionDirs(conversationId, readEnv("STATE_DIR")),
|
|
424
|
+
context: { conversationId, workspaceDir, model, thinkingLevel },
|
|
425
|
+
services: buildExtensionHostServices({ workspaceDir, vaultManager, platformNotifier }),
|
|
378
426
|
});
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
agent.state.messages = loadedSession.messages;
|
|
382
|
-
log.logInfo(`[${conversationId}] Reloaded ${loadedSession.messages.length} messages from session context`);
|
|
427
|
+
for (const err of extensionsResult.errors) {
|
|
428
|
+
log.logWarning(`[${conversationId}] Extension load error: ${err.path}`, err.error);
|
|
383
429
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
430
|
+
if (extensionsResult.extensions.length > 0) {
|
|
431
|
+
log.logInfo(`[${conversationId}] Loaded ${extensionsResult.extensions.length} extension(s): ${extensionsResult.extensions.map((extension) => extension.name).join(", ")}`);
|
|
432
|
+
}
|
|
433
|
+
const session = new MikanAgentSession({
|
|
387
434
|
systemPrompt,
|
|
435
|
+
model,
|
|
436
|
+
thinkingLevel,
|
|
437
|
+
tools,
|
|
438
|
+
models,
|
|
439
|
+
sessionStore,
|
|
440
|
+
extensions: extensionsResult.registry,
|
|
388
441
|
});
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (extResult.errors.length > 0) {
|
|
393
|
-
for (const err of extResult.errors) {
|
|
394
|
-
log.logWarning(`[${conversationId}] Extension load error: ${err.path}`, err.error);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
log.logInfo(`[${conversationId}] Loaded ${extResult.extensions.length} extension(s): ${extResult.extensions.map((extension) => extension.path).join(", ")}`);
|
|
398
|
-
}
|
|
399
|
-
catch (error) {
|
|
400
|
-
log.logWarning(`[${conversationId}] Failed to load resources`, String(error));
|
|
442
|
+
const reloaded = session.reloadFromSession();
|
|
443
|
+
if (reloaded > 0) {
|
|
444
|
+
log.logInfo(`[${conversationId}] Reloaded ${reloaded} messages from session context`);
|
|
401
445
|
}
|
|
402
|
-
|
|
403
|
-
const session = new AgentSession({
|
|
404
|
-
agent,
|
|
405
|
-
sessionManager,
|
|
406
|
-
settingsManager,
|
|
407
|
-
cwd: runtimeWorkspaceRoot,
|
|
408
|
-
modelRegistry,
|
|
409
|
-
resourceLoader,
|
|
410
|
-
baseToolsOverride,
|
|
411
|
-
});
|
|
412
|
-
return { agent, session };
|
|
446
|
+
return { session, extensionSkills: extensionsResult.skills };
|
|
413
447
|
}
|
|
414
448
|
function createEmptyUsageTotals() {
|
|
415
449
|
return {
|
|
@@ -705,15 +739,14 @@ async function reportUsageSummary(ctx) {
|
|
|
705
739
|
await waitForQueue();
|
|
706
740
|
}
|
|
707
741
|
}
|
|
708
|
-
function reloadSessionMessages(
|
|
709
|
-
const
|
|
710
|
-
if (
|
|
711
|
-
|
|
712
|
-
log.logInfo(`[${conversationId}] Reloaded ${messages.length} messages from context`);
|
|
742
|
+
function reloadSessionMessages(session, conversationId) {
|
|
743
|
+
const reloaded = session.reloadFromSession();
|
|
744
|
+
if (reloaded > 0) {
|
|
745
|
+
log.logInfo(`[${conversationId}] Reloaded ${reloaded} messages from context`);
|
|
713
746
|
}
|
|
714
747
|
}
|
|
715
748
|
async function prepareRunContext(params) {
|
|
716
|
-
const { message, responder, platform, conversationId, conversationDir, sessionUuid, runState, executor, executionResolver, resolveExecutorForRun, getPathContext,
|
|
749
|
+
const { message, responder, platform, conversationId, conversationDir, sessionUuid, runState, executor, executionResolver, resolveExecutorForRun, getPathContext, session, setEventContext, setSandboxContext, setUploadFunction, } = params;
|
|
717
750
|
let pathContext = params.pathContext;
|
|
718
751
|
const sessionConversation = message.sessionKey.split(":")[0];
|
|
719
752
|
await mkdir(join(conversationDir, "scratch"), { recursive: true });
|
|
@@ -725,12 +758,19 @@ async function prepareRunContext(params) {
|
|
|
725
758
|
});
|
|
726
759
|
pathContext = getPathContext();
|
|
727
760
|
}
|
|
728
|
-
reloadSessionMessages(
|
|
761
|
+
reloadSessionMessages(session, conversationId);
|
|
729
762
|
const memory = await getMemory(conversationDir);
|
|
730
|
-
const skills = loadMikanSkills(conversationDir, pathContext.runtimeWorkspaceRoot);
|
|
763
|
+
const skills = mergeExtensionSkills(loadMikanSkills(conversationDir, pathContext.runtimeWorkspaceRoot), params.extensionSkills ?? []);
|
|
731
764
|
const triggerAttribution = resolveTriggerAttribution(message);
|
|
732
|
-
const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, conversationId, message.conversationKind, message.userId, memory, executor.getSandboxConfig(), platform, skills
|
|
765
|
+
const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, conversationId, message.conversationKind, message.userId, memory, executor.getSandboxConfig(), platform, skills);
|
|
733
766
|
session.agent.state.systemPrompt = systemPrompt;
|
|
767
|
+
// Cache diagnosis: a byte-stable system prompt is the precondition for
|
|
768
|
+
// provider prompt caching. If this hash changes between turns of one
|
|
769
|
+
// conversation, something turn-varying leaked into the prompt; if it is
|
|
770
|
+
// stable and cacheRead stays 0, the miss is provider-side (e.g. OpenRouter
|
|
771
|
+
// routing the model across upstream hosts).
|
|
772
|
+
const promptHash = createHash("sha256").update(systemPrompt).digest("hex").slice(0, 8);
|
|
773
|
+
log.logInfo(`[${conversationId}] System prompt: ${systemPrompt.length} chars, sha ${promptHash}`);
|
|
734
774
|
setEventContext({
|
|
735
775
|
platform: platform.name,
|
|
736
776
|
conversationId,
|
|
@@ -748,11 +788,13 @@ async function prepareRunContext(params) {
|
|
|
748
788
|
log.logInfo(`Context sizes - system: ${systemPrompt.length} chars, memory: ${memory.length} chars`);
|
|
749
789
|
log.logInfo(`Channels: ${platform.channels.length}, Users: ${platform.users.length}`);
|
|
750
790
|
const { userMessage, imageAttachments } = buildPromptPayload(message, pathContext.runtimeWorkspaceRoot, pathContext);
|
|
751
|
-
|
|
791
|
+
const turnInstructions = buildTurnInstructions(message.id.startsWith("event:"), triggerAttribution, platform.name);
|
|
792
|
+
const finalUserMessage = turnInstructions ? `${turnInstructions}\n\n${userMessage}` : userMessage;
|
|
793
|
+
await writePromptDebugContext(conversationDir, systemPrompt, session, finalUserMessage, imageAttachments.length);
|
|
752
794
|
return {
|
|
753
795
|
sessionConversation,
|
|
754
796
|
runQueue,
|
|
755
|
-
userMessage,
|
|
797
|
+
userMessage: finalUserMessage,
|
|
756
798
|
imageAttachments,
|
|
757
799
|
triggerAttribution,
|
|
758
800
|
pathContext,
|
|
@@ -1015,6 +1057,16 @@ function attachSessionEventHandlers(params) {
|
|
|
1015
1057
|
});
|
|
1016
1058
|
queue.enqueue(() => responder.respond(text), "retry");
|
|
1017
1059
|
}
|
|
1060
|
+
if (event.type === "budget_exceeded") {
|
|
1061
|
+
log.logWarning("Run stopped by budget circuit breaker", `${event.reason} (tokens=${event.tokens}, cost=${event.costUsd.toFixed(2)}, calls=${event.llmCalls}, ${event.durationMs}ms)`);
|
|
1062
|
+
const text = `_Stopped: run budget exceeded (${event.reason})_`;
|
|
1063
|
+
sendAgentEvent({
|
|
1064
|
+
sessionId: agentEventSessionId,
|
|
1065
|
+
actorName: formatAgentActorName(logCtx.userName, logCtx.conversationId),
|
|
1066
|
+
event: { kind: "diagnostic", text },
|
|
1067
|
+
});
|
|
1068
|
+
queue.enqueue(() => responder.respondDiagnostic(text, { style: "error" }), "budget exceeded");
|
|
1069
|
+
}
|
|
1018
1070
|
});
|
|
1019
1071
|
}
|
|
1020
1072
|
function extractToolResultText(result) {
|
|
@@ -1045,15 +1097,17 @@ function extractToolResultText(result) {
|
|
|
1045
1097
|
* Runner caching is handled by the caller (channelStates in main.ts).
|
|
1046
1098
|
* This is a stateless factory function.
|
|
1047
1099
|
*/
|
|
1048
|
-
export async function createRunner(sandboxConfig, sessionKey, conversationId, conversationDir, workspaceDir, sessionScope, vaultManager, provisioner, sessionView) {
|
|
1100
|
+
export async function createRunner(sandboxConfig, sessionKey, conversationId, conversationDir, workspaceDir, sessionScope, vaultManager, provisioner, sessionView, platformNotifier) {
|
|
1049
1101
|
const agentConfig = resolveConversationSettings(conversationDir);
|
|
1050
1102
|
const workspaceBase = join(conversationDir, "..");
|
|
1051
1103
|
const { executionResolver, executor, getPathContext, resolveExecutorForRun } = createRunnerExecutionContext(sandboxConfig, vaultManager, provisioner, workspaceDir, workspaceBase);
|
|
1052
1104
|
let pathContext = getUnresolvedSandboxPathContext(sandboxConfig, workspaceBase);
|
|
1053
1105
|
// Create tools (per-runner, with per-runner upload function setter)
|
|
1054
1106
|
const { tools, setUploadFunction, setEventContext, setSandboxContext } = createMikanTools(executor, workspaceDir, { sandbox: sandboxConfig, provisioner });
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1107
|
+
const modelRegistry = MikanModels.create();
|
|
1108
|
+
if (modelRegistry.getError()) {
|
|
1109
|
+
log.logWarning("models.json load error", modelRegistry.getError());
|
|
1110
|
+
}
|
|
1057
1111
|
const model = resolveConfiguredModel(modelRegistry, agentConfig.provider, agentConfig.model);
|
|
1058
1112
|
// Initial system prompt (will be updated each run with fresh memory/channels/users/skills)
|
|
1059
1113
|
const memory = await getMemory(conversationDir);
|
|
@@ -1069,26 +1123,25 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
|
|
|
1069
1123
|
// use the conversation's current pointer; scoped sessions use fixed files.
|
|
1070
1124
|
// Platform-specific scope behavior is resolved before runner creation.
|
|
1071
1125
|
const isThread = sessionKey.includes(":");
|
|
1072
|
-
const {
|
|
1073
|
-
const sessionManager = openManagedSession(contextFile,
|
|
1126
|
+
const { contextFile, threadRootMessage } = sessionScope;
|
|
1127
|
+
const sessionManager = openManagedSession(contextFile, pathContext.runtimeWorkspaceRoot);
|
|
1074
1128
|
const threadSessionName = buildThreadSessionName(threadRootMessage);
|
|
1075
1129
|
if (isThread && threadSessionName && sessionManager.getSessionName() !== threadSessionName) {
|
|
1076
1130
|
sessionManager.appendSessionInfo(threadSessionName);
|
|
1077
1131
|
}
|
|
1078
1132
|
const sessionUuid = extractSessionUuid(contextFile);
|
|
1079
1133
|
const chatSessionManager = new AgentMemoryFileManager();
|
|
1080
|
-
const
|
|
1081
|
-
const { agent, session } = await createConfiguredAgentSession({
|
|
1134
|
+
const { session, extensionSkills } = await createConfiguredAgentSession({
|
|
1082
1135
|
conversationId,
|
|
1083
1136
|
workspaceDir,
|
|
1084
|
-
runtimeWorkspaceRoot: pathContext.runtimeWorkspaceRoot,
|
|
1085
1137
|
systemPrompt,
|
|
1086
1138
|
model,
|
|
1087
1139
|
thinkingLevel: agentConfig.thinkingLevel,
|
|
1088
1140
|
tools,
|
|
1089
|
-
sessionManager,
|
|
1090
|
-
|
|
1091
|
-
|
|
1141
|
+
sessionStore: sessionManager,
|
|
1142
|
+
models: modelRegistry,
|
|
1143
|
+
vaultManager,
|
|
1144
|
+
platformNotifier,
|
|
1092
1145
|
});
|
|
1093
1146
|
// Mutable per-run state - event handler references this
|
|
1094
1147
|
const runState = createRunState();
|
|
@@ -1115,9 +1168,8 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
|
|
|
1115
1168
|
executionResolver,
|
|
1116
1169
|
resolveExecutorForRun,
|
|
1117
1170
|
getPathContext,
|
|
1118
|
-
sessionManager,
|
|
1119
1171
|
session,
|
|
1120
|
-
|
|
1172
|
+
extensionSkills,
|
|
1121
1173
|
setEventContext,
|
|
1122
1174
|
setSandboxContext,
|
|
1123
1175
|
setUploadFunction,
|
|
@@ -1146,7 +1198,13 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
|
|
|
1146
1198
|
actorName: formatAgentActorName(message.userName, prepared.sessionConversation),
|
|
1147
1199
|
event: { kind: "sessionStart" },
|
|
1148
1200
|
});
|
|
1149
|
-
|
|
1201
|
+
// Autonomous (event/trigger) runs get an explicit resource ceiling since
|
|
1202
|
+
// no human is watching the loop; interactive turns stay human-gated.
|
|
1203
|
+
const isEventRun = message.id.startsWith("event:");
|
|
1204
|
+
await session.prompt(prepared.userMessage, {
|
|
1205
|
+
...(prepared.imageAttachments.length > 0 ? { images: prepared.imageAttachments } : {}),
|
|
1206
|
+
...(isEventRun ? { budget: DEFAULT_EVENT_BUDGET } : {}),
|
|
1207
|
+
});
|
|
1150
1208
|
// Wait for queued messages
|
|
1151
1209
|
await prepared.runQueue.wait();
|
|
1152
1210
|
const sessionViewTokenStore = sessionView?.tokenStore;
|