@geminixiang/mikan 0.2.0
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 +324 -0
- package/LICENSE +22 -0
- package/README.md +297 -0
- package/dist/adapter.d.ts +134 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +2 -0
- package/dist/adapter.js.map +1 -0
- package/dist/adapters/discord/bot.d.ts +63 -0
- package/dist/adapters/discord/bot.d.ts.map +1 -0
- package/dist/adapters/discord/bot.js +577 -0
- package/dist/adapters/discord/bot.js.map +1 -0
- package/dist/adapters/discord/context.d.ts +9 -0
- package/dist/adapters/discord/context.d.ts.map +1 -0
- package/dist/adapters/discord/context.js +245 -0
- package/dist/adapters/discord/context.js.map +1 -0
- package/dist/adapters/discord/index.d.ts +3 -0
- package/dist/adapters/discord/index.d.ts.map +1 -0
- package/dist/adapters/discord/index.js +3 -0
- package/dist/adapters/discord/index.js.map +1 -0
- package/dist/adapters/shared.d.ts +91 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/adapters/shared.js +191 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/slack/bot.d.ts +139 -0
- package/dist/adapters/slack/bot.d.ts.map +1 -0
- package/dist/adapters/slack/bot.js +1272 -0
- package/dist/adapters/slack/bot.js.map +1 -0
- package/dist/adapters/slack/branch-manager.d.ts +28 -0
- package/dist/adapters/slack/branch-manager.d.ts.map +1 -0
- package/dist/adapters/slack/branch-manager.js +117 -0
- package/dist/adapters/slack/branch-manager.js.map +1 -0
- package/dist/adapters/slack/context.d.ts +12 -0
- package/dist/adapters/slack/context.d.ts.map +1 -0
- package/dist/adapters/slack/context.js +327 -0
- package/dist/adapters/slack/context.js.map +1 -0
- package/dist/adapters/slack/index.d.ts +3 -0
- package/dist/adapters/slack/index.d.ts.map +1 -0
- package/dist/adapters/slack/index.js +3 -0
- package/dist/adapters/slack/index.js.map +1 -0
- package/dist/adapters/slack/session.d.ts +38 -0
- package/dist/adapters/slack/session.d.ts.map +1 -0
- package/dist/adapters/slack/session.js +66 -0
- package/dist/adapters/slack/session.js.map +1 -0
- package/dist/adapters/slack/tools/attach.d.ts +12 -0
- package/dist/adapters/slack/tools/attach.d.ts.map +1 -0
- package/dist/adapters/slack/tools/attach.js +40 -0
- package/dist/adapters/slack/tools/attach.js.map +1 -0
- package/dist/adapters/telegram/bot.d.ts +51 -0
- package/dist/adapters/telegram/bot.d.ts.map +1 -0
- package/dist/adapters/telegram/bot.js +430 -0
- package/dist/adapters/telegram/bot.js.map +1 -0
- package/dist/adapters/telegram/context.d.ts +9 -0
- package/dist/adapters/telegram/context.d.ts.map +1 -0
- package/dist/adapters/telegram/context.js +190 -0
- package/dist/adapters/telegram/context.js.map +1 -0
- package/dist/adapters/telegram/html.d.ts +3 -0
- package/dist/adapters/telegram/html.d.ts.map +1 -0
- package/dist/adapters/telegram/html.js +98 -0
- package/dist/adapters/telegram/html.js.map +1 -0
- package/dist/adapters/telegram/index.d.ts +3 -0
- package/dist/adapters/telegram/index.d.ts.map +1 -0
- package/dist/adapters/telegram/index.js +3 -0
- package/dist/adapters/telegram/index.js.map +1 -0
- package/dist/agent.d.ts +36 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +1147 -0
- package/dist/agent.js.map +1 -0
- package/dist/commands/auto-reply.d.ts +5 -0
- package/dist/commands/auto-reply.d.ts.map +1 -0
- package/dist/commands/auto-reply.js +79 -0
- package/dist/commands/auto-reply.js.map +1 -0
- package/dist/commands/index.d.ts +5 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +18 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/login.d.ts +5 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +91 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/model.d.ts +14 -0
- package/dist/commands/model.d.ts.map +1 -0
- package/dist/commands/model.js +110 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/commands/new.d.ts +5 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +24 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/parse.d.ts +7 -0
- package/dist/commands/parse.d.ts.map +1 -0
- package/dist/commands/parse.js +17 -0
- package/dist/commands/parse.js.map +1 -0
- package/dist/commands/registry.d.ts +4 -0
- package/dist/commands/registry.d.ts.map +1 -0
- package/dist/commands/registry.js +9 -0
- package/dist/commands/registry.js.map +1 -0
- package/dist/commands/sandbox.d.ts +10 -0
- package/dist/commands/sandbox.d.ts.map +1 -0
- package/dist/commands/sandbox.js +83 -0
- package/dist/commands/sandbox.js.map +1 -0
- package/dist/commands/session-view.d.ts +5 -0
- package/dist/commands/session-view.d.ts.map +1 -0
- package/dist/commands/session-view.js +62 -0
- package/dist/commands/session-view.js.map +1 -0
- package/dist/commands/types.d.ts +41 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +2 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/utils.d.ts +8 -0
- package/dist/commands/utils.d.ts.map +1 -0
- package/dist/commands/utils.js +14 -0
- package/dist/commands/utils.js.map +1 -0
- package/dist/config.d.ts +59 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +370 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +17 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +24 -0
- package/dist/context.js.map +1 -0
- package/dist/conversation-history.d.ts +16 -0
- package/dist/conversation-history.d.ts.map +1 -0
- package/dist/conversation-history.js +144 -0
- package/dist/conversation-history.js.map +1 -0
- package/dist/download.d.ts +2 -0
- package/dist/download.d.ts.map +1 -0
- package/dist/download.js +89 -0
- package/dist/download.js.map +1 -0
- package/dist/env.d.ts +3 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +12 -0
- package/dist/env.js.map +1 -0
- package/dist/events.d.ts +85 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +483 -0
- package/dist/events.js.map +1 -0
- package/dist/execution-resolver.d.ts +25 -0
- package/dist/execution-resolver.d.ts.map +1 -0
- package/dist/execution-resolver.js +167 -0
- package/dist/execution-resolver.js.map +1 -0
- package/dist/file-guards.d.ts +9 -0
- package/dist/file-guards.d.ts.map +1 -0
- package/dist/file-guards.js +56 -0
- package/dist/file-guards.js.map +1 -0
- package/dist/fs-atomic.d.ts +10 -0
- package/dist/fs-atomic.d.ts.map +1 -0
- package/dist/fs-atomic.js +45 -0
- package/dist/fs-atomic.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/instrument.d.ts +2 -0
- package/dist/instrument.d.ts.map +1 -0
- package/dist/instrument.js +10 -0
- package/dist/instrument.js.map +1 -0
- package/dist/log.d.ts +36 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +206 -0
- package/dist/log.js.map +1 -0
- package/dist/login/index.d.ts +42 -0
- package/dist/login/index.d.ts.map +1 -0
- package/dist/login/index.js +239 -0
- package/dist/login/index.js.map +1 -0
- package/dist/login/portal.d.ts +19 -0
- package/dist/login/portal.d.ts.map +1 -0
- package/dist/login/portal.js +1544 -0
- package/dist/login/portal.js.map +1 -0
- package/dist/login/session.d.ts +26 -0
- package/dist/login/session.d.ts.map +1 -0
- package/dist/login/session.js +56 -0
- package/dist/login/session.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +366 -0
- package/dist/main.js.map +1 -0
- package/dist/provisioner.d.ts +83 -0
- package/dist/provisioner.d.ts.map +1 -0
- package/dist/provisioner.js +500 -0
- package/dist/provisioner.js.map +1 -0
- package/dist/runtime/conversation-orchestrator.d.ts +40 -0
- package/dist/runtime/conversation-orchestrator.d.ts.map +1 -0
- package/dist/runtime/conversation-orchestrator.js +183 -0
- package/dist/runtime/conversation-orchestrator.js.map +1 -0
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/session-runtime.d.ts +26 -0
- package/dist/runtime/session-runtime.d.ts.map +1 -0
- package/dist/runtime/session-runtime.js +221 -0
- package/dist/runtime/session-runtime.js.map +1 -0
- package/dist/sandbox/cloudflare.d.ts +15 -0
- package/dist/sandbox/cloudflare.d.ts.map +1 -0
- package/dist/sandbox/cloudflare.js +138 -0
- package/dist/sandbox/cloudflare.js.map +1 -0
- package/dist/sandbox/container.d.ts +16 -0
- package/dist/sandbox/container.d.ts.map +1 -0
- package/dist/sandbox/container.js +138 -0
- package/dist/sandbox/container.js.map +1 -0
- package/dist/sandbox/errors.d.ts +6 -0
- package/dist/sandbox/errors.d.ts.map +1 -0
- package/dist/sandbox/errors.js +11 -0
- package/dist/sandbox/errors.js.map +1 -0
- package/dist/sandbox/firecracker.d.ts +17 -0
- package/dist/sandbox/firecracker.d.ts.map +1 -0
- package/dist/sandbox/firecracker.js +212 -0
- package/dist/sandbox/firecracker.js.map +1 -0
- package/dist/sandbox/host.d.ts +11 -0
- package/dist/sandbox/host.d.ts.map +1 -0
- package/dist/sandbox/host.js +89 -0
- package/dist/sandbox/host.js.map +1 -0
- package/dist/sandbox/image.d.ts +5 -0
- package/dist/sandbox/image.d.ts.map +1 -0
- package/dist/sandbox/image.js +30 -0
- package/dist/sandbox/image.js.map +1 -0
- package/dist/sandbox/index.d.ts +22 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +54 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/path-context.d.ts +4 -0
- package/dist/sandbox/path-context.d.ts.map +1 -0
- package/dist/sandbox/path-context.js +20 -0
- package/dist/sandbox/path-context.js.map +1 -0
- package/dist/sandbox/types.d.ts +67 -0
- package/dist/sandbox/types.d.ts.map +1 -0
- package/dist/sandbox/types.js +2 -0
- package/dist/sandbox/types.js.map +1 -0
- package/dist/sandbox/utils.d.ts +4 -0
- package/dist/sandbox/utils.d.ts.map +1 -0
- package/dist/sandbox/utils.js +51 -0
- package/dist/sandbox/utils.js.map +1 -0
- package/dist/sentry.d.ts +50 -0
- package/dist/sentry.d.ts.map +1 -0
- package/dist/sentry.js +257 -0
- package/dist/sentry.js.map +1 -0
- package/dist/session-view/command.d.ts +5 -0
- package/dist/session-view/command.d.ts.map +1 -0
- package/dist/session-view/command.js +7 -0
- package/dist/session-view/command.js.map +1 -0
- package/dist/session-view/portal.d.ts +16 -0
- package/dist/session-view/portal.d.ts.map +1 -0
- package/dist/session-view/portal.js +1822 -0
- package/dist/session-view/portal.js.map +1 -0
- package/dist/session-view/service.d.ts +34 -0
- package/dist/session-view/service.d.ts.map +1 -0
- package/dist/session-view/service.js +434 -0
- package/dist/session-view/service.js.map +1 -0
- package/dist/session-view/store.d.ts +18 -0
- package/dist/session-view/store.d.ts.map +1 -0
- package/dist/session-view/store.js +36 -0
- package/dist/session-view/store.js.map +1 -0
- package/dist/sessions/metadata.d.ts +15 -0
- package/dist/sessions/metadata.d.ts.map +1 -0
- package/dist/sessions/metadata.js +11 -0
- package/dist/sessions/metadata.js.map +1 -0
- package/dist/sessions/policy.d.ts +13 -0
- package/dist/sessions/policy.d.ts.map +1 -0
- package/dist/sessions/policy.js +23 -0
- package/dist/sessions/policy.js.map +1 -0
- package/dist/sessions/store.d.ts +103 -0
- package/dist/sessions/store.d.ts.map +1 -0
- package/dist/sessions/store.js +349 -0
- package/dist/sessions/store.js.map +1 -0
- package/dist/store.d.ts +58 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +152 -0
- package/dist/store.js.map +1 -0
- package/dist/tool-diagnostics.d.ts +2 -0
- package/dist/tool-diagnostics.d.ts.map +1 -0
- package/dist/tool-diagnostics.js +7 -0
- package/dist/tool-diagnostics.js.map +1 -0
- package/dist/tools/bash.d.ts +10 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +80 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit.d.ts +11 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +133 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/event.d.ts +62 -0
- package/dist/tools/event.d.ts.map +1 -0
- package/dist/tools/event.js +138 -0
- package/dist/tools/event.js.map +1 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +23 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/read.d.ts +11 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +136 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/truncate.d.ts +57 -0
- package/dist/tools/truncate.d.ts.map +1 -0
- package/dist/tools/truncate.js +184 -0
- package/dist/tools/truncate.js.map +1 -0
- package/dist/tools/write.d.ts +10 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +33 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/trigger.d.ts +31 -0
- package/dist/trigger.d.ts.map +1 -0
- package/dist/trigger.js +98 -0
- package/dist/trigger.js.map +1 -0
- package/dist/ui-copy.d.ts +12 -0
- package/dist/ui-copy.d.ts.map +1 -0
- package/dist/ui-copy.js +36 -0
- package/dist/ui-copy.js.map +1 -0
- package/dist/vault-routing.d.ts +4 -0
- package/dist/vault-routing.d.ts.map +1 -0
- package/dist/vault-routing.js +16 -0
- package/dist/vault-routing.js.map +1 -0
- package/dist/vault.d.ts +72 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +281 -0
- package/dist/vault.js.map +1 -0
- package/package.json +83 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
|
+
export declare class MissingGlobalSettingsError extends Error {
|
|
3
|
+
readonly settingsPath: string;
|
|
4
|
+
constructor(settingsPath: string);
|
|
5
|
+
}
|
|
6
|
+
export interface AgentConfig {
|
|
7
|
+
provider: string;
|
|
8
|
+
model: string;
|
|
9
|
+
thinkingLevel: ThinkingLevel;
|
|
10
|
+
sentryDsn?: string;
|
|
11
|
+
sandboxCpus?: string;
|
|
12
|
+
sandboxMemory?: string;
|
|
13
|
+
sandboxBoostCpus?: string;
|
|
14
|
+
sandboxBoostMemory?: string;
|
|
15
|
+
sandboxImageWorkspaceMount?: "private" | "full";
|
|
16
|
+
defaultSharedVault?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface AutoReplyConfig {
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
rules: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface JudgeModelConfig {
|
|
23
|
+
provider: string;
|
|
24
|
+
model: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function loadAgentConfig(): AgentConfig;
|
|
27
|
+
export declare function loadAgentConfigForConversation(conversationDir: string): AgentConfig;
|
|
28
|
+
export declare function saveConversationModelConfig(conversationDir: string, config: Pick<AgentConfig, "provider" | "model"> & Partial<Pick<AgentConfig, "thinkingLevel">>): void;
|
|
29
|
+
export declare function saveConversationSandboxConfig(conversationDir: string, config: {
|
|
30
|
+
imageWorkspaceMount: AgentConfig["sandboxImageWorkspaceMount"];
|
|
31
|
+
}): void;
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the model used to judge auto-reply rules. Falls back to the main
|
|
34
|
+
* llm.{provider,model} when llm.autoReply is not set, so a missing override
|
|
35
|
+
* keeps current behavior.
|
|
36
|
+
*/
|
|
37
|
+
export declare function loadAutoReplyJudgeModel(conversationDir?: string): JudgeModelConfig;
|
|
38
|
+
/**
|
|
39
|
+
* Load the mom-compatible auto-reply marker file state for a conversation.
|
|
40
|
+
*
|
|
41
|
+
* - `auto-reply` exists: enabled; empty file means reply to any top-level message.
|
|
42
|
+
* - `auto-reply.disabled` exists: disabled, preserving any rules text for re-enable.
|
|
43
|
+
* - neither exists: disabled.
|
|
44
|
+
*/
|
|
45
|
+
export declare function loadConversationAutoReplyConfig(conversationDir: string): AutoReplyConfig;
|
|
46
|
+
/** Save auto-reply state using mom-compatible marker files. */
|
|
47
|
+
export declare function saveConversationAutoReplyConfig(conversationDir: string, config: AutoReplyConfig): void;
|
|
48
|
+
export declare function resolveWorkspaceDirFromArgv(args?: string[]): string | undefined;
|
|
49
|
+
export declare function resolveStateDirFromArgv(args?: string[]): string;
|
|
50
|
+
export declare function resolveSentryDsn(): string | undefined;
|
|
51
|
+
export declare function createGlobalSettingsFile(stateDir: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Externally-visible base URL of the link/OAuth server, e.g.
|
|
54
|
+
* `https://mikan.example.com` (no trailing slash). Read from `LINK_URL` or
|
|
55
|
+
* `MIKAN_LINK_URL`, the same env var the bot uses to build credential onboarding links.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveLinkBaseUrl(): string | undefined;
|
|
58
|
+
export declare function saveAgentConfig(config: Partial<AgentConfig>): void;
|
|
59
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AASnE,qBAAa,0BAA2B,SAAQ,KAAK;aACvB,YAAY,EAAE,MAAM;IAAhD,YAA4B,YAAY,EAAE,MAAM,EAG/C;CACF;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAChD,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAgLD,wBAAgB,eAAe,IAAI,WAAW,CAE7C;AAED,wBAAgB,8BAA8B,CAAC,eAAe,EAAE,MAAM,GAAG,WAAW,CAMnF;AAED,wBAAgB,2BAA2B,CACzC,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC,GAC5F,IAAI,CAWN;AAED,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE;IAAE,mBAAmB,EAAE,WAAW,CAAC,4BAA4B,CAAC,CAAA;CAAE,GACzE,IAAI,CAiBN;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAUlF;AAUD;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,eAAe,EAAE,MAAM,GAAG,eAAe,CAYxF;AAED,+DAA+D;AAC/D,wBAAgB,+BAA+B,CAC7C,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,eAAe,GACtB,IAAI,CAeN;AAED,wBAAgB,2BAA2B,CAAC,IAAI,WAAwB,GAAG,MAAM,GAAG,SAAS,CA2B5F;AAED,wBAAgB,uBAAuB,CAAC,IAAI,WAAwB,GAAG,MAAM,CAY5E;AAED,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAOrD;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAIvD;AAmDD,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI,CAsBlE","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport { Type, type Static } from \"@sinclair/typebox\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { readEnv } from \"./env.js\";\nimport { ensureDirExists, readJsonSchemaFileIfExists } from \"./file-guards.js\";\nimport { atomicWritePrivateFile } from \"./fs-atomic.js\";\n\nexport class MissingGlobalSettingsError extends Error {\n constructor(public readonly settingsPath: string) {\n super(`Missing global settings file at ${settingsPath}`);\n this.name = \"MissingGlobalSettingsError\";\n }\n}\n\nexport interface AgentConfig {\n provider: string;\n model: string;\n thinkingLevel: ThinkingLevel;\n sentryDsn?: string;\n sandboxCpus?: string;\n sandboxMemory?: string;\n sandboxBoostCpus?: string;\n sandboxBoostMemory?: string;\n sandboxImageWorkspaceMount?: \"private\" | \"full\";\n defaultSharedVault?: string;\n}\n\nexport interface AutoReplyConfig {\n enabled: boolean;\n rules: string[];\n}\n\nexport interface JudgeModelConfig {\n provider: string;\n model: string;\n}\n\nconst ONBOARD_SETTINGS: SettingsFileConfig = {\n llm: {\n provider: \"anthropic\",\n model: \"claude-sonnet-4-6\",\n thinkingLevel: \"off\",\n autoReply: {\n provider: \"anthropic\",\n model: \"claude-haiku-4-5\",\n },\n },\n sandbox: {\n cpus: \"0.5\",\n memory: \"1g\",\n boost: {\n cpus: \"2\",\n memory: \"4g\",\n },\n image: {\n workspaceMount: \"private\",\n },\n defaultSharedVault: \"\",\n },\n};\n\nconst SettingsFileSchema = Type.Object({\n llm: Type.Optional(\n Type.Object({\n provider: Type.Optional(Type.String()),\n model: Type.Optional(Type.String()),\n thinkingLevel: Type.Optional(\n Type.Union([\n Type.Literal(\"off\"),\n Type.Literal(\"minimal\"),\n Type.Literal(\"low\"),\n Type.Literal(\"medium\"),\n Type.Literal(\"high\"),\n Type.Literal(\"xhigh\"),\n ]),\n ),\n autoReply: Type.Optional(\n Type.Object({\n provider: Type.Optional(Type.String()),\n model: Type.Optional(Type.String()),\n }),\n ),\n }),\n ),\n sentry: Type.Optional(\n Type.Object({\n dsn: Type.Optional(Type.String()),\n }),\n ),\n sandbox: Type.Optional(\n Type.Object({\n cpus: Type.Optional(Type.String()),\n memory: Type.Optional(Type.String()),\n boost: Type.Optional(\n Type.Object({\n cpus: Type.Optional(Type.String()),\n memory: Type.Optional(Type.String()),\n }),\n ),\n image: Type.Optional(\n Type.Object({\n workspaceMount: Type.Optional(\n Type.Union([Type.Literal(\"private\"), Type.Literal(\"full\")]),\n ),\n }),\n ),\n defaultSharedVault: Type.Optional(Type.String()),\n }),\n ),\n autoReply: Type.Optional(\n Type.Object({\n enabled: Type.Optional(Type.Boolean()),\n rules: Type.Optional(Type.Array(Type.String())),\n }),\n ),\n});\n\ntype SettingsFileConfig = Static<typeof SettingsFileSchema>;\n\nfunction loadSettingsFile(settingsPath: string): SettingsFileConfig | undefined {\n return readJsonSchemaFileIfExists(settingsPath, SettingsFileSchema, (detail) =>\n detail === \"unexpected JSON shape\"\n ? `Malformed settings file at ${settingsPath}: expected a JSON object at the top level`\n : `Malformed settings file at ${settingsPath}: ${detail}`,\n );\n}\n\nfunction getStateDir(): string {\n const raw = readEnv(\"STATE_DIR\");\n return raw ? resolve(raw) : join(homedir(), \".mikan\");\n}\n\nfunction normalizeSettingsConfig(config: SettingsFileConfig): Partial<AgentConfig> {\n return {\n ...(config.llm?.provider !== undefined ? { provider: config.llm.provider } : {}),\n ...(config.llm?.model !== undefined ? { model: config.llm.model } : {}),\n ...(config.llm?.thinkingLevel !== undefined ? { thinkingLevel: config.llm.thinkingLevel } : {}),\n ...(config.sentry?.dsn !== undefined ? { sentryDsn: config.sentry.dsn } : {}),\n ...(config.sandbox?.cpus !== undefined ? { sandboxCpus: config.sandbox.cpus } : {}),\n ...(config.sandbox?.memory !== undefined ? { sandboxMemory: config.sandbox.memory } : {}),\n ...(config.sandbox?.boost?.cpus !== undefined\n ? { sandboxBoostCpus: config.sandbox.boost.cpus }\n : {}),\n ...(config.sandbox?.boost?.memory !== undefined\n ? { sandboxBoostMemory: config.sandbox.boost.memory }\n : {}),\n ...(config.sandbox?.image?.workspaceMount !== undefined\n ? { sandboxImageWorkspaceMount: config.sandbox.image.workspaceMount }\n : {}),\n ...(config.sandbox?.defaultSharedVault?.trim()\n ? { defaultSharedVault: config.sandbox.defaultSharedVault.trim() }\n : {}),\n };\n}\n\nfunction getSettingsPath(): string {\n return join(getStateDir(), \"settings.json\");\n}\n\nfunction requireGlobalSettings(): SettingsFileConfig {\n const settingsPath = getSettingsPath();\n const config = loadSettingsFile(settingsPath);\n if (!config) {\n throw new MissingGlobalSettingsError(settingsPath);\n }\n return config;\n}\n\nfunction requireString(value: string | undefined, path: string): string {\n if (!value) {\n throw new Error(\n `Missing required global setting: ${path}. Run \\`mikan --onboard\\` to create settings.json.`,\n );\n }\n return value;\n}\n\nfunction requireThinkingLevel(value: ThinkingLevel | undefined): ThinkingLevel {\n return requireString(value, \"llm.thinkingLevel\") as ThinkingLevel;\n}\n\nfunction toAgentConfig(fromFile: Partial<AgentConfig>): AgentConfig {\n const provider = requireString(fromFile.provider, \"llm.provider\");\n const model = requireString(fromFile.model, \"llm.model\");\n const thinkingLevel = requireThinkingLevel(fromFile.thinkingLevel);\n const sentryDsn = fromFile.sentryDsn ?? process.env.SENTRY_DSN;\n const sandboxCpus = fromFile.sandboxCpus;\n const sandboxMemory = fromFile.sandboxMemory;\n const sandboxBoostCpus = fromFile.sandboxBoostCpus;\n const sandboxBoostMemory = fromFile.sandboxBoostMemory;\n const sandboxImageWorkspaceMount = fromFile.sandboxImageWorkspaceMount;\n const defaultSharedVault = fromFile.defaultSharedVault;\n\n return {\n provider,\n model,\n thinkingLevel,\n sentryDsn,\n sandboxCpus,\n sandboxMemory,\n sandboxBoostCpus,\n sandboxBoostMemory,\n sandboxImageWorkspaceMount,\n defaultSharedVault,\n };\n}\n\nfunction loadRawAgentConfig(): Partial<AgentConfig> {\n return normalizeSettingsConfig(requireGlobalSettings());\n}\n\nexport function loadAgentConfig(): AgentConfig {\n return toAgentConfig(loadRawAgentConfig());\n}\n\nexport function loadAgentConfigForConversation(conversationDir: string): AgentConfig {\n const globalConfig = loadRawAgentConfig();\n const conversationConfig = normalizeSettingsConfig(\n loadSettingsFile(join(conversationDir, \"settings.json\")) ?? {},\n );\n return toAgentConfig({ ...globalConfig, ...conversationConfig });\n}\n\nexport function saveConversationModelConfig(\n conversationDir: string,\n config: Pick<AgentConfig, \"provider\" | \"model\"> & Partial<Pick<AgentConfig, \"thinkingLevel\">>,\n): void {\n if (!existsSync(conversationDir)) {\n ensureDirExists(conversationDir);\n }\n const settingsPath = join(conversationDir, \"settings.json\");\n const existing = loadSettingsFile(settingsPath) ?? {};\n const scopedConfig: SettingsFileConfig = {\n ...existing,\n llm: { ...existing.llm, ...config },\n };\n atomicWritePrivateFile(settingsPath, JSON.stringify(scopedConfig, null, 2));\n}\n\nexport function saveConversationSandboxConfig(\n conversationDir: string,\n config: { imageWorkspaceMount: AgentConfig[\"sandboxImageWorkspaceMount\"] },\n): void {\n if (!existsSync(conversationDir)) {\n ensureDirExists(conversationDir);\n }\n const settingsPath = join(conversationDir, \"settings.json\");\n const existing = loadSettingsFile(settingsPath) ?? {};\n const scopedConfig: SettingsFileConfig = {\n ...existing,\n sandbox: {\n ...existing.sandbox,\n image: {\n ...existing.sandbox?.image,\n workspaceMount: config.imageWorkspaceMount,\n },\n },\n };\n atomicWritePrivateFile(settingsPath, JSON.stringify(scopedConfig, null, 2));\n}\n\n/**\n * Resolve the model used to judge auto-reply rules. Falls back to the main\n * llm.{provider,model} when llm.autoReply is not set, so a missing override\n * keeps current behavior.\n */\nexport function loadAutoReplyJudgeModel(conversationDir?: string): JudgeModelConfig {\n const global = requireGlobalSettings();\n const local = conversationDir\n ? (loadSettingsFile(join(conversationDir, \"settings.json\")) ?? {})\n : {};\n const merged: SettingsFileConfig[\"llm\"] = { ...global.llm, ...local.llm };\n const judge = { ...global.llm?.autoReply, ...local.llm?.autoReply };\n const provider = requireString(judge.provider ?? merged?.provider, \"llm.autoReply.provider\");\n const model = requireString(judge.model ?? merged?.model, \"llm.autoReply.model\");\n return { provider, model };\n}\n\nconst AUTO_REPLY_FILE = \"auto-reply\";\nconst AUTO_REPLY_DISABLED_FILE = \"auto-reply.disabled\";\n\nfunction readAutoReplyRulesFile(path: string): string[] {\n const text = readFileSync(path, \"utf-8\").trim();\n return text ? [text] : [];\n}\n\n/**\n * Load the mom-compatible auto-reply marker file state for a conversation.\n *\n * - `auto-reply` exists: enabled; empty file means reply to any top-level message.\n * - `auto-reply.disabled` exists: disabled, preserving any rules text for re-enable.\n * - neither exists: disabled.\n */\nexport function loadConversationAutoReplyConfig(conversationDir: string): AutoReplyConfig {\n const enabledPath = join(conversationDir, AUTO_REPLY_FILE);\n if (existsSync(enabledPath)) {\n return { enabled: true, rules: readAutoReplyRulesFile(enabledPath) };\n }\n\n const disabledPath = join(conversationDir, AUTO_REPLY_DISABLED_FILE);\n if (existsSync(disabledPath)) {\n return { enabled: false, rules: readAutoReplyRulesFile(disabledPath) };\n }\n\n return { enabled: false, rules: [] };\n}\n\n/** Save auto-reply state using mom-compatible marker files. */\nexport function saveConversationAutoReplyConfig(\n conversationDir: string,\n config: AutoReplyConfig,\n): void {\n if (!existsSync(conversationDir)) {\n mkdirSync(conversationDir, { recursive: true });\n }\n\n const enabledPath = join(conversationDir, AUTO_REPLY_FILE);\n const disabledPath = join(conversationDir, AUTO_REPLY_DISABLED_FILE);\n const targetPath = config.enabled ? enabledPath : disabledPath;\n const otherPath = config.enabled ? disabledPath : enabledPath;\n\n if (existsSync(otherPath)) {\n renameSync(otherPath, targetPath);\n }\n\n writeFileSync(targetPath, config.rules.join(\"\\n\"), \"utf-8\");\n}\n\nexport function resolveWorkspaceDirFromArgv(args = process.argv.slice(2)): string | undefined {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n if (arg === \"--sandbox\" || arg === \"--download\" || arg === \"--state-dir\") {\n i += 1;\n continue;\n }\n\n if (arg === \"--version\" || arg === \"-v\" || arg === \"-V\" || arg === \"--onboard\") {\n continue;\n }\n\n if (\n arg.startsWith(\"--sandbox=\") ||\n arg.startsWith(\"--download=\") ||\n arg.startsWith(\"--state-dir=\")\n ) {\n continue;\n }\n\n if (!arg.startsWith(\"-\")) {\n return arg;\n }\n }\n\n return undefined;\n}\n\nexport function resolveStateDirFromArgv(args = process.argv.slice(2)): string {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg.startsWith(\"--state-dir=\")) {\n return resolve(arg.slice(\"--state-dir=\".length));\n }\n if (arg === \"--state-dir\") {\n return resolve(args[++i] || \"\");\n }\n }\n\n return join(homedir(), \".mikan\");\n}\n\nexport function resolveSentryDsn(): string | undefined {\n const fromFile = normalizeSettingsConfig(loadSettingsFile(getSettingsPath()) ?? {});\n if (fromFile.sentryDsn) {\n return fromFile.sentryDsn;\n }\n\n return process.env.SENTRY_DSN;\n}\n\nexport function createGlobalSettingsFile(stateDir: string): string {\n const settingsPath = join(stateDir, \"settings.json\");\n if (existsSync(settingsPath)) {\n throw new Error(`Global settings already exists at ${settingsPath}`);\n }\n if (!existsSync(stateDir)) {\n ensureDirExists(stateDir);\n }\n atomicWritePrivateFile(settingsPath, JSON.stringify(ONBOARD_SETTINGS, null, 2));\n return settingsPath;\n}\n\n/**\n * Externally-visible base URL of the link/OAuth server, e.g.\n * `https://mikan.example.com` (no trailing slash). Read from `LINK_URL` or\n * `MIKAN_LINK_URL`, the same env var the bot uses to build credential onboarding links.\n */\nexport function resolveLinkBaseUrl(): string | undefined {\n const raw = readEnv(\"LINK_URL\");\n if (!raw) return undefined;\n return raw.replace(/\\/+$/, \"\");\n}\n\nfunction hasDefinedValue(values: Record<string, unknown> | undefined): boolean {\n return values !== undefined && Object.values(values).some((value) => value !== undefined);\n}\n\nfunction compactSettingsConfig(config: SettingsFileConfig): SettingsFileConfig {\n return {\n ...(hasDefinedValue(config.llm) ? { llm: config.llm } : {}),\n ...(hasDefinedValue(config.sentry) ? { sentry: config.sentry } : {}),\n ...(hasDefinedValue(config.sandbox) ? { sandbox: config.sandbox } : {}),\n ...(hasDefinedValue(config.autoReply) ? { autoReply: config.autoReply } : {}),\n };\n}\n\nfunction patchSettingsConfig(\n existing: SettingsFileConfig,\n config: Partial<AgentConfig>,\n): SettingsFileConfig {\n const patched: SettingsFileConfig = {\n ...existing,\n llm: {\n ...existing.llm,\n ...(config.provider !== undefined ? { provider: config.provider } : {}),\n ...(config.model !== undefined ? { model: config.model } : {}),\n ...(config.thinkingLevel !== undefined ? { thinkingLevel: config.thinkingLevel } : {}),\n },\n sentry: {\n ...existing.sentry,\n ...(config.sentryDsn !== undefined ? { dsn: config.sentryDsn } : {}),\n },\n sandbox: {\n ...existing.sandbox,\n ...(config.sandboxCpus !== undefined ? { cpus: config.sandboxCpus } : {}),\n ...(config.sandboxMemory !== undefined ? { memory: config.sandboxMemory } : {}),\n ...(config.sandboxBoostCpus !== undefined || config.sandboxBoostMemory !== undefined\n ? {\n boost: {\n ...existing.sandbox?.boost,\n ...(config.sandboxBoostCpus !== undefined ? { cpus: config.sandboxBoostCpus } : {}),\n ...(config.sandboxBoostMemory !== undefined\n ? { memory: config.sandboxBoostMemory }\n : {}),\n },\n }\n : {}),\n },\n };\n return compactSettingsConfig(patched);\n}\n\nexport function saveAgentConfig(config: Partial<AgentConfig>): void {\n const settingsPath = join(getStateDir(), \"settings.json\");\n\n let existing: SettingsFileConfig = ONBOARD_SETTINGS;\n if (existsSync(settingsPath)) {\n try {\n existing = loadSettingsFile(settingsPath) ?? {};\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n const message = detail.startsWith(\"Malformed settings file\")\n ? detail.replace(\"Malformed settings file\", \"Refusing to overwrite malformed settings file\")\n : detail;\n throw new Error(message, { cause: err });\n }\n }\n\n const merged = patchSettingsConfig(existing, config);\n\n const dir = dirname(settingsPath);\n ensureDirExists(dir);\n\n atomicWritePrivateFile(settingsPath, JSON.stringify(merged, null, 2));\n}\n"]}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { dirname, join, resolve } from "path";
|
|
5
|
+
import { readEnv } from "./env.js";
|
|
6
|
+
import { ensureDirExists, readJsonSchemaFileIfExists } from "./file-guards.js";
|
|
7
|
+
import { atomicWritePrivateFile } from "./fs-atomic.js";
|
|
8
|
+
export class MissingGlobalSettingsError extends Error {
|
|
9
|
+
constructor(settingsPath) {
|
|
10
|
+
super(`Missing global settings file at ${settingsPath}`);
|
|
11
|
+
this.settingsPath = settingsPath;
|
|
12
|
+
this.name = "MissingGlobalSettingsError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const ONBOARD_SETTINGS = {
|
|
16
|
+
llm: {
|
|
17
|
+
provider: "anthropic",
|
|
18
|
+
model: "claude-sonnet-4-6",
|
|
19
|
+
thinkingLevel: "off",
|
|
20
|
+
autoReply: {
|
|
21
|
+
provider: "anthropic",
|
|
22
|
+
model: "claude-haiku-4-5",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
sandbox: {
|
|
26
|
+
cpus: "0.5",
|
|
27
|
+
memory: "1g",
|
|
28
|
+
boost: {
|
|
29
|
+
cpus: "2",
|
|
30
|
+
memory: "4g",
|
|
31
|
+
},
|
|
32
|
+
image: {
|
|
33
|
+
workspaceMount: "private",
|
|
34
|
+
},
|
|
35
|
+
defaultSharedVault: "",
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
const SettingsFileSchema = Type.Object({
|
|
39
|
+
llm: Type.Optional(Type.Object({
|
|
40
|
+
provider: Type.Optional(Type.String()),
|
|
41
|
+
model: Type.Optional(Type.String()),
|
|
42
|
+
thinkingLevel: Type.Optional(Type.Union([
|
|
43
|
+
Type.Literal("off"),
|
|
44
|
+
Type.Literal("minimal"),
|
|
45
|
+
Type.Literal("low"),
|
|
46
|
+
Type.Literal("medium"),
|
|
47
|
+
Type.Literal("high"),
|
|
48
|
+
Type.Literal("xhigh"),
|
|
49
|
+
])),
|
|
50
|
+
autoReply: Type.Optional(Type.Object({
|
|
51
|
+
provider: Type.Optional(Type.String()),
|
|
52
|
+
model: Type.Optional(Type.String()),
|
|
53
|
+
})),
|
|
54
|
+
})),
|
|
55
|
+
sentry: Type.Optional(Type.Object({
|
|
56
|
+
dsn: Type.Optional(Type.String()),
|
|
57
|
+
})),
|
|
58
|
+
sandbox: Type.Optional(Type.Object({
|
|
59
|
+
cpus: Type.Optional(Type.String()),
|
|
60
|
+
memory: Type.Optional(Type.String()),
|
|
61
|
+
boost: Type.Optional(Type.Object({
|
|
62
|
+
cpus: Type.Optional(Type.String()),
|
|
63
|
+
memory: Type.Optional(Type.String()),
|
|
64
|
+
})),
|
|
65
|
+
image: Type.Optional(Type.Object({
|
|
66
|
+
workspaceMount: Type.Optional(Type.Union([Type.Literal("private"), Type.Literal("full")])),
|
|
67
|
+
})),
|
|
68
|
+
defaultSharedVault: Type.Optional(Type.String()),
|
|
69
|
+
})),
|
|
70
|
+
autoReply: Type.Optional(Type.Object({
|
|
71
|
+
enabled: Type.Optional(Type.Boolean()),
|
|
72
|
+
rules: Type.Optional(Type.Array(Type.String())),
|
|
73
|
+
})),
|
|
74
|
+
});
|
|
75
|
+
function loadSettingsFile(settingsPath) {
|
|
76
|
+
return readJsonSchemaFileIfExists(settingsPath, SettingsFileSchema, (detail) => detail === "unexpected JSON shape"
|
|
77
|
+
? `Malformed settings file at ${settingsPath}: expected a JSON object at the top level`
|
|
78
|
+
: `Malformed settings file at ${settingsPath}: ${detail}`);
|
|
79
|
+
}
|
|
80
|
+
function getStateDir() {
|
|
81
|
+
const raw = readEnv("STATE_DIR");
|
|
82
|
+
return raw ? resolve(raw) : join(homedir(), ".mikan");
|
|
83
|
+
}
|
|
84
|
+
function normalizeSettingsConfig(config) {
|
|
85
|
+
return {
|
|
86
|
+
...(config.llm?.provider !== undefined ? { provider: config.llm.provider } : {}),
|
|
87
|
+
...(config.llm?.model !== undefined ? { model: config.llm.model } : {}),
|
|
88
|
+
...(config.llm?.thinkingLevel !== undefined ? { thinkingLevel: config.llm.thinkingLevel } : {}),
|
|
89
|
+
...(config.sentry?.dsn !== undefined ? { sentryDsn: config.sentry.dsn } : {}),
|
|
90
|
+
...(config.sandbox?.cpus !== undefined ? { sandboxCpus: config.sandbox.cpus } : {}),
|
|
91
|
+
...(config.sandbox?.memory !== undefined ? { sandboxMemory: config.sandbox.memory } : {}),
|
|
92
|
+
...(config.sandbox?.boost?.cpus !== undefined
|
|
93
|
+
? { sandboxBoostCpus: config.sandbox.boost.cpus }
|
|
94
|
+
: {}),
|
|
95
|
+
...(config.sandbox?.boost?.memory !== undefined
|
|
96
|
+
? { sandboxBoostMemory: config.sandbox.boost.memory }
|
|
97
|
+
: {}),
|
|
98
|
+
...(config.sandbox?.image?.workspaceMount !== undefined
|
|
99
|
+
? { sandboxImageWorkspaceMount: config.sandbox.image.workspaceMount }
|
|
100
|
+
: {}),
|
|
101
|
+
...(config.sandbox?.defaultSharedVault?.trim()
|
|
102
|
+
? { defaultSharedVault: config.sandbox.defaultSharedVault.trim() }
|
|
103
|
+
: {}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function getSettingsPath() {
|
|
107
|
+
return join(getStateDir(), "settings.json");
|
|
108
|
+
}
|
|
109
|
+
function requireGlobalSettings() {
|
|
110
|
+
const settingsPath = getSettingsPath();
|
|
111
|
+
const config = loadSettingsFile(settingsPath);
|
|
112
|
+
if (!config) {
|
|
113
|
+
throw new MissingGlobalSettingsError(settingsPath);
|
|
114
|
+
}
|
|
115
|
+
return config;
|
|
116
|
+
}
|
|
117
|
+
function requireString(value, path) {
|
|
118
|
+
if (!value) {
|
|
119
|
+
throw new Error(`Missing required global setting: ${path}. Run \`mikan --onboard\` to create settings.json.`);
|
|
120
|
+
}
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
function requireThinkingLevel(value) {
|
|
124
|
+
return requireString(value, "llm.thinkingLevel");
|
|
125
|
+
}
|
|
126
|
+
function toAgentConfig(fromFile) {
|
|
127
|
+
const provider = requireString(fromFile.provider, "llm.provider");
|
|
128
|
+
const model = requireString(fromFile.model, "llm.model");
|
|
129
|
+
const thinkingLevel = requireThinkingLevel(fromFile.thinkingLevel);
|
|
130
|
+
const sentryDsn = fromFile.sentryDsn ?? process.env.SENTRY_DSN;
|
|
131
|
+
const sandboxCpus = fromFile.sandboxCpus;
|
|
132
|
+
const sandboxMemory = fromFile.sandboxMemory;
|
|
133
|
+
const sandboxBoostCpus = fromFile.sandboxBoostCpus;
|
|
134
|
+
const sandboxBoostMemory = fromFile.sandboxBoostMemory;
|
|
135
|
+
const sandboxImageWorkspaceMount = fromFile.sandboxImageWorkspaceMount;
|
|
136
|
+
const defaultSharedVault = fromFile.defaultSharedVault;
|
|
137
|
+
return {
|
|
138
|
+
provider,
|
|
139
|
+
model,
|
|
140
|
+
thinkingLevel,
|
|
141
|
+
sentryDsn,
|
|
142
|
+
sandboxCpus,
|
|
143
|
+
sandboxMemory,
|
|
144
|
+
sandboxBoostCpus,
|
|
145
|
+
sandboxBoostMemory,
|
|
146
|
+
sandboxImageWorkspaceMount,
|
|
147
|
+
defaultSharedVault,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function loadRawAgentConfig() {
|
|
151
|
+
return normalizeSettingsConfig(requireGlobalSettings());
|
|
152
|
+
}
|
|
153
|
+
export function loadAgentConfig() {
|
|
154
|
+
return toAgentConfig(loadRawAgentConfig());
|
|
155
|
+
}
|
|
156
|
+
export function loadAgentConfigForConversation(conversationDir) {
|
|
157
|
+
const globalConfig = loadRawAgentConfig();
|
|
158
|
+
const conversationConfig = normalizeSettingsConfig(loadSettingsFile(join(conversationDir, "settings.json")) ?? {});
|
|
159
|
+
return toAgentConfig({ ...globalConfig, ...conversationConfig });
|
|
160
|
+
}
|
|
161
|
+
export function saveConversationModelConfig(conversationDir, config) {
|
|
162
|
+
if (!existsSync(conversationDir)) {
|
|
163
|
+
ensureDirExists(conversationDir);
|
|
164
|
+
}
|
|
165
|
+
const settingsPath = join(conversationDir, "settings.json");
|
|
166
|
+
const existing = loadSettingsFile(settingsPath) ?? {};
|
|
167
|
+
const scopedConfig = {
|
|
168
|
+
...existing,
|
|
169
|
+
llm: { ...existing.llm, ...config },
|
|
170
|
+
};
|
|
171
|
+
atomicWritePrivateFile(settingsPath, JSON.stringify(scopedConfig, null, 2));
|
|
172
|
+
}
|
|
173
|
+
export function saveConversationSandboxConfig(conversationDir, config) {
|
|
174
|
+
if (!existsSync(conversationDir)) {
|
|
175
|
+
ensureDirExists(conversationDir);
|
|
176
|
+
}
|
|
177
|
+
const settingsPath = join(conversationDir, "settings.json");
|
|
178
|
+
const existing = loadSettingsFile(settingsPath) ?? {};
|
|
179
|
+
const scopedConfig = {
|
|
180
|
+
...existing,
|
|
181
|
+
sandbox: {
|
|
182
|
+
...existing.sandbox,
|
|
183
|
+
image: {
|
|
184
|
+
...existing.sandbox?.image,
|
|
185
|
+
workspaceMount: config.imageWorkspaceMount,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
atomicWritePrivateFile(settingsPath, JSON.stringify(scopedConfig, null, 2));
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Resolve the model used to judge auto-reply rules. Falls back to the main
|
|
193
|
+
* llm.{provider,model} when llm.autoReply is not set, so a missing override
|
|
194
|
+
* keeps current behavior.
|
|
195
|
+
*/
|
|
196
|
+
export function loadAutoReplyJudgeModel(conversationDir) {
|
|
197
|
+
const global = requireGlobalSettings();
|
|
198
|
+
const local = conversationDir
|
|
199
|
+
? (loadSettingsFile(join(conversationDir, "settings.json")) ?? {})
|
|
200
|
+
: {};
|
|
201
|
+
const merged = { ...global.llm, ...local.llm };
|
|
202
|
+
const judge = { ...global.llm?.autoReply, ...local.llm?.autoReply };
|
|
203
|
+
const provider = requireString(judge.provider ?? merged?.provider, "llm.autoReply.provider");
|
|
204
|
+
const model = requireString(judge.model ?? merged?.model, "llm.autoReply.model");
|
|
205
|
+
return { provider, model };
|
|
206
|
+
}
|
|
207
|
+
const AUTO_REPLY_FILE = "auto-reply";
|
|
208
|
+
const AUTO_REPLY_DISABLED_FILE = "auto-reply.disabled";
|
|
209
|
+
function readAutoReplyRulesFile(path) {
|
|
210
|
+
const text = readFileSync(path, "utf-8").trim();
|
|
211
|
+
return text ? [text] : [];
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Load the mom-compatible auto-reply marker file state for a conversation.
|
|
215
|
+
*
|
|
216
|
+
* - `auto-reply` exists: enabled; empty file means reply to any top-level message.
|
|
217
|
+
* - `auto-reply.disabled` exists: disabled, preserving any rules text for re-enable.
|
|
218
|
+
* - neither exists: disabled.
|
|
219
|
+
*/
|
|
220
|
+
export function loadConversationAutoReplyConfig(conversationDir) {
|
|
221
|
+
const enabledPath = join(conversationDir, AUTO_REPLY_FILE);
|
|
222
|
+
if (existsSync(enabledPath)) {
|
|
223
|
+
return { enabled: true, rules: readAutoReplyRulesFile(enabledPath) };
|
|
224
|
+
}
|
|
225
|
+
const disabledPath = join(conversationDir, AUTO_REPLY_DISABLED_FILE);
|
|
226
|
+
if (existsSync(disabledPath)) {
|
|
227
|
+
return { enabled: false, rules: readAutoReplyRulesFile(disabledPath) };
|
|
228
|
+
}
|
|
229
|
+
return { enabled: false, rules: [] };
|
|
230
|
+
}
|
|
231
|
+
/** Save auto-reply state using mom-compatible marker files. */
|
|
232
|
+
export function saveConversationAutoReplyConfig(conversationDir, config) {
|
|
233
|
+
if (!existsSync(conversationDir)) {
|
|
234
|
+
mkdirSync(conversationDir, { recursive: true });
|
|
235
|
+
}
|
|
236
|
+
const enabledPath = join(conversationDir, AUTO_REPLY_FILE);
|
|
237
|
+
const disabledPath = join(conversationDir, AUTO_REPLY_DISABLED_FILE);
|
|
238
|
+
const targetPath = config.enabled ? enabledPath : disabledPath;
|
|
239
|
+
const otherPath = config.enabled ? disabledPath : enabledPath;
|
|
240
|
+
if (existsSync(otherPath)) {
|
|
241
|
+
renameSync(otherPath, targetPath);
|
|
242
|
+
}
|
|
243
|
+
writeFileSync(targetPath, config.rules.join("\n"), "utf-8");
|
|
244
|
+
}
|
|
245
|
+
export function resolveWorkspaceDirFromArgv(args = process.argv.slice(2)) {
|
|
246
|
+
for (let i = 0; i < args.length; i++) {
|
|
247
|
+
const arg = args[i];
|
|
248
|
+
if (arg === "--sandbox" || arg === "--download" || arg === "--state-dir") {
|
|
249
|
+
i += 1;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (arg === "--version" || arg === "-v" || arg === "-V" || arg === "--onboard") {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (arg.startsWith("--sandbox=") ||
|
|
256
|
+
arg.startsWith("--download=") ||
|
|
257
|
+
arg.startsWith("--state-dir=")) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (!arg.startsWith("-")) {
|
|
261
|
+
return arg;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
export function resolveStateDirFromArgv(args = process.argv.slice(2)) {
|
|
267
|
+
for (let i = 0; i < args.length; i++) {
|
|
268
|
+
const arg = args[i];
|
|
269
|
+
if (arg.startsWith("--state-dir=")) {
|
|
270
|
+
return resolve(arg.slice("--state-dir=".length));
|
|
271
|
+
}
|
|
272
|
+
if (arg === "--state-dir") {
|
|
273
|
+
return resolve(args[++i] || "");
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return join(homedir(), ".mikan");
|
|
277
|
+
}
|
|
278
|
+
export function resolveSentryDsn() {
|
|
279
|
+
const fromFile = normalizeSettingsConfig(loadSettingsFile(getSettingsPath()) ?? {});
|
|
280
|
+
if (fromFile.sentryDsn) {
|
|
281
|
+
return fromFile.sentryDsn;
|
|
282
|
+
}
|
|
283
|
+
return process.env.SENTRY_DSN;
|
|
284
|
+
}
|
|
285
|
+
export function createGlobalSettingsFile(stateDir) {
|
|
286
|
+
const settingsPath = join(stateDir, "settings.json");
|
|
287
|
+
if (existsSync(settingsPath)) {
|
|
288
|
+
throw new Error(`Global settings already exists at ${settingsPath}`);
|
|
289
|
+
}
|
|
290
|
+
if (!existsSync(stateDir)) {
|
|
291
|
+
ensureDirExists(stateDir);
|
|
292
|
+
}
|
|
293
|
+
atomicWritePrivateFile(settingsPath, JSON.stringify(ONBOARD_SETTINGS, null, 2));
|
|
294
|
+
return settingsPath;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Externally-visible base URL of the link/OAuth server, e.g.
|
|
298
|
+
* `https://mikan.example.com` (no trailing slash). Read from `LINK_URL` or
|
|
299
|
+
* `MIKAN_LINK_URL`, the same env var the bot uses to build credential onboarding links.
|
|
300
|
+
*/
|
|
301
|
+
export function resolveLinkBaseUrl() {
|
|
302
|
+
const raw = readEnv("LINK_URL");
|
|
303
|
+
if (!raw)
|
|
304
|
+
return undefined;
|
|
305
|
+
return raw.replace(/\/+$/, "");
|
|
306
|
+
}
|
|
307
|
+
function hasDefinedValue(values) {
|
|
308
|
+
return values !== undefined && Object.values(values).some((value) => value !== undefined);
|
|
309
|
+
}
|
|
310
|
+
function compactSettingsConfig(config) {
|
|
311
|
+
return {
|
|
312
|
+
...(hasDefinedValue(config.llm) ? { llm: config.llm } : {}),
|
|
313
|
+
...(hasDefinedValue(config.sentry) ? { sentry: config.sentry } : {}),
|
|
314
|
+
...(hasDefinedValue(config.sandbox) ? { sandbox: config.sandbox } : {}),
|
|
315
|
+
...(hasDefinedValue(config.autoReply) ? { autoReply: config.autoReply } : {}),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
function patchSettingsConfig(existing, config) {
|
|
319
|
+
const patched = {
|
|
320
|
+
...existing,
|
|
321
|
+
llm: {
|
|
322
|
+
...existing.llm,
|
|
323
|
+
...(config.provider !== undefined ? { provider: config.provider } : {}),
|
|
324
|
+
...(config.model !== undefined ? { model: config.model } : {}),
|
|
325
|
+
...(config.thinkingLevel !== undefined ? { thinkingLevel: config.thinkingLevel } : {}),
|
|
326
|
+
},
|
|
327
|
+
sentry: {
|
|
328
|
+
...existing.sentry,
|
|
329
|
+
...(config.sentryDsn !== undefined ? { dsn: config.sentryDsn } : {}),
|
|
330
|
+
},
|
|
331
|
+
sandbox: {
|
|
332
|
+
...existing.sandbox,
|
|
333
|
+
...(config.sandboxCpus !== undefined ? { cpus: config.sandboxCpus } : {}),
|
|
334
|
+
...(config.sandboxMemory !== undefined ? { memory: config.sandboxMemory } : {}),
|
|
335
|
+
...(config.sandboxBoostCpus !== undefined || config.sandboxBoostMemory !== undefined
|
|
336
|
+
? {
|
|
337
|
+
boost: {
|
|
338
|
+
...existing.sandbox?.boost,
|
|
339
|
+
...(config.sandboxBoostCpus !== undefined ? { cpus: config.sandboxBoostCpus } : {}),
|
|
340
|
+
...(config.sandboxBoostMemory !== undefined
|
|
341
|
+
? { memory: config.sandboxBoostMemory }
|
|
342
|
+
: {}),
|
|
343
|
+
},
|
|
344
|
+
}
|
|
345
|
+
: {}),
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
return compactSettingsConfig(patched);
|
|
349
|
+
}
|
|
350
|
+
export function saveAgentConfig(config) {
|
|
351
|
+
const settingsPath = join(getStateDir(), "settings.json");
|
|
352
|
+
let existing = ONBOARD_SETTINGS;
|
|
353
|
+
if (existsSync(settingsPath)) {
|
|
354
|
+
try {
|
|
355
|
+
existing = loadSettingsFile(settingsPath) ?? {};
|
|
356
|
+
}
|
|
357
|
+
catch (err) {
|
|
358
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
359
|
+
const message = detail.startsWith("Malformed settings file")
|
|
360
|
+
? detail.replace("Malformed settings file", "Refusing to overwrite malformed settings file")
|
|
361
|
+
: detail;
|
|
362
|
+
throw new Error(message, { cause: err });
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const merged = patchSettingsConfig(existing, config);
|
|
366
|
+
const dir = dirname(settingsPath);
|
|
367
|
+
ensureDirExists(dir);
|
|
368
|
+
atomicWritePrivateFile(settingsPath, JSON.stringify(merged, null, 2));
|
|
369
|
+
}
|
|
370
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport { Type, type Static } from \"@sinclair/typebox\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { dirname, join, resolve } from \"path\";\nimport { readEnv } from \"./env.js\";\nimport { ensureDirExists, readJsonSchemaFileIfExists } from \"./file-guards.js\";\nimport { atomicWritePrivateFile } from \"./fs-atomic.js\";\n\nexport class MissingGlobalSettingsError extends Error {\n constructor(public readonly settingsPath: string) {\n super(`Missing global settings file at ${settingsPath}`);\n this.name = \"MissingGlobalSettingsError\";\n }\n}\n\nexport interface AgentConfig {\n provider: string;\n model: string;\n thinkingLevel: ThinkingLevel;\n sentryDsn?: string;\n sandboxCpus?: string;\n sandboxMemory?: string;\n sandboxBoostCpus?: string;\n sandboxBoostMemory?: string;\n sandboxImageWorkspaceMount?: \"private\" | \"full\";\n defaultSharedVault?: string;\n}\n\nexport interface AutoReplyConfig {\n enabled: boolean;\n rules: string[];\n}\n\nexport interface JudgeModelConfig {\n provider: string;\n model: string;\n}\n\nconst ONBOARD_SETTINGS: SettingsFileConfig = {\n llm: {\n provider: \"anthropic\",\n model: \"claude-sonnet-4-6\",\n thinkingLevel: \"off\",\n autoReply: {\n provider: \"anthropic\",\n model: \"claude-haiku-4-5\",\n },\n },\n sandbox: {\n cpus: \"0.5\",\n memory: \"1g\",\n boost: {\n cpus: \"2\",\n memory: \"4g\",\n },\n image: {\n workspaceMount: \"private\",\n },\n defaultSharedVault: \"\",\n },\n};\n\nconst SettingsFileSchema = Type.Object({\n llm: Type.Optional(\n Type.Object({\n provider: Type.Optional(Type.String()),\n model: Type.Optional(Type.String()),\n thinkingLevel: Type.Optional(\n Type.Union([\n Type.Literal(\"off\"),\n Type.Literal(\"minimal\"),\n Type.Literal(\"low\"),\n Type.Literal(\"medium\"),\n Type.Literal(\"high\"),\n Type.Literal(\"xhigh\"),\n ]),\n ),\n autoReply: Type.Optional(\n Type.Object({\n provider: Type.Optional(Type.String()),\n model: Type.Optional(Type.String()),\n }),\n ),\n }),\n ),\n sentry: Type.Optional(\n Type.Object({\n dsn: Type.Optional(Type.String()),\n }),\n ),\n sandbox: Type.Optional(\n Type.Object({\n cpus: Type.Optional(Type.String()),\n memory: Type.Optional(Type.String()),\n boost: Type.Optional(\n Type.Object({\n cpus: Type.Optional(Type.String()),\n memory: Type.Optional(Type.String()),\n }),\n ),\n image: Type.Optional(\n Type.Object({\n workspaceMount: Type.Optional(\n Type.Union([Type.Literal(\"private\"), Type.Literal(\"full\")]),\n ),\n }),\n ),\n defaultSharedVault: Type.Optional(Type.String()),\n }),\n ),\n autoReply: Type.Optional(\n Type.Object({\n enabled: Type.Optional(Type.Boolean()),\n rules: Type.Optional(Type.Array(Type.String())),\n }),\n ),\n});\n\ntype SettingsFileConfig = Static<typeof SettingsFileSchema>;\n\nfunction loadSettingsFile(settingsPath: string): SettingsFileConfig | undefined {\n return readJsonSchemaFileIfExists(settingsPath, SettingsFileSchema, (detail) =>\n detail === \"unexpected JSON shape\"\n ? `Malformed settings file at ${settingsPath}: expected a JSON object at the top level`\n : `Malformed settings file at ${settingsPath}: ${detail}`,\n );\n}\n\nfunction getStateDir(): string {\n const raw = readEnv(\"STATE_DIR\");\n return raw ? resolve(raw) : join(homedir(), \".mikan\");\n}\n\nfunction normalizeSettingsConfig(config: SettingsFileConfig): Partial<AgentConfig> {\n return {\n ...(config.llm?.provider !== undefined ? { provider: config.llm.provider } : {}),\n ...(config.llm?.model !== undefined ? { model: config.llm.model } : {}),\n ...(config.llm?.thinkingLevel !== undefined ? { thinkingLevel: config.llm.thinkingLevel } : {}),\n ...(config.sentry?.dsn !== undefined ? { sentryDsn: config.sentry.dsn } : {}),\n ...(config.sandbox?.cpus !== undefined ? { sandboxCpus: config.sandbox.cpus } : {}),\n ...(config.sandbox?.memory !== undefined ? { sandboxMemory: config.sandbox.memory } : {}),\n ...(config.sandbox?.boost?.cpus !== undefined\n ? { sandboxBoostCpus: config.sandbox.boost.cpus }\n : {}),\n ...(config.sandbox?.boost?.memory !== undefined\n ? { sandboxBoostMemory: config.sandbox.boost.memory }\n : {}),\n ...(config.sandbox?.image?.workspaceMount !== undefined\n ? { sandboxImageWorkspaceMount: config.sandbox.image.workspaceMount }\n : {}),\n ...(config.sandbox?.defaultSharedVault?.trim()\n ? { defaultSharedVault: config.sandbox.defaultSharedVault.trim() }\n : {}),\n };\n}\n\nfunction getSettingsPath(): string {\n return join(getStateDir(), \"settings.json\");\n}\n\nfunction requireGlobalSettings(): SettingsFileConfig {\n const settingsPath = getSettingsPath();\n const config = loadSettingsFile(settingsPath);\n if (!config) {\n throw new MissingGlobalSettingsError(settingsPath);\n }\n return config;\n}\n\nfunction requireString(value: string | undefined, path: string): string {\n if (!value) {\n throw new Error(\n `Missing required global setting: ${path}. Run \\`mikan --onboard\\` to create settings.json.`,\n );\n }\n return value;\n}\n\nfunction requireThinkingLevel(value: ThinkingLevel | undefined): ThinkingLevel {\n return requireString(value, \"llm.thinkingLevel\") as ThinkingLevel;\n}\n\nfunction toAgentConfig(fromFile: Partial<AgentConfig>): AgentConfig {\n const provider = requireString(fromFile.provider, \"llm.provider\");\n const model = requireString(fromFile.model, \"llm.model\");\n const thinkingLevel = requireThinkingLevel(fromFile.thinkingLevel);\n const sentryDsn = fromFile.sentryDsn ?? process.env.SENTRY_DSN;\n const sandboxCpus = fromFile.sandboxCpus;\n const sandboxMemory = fromFile.sandboxMemory;\n const sandboxBoostCpus = fromFile.sandboxBoostCpus;\n const sandboxBoostMemory = fromFile.sandboxBoostMemory;\n const sandboxImageWorkspaceMount = fromFile.sandboxImageWorkspaceMount;\n const defaultSharedVault = fromFile.defaultSharedVault;\n\n return {\n provider,\n model,\n thinkingLevel,\n sentryDsn,\n sandboxCpus,\n sandboxMemory,\n sandboxBoostCpus,\n sandboxBoostMemory,\n sandboxImageWorkspaceMount,\n defaultSharedVault,\n };\n}\n\nfunction loadRawAgentConfig(): Partial<AgentConfig> {\n return normalizeSettingsConfig(requireGlobalSettings());\n}\n\nexport function loadAgentConfig(): AgentConfig {\n return toAgentConfig(loadRawAgentConfig());\n}\n\nexport function loadAgentConfigForConversation(conversationDir: string): AgentConfig {\n const globalConfig = loadRawAgentConfig();\n const conversationConfig = normalizeSettingsConfig(\n loadSettingsFile(join(conversationDir, \"settings.json\")) ?? {},\n );\n return toAgentConfig({ ...globalConfig, ...conversationConfig });\n}\n\nexport function saveConversationModelConfig(\n conversationDir: string,\n config: Pick<AgentConfig, \"provider\" | \"model\"> & Partial<Pick<AgentConfig, \"thinkingLevel\">>,\n): void {\n if (!existsSync(conversationDir)) {\n ensureDirExists(conversationDir);\n }\n const settingsPath = join(conversationDir, \"settings.json\");\n const existing = loadSettingsFile(settingsPath) ?? {};\n const scopedConfig: SettingsFileConfig = {\n ...existing,\n llm: { ...existing.llm, ...config },\n };\n atomicWritePrivateFile(settingsPath, JSON.stringify(scopedConfig, null, 2));\n}\n\nexport function saveConversationSandboxConfig(\n conversationDir: string,\n config: { imageWorkspaceMount: AgentConfig[\"sandboxImageWorkspaceMount\"] },\n): void {\n if (!existsSync(conversationDir)) {\n ensureDirExists(conversationDir);\n }\n const settingsPath = join(conversationDir, \"settings.json\");\n const existing = loadSettingsFile(settingsPath) ?? {};\n const scopedConfig: SettingsFileConfig = {\n ...existing,\n sandbox: {\n ...existing.sandbox,\n image: {\n ...existing.sandbox?.image,\n workspaceMount: config.imageWorkspaceMount,\n },\n },\n };\n atomicWritePrivateFile(settingsPath, JSON.stringify(scopedConfig, null, 2));\n}\n\n/**\n * Resolve the model used to judge auto-reply rules. Falls back to the main\n * llm.{provider,model} when llm.autoReply is not set, so a missing override\n * keeps current behavior.\n */\nexport function loadAutoReplyJudgeModel(conversationDir?: string): JudgeModelConfig {\n const global = requireGlobalSettings();\n const local = conversationDir\n ? (loadSettingsFile(join(conversationDir, \"settings.json\")) ?? {})\n : {};\n const merged: SettingsFileConfig[\"llm\"] = { ...global.llm, ...local.llm };\n const judge = { ...global.llm?.autoReply, ...local.llm?.autoReply };\n const provider = requireString(judge.provider ?? merged?.provider, \"llm.autoReply.provider\");\n const model = requireString(judge.model ?? merged?.model, \"llm.autoReply.model\");\n return { provider, model };\n}\n\nconst AUTO_REPLY_FILE = \"auto-reply\";\nconst AUTO_REPLY_DISABLED_FILE = \"auto-reply.disabled\";\n\nfunction readAutoReplyRulesFile(path: string): string[] {\n const text = readFileSync(path, \"utf-8\").trim();\n return text ? [text] : [];\n}\n\n/**\n * Load the mom-compatible auto-reply marker file state for a conversation.\n *\n * - `auto-reply` exists: enabled; empty file means reply to any top-level message.\n * - `auto-reply.disabled` exists: disabled, preserving any rules text for re-enable.\n * - neither exists: disabled.\n */\nexport function loadConversationAutoReplyConfig(conversationDir: string): AutoReplyConfig {\n const enabledPath = join(conversationDir, AUTO_REPLY_FILE);\n if (existsSync(enabledPath)) {\n return { enabled: true, rules: readAutoReplyRulesFile(enabledPath) };\n }\n\n const disabledPath = join(conversationDir, AUTO_REPLY_DISABLED_FILE);\n if (existsSync(disabledPath)) {\n return { enabled: false, rules: readAutoReplyRulesFile(disabledPath) };\n }\n\n return { enabled: false, rules: [] };\n}\n\n/** Save auto-reply state using mom-compatible marker files. */\nexport function saveConversationAutoReplyConfig(\n conversationDir: string,\n config: AutoReplyConfig,\n): void {\n if (!existsSync(conversationDir)) {\n mkdirSync(conversationDir, { recursive: true });\n }\n\n const enabledPath = join(conversationDir, AUTO_REPLY_FILE);\n const disabledPath = join(conversationDir, AUTO_REPLY_DISABLED_FILE);\n const targetPath = config.enabled ? enabledPath : disabledPath;\n const otherPath = config.enabled ? disabledPath : enabledPath;\n\n if (existsSync(otherPath)) {\n renameSync(otherPath, targetPath);\n }\n\n writeFileSync(targetPath, config.rules.join(\"\\n\"), \"utf-8\");\n}\n\nexport function resolveWorkspaceDirFromArgv(args = process.argv.slice(2)): string | undefined {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n\n if (arg === \"--sandbox\" || arg === \"--download\" || arg === \"--state-dir\") {\n i += 1;\n continue;\n }\n\n if (arg === \"--version\" || arg === \"-v\" || arg === \"-V\" || arg === \"--onboard\") {\n continue;\n }\n\n if (\n arg.startsWith(\"--sandbox=\") ||\n arg.startsWith(\"--download=\") ||\n arg.startsWith(\"--state-dir=\")\n ) {\n continue;\n }\n\n if (!arg.startsWith(\"-\")) {\n return arg;\n }\n }\n\n return undefined;\n}\n\nexport function resolveStateDirFromArgv(args = process.argv.slice(2)): string {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg.startsWith(\"--state-dir=\")) {\n return resolve(arg.slice(\"--state-dir=\".length));\n }\n if (arg === \"--state-dir\") {\n return resolve(args[++i] || \"\");\n }\n }\n\n return join(homedir(), \".mikan\");\n}\n\nexport function resolveSentryDsn(): string | undefined {\n const fromFile = normalizeSettingsConfig(loadSettingsFile(getSettingsPath()) ?? {});\n if (fromFile.sentryDsn) {\n return fromFile.sentryDsn;\n }\n\n return process.env.SENTRY_DSN;\n}\n\nexport function createGlobalSettingsFile(stateDir: string): string {\n const settingsPath = join(stateDir, \"settings.json\");\n if (existsSync(settingsPath)) {\n throw new Error(`Global settings already exists at ${settingsPath}`);\n }\n if (!existsSync(stateDir)) {\n ensureDirExists(stateDir);\n }\n atomicWritePrivateFile(settingsPath, JSON.stringify(ONBOARD_SETTINGS, null, 2));\n return settingsPath;\n}\n\n/**\n * Externally-visible base URL of the link/OAuth server, e.g.\n * `https://mikan.example.com` (no trailing slash). Read from `LINK_URL` or\n * `MIKAN_LINK_URL`, the same env var the bot uses to build credential onboarding links.\n */\nexport function resolveLinkBaseUrl(): string | undefined {\n const raw = readEnv(\"LINK_URL\");\n if (!raw) return undefined;\n return raw.replace(/\\/+$/, \"\");\n}\n\nfunction hasDefinedValue(values: Record<string, unknown> | undefined): boolean {\n return values !== undefined && Object.values(values).some((value) => value !== undefined);\n}\n\nfunction compactSettingsConfig(config: SettingsFileConfig): SettingsFileConfig {\n return {\n ...(hasDefinedValue(config.llm) ? { llm: config.llm } : {}),\n ...(hasDefinedValue(config.sentry) ? { sentry: config.sentry } : {}),\n ...(hasDefinedValue(config.sandbox) ? { sandbox: config.sandbox } : {}),\n ...(hasDefinedValue(config.autoReply) ? { autoReply: config.autoReply } : {}),\n };\n}\n\nfunction patchSettingsConfig(\n existing: SettingsFileConfig,\n config: Partial<AgentConfig>,\n): SettingsFileConfig {\n const patched: SettingsFileConfig = {\n ...existing,\n llm: {\n ...existing.llm,\n ...(config.provider !== undefined ? { provider: config.provider } : {}),\n ...(config.model !== undefined ? { model: config.model } : {}),\n ...(config.thinkingLevel !== undefined ? { thinkingLevel: config.thinkingLevel } : {}),\n },\n sentry: {\n ...existing.sentry,\n ...(config.sentryDsn !== undefined ? { dsn: config.sentryDsn } : {}),\n },\n sandbox: {\n ...existing.sandbox,\n ...(config.sandboxCpus !== undefined ? { cpus: config.sandboxCpus } : {}),\n ...(config.sandboxMemory !== undefined ? { memory: config.sandboxMemory } : {}),\n ...(config.sandboxBoostCpus !== undefined || config.sandboxBoostMemory !== undefined\n ? {\n boost: {\n ...existing.sandbox?.boost,\n ...(config.sandboxBoostCpus !== undefined ? { cpus: config.sandboxBoostCpus } : {}),\n ...(config.sandboxBoostMemory !== undefined\n ? { memory: config.sandboxBoostMemory }\n : {}),\n },\n }\n : {}),\n },\n };\n return compactSettingsConfig(patched);\n}\n\nexport function saveAgentConfig(config: Partial<AgentConfig>): void {\n const settingsPath = join(getStateDir(), \"settings.json\");\n\n let existing: SettingsFileConfig = ONBOARD_SETTINGS;\n if (existsSync(settingsPath)) {\n try {\n existing = loadSettingsFile(settingsPath) ?? {};\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n const message = detail.startsWith(\"Malformed settings file\")\n ? detail.replace(\"Malformed settings file\", \"Refusing to overwrite malformed settings file\")\n : detail;\n throw new Error(message, { cause: err });\n }\n }\n\n const merged = patchSettingsConfig(existing, config);\n\n const dir = dirname(settingsPath);\n ensureDirExists(dir);\n\n atomicWritePrivateFile(settingsPath, JSON.stringify(merged, null, 2));\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform conversation history entry from log.jsonl.
|
|
3
|
+
*
|
|
4
|
+
* log.jsonl records what happened on the chat platform. sessions/*.jsonl are
|
|
5
|
+
* LLM working contexts/records derived from platform messages and agent runs.
|
|
6
|
+
*/
|
|
7
|
+
export interface ConversationLogMessage {
|
|
8
|
+
date?: string;
|
|
9
|
+
ts?: string;
|
|
10
|
+
threadTs?: string;
|
|
11
|
+
user?: string;
|
|
12
|
+
userName?: string;
|
|
13
|
+
text?: string;
|
|
14
|
+
isBot?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function findLogMessageById(conversationDir: string, messageId: string): Promise<ConversationLogMessage | null>;
|
|
17
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,kBAAkB,CACtC,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CA0BxC","sourcesContent":["import { join } from \"path\";\nimport { isRecord, parseJsonValue, readTextFileIfExists } from \"./file-guards.js\";\nimport * as log from \"./log.js\";\n\n/**\n * Platform conversation history entry from log.jsonl.\n *\n * log.jsonl records what happened on the chat platform. sessions/*.jsonl are\n * LLM working contexts/records derived from platform messages and agent runs.\n */\nexport interface ConversationLogMessage {\n date?: string;\n ts?: string;\n threadTs?: string;\n user?: string;\n userName?: string;\n text?: string;\n isBot?: boolean;\n}\n\nexport async function findLogMessageById(\n conversationDir: string,\n messageId: string,\n): Promise<ConversationLogMessage | null> {\n const logFile = join(conversationDir, \"log.jsonl\");\n const logContent = readTextFileIfExists(logFile);\n if (logContent === undefined) return null;\n const logLines = logContent.trim().split(\"\\n\").filter(Boolean);\n\n for (let i = logLines.length - 1; i >= 0; i--) {\n try {\n const entry = parseJsonValue(\n logLines[i],\n (value): value is ConversationLogMessage => isRecord(value),\n (detail) => (detail === \"unexpected JSON shape\" ? \"expected a JSON object\" : detail),\n );\n if (entry.ts === messageId) {\n return entry;\n }\n } catch (err) {\n log.logWarning(\n `Skipping malformed log entry at ${logFile}:${i + 1}`,\n err instanceof Error ? err.message : String(err),\n );\n continue;\n }\n }\n\n return null;\n}\n"]}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { isRecord, parseJsonValue, readTextFileIfExists } from "./file-guards.js";
|
|
3
|
+
import * as log from "./log.js";
|
|
4
|
+
export async function findLogMessageById(conversationDir, messageId) {
|
|
5
|
+
const logFile = join(conversationDir, "log.jsonl");
|
|
6
|
+
const logContent = readTextFileIfExists(logFile);
|
|
7
|
+
if (logContent === undefined)
|
|
8
|
+
return null;
|
|
9
|
+
const logLines = logContent.trim().split("\n").filter(Boolean);
|
|
10
|
+
for (let i = logLines.length - 1; i >= 0; i--) {
|
|
11
|
+
try {
|
|
12
|
+
const entry = parseJsonValue(logLines[i], (value) => isRecord(value), (detail) => (detail === "unexpected JSON shape" ? "expected a JSON object" : detail));
|
|
13
|
+
if (entry.ts === messageId) {
|
|
14
|
+
return entry;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
log.logWarning(`Skipping malformed log entry at ${logFile}:${i + 1}`, err instanceof Error ? err.message : String(err));
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAkBhC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,eAAuB,EACvB,SAAiB;IAEjB,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE/D,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,cAAc,CAC1B,QAAQ,CAAC,CAAC,CAAC,EACX,CAAC,KAAK,EAAmC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAC3D,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,uBAAuB,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,MAAM,CAAC,CACrF,CAAC;YACF,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;gBAC3B,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,UAAU,CACZ,mCAAmC,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EACrD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;YACF,SAAS;QACX,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import { join } from \"path\";\nimport { isRecord, parseJsonValue, readTextFileIfExists } from \"./file-guards.js\";\nimport * as log from \"./log.js\";\n\n/**\n * Platform conversation history entry from log.jsonl.\n *\n * log.jsonl records what happened on the chat platform. sessions/*.jsonl are\n * LLM working contexts/records derived from platform messages and agent runs.\n */\nexport interface ConversationLogMessage {\n date?: string;\n ts?: string;\n threadTs?: string;\n user?: string;\n userName?: string;\n text?: string;\n isBot?: boolean;\n}\n\nexport async function findLogMessageById(\n conversationDir: string,\n messageId: string,\n): Promise<ConversationLogMessage | null> {\n const logFile = join(conversationDir, \"log.jsonl\");\n const logContent = readTextFileIfExists(logFile);\n if (logContent === undefined) return null;\n const logLines = logContent.trim().split(\"\\n\").filter(Boolean);\n\n for (let i = logLines.length - 1; i >= 0; i--) {\n try {\n const entry = parseJsonValue(\n logLines[i],\n (value): value is ConversationLogMessage => isRecord(value),\n (detail) => (detail === \"unexpected JSON shape\" ? \"expected a JSON object\" : detail),\n );\n if (entry.ts === messageId) {\n return entry;\n }\n } catch (err) {\n log.logWarning(\n `Skipping malformed log entry at ${logFile}:${i + 1}`,\n err instanceof Error ? err.message : String(err),\n );\n continue;\n }\n }\n\n return null;\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ConversationLogMessage } from "./context.js";
|
|
2
|
+
export interface MaterializeTopLevelHistoryOptions {
|
|
3
|
+
conversationDir: string;
|
|
4
|
+
sessionDir: string;
|
|
5
|
+
cwd: string;
|
|
6
|
+
recentDays?: number;
|
|
7
|
+
maxMessages?: number;
|
|
8
|
+
now?: Date;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveUsableTopLevelHistorySession(options: MaterializeTopLevelHistoryOptions & {
|
|
11
|
+
existingSessionFile: string | null;
|
|
12
|
+
}): string | null;
|
|
13
|
+
export declare function materializeTopLevelHistorySession(options: MaterializeTopLevelHistoryOptions): string | null;
|
|
14
|
+
export declare function latestTopLevelHistoryTime(options: Omit<MaterializeTopLevelHistoryOptions, "sessionDir" | "cwd">): number | null;
|
|
15
|
+
export declare function readTopLevelHistoryMessages(options: Omit<MaterializeTopLevelHistoryOptions, "sessionDir" | "cwd">): ConversationLogMessage[];
|
|
16
|
+
//# sourceMappingURL=conversation-history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-history.d.ts","sourceRoot":"","sources":["../src/conversation-history.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAG3D,MAAM,WAAW,iCAAiC;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAKD,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,iCAAiC,GAAG;IAAE,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAClF,MAAM,GAAG,IAAI,CAKf;AAED,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,iCAAiC,GACzC,MAAM,GAAG,IAAI,CAiCf;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,IAAI,CAAC,iCAAiC,EAAE,YAAY,GAAG,KAAK,CAAC,GACrE,MAAM,GAAG,IAAI,CAMf;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,IAAI,CAAC,iCAAiC,EAAE,YAAY,GAAG,KAAK,CAAC,GACrE,sBAAsB,EAAE,CAiC1B","sourcesContent":["import { randomUUID } from \"crypto\";\nimport { mkdirSync, statSync } from \"fs\";\nimport { join } from \"path\";\nimport { isRecord, parseJsonValue, readTextFileIfExists } from \"./file-guards.js\";\nimport { atomicWritePrivateFile } from \"./fs-atomic.js\";\nimport type { ConversationLogMessage } from \"./context.js\";\nimport * as log from \"./log.js\";\n\nexport interface MaterializeTopLevelHistoryOptions {\n conversationDir: string;\n sessionDir: string;\n cwd: string;\n recentDays?: number;\n maxMessages?: number;\n now?: Date;\n}\n\nconst DEFAULT_RECENT_DAYS = 14;\nconst DEFAULT_MAX_MESSAGES = 200;\n\nexport function resolveUsableTopLevelHistorySession(\n options: MaterializeTopLevelHistoryOptions & { existingSessionFile: string | null },\n): string | null {\n if (options.existingSessionFile && isSessionFreshForTopLevelHistory(options)) {\n return options.existingSessionFile;\n }\n return materializeTopLevelHistorySession(options);\n}\n\nexport function materializeTopLevelHistorySession(\n options: MaterializeTopLevelHistoryOptions,\n): string | null {\n const messages = readTopLevelHistoryMessages(options);\n if (messages.length === 0) return null;\n\n mkdirSync(options.sessionDir, { recursive: true });\n const now = options.now ?? new Date();\n const sessionId = randomUUID();\n const filename = `${now.toISOString().replace(/[:.]/g, \"-\")}_${sessionId.slice(0, 8)}_history.jsonl`;\n const sessionFile = join(options.sessionDir, filename);\n const header = {\n type: \"session\",\n version: 3,\n id: sessionId,\n timestamp: now.toISOString(),\n cwd: options.cwd,\n source: {\n kind: \"platform-history\",\n file: \"log.jsonl\",\n recentDays: options.recentDays ?? DEFAULT_RECENT_DAYS,\n },\n };\n const entries = messages.map((message) => ({\n type: \"message\",\n id: randomUUID().slice(0, 8),\n parentId: null,\n timestamp: new Date(message.date ?? now.toISOString()).toISOString(),\n message: buildHistorySessionMessage(message),\n }));\n\n const content = [header, ...entries].map((entry) => JSON.stringify(entry)).join(\"\\n\");\n atomicWritePrivateFile(sessionFile, `${content}\\n`);\n atomicWritePrivateFile(join(options.sessionDir, \"current\"), filename);\n return sessionFile;\n}\n\nexport function latestTopLevelHistoryTime(\n options: Omit<MaterializeTopLevelHistoryOptions, \"sessionDir\" | \"cwd\">,\n): number | null {\n const messages = readTopLevelHistoryMessages({ ...options, maxMessages: 1 });\n const latest = messages.at(-1);\n if (!latest?.date) return null;\n const ms = new Date(latest.date).getTime();\n return Number.isFinite(ms) ? ms : null;\n}\n\nexport function readTopLevelHistoryMessages(\n options: Omit<MaterializeTopLevelHistoryOptions, \"sessionDir\" | \"cwd\">,\n): ConversationLogMessage[] {\n const logFile = join(options.conversationDir, \"log.jsonl\");\n const raw = readTextFileIfExists(logFile);\n if (raw === undefined) return [];\n\n const nowMs = (options.now ?? new Date()).getTime();\n const sinceMs = nowMs - (options.recentDays ?? DEFAULT_RECENT_DAYS) * 24 * 60 * 60 * 1000;\n const messages: ConversationLogMessage[] = [];\n const lines = raw.trim().split(\"\\n\").filter(Boolean);\n\n for (let i = 0; i < lines.length; i++) {\n try {\n const entry = parseJsonValue(\n lines[i],\n (value): value is ConversationLogMessage => isRecord(value),\n (detail) => (detail === \"unexpected JSON shape\" ? \"expected a JSON object\" : detail),\n );\n if (entry.threadTs) continue;\n if (!entry.text?.trim()) continue;\n if (entry.date) {\n const dateMs = new Date(entry.date).getTime();\n if (Number.isFinite(dateMs) && dateMs < sinceMs) continue;\n }\n messages.push(entry);\n } catch (err) {\n log.logWarning(\n `Skipping malformed log entry at ${logFile}:${i + 1}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n return messages.slice(-(options.maxMessages ?? DEFAULT_MAX_MESSAGES));\n}\n\nfunction isSessionFreshForTopLevelHistory(\n options: MaterializeTopLevelHistoryOptions & { existingSessionFile: string | null },\n): boolean {\n if (!options.existingSessionFile) return false;\n const latestHistoryMs = latestTopLevelHistoryTime(options);\n if (latestHistoryMs === null) return true;\n\n try {\n return statSync(options.existingSessionFile).mtimeMs >= latestHistoryMs;\n } catch {\n return false;\n }\n}\n\nfunction buildHistorySessionMessage(message: ConversationLogMessage): object {\n const base = {\n role: message.isBot ? \"assistant\" : \"user\",\n content: [{ type: \"text\", text: formatHistoryMessage(message) }],\n ...(message.date ? { timestamp: new Date(message.date).getTime() } : {}),\n };\n if (!message.isBot) return base;\n\n return {\n ...base,\n api: \"platform-history\",\n provider: \"platform-history\",\n model: \"platform-history\",\n usage: zeroUsage(),\n stopReason: \"stop\",\n };\n}\n\nfunction zeroUsage(): object {\n return {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n\nfunction formatHistoryMessage(message: ConversationLogMessage): string {\n const text = message.text?.trim() ?? \"\";\n if (message.isBot) return text;\n const userLabel = message.userName || message.user || \"unknown\";\n const timestamp = message.date ? formatLocalTimestamp(new Date(message.date)) : null;\n return timestamp ? `[${timestamp}] [${userLabel}]: ${text}` : `[${userLabel}]: ${text}`;\n}\n\nfunction formatLocalTimestamp(date: Date): string {\n const offset = -date.getTimezoneOffset();\n const sign = offset >= 0 ? \"+\" : \"-\";\n const abs = Math.abs(offset);\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +\n `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}` +\n `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`\n );\n}\n\nfunction pad(n: number): string {\n return n.toString().padStart(2, \"0\");\n}\n"]}
|