@hivemagnus/ardent 0.0.0-stage → 1.0.2
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/LICENSE +202 -0
- package/README.md +274 -2
- package/dist/a2a-client.d.ts +35 -0
- package/dist/a2a-client.js +172 -0
- package/dist/a2a-hermes-alias.d.ts +3 -0
- package/dist/a2a-hermes-alias.js +57 -0
- package/dist/a2a-idempotency.d.ts +13 -0
- package/dist/a2a-idempotency.js +86 -0
- package/dist/a2a-peers.d.ts +28 -0
- package/dist/a2a-peers.js +108 -0
- package/dist/a2a-push-inbox.d.ts +22 -0
- package/dist/a2a-push-inbox.js +84 -0
- package/dist/a2a-push.d.ts +27 -0
- package/dist/a2a-push.js +188 -0
- package/dist/a2a-server.d.ts +37 -0
- package/dist/a2a-server.js +186 -0
- package/dist/a2a-store.d.ts +10 -0
- package/dist/a2a-store.js +122 -0
- package/dist/a2a.d.ts +19 -0
- package/dist/a2a.js +466 -0
- package/dist/api.d.ts +6 -0
- package/dist/api.js +381 -0
- package/dist/artifacts.d.ts +15 -0
- package/dist/artifacts.js +41 -0
- package/dist/backend-events.d.ts +49 -0
- package/dist/backend-events.js +8 -0
- package/dist/channels.d.ts +52 -0
- package/dist/channels.js +184 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +845 -0
- package/dist/codex-output-budget.d.ts +17 -0
- package/dist/codex-output-budget.js +31 -0
- package/dist/codex-output-budget.test.d.ts +1 -0
- package/dist/codex-output-budget.test.js +29 -0
- package/dist/codex.d.ts +82 -0
- package/dist/codex.js +431 -0
- package/dist/config.d.ts +171 -0
- package/dist/config.js +244 -0
- package/dist/context-checkpoints.d.ts +49 -0
- package/dist/context-checkpoints.js +248 -0
- package/dist/context.d.ts +16 -0
- package/dist/context.js +101 -0
- package/dist/gateway-lock.d.ts +18 -0
- package/dist/gateway-lock.js +104 -0
- package/dist/hermes-compat.d.ts +15 -0
- package/dist/hermes-compat.js +62 -0
- package/dist/identity.d.ts +15 -0
- package/dist/identity.js +52 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +8 -0
- package/dist/job-engine.d.ts +222 -0
- package/dist/job-engine.js +497 -0
- package/dist/job-news-handlers.d.ts +54 -0
- package/dist/job-news-handlers.js +153 -0
- package/dist/job-ownership.d.ts +9 -0
- package/dist/job-ownership.js +17 -0
- package/dist/json-schema.d.ts +9 -0
- package/dist/json-schema.js +20 -0
- package/dist/llamacpp.d.ts +49 -0
- package/dist/llamacpp.js +216 -0
- package/dist/magnus-control.d.ts +3 -0
- package/dist/magnus-control.js +99 -0
- package/dist/magnus-node-dependency-impact.d.ts +1 -0
- package/dist/magnus-node-dependency-impact.js +126 -0
- package/dist/magnus-owner-reads.d.ts +4 -0
- package/dist/magnus-owner-reads.js +99 -0
- package/dist/magnus-readiness.d.ts +1 -0
- package/dist/magnus-readiness.js +108 -0
- package/dist/mattermost.d.ts +18 -0
- package/dist/mattermost.js +303 -0
- package/dist/memory-tools.d.ts +5 -0
- package/dist/memory-tools.js +49 -0
- package/dist/memory.d.ts +40 -0
- package/dist/memory.js +128 -0
- package/dist/model-policy.d.ts +64 -0
- package/dist/model-policy.js +56 -0
- package/dist/news.d.ts +10 -0
- package/dist/news.js +291 -0
- package/dist/owner-read.d.ts +32 -0
- package/dist/owner-read.js +130 -0
- package/dist/plugins.d.ts +74 -0
- package/dist/plugins.js +115 -0
- package/dist/research.d.ts +110 -0
- package/dist/research.js +164 -0
- package/dist/runtime.d.ts +33 -0
- package/dist/runtime.js +665 -0
- package/dist/skills.d.ts +13 -0
- package/dist/skills.js +87 -0
- package/dist/store.d.ts +142 -0
- package/dist/store.js +322 -0
- package/dist/team-artifacts.d.ts +138 -0
- package/dist/team-artifacts.js +241 -0
- package/dist/team-child.d.ts +21 -0
- package/dist/team-child.js +130 -0
- package/dist/team-contract.d.ts +116 -0
- package/dist/team-contract.js +84 -0
- package/dist/team-control.d.ts +8 -0
- package/dist/team-control.js +152 -0
- package/dist/telegram.d.ts +35 -0
- package/dist/telegram.js +284 -0
- package/dist/tools.d.ts +48 -0
- package/dist/tools.js +58 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/model-policy.default.json +20 -0
- package/package.json +84 -4
- package/profiles/assistant/IDENTITY.md +9 -0
- package/profiles/assistant/SOUL.md +8 -0
- package/profiles/assistant/config.yaml +88 -0
- package/profiles/assistant/peers.yaml +12 -0
- package/profiles/assistant/skills/grounded-answers/SKILL.md +18 -0
- package/protocol/LICENSE-A2A.txt +202 -0
- package/protocol/README.md +12 -0
- package/protocol/a2a.proto +811 -0
- package/protocol/lock.json +9 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** The host's IANA timezone, or UTC when it cannot be determined. */
|
|
3
|
+
export declare function systemTimezone(): string;
|
|
4
|
+
export declare const HiveMemConfigSchema: z.ZodObject<{
|
|
5
|
+
provider: z.ZodLiteral<"hivemem">;
|
|
6
|
+
base_url: z.ZodString;
|
|
7
|
+
token_env: z.ZodString;
|
|
8
|
+
agent_id: z.ZodString;
|
|
9
|
+
workspace_id: z.ZodString;
|
|
10
|
+
default_scope: z.ZodDefault<z.ZodEnum<{
|
|
11
|
+
private: "private";
|
|
12
|
+
project: "project";
|
|
13
|
+
}>>;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export type HiveMemConfig = z.infer<typeof HiveMemConfigSchema>;
|
|
16
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
17
|
+
schema_version: z.ZodLiteral<1>;
|
|
18
|
+
profile: z.ZodString;
|
|
19
|
+
name: z.ZodString;
|
|
20
|
+
product: z.ZodDefault<z.ZodString>;
|
|
21
|
+
timezone: z.ZodDefault<z.ZodString>;
|
|
22
|
+
identity: z.ZodDefault<z.ZodObject<{
|
|
23
|
+
soul_file: z.ZodDefault<z.ZodString>;
|
|
24
|
+
identity_file: z.ZodDefault<z.ZodString>;
|
|
25
|
+
}, z.core.$strict>>;
|
|
26
|
+
execution: z.ZodObject<{
|
|
27
|
+
backend: z.ZodEnum<{
|
|
28
|
+
llamacpp: "llamacpp";
|
|
29
|
+
codex: "codex";
|
|
30
|
+
}>;
|
|
31
|
+
max_model_steps: z.ZodNumber;
|
|
32
|
+
max_tool_calls: z.ZodNumber;
|
|
33
|
+
max_parallel_tools: z.ZodNumber;
|
|
34
|
+
deadline_seconds: z.ZodNumber;
|
|
35
|
+
max_delegation_depth: z.ZodNumber;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
providers: z.ZodObject<{
|
|
38
|
+
llamacpp: z.ZodObject<{
|
|
39
|
+
base_url: z.ZodString;
|
|
40
|
+
model: z.ZodString;
|
|
41
|
+
api_key_env: z.ZodString;
|
|
42
|
+
request_timeout_seconds: z.ZodNumber;
|
|
43
|
+
context_window_tokens: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
max_output_tokens: z.ZodOptional<z.ZodNumber>;
|
|
45
|
+
reasoning_effort: z.ZodOptional<z.ZodEnum<{
|
|
46
|
+
none: "none";
|
|
47
|
+
low: "low";
|
|
48
|
+
medium: "medium";
|
|
49
|
+
xhigh: "xhigh";
|
|
50
|
+
}>>;
|
|
51
|
+
}, z.core.$strict>;
|
|
52
|
+
codex: z.ZodObject<{
|
|
53
|
+
command: z.ZodString;
|
|
54
|
+
model: z.ZodString;
|
|
55
|
+
auth_mode: z.ZodDefault<z.ZodEnum<{
|
|
56
|
+
chatgpt: "chatgpt";
|
|
57
|
+
api_key: "api_key";
|
|
58
|
+
}>>;
|
|
59
|
+
approval_policy: z.ZodLiteral<"never">;
|
|
60
|
+
sandbox: z.ZodLiteral<"read-only">;
|
|
61
|
+
}, z.core.$strict>;
|
|
62
|
+
}, z.core.$strict>;
|
|
63
|
+
memory: z.ZodDefault<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
64
|
+
provider: z.ZodLiteral<"none">;
|
|
65
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
66
|
+
provider: z.ZodLiteral<"hivemem">;
|
|
67
|
+
base_url: z.ZodString;
|
|
68
|
+
token_env: z.ZodString;
|
|
69
|
+
agent_id: z.ZodString;
|
|
70
|
+
workspace_id: z.ZodString;
|
|
71
|
+
default_scope: z.ZodDefault<z.ZodEnum<{
|
|
72
|
+
private: "private";
|
|
73
|
+
project: "project";
|
|
74
|
+
}>>;
|
|
75
|
+
}, z.core.$strict>], "provider">>;
|
|
76
|
+
research: z.ZodDefault<z.ZodObject<{
|
|
77
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
78
|
+
searxng_url: z.ZodDefault<z.ZodString>;
|
|
79
|
+
firecrawl_url: z.ZodDefault<z.ZodString>;
|
|
80
|
+
firecrawl_token_env: z.ZodDefault<z.ZodString>;
|
|
81
|
+
}, z.core.$strict>>;
|
|
82
|
+
skills: z.ZodDefault<z.ZodObject<{
|
|
83
|
+
directories: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
84
|
+
enabled: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
85
|
+
}, z.core.$strict>>;
|
|
86
|
+
a2a: z.ZodDefault<z.ZodObject<{
|
|
87
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
88
|
+
protocol_version: z.ZodDefault<z.ZodLiteral<"1.0">>;
|
|
89
|
+
bindings: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
90
|
+
jsonrpc: "jsonrpc";
|
|
91
|
+
"http-json": "http-json";
|
|
92
|
+
grpc: "grpc";
|
|
93
|
+
}>>>;
|
|
94
|
+
public_url: z.ZodDefault<z.ZodString>;
|
|
95
|
+
listen_host: z.ZodDefault<z.ZodString>;
|
|
96
|
+
listen_port: z.ZodOptional<z.ZodNumber>;
|
|
97
|
+
peers_file: z.ZodDefault<z.ZodString>;
|
|
98
|
+
hermes_compat: z.ZodDefault<z.ZodBoolean>;
|
|
99
|
+
skill: z.ZodOptional<z.ZodObject<{
|
|
100
|
+
id: z.ZodString;
|
|
101
|
+
name: z.ZodString;
|
|
102
|
+
description: z.ZodString;
|
|
103
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
104
|
+
example: z.ZodOptional<z.ZodString>;
|
|
105
|
+
}, z.core.$strict>>;
|
|
106
|
+
}, z.core.$strict>>;
|
|
107
|
+
channels: z.ZodDefault<z.ZodObject<{
|
|
108
|
+
telegram: z.ZodDefault<z.ZodObject<{
|
|
109
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
110
|
+
token_env: z.ZodDefault<z.ZodString>;
|
|
111
|
+
allowed_user_ids: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
112
|
+
allowed_chat_ids: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
113
|
+
receive_mode: z.ZodDefault<z.ZodLiteral<"polling">>;
|
|
114
|
+
}, z.core.$strict>>;
|
|
115
|
+
mattermost: z.ZodDefault<z.ZodObject<{
|
|
116
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
117
|
+
base_url: z.ZodDefault<z.ZodString>;
|
|
118
|
+
token_env: z.ZodDefault<z.ZodString>;
|
|
119
|
+
allowed_user_ids: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
120
|
+
allowed_channel_ids: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
121
|
+
require_mention: z.ZodDefault<z.ZodBoolean>;
|
|
122
|
+
reply_mode: z.ZodDefault<z.ZodEnum<{
|
|
123
|
+
thread: "thread";
|
|
124
|
+
flat: "flat";
|
|
125
|
+
}>>;
|
|
126
|
+
}, z.core.$strict>>;
|
|
127
|
+
}, z.core.$strict>>;
|
|
128
|
+
integrations: z.ZodDefault<z.ZodObject<{
|
|
129
|
+
magnus_control: z.ZodDefault<z.ZodObject<{
|
|
130
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
131
|
+
base_url: z.ZodDefault<z.ZodString>;
|
|
132
|
+
token_env: z.ZodDefault<z.ZodString>;
|
|
133
|
+
}, z.core.$strict>>;
|
|
134
|
+
}, z.core.$strict>>;
|
|
135
|
+
plugins: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
136
|
+
api: z.ZodDefault<z.ZodObject<{
|
|
137
|
+
host: z.ZodDefault<z.ZodString>;
|
|
138
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
139
|
+
token_env: z.ZodDefault<z.ZodString>;
|
|
140
|
+
}, z.core.$strict>>;
|
|
141
|
+
}, z.core.$strict>;
|
|
142
|
+
export type ArdentConfig = z.infer<typeof ConfigSchema>;
|
|
143
|
+
export type Profile = {
|
|
144
|
+
name: string;
|
|
145
|
+
path: string;
|
|
146
|
+
};
|
|
147
|
+
export type LoadedConfig = {
|
|
148
|
+
config: ArdentConfig;
|
|
149
|
+
revision: string;
|
|
150
|
+
text: string;
|
|
151
|
+
path: string;
|
|
152
|
+
};
|
|
153
|
+
export declare function ardentHome(env?: NodeJS.ProcessEnv): string;
|
|
154
|
+
export declare const DEFAULT_PROFILE = "assistant";
|
|
155
|
+
export declare function profilePath(name?: string, override?: string, env?: NodeJS.ProcessEnv): string;
|
|
156
|
+
export declare function revision(text: string): string;
|
|
157
|
+
export declare function parseConfig(text: string): ArdentConfig;
|
|
158
|
+
export declare function loadConfig(dir: string): Promise<LoadedConfig>;
|
|
159
|
+
export declare function resolveReference(value: string, env?: NodeJS.ProcessEnv): string | undefined;
|
|
160
|
+
export declare function runtimeProblems(config: ArdentConfig, env?: NodeJS.ProcessEnv): string[];
|
|
161
|
+
export declare function redactedConfig(config: ArdentConfig): ArdentConfig;
|
|
162
|
+
/** Optional Magnus Control read tools are enabled by config or `ARDENT_MAGNUS_CONTROL_TOOLS=true`. */
|
|
163
|
+
export declare function magnusControlEnabled(config: ArdentConfig, env?: NodeJS.ProcessEnv): boolean;
|
|
164
|
+
/** The bundled neutral profile template copied by `ardent init`. */
|
|
165
|
+
export declare const PROFILE_TEMPLATE = "assistant";
|
|
166
|
+
export declare function initProfile(dir: string, options?: {
|
|
167
|
+
profile?: string;
|
|
168
|
+
apiPort?: number;
|
|
169
|
+
template?: string;
|
|
170
|
+
}): Promise<void>;
|
|
171
|
+
export declare function saveConfig(dir: string, nextText: string, expectedRevision: string): Promise<string>;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { createHash, randomBytes, randomUUID } from 'node:crypto';
|
|
2
|
+
import { readFile, writeFile, mkdir, rename, copyFile, chmod, cp, open, unlink, lstat, rm, readdir } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import YAML from 'yaml';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
const envName = /^[A-Z][A-Z0-9_]*$/;
|
|
9
|
+
const pathName = /^[A-Za-z0-9._-]+\.md$/;
|
|
10
|
+
function safeUrl(value) {
|
|
11
|
+
try {
|
|
12
|
+
const url = new URL(value);
|
|
13
|
+
return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password && !url.hash && !url.search;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const urlReference = z.string().min(1).max(2048).refine(value => /^\$\{[A-Z][A-Z0-9_]*\}$/.test(value) || safeUrl(value), 'Expected an HTTP(S) URL without embedded credentials or an environment reference');
|
|
20
|
+
const provider = z.object({ base_url: urlReference, model: z.string().min(1).max(200), api_key_env: z.string().regex(envName), request_timeout_seconds: z.number().int().min(1).max(600), context_window_tokens: z.number().int().min(2048).max(2_000_000).optional(), max_output_tokens: z.number().int().min(256).max(32768).optional(), reasoning_effort: z.enum(['none', 'low', 'medium', 'xhigh']).optional() }).strict();
|
|
21
|
+
/** The host's IANA timezone, or UTC when it cannot be determined. */
|
|
22
|
+
export function systemTimezone() {
|
|
23
|
+
try {
|
|
24
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return 'UTC';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const ianaTimezone = z.string().min(1).max(100).refine(value => {
|
|
31
|
+
try {
|
|
32
|
+
new Intl.DateTimeFormat('en-US', { timeZone: value });
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}, 'Unknown IANA timezone');
|
|
39
|
+
const NoMemory = z.object({ provider: z.literal('none') }).strict();
|
|
40
|
+
export const HiveMemConfigSchema = z.object({ provider: z.literal('hivemem'), base_url: urlReference, token_env: z.string().regex(envName), agent_id: z.string().min(1).max(100), workspace_id: z.string().min(1).max(100), default_scope: z.enum(['private', 'project']).default('private') }).strict();
|
|
41
|
+
const TelegramChannel = z.object({ enabled: z.boolean().default(false), token_env: z.string().regex(envName).default('TELEGRAM_BOT_TOKEN'), allowed_user_ids: z.array(z.number().int().positive()).default([]), allowed_chat_ids: z.array(z.number().int()).default([]), receive_mode: z.literal('polling').default('polling') }).strict();
|
|
42
|
+
const MattermostChannel = z.object({ enabled: z.boolean().default(false), base_url: urlReference.default('${MATTERMOST_URL}'), token_env: z.string().regex(envName).default('MATTERMOST_TOKEN'), allowed_user_ids: z.array(z.string().min(1)).default([]), allowed_channel_ids: z.array(z.string().min(1)).default([]), require_mention: z.boolean().default(true), reply_mode: z.enum(['thread', 'flat']).default('thread') }).strict();
|
|
43
|
+
/** A plugin module specifier: an npm package name, or an absolute path / file URL to an ES module. */
|
|
44
|
+
const pluginSpecifier = z.string().min(1).max(500).refine(value => /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*(?:\/[A-Za-z0-9._-]+)*$/.test(value) || value.startsWith('/') || value.startsWith('file://'), 'Expected an npm package name, an absolute path, or a file:// URL');
|
|
45
|
+
const magnusControlDefault = { enabled: false, base_url: '${ARDENT_MAGNUS_API_URL}', token_env: 'ARDENT_MAGNUS_READ_TOKEN' };
|
|
46
|
+
const a2aDefault = { enabled: false, protocol_version: '1.0', bindings: ['jsonrpc', 'http-json'], public_url: '${ARDENT_A2A_PUBLIC_URL}', listen_host: '127.0.0.1', peers_file: 'peers.yaml', hermes_compat: false };
|
|
47
|
+
const researchDefault = { enabled: false, searxng_url: '${ARDENT_SEARXNG_URL}', firecrawl_url: '${ARDENT_FIRECRAWL_URL}', firecrawl_token_env: 'FIRECRAWL_API_KEY' };
|
|
48
|
+
export const ConfigSchema = z.object({
|
|
49
|
+
schema_version: z.literal(1),
|
|
50
|
+
profile: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/),
|
|
51
|
+
name: z.string().min(1).max(100),
|
|
52
|
+
product: z.string().min(1).max(100).default('Ardent'),
|
|
53
|
+
/** IANA timezone for schedules and dates shown to the model. Defaults to the host zone. */
|
|
54
|
+
timezone: ianaTimezone.default(systemTimezone),
|
|
55
|
+
identity: z.object({ soul_file: z.string().regex(pathName).default('SOUL.md'), identity_file: z.string().regex(pathName).default('IDENTITY.md') }).strict().default({ soul_file: 'SOUL.md', identity_file: 'IDENTITY.md' }),
|
|
56
|
+
execution: z.object({ backend: z.enum(['llamacpp', 'codex']), max_model_steps: z.number().int().min(1).max(200), max_tool_calls: z.number().int().min(0).max(500), max_parallel_tools: z.number().int().min(1).max(16), deadline_seconds: z.number().int().min(1).max(86400), max_delegation_depth: z.number().int().min(0).max(8) }).strict(),
|
|
57
|
+
providers: z.object({ llamacpp: provider, codex: z.object({ command: z.string().min(1).max(500), model: z.string().min(1).max(200), auth_mode: z.enum(['chatgpt', 'api_key']).default('chatgpt'), approval_policy: z.literal('never'), sandbox: z.literal('read-only') }).strict() }).strict(),
|
|
58
|
+
/** Long-term memory. `none` (default) disables memory tools; `hivemem` uses a HiveMem server. */
|
|
59
|
+
memory: z.discriminatedUnion('provider', [NoMemory, HiveMemConfigSchema]).default({ provider: 'none' }),
|
|
60
|
+
research: z.object({ enabled: z.boolean().default(false), searxng_url: urlReference.default('${ARDENT_SEARXNG_URL}'), firecrawl_url: urlReference.default('${ARDENT_FIRECRAWL_URL}'), firecrawl_token_env: z.string().regex(envName).default('FIRECRAWL_API_KEY') }).strict().default(researchDefault),
|
|
61
|
+
skills: z.object({ directories: z.array(z.string().regex(/^[A-Za-z0-9._/-]+$/)).max(16).default(['skills']), enabled: z.array(z.string().min(1).max(100)).max(100).default([]) }).strict().default({ directories: ['skills'], enabled: [] }),
|
|
62
|
+
a2a: z.object({ enabled: z.boolean().default(false), protocol_version: z.literal('1.0').default('1.0'), bindings: z.array(z.enum(['jsonrpc', 'http-json', 'grpc'])).min(1).default(['jsonrpc', 'http-json']), public_url: urlReference.default('${ARDENT_A2A_PUBLIC_URL}'), listen_host: z.string().min(1).default('127.0.0.1'), listen_port: z.number().int().min(1).max(65534).optional(), peers_file: z.string().regex(/^[A-Za-z0-9._-]+\.yaml$/).default('peers.yaml'),
|
|
63
|
+
/** Serve Hermes Agent team-card discovery and slash-style JSON-RPC method aliases. */
|
|
64
|
+
hermes_compat: z.boolean().default(false),
|
|
65
|
+
/** Agent Card skill advertised to peers. */
|
|
66
|
+
skill: z.object({ id: z.string().regex(/^[a-z][a-z0-9-]{0,63}$/), name: z.string().min(1).max(100), description: z.string().min(1).max(500), tags: z.array(z.string().min(1).max(40)).max(10).default([]), example: z.string().min(1).max(300).optional() }).strict().optional() }).strict().default(a2aDefault),
|
|
67
|
+
channels: z.object({ telegram: TelegramChannel.default(TelegramChannel.parse({})), mattermost: MattermostChannel.default(MattermostChannel.parse({})) }).strict().default({ telegram: TelegramChannel.parse({}), mattermost: MattermostChannel.parse({}) }),
|
|
68
|
+
integrations: z.object({
|
|
69
|
+
/** Optional read-only Magnus Control tools. URL and token come only from config or the environment. */
|
|
70
|
+
magnus_control: z.object({ enabled: z.boolean().default(false), base_url: urlReference.default('${ARDENT_MAGNUS_API_URL}'), token_env: z.string().regex(envName).default('ARDENT_MAGNUS_READ_TOKEN') }).strict().default(magnusControlDefault)
|
|
71
|
+
}).strict().default({ magnus_control: magnusControlDefault }),
|
|
72
|
+
/** Plugin modules loaded by the gateway (npm package names, absolute paths, or file:// URLs). */
|
|
73
|
+
plugins: z.array(pluginSpecifier).max(32).default([]),
|
|
74
|
+
api: z.object({ host: z.string().min(1).default('127.0.0.1'), port: z.number().int().min(1).max(65535).default(9910), token_env: z.string().regex(envName).default('ARDENT_API_TOKEN') }).strict().default({ host: '127.0.0.1', port: 9910, token_env: 'ARDENT_API_TOKEN' })
|
|
75
|
+
}).strict();
|
|
76
|
+
export function ardentHome(env = process.env) {
|
|
77
|
+
return resolve(env.ARDENT_HOME ?? join(homedir(), '.ardent'));
|
|
78
|
+
}
|
|
79
|
+
export const DEFAULT_PROFILE = 'assistant';
|
|
80
|
+
export function profilePath(name = DEFAULT_PROFILE, override, env = process.env) {
|
|
81
|
+
if (override) {
|
|
82
|
+
if (!isAbsolute(override))
|
|
83
|
+
throw new Error('Profile path override must be absolute');
|
|
84
|
+
return resolve(override);
|
|
85
|
+
}
|
|
86
|
+
if (!/^[A-Za-z0-9_-]{1,64}$/.test(name))
|
|
87
|
+
throw new Error('Invalid profile name');
|
|
88
|
+
return join(ardentHome(env), 'profiles', name);
|
|
89
|
+
}
|
|
90
|
+
export function revision(text) { return createHash('sha256').update(text).digest('hex'); }
|
|
91
|
+
export function parseConfig(text) {
|
|
92
|
+
const doc = YAML.parseDocument(text, { uniqueKeys: true });
|
|
93
|
+
if (doc.errors.length)
|
|
94
|
+
throw new Error(`Invalid YAML: ${doc.errors[0]?.message ?? 'unknown error'}`);
|
|
95
|
+
const value = doc.toJS();
|
|
96
|
+
const parsed = ConfigSchema.safeParse(value);
|
|
97
|
+
if (!parsed.success)
|
|
98
|
+
throw new Error(`Invalid Ardent config: ${parsed.error.issues.map(i => `${i.path.join('.')}: ${i.message}`).join('; ')}`);
|
|
99
|
+
return parsed.data;
|
|
100
|
+
}
|
|
101
|
+
export async function loadConfig(dir) {
|
|
102
|
+
const path = join(dir, 'config.yaml');
|
|
103
|
+
const text = await readFile(path, 'utf8');
|
|
104
|
+
return { config: parseConfig(text), revision: revision(text), text, path };
|
|
105
|
+
}
|
|
106
|
+
export function resolveReference(value, env = process.env) {
|
|
107
|
+
const match = /^\$\{([A-Z][A-Z0-9_]*)\}$/.exec(value);
|
|
108
|
+
if (!match)
|
|
109
|
+
return value;
|
|
110
|
+
return env[match[1] ?? ''];
|
|
111
|
+
}
|
|
112
|
+
export function runtimeProblems(config, env = process.env) {
|
|
113
|
+
const problems = [];
|
|
114
|
+
const need = (field, value) => {
|
|
115
|
+
const found = resolveReference(value, env);
|
|
116
|
+
if (!found)
|
|
117
|
+
problems.push(`${field} is unresolved`);
|
|
118
|
+
return found;
|
|
119
|
+
};
|
|
120
|
+
if (config.execution.backend === 'llamacpp') {
|
|
121
|
+
const base = need('providers.llamacpp.base_url', config.providers.llamacpp.base_url);
|
|
122
|
+
need('providers.llamacpp.model', config.providers.llamacpp.model);
|
|
123
|
+
if (base && !safeUrl(base))
|
|
124
|
+
problems.push('providers.llamacpp.base_url must be a credential-free HTTP(S) URL');
|
|
125
|
+
}
|
|
126
|
+
else
|
|
127
|
+
need('providers.codex.model', config.providers.codex.model);
|
|
128
|
+
if (config.channels.telegram.enabled && !env[config.channels.telegram.token_env])
|
|
129
|
+
problems.push('Telegram token is missing');
|
|
130
|
+
if (config.channels.mattermost.enabled && !env[config.channels.mattermost.token_env])
|
|
131
|
+
problems.push('Mattermost token is missing');
|
|
132
|
+
if (config.channels.mattermost.enabled)
|
|
133
|
+
need('channels.mattermost.base_url', config.channels.mattermost.base_url);
|
|
134
|
+
if (config.a2a.enabled)
|
|
135
|
+
need('a2a.public_url', config.a2a.public_url);
|
|
136
|
+
if (config.memory.provider === 'hivemem') {
|
|
137
|
+
need('memory.base_url', config.memory.base_url);
|
|
138
|
+
if (!env[config.memory.token_env])
|
|
139
|
+
problems.push('HiveMem token is missing');
|
|
140
|
+
}
|
|
141
|
+
if (config.research.enabled) {
|
|
142
|
+
need('research.searxng_url', config.research.searxng_url);
|
|
143
|
+
need('research.firecrawl_url', config.research.firecrawl_url);
|
|
144
|
+
}
|
|
145
|
+
if (magnusControlEnabled(config, env)) {
|
|
146
|
+
need('integrations.magnus_control.base_url', config.integrations.magnus_control.base_url);
|
|
147
|
+
if (!env[config.integrations.magnus_control.token_env])
|
|
148
|
+
problems.push('Magnus Control read token is missing');
|
|
149
|
+
}
|
|
150
|
+
return problems;
|
|
151
|
+
}
|
|
152
|
+
export function redactedConfig(config) {
|
|
153
|
+
return structuredClone(config); // Values are references only; no resolution is performed here.
|
|
154
|
+
}
|
|
155
|
+
/** Optional Magnus Control read tools are enabled by config or `ARDENT_MAGNUS_CONTROL_TOOLS=true`. */
|
|
156
|
+
export function magnusControlEnabled(config, env = process.env) {
|
|
157
|
+
return config.integrations.magnus_control.enabled || env.ARDENT_MAGNUS_CONTROL_TOOLS === 'true';
|
|
158
|
+
}
|
|
159
|
+
const templates = fileURLToPath(new URL('../profiles/', import.meta.url));
|
|
160
|
+
/** The bundled neutral profile template copied by `ardent init`. */
|
|
161
|
+
export const PROFILE_TEMPLATE = 'assistant';
|
|
162
|
+
export async function initProfile(dir, options = {}) {
|
|
163
|
+
const profile = options.profile ?? DEFAULT_PROFILE;
|
|
164
|
+
if (!/^[A-Za-z0-9_-]{1,64}$/.test(profile))
|
|
165
|
+
throw new Error('Invalid profile name');
|
|
166
|
+
const apiPort = options.apiPort ?? 9910;
|
|
167
|
+
if (!Number.isSafeInteger(apiPort) || apiPort < 1024 || apiPort > 65535)
|
|
168
|
+
throw new Error('Profile API port must be 1024-65535');
|
|
169
|
+
const target = resolve(dir);
|
|
170
|
+
const templateName = options.template ?? PROFILE_TEMPLATE;
|
|
171
|
+
if (!/^[A-Za-z0-9_-]{1,64}$/.test(templateName))
|
|
172
|
+
throw new Error('Invalid profile template');
|
|
173
|
+
const template = join(templates, templateName);
|
|
174
|
+
const parent = dirname(target);
|
|
175
|
+
await mkdir(parent, { recursive: true, mode: 0o700 });
|
|
176
|
+
const lockPath = join(parent, `.ardent-init-${createHash('sha256').update(target).digest('hex').slice(0, 24)}.lock`);
|
|
177
|
+
const lock = await open(lockPath, 'wx', 0o600).catch((error) => {
|
|
178
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'EEXIST')
|
|
179
|
+
throw new Error('Profile initialization is already in progress');
|
|
180
|
+
throw error;
|
|
181
|
+
});
|
|
182
|
+
const staged = join(parent, `.${profile}.${randomUUID()}.staging`);
|
|
183
|
+
try {
|
|
184
|
+
const existing = await lstat(target).catch((error) => {
|
|
185
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT')
|
|
186
|
+
return undefined;
|
|
187
|
+
throw error;
|
|
188
|
+
});
|
|
189
|
+
if (existing && (!existing.isDirectory() || (await readdir(target)).length > 0))
|
|
190
|
+
throw new Error('Profile path already exists');
|
|
191
|
+
await mkdir(staged, { mode: 0o700 });
|
|
192
|
+
for (const name of ['SOUL.md', 'IDENTITY.md', 'peers.yaml']) {
|
|
193
|
+
await copyFile(join(template, name), join(staged, name));
|
|
194
|
+
await chmod(join(staged, name), 0o600);
|
|
195
|
+
}
|
|
196
|
+
const document = YAML.parseDocument(await readFile(join(template, 'config.yaml'), 'utf8'));
|
|
197
|
+
document.set('profile', profile);
|
|
198
|
+
if (profile !== templateName)
|
|
199
|
+
document.set('name', profile);
|
|
200
|
+
if (!document.has('timezone'))
|
|
201
|
+
document.set('timezone', systemTimezone());
|
|
202
|
+
document.setIn(['api', 'port'], apiPort);
|
|
203
|
+
const text = String(document);
|
|
204
|
+
parseConfig(text);
|
|
205
|
+
await writeFile(join(staged, 'config.yaml'), text, { flag: 'wx', mode: 0o600 });
|
|
206
|
+
await writeFile(join(staged, '.env'), `ARDENT_API_TOKEN=${randomBytes(32).toString('hex')}\n`, { flag: 'wx', mode: 0o600 });
|
|
207
|
+
for (const name of ['workspace', 'state', 'artifacts', 'logs'])
|
|
208
|
+
await mkdir(join(staged, name), { mode: 0o700 });
|
|
209
|
+
await cp(join(template, 'skills'), join(staged, 'skills'), { recursive: true, force: false, errorOnExist: true });
|
|
210
|
+
await rename(staged, target);
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
await rm(staged, { recursive: true, force: true });
|
|
214
|
+
await lock.close();
|
|
215
|
+
await unlink(lockPath);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
export async function saveConfig(dir, nextText, expectedRevision) {
|
|
219
|
+
const path = join(dir, 'config.yaml');
|
|
220
|
+
const lockPath = join(dir, '.config.write.lock');
|
|
221
|
+
const lock = await open(lockPath, 'wx', 0o600).catch((error) => {
|
|
222
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'EEXIST')
|
|
223
|
+
throw new Error('Configuration edit is already in progress');
|
|
224
|
+
throw error;
|
|
225
|
+
});
|
|
226
|
+
let tmp;
|
|
227
|
+
try {
|
|
228
|
+
const current = await loadConfig(dir);
|
|
229
|
+
if (current.revision !== expectedRevision)
|
|
230
|
+
throw new Error('Configuration revision conflict');
|
|
231
|
+
parseConfig(nextText);
|
|
232
|
+
tmp = join(dirname(path), `.config.${randomUUID()}.tmp`);
|
|
233
|
+
await writeFile(tmp, nextText, { mode: 0o600, flag: 'wx' });
|
|
234
|
+
await rename(tmp, path);
|
|
235
|
+
tmp = undefined;
|
|
236
|
+
return revision(nextText);
|
|
237
|
+
}
|
|
238
|
+
finally {
|
|
239
|
+
if (tmp)
|
|
240
|
+
await unlink(tmp).catch(() => { });
|
|
241
|
+
await lock.close();
|
|
242
|
+
await unlink(lockPath);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ChatMessage } from './llamacpp.js';
|
|
3
|
+
import type { LoadedModelPolicy, ModelPolicy } from './model-policy.js';
|
|
4
|
+
import type { Task, TaskStore } from './store.js';
|
|
5
|
+
export declare const SummarySchema: z.ZodObject<{
|
|
6
|
+
items: z.ZodArray<z.ZodObject<{
|
|
7
|
+
kind: z.ZodEnum<{
|
|
8
|
+
status: "status";
|
|
9
|
+
observation: "observation";
|
|
10
|
+
decision: "decision";
|
|
11
|
+
open_item: "open_item";
|
|
12
|
+
correction: "correction";
|
|
13
|
+
}>;
|
|
14
|
+
text: z.ZodString;
|
|
15
|
+
source_message_ids: z.ZodArray<z.ZodNumber>;
|
|
16
|
+
historical_status: z.ZodLiteral<"historical_unverified">;
|
|
17
|
+
}, z.core.$strict>>;
|
|
18
|
+
}, z.core.$strict>;
|
|
19
|
+
export type ContextSummary = z.infer<typeof SummarySchema>;
|
|
20
|
+
type ContextTask = Pick<Task, 'id' | 'session_id' | 'caller' | 'context' | 'prompt'>;
|
|
21
|
+
export type CompactionCall = (route: ModelPolicy['compaction']['routes'][number], prompt: string, signal: AbortSignal) => Promise<string>;
|
|
22
|
+
export declare function tokenEstimate(value: unknown): number;
|
|
23
|
+
export declare class ContextCheckpoints {
|
|
24
|
+
readonly store: TaskStore;
|
|
25
|
+
readonly compactor: CompactionCall;
|
|
26
|
+
constructor(store: TaskStore, compactor: CompactionCall);
|
|
27
|
+
plainHistory(task: ContextTask): ChatMessage[];
|
|
28
|
+
private session;
|
|
29
|
+
private rows;
|
|
30
|
+
private exchanges;
|
|
31
|
+
private head;
|
|
32
|
+
status(sessionId: string, caller: string, context: string): Record<string, unknown>;
|
|
33
|
+
private record;
|
|
34
|
+
private history;
|
|
35
|
+
prepare(task: ContextTask, system: string, tools: unknown[], loaded: LoadedModelPolicy, signal: AbortSignal, options?: {
|
|
36
|
+
apply?: boolean;
|
|
37
|
+
force?: boolean;
|
|
38
|
+
expectedHead?: string | null;
|
|
39
|
+
expectedPolicyRevision?: number;
|
|
40
|
+
maxInputTokens?: number;
|
|
41
|
+
countTokens?: (messages: ChatMessage[], tools: unknown[]) => Promise<number | null>;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
messages: ChatMessage[];
|
|
44
|
+
compacted: boolean;
|
|
45
|
+
status: Record<string, unknown>;
|
|
46
|
+
proposal: Record<string, unknown> | null;
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
export {};
|