nemoris 0.1.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/.env.example +49 -0
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/SECURITY.md +119 -0
- package/bin/nemoris +46 -0
- package/config/agents/agent.toml.example +28 -0
- package/config/agents/default.toml +22 -0
- package/config/agents/orchestrator.toml +18 -0
- package/config/delivery.toml +73 -0
- package/config/embeddings.toml +5 -0
- package/config/identity/default-purpose.md +1 -0
- package/config/identity/default-soul.md +3 -0
- package/config/identity/orchestrator-purpose.md +1 -0
- package/config/identity/orchestrator-soul.md +1 -0
- package/config/improvement-targets.toml +15 -0
- package/config/jobs/heartbeat-check.toml +30 -0
- package/config/jobs/memory-rollup.toml +46 -0
- package/config/jobs/workspace-health.toml +63 -0
- package/config/mcp.toml +16 -0
- package/config/output-contracts.toml +17 -0
- package/config/peers.toml +32 -0
- package/config/peers.toml.example +32 -0
- package/config/policies/memory-default.toml +10 -0
- package/config/policies/memory-heartbeat.toml +5 -0
- package/config/policies/memory-ops.toml +10 -0
- package/config/policies/tools-heartbeat-minimal.toml +8 -0
- package/config/policies/tools-interactive-safe.toml +8 -0
- package/config/policies/tools-ops-bounded.toml +8 -0
- package/config/policies/tools-orchestrator.toml +7 -0
- package/config/providers/anthropic.toml +15 -0
- package/config/providers/ollama.toml +5 -0
- package/config/providers/openai-codex.toml +9 -0
- package/config/providers/openrouter.toml +5 -0
- package/config/router.toml +22 -0
- package/config/runtime.toml +114 -0
- package/config/skills/self-improvement.toml +15 -0
- package/config/skills/telegram-onboarding-spec.md +240 -0
- package/config/skills/workspace-monitor.toml +15 -0
- package/config/task-router.toml +42 -0
- package/install.sh +50 -0
- package/package.json +90 -0
- package/src/auth/auth-profiles.js +169 -0
- package/src/auth/openai-codex-oauth.js +285 -0
- package/src/battle.js +449 -0
- package/src/cli/help.js +265 -0
- package/src/cli/output-filter.js +49 -0
- package/src/cli/runtime-control.js +704 -0
- package/src/cli-main.js +2763 -0
- package/src/cli.js +78 -0
- package/src/config/loader.js +332 -0
- package/src/config/schema-validator.js +214 -0
- package/src/config/toml-lite.js +8 -0
- package/src/daemon/action-handlers.js +71 -0
- package/src/daemon/healing-tick.js +87 -0
- package/src/daemon/health-probes.js +90 -0
- package/src/daemon/notifier.js +57 -0
- package/src/daemon/nurse.js +218 -0
- package/src/daemon/repair-log.js +106 -0
- package/src/daemon/rule-staging.js +90 -0
- package/src/daemon/rules.js +29 -0
- package/src/daemon/telegram-commands.js +54 -0
- package/src/daemon/updater.js +85 -0
- package/src/jobs/job-runner.js +78 -0
- package/src/mcp/consumer.js +129 -0
- package/src/memory/active-recall.js +171 -0
- package/src/memory/backend-manager.js +97 -0
- package/src/memory/backends/file-backend.js +38 -0
- package/src/memory/backends/qmd-backend.js +219 -0
- package/src/memory/embedding-guards.js +24 -0
- package/src/memory/embedding-index.js +118 -0
- package/src/memory/embedding-service.js +179 -0
- package/src/memory/file-index.js +177 -0
- package/src/memory/memory-signature.js +5 -0
- package/src/memory/memory-store.js +648 -0
- package/src/memory/retrieval-planner.js +66 -0
- package/src/memory/scoring.js +145 -0
- package/src/memory/simhash.js +78 -0
- package/src/memory/sqlite-active-store.js +824 -0
- package/src/memory/write-policy.js +36 -0
- package/src/onboarding/aliases.js +33 -0
- package/src/onboarding/auth/api-key.js +224 -0
- package/src/onboarding/auth/ollama-detect.js +42 -0
- package/src/onboarding/clack-prompter.js +77 -0
- package/src/onboarding/doctor.js +530 -0
- package/src/onboarding/lock.js +42 -0
- package/src/onboarding/model-catalog.js +344 -0
- package/src/onboarding/phases/auth.js +589 -0
- package/src/onboarding/phases/build.js +130 -0
- package/src/onboarding/phases/choose.js +82 -0
- package/src/onboarding/phases/detect.js +98 -0
- package/src/onboarding/phases/hatch.js +216 -0
- package/src/onboarding/phases/identity.js +79 -0
- package/src/onboarding/phases/ollama.js +345 -0
- package/src/onboarding/phases/scaffold.js +99 -0
- package/src/onboarding/phases/telegram.js +377 -0
- package/src/onboarding/phases/validate.js +204 -0
- package/src/onboarding/phases/verify.js +206 -0
- package/src/onboarding/platform.js +482 -0
- package/src/onboarding/status-bar.js +95 -0
- package/src/onboarding/templates.js +794 -0
- package/src/onboarding/toml-writer.js +38 -0
- package/src/onboarding/tui.js +250 -0
- package/src/onboarding/uninstall.js +153 -0
- package/src/onboarding/wizard.js +499 -0
- package/src/providers/anthropic.js +168 -0
- package/src/providers/base.js +247 -0
- package/src/providers/circuit-breaker.js +136 -0
- package/src/providers/ollama.js +163 -0
- package/src/providers/openai-codex.js +149 -0
- package/src/providers/openrouter.js +136 -0
- package/src/providers/registry.js +36 -0
- package/src/providers/router.js +16 -0
- package/src/runtime/bootstrap-cache.js +47 -0
- package/src/runtime/capabilities-prompt.js +25 -0
- package/src/runtime/completion-ping.js +99 -0
- package/src/runtime/config-validator.js +121 -0
- package/src/runtime/context-ledger.js +360 -0
- package/src/runtime/cutover-readiness.js +42 -0
- package/src/runtime/daemon.js +729 -0
- package/src/runtime/delivery-ack.js +195 -0
- package/src/runtime/delivery-adapters/local-file.js +41 -0
- package/src/runtime/delivery-adapters/openclaw-cli.js +94 -0
- package/src/runtime/delivery-adapters/openclaw-peer.js +98 -0
- package/src/runtime/delivery-adapters/shadow.js +13 -0
- package/src/runtime/delivery-adapters/standalone-http.js +98 -0
- package/src/runtime/delivery-adapters/telegram.js +104 -0
- package/src/runtime/delivery-adapters/tui.js +128 -0
- package/src/runtime/delivery-manager.js +807 -0
- package/src/runtime/delivery-store.js +168 -0
- package/src/runtime/dependency-health.js +118 -0
- package/src/runtime/envelope.js +114 -0
- package/src/runtime/evaluation.js +1089 -0
- package/src/runtime/exec-approvals.js +216 -0
- package/src/runtime/executor.js +500 -0
- package/src/runtime/failure-ping.js +67 -0
- package/src/runtime/flows.js +83 -0
- package/src/runtime/guards.js +45 -0
- package/src/runtime/handoff.js +51 -0
- package/src/runtime/identity-cache.js +28 -0
- package/src/runtime/improvement-engine.js +109 -0
- package/src/runtime/improvement-harness.js +581 -0
- package/src/runtime/input-sanitiser.js +72 -0
- package/src/runtime/interaction-contract.js +347 -0
- package/src/runtime/lane-readiness.js +226 -0
- package/src/runtime/migration.js +323 -0
- package/src/runtime/model-resolution.js +78 -0
- package/src/runtime/network.js +64 -0
- package/src/runtime/notification-store.js +97 -0
- package/src/runtime/notifier.js +256 -0
- package/src/runtime/orchestrator.js +53 -0
- package/src/runtime/orphan-reaper.js +41 -0
- package/src/runtime/output-contract-schema.js +139 -0
- package/src/runtime/output-contract-validator.js +439 -0
- package/src/runtime/peer-readiness.js +69 -0
- package/src/runtime/peer-registry.js +133 -0
- package/src/runtime/pilot-status.js +108 -0
- package/src/runtime/prompt-builder.js +261 -0
- package/src/runtime/provider-attempt.js +582 -0
- package/src/runtime/report-fallback.js +71 -0
- package/src/runtime/result-normalizer.js +183 -0
- package/src/runtime/retention.js +74 -0
- package/src/runtime/review.js +244 -0
- package/src/runtime/route-job.js +15 -0
- package/src/runtime/run-store.js +38 -0
- package/src/runtime/schedule.js +88 -0
- package/src/runtime/scheduler-state.js +434 -0
- package/src/runtime/scheduler.js +656 -0
- package/src/runtime/session-compactor.js +182 -0
- package/src/runtime/session-search.js +155 -0
- package/src/runtime/slack-inbound.js +249 -0
- package/src/runtime/ssrf.js +102 -0
- package/src/runtime/status-aggregator.js +330 -0
- package/src/runtime/task-contract.js +140 -0
- package/src/runtime/task-packet.js +107 -0
- package/src/runtime/task-router.js +140 -0
- package/src/runtime/telegram-inbound.js +1565 -0
- package/src/runtime/token-counter.js +134 -0
- package/src/runtime/token-estimator.js +59 -0
- package/src/runtime/tool-loop.js +200 -0
- package/src/runtime/transport-server.js +311 -0
- package/src/runtime/tui-server.js +411 -0
- package/src/runtime/ulid.js +44 -0
- package/src/security/ssrf-check.js +197 -0
- package/src/setup.js +369 -0
- package/src/shadow/bridge.js +303 -0
- package/src/skills/loader.js +84 -0
- package/src/tools/catalog.json +49 -0
- package/src/tools/cli-delegate.js +44 -0
- package/src/tools/mcp-client.js +106 -0
- package/src/tools/micro/cancel-task.js +6 -0
- package/src/tools/micro/complete-task.js +6 -0
- package/src/tools/micro/fail-task.js +6 -0
- package/src/tools/micro/http-fetch.js +74 -0
- package/src/tools/micro/index.js +36 -0
- package/src/tools/micro/lcm-recall.js +60 -0
- package/src/tools/micro/list-dir.js +17 -0
- package/src/tools/micro/list-skills.js +46 -0
- package/src/tools/micro/load-skill.js +38 -0
- package/src/tools/micro/memory-search.js +45 -0
- package/src/tools/micro/read-file.js +11 -0
- package/src/tools/micro/session-search.js +54 -0
- package/src/tools/micro/shell-exec.js +43 -0
- package/src/tools/micro/trigger-job.js +79 -0
- package/src/tools/micro/web-search.js +58 -0
- package/src/tools/micro/workspace-paths.js +39 -0
- package/src/tools/micro/write-file.js +14 -0
- package/src/tools/micro/write-memory.js +41 -0
- package/src/tools/registry.js +348 -0
- package/src/tools/tool-result-contract.js +36 -0
- package/src/tui/chat.js +835 -0
- package/src/tui/renderer.js +175 -0
- package/src/tui/socket-client.js +217 -0
- package/src/utils/canonical-json.js +29 -0
- package/src/utils/compaction.js +30 -0
- package/src/utils/env-loader.js +5 -0
- package/src/utils/errors.js +80 -0
- package/src/utils/fs.js +101 -0
- package/src/utils/ids.js +5 -0
- package/src/utils/model-context-limits.js +30 -0
- package/src/utils/token-budget.js +74 -0
- package/src/utils/usage-cost.js +25 -0
- package/src/utils/usage-metrics.js +14 -0
- package/vendor/smol-toml-1.5.2.tgz +0 -0
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled TOML and markdown templates for the onboarding scaffold phase.
|
|
3
|
+
* Each function accepts injected values and returns file content as a string.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { writeFile as writeFileAsync, mkdir as mkdirAsync, access as accessAsync } from "node:fs/promises";
|
|
8
|
+
|
|
9
|
+
// ── Agent manifest ───────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generates config/agents/{agentId}.toml
|
|
13
|
+
* Must satisfy AGENT_SCHEMA: id, primaryLane, memoryPolicy, toolPolicy
|
|
14
|
+
*/
|
|
15
|
+
export function agentTemplate({ agentId, installDir = "", workspaceRoot = "" }) {
|
|
16
|
+
// Normalise to forward slashes — backslashes break TOML string parsing (\U etc.)
|
|
17
|
+
const safeInstallDir = installDir.replace(/\\/g, "/");
|
|
18
|
+
const identityBase = safeInstallDir
|
|
19
|
+
? `${safeInstallDir}/config/identity`
|
|
20
|
+
: `config/identity`;
|
|
21
|
+
const wsRoot = (workspaceRoot || (safeInstallDir ? `${safeInstallDir}/workspace` : "workspace")).replace(/\\/g, "/");
|
|
22
|
+
|
|
23
|
+
return `id = "${agentId}"
|
|
24
|
+
primary_lane = "interactive_primary"
|
|
25
|
+
memory_policy = "default"
|
|
26
|
+
tool_policy = "interactive_safe"
|
|
27
|
+
soul_ref = "${identityBase}/${agentId}-soul.md"
|
|
28
|
+
purpose_ref = "${identityBase}/${agentId}-purpose.md"
|
|
29
|
+
workspace_root = "${wsRoot}"
|
|
30
|
+
workspace_context_files = ["MEMORY.md", "USER.md", "AGENTS.md"]
|
|
31
|
+
workspace_context_cap = 8000
|
|
32
|
+
checkpoint_policy = "compact"
|
|
33
|
+
|
|
34
|
+
[limits]
|
|
35
|
+
max_tokens_per_turn = 16000
|
|
36
|
+
max_tool_calls_per_turn = 6
|
|
37
|
+
max_runtime_seconds = 120
|
|
38
|
+
|
|
39
|
+
[access]
|
|
40
|
+
workspace = "rw"
|
|
41
|
+
network = "restricted"
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── Identity files ───────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Generates config/identity/{agentId}-soul.md
|
|
49
|
+
*/
|
|
50
|
+
export function soulTemplate({ agentName, userName, date }) {
|
|
51
|
+
return `# Soul — ${agentName}
|
|
52
|
+
|
|
53
|
+
Created: ${date}
|
|
54
|
+
Operator: ${userName}
|
|
55
|
+
|
|
56
|
+
You are a pragmatic technical coworker assisting ${userName}.
|
|
57
|
+
|
|
58
|
+
Values:
|
|
59
|
+
|
|
60
|
+
- clarity over performance theater
|
|
61
|
+
- durable systems over clever hacks
|
|
62
|
+
- truthful reporting over optimistic guessing
|
|
63
|
+
- calm execution under ambiguity
|
|
64
|
+
|
|
65
|
+
Behavior:
|
|
66
|
+
|
|
67
|
+
- speak directly
|
|
68
|
+
- keep context lean
|
|
69
|
+
- document what matters
|
|
70
|
+
- protect ${userName}'s trust and data
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generates config/identity/{agentId}-purpose.md
|
|
76
|
+
*/
|
|
77
|
+
export function purposeTemplate({ agentName, userName, userGoal, date }) {
|
|
78
|
+
return `# Purpose — ${agentName}
|
|
79
|
+
|
|
80
|
+
Created: ${date}
|
|
81
|
+
Operator: ${userName}
|
|
82
|
+
|
|
83
|
+
Help ${userName} ${userGoal}.
|
|
84
|
+
|
|
85
|
+
Primary responsibilities:
|
|
86
|
+
|
|
87
|
+
- drive implementation forward
|
|
88
|
+
- preserve continuity across sessions
|
|
89
|
+
- surface risk early
|
|
90
|
+
- convert messy state into clear action
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Router ───────────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Generates config/router.toml with lanes based on available providers.
|
|
98
|
+
* Must satisfy ROUTER_SCHEMA: minLanes = 1
|
|
99
|
+
*/
|
|
100
|
+
export function routerTemplate({ anthropic = false, openrouter = false, openai = false, ollama = false, selectedModels = {} }) {
|
|
101
|
+
const pick = (provider, defaults = []) => {
|
|
102
|
+
const chosen = Array.isArray(selectedModels?.[provider]) && selectedModels[provider].length > 0
|
|
103
|
+
? selectedModels[provider]
|
|
104
|
+
: defaults;
|
|
105
|
+
return {
|
|
106
|
+
primary: chosen[0] || defaults[0],
|
|
107
|
+
secondary: chosen[1] || defaults[1] || chosen[0] || defaults[0],
|
|
108
|
+
tertiary: chosen[2] || defaults[2] || chosen[1] || defaults[1] || chosen[0] || defaults[0],
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const anthropicModels = pick("anthropic", [
|
|
113
|
+
"anthropic/claude-haiku-4-5",
|
|
114
|
+
"anthropic/claude-sonnet-4-6",
|
|
115
|
+
"anthropic/claude-opus-4-6",
|
|
116
|
+
]);
|
|
117
|
+
const openrouterModels = pick("openrouter", [
|
|
118
|
+
"openrouter/anthropic/claude-haiku-4-5",
|
|
119
|
+
"openrouter/anthropic/claude-sonnet-4-6",
|
|
120
|
+
"openrouter/anthropic/claude-opus-4-6",
|
|
121
|
+
]);
|
|
122
|
+
const openaiModels = pick("openai", [
|
|
123
|
+
"openai-codex/gpt-4.1",
|
|
124
|
+
"openai-codex/gpt-4o",
|
|
125
|
+
"openai-codex/o4-mini",
|
|
126
|
+
]);
|
|
127
|
+
const ollamaModels = pick("ollama", [
|
|
128
|
+
"ollama/qwen2.5:0.5b",
|
|
129
|
+
"ollama/qwen3:8b",
|
|
130
|
+
"ollama/qwen3:14b",
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
const sections = [];
|
|
134
|
+
|
|
135
|
+
// interactive_primary lane — must always exist
|
|
136
|
+
if (openrouter && anthropic) {
|
|
137
|
+
sections.push(`[lanes.interactive_primary]
|
|
138
|
+
primary = "${openrouterModels.primary}"
|
|
139
|
+
fallback = "${anthropicModels.primary}"
|
|
140
|
+
manual_bump = "${openrouterModels.secondary}"`);
|
|
141
|
+
} else if (openrouter) {
|
|
142
|
+
sections.push(`[lanes.interactive_primary]
|
|
143
|
+
primary = "${openrouterModels.primary}"
|
|
144
|
+
manual_bump = "${openrouterModels.secondary}"`);
|
|
145
|
+
} else if (anthropic) {
|
|
146
|
+
sections.push(`[lanes.interactive_primary]
|
|
147
|
+
primary = "${anthropicModels.primary}"
|
|
148
|
+
manual_bump = "${anthropicModels.secondary}"`);
|
|
149
|
+
} else if (openai) {
|
|
150
|
+
sections.push(`[lanes.interactive_primary]
|
|
151
|
+
primary = "${openaiModels.primary}"
|
|
152
|
+
manual_bump = "${openaiModels.secondary}"`);
|
|
153
|
+
} else if (ollama) {
|
|
154
|
+
// ollama-only fallback for interactive
|
|
155
|
+
sections.push(`[lanes.interactive_primary]
|
|
156
|
+
primary = "${ollamaModels.secondary}"`);
|
|
157
|
+
} else {
|
|
158
|
+
// No providers at all — emit a placeholder lane so router.toml satisfies minLanes = 1
|
|
159
|
+
sections.push(`# No providers configured — add one with: nemoris setup
|
|
160
|
+
[lanes.interactive_primary]
|
|
161
|
+
primary = "none"`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// local lanes — only if ollama is available
|
|
165
|
+
if (ollama) {
|
|
166
|
+
sections.push(`[lanes.local_cheap]
|
|
167
|
+
primary = "${ollamaModels.primary}"`);
|
|
168
|
+
|
|
169
|
+
if (openrouter) {
|
|
170
|
+
sections.push(`[lanes.local_report]
|
|
171
|
+
primary = "${ollamaModels.secondary}"
|
|
172
|
+
fallback = "${openrouterModels.primary}"
|
|
173
|
+
manual_bump = "${ollamaModels.tertiary}"`);
|
|
174
|
+
} else if (openai) {
|
|
175
|
+
sections.push(`[lanes.local_report]
|
|
176
|
+
primary = "${ollamaModels.secondary}"
|
|
177
|
+
fallback = "${openaiModels.primary}"
|
|
178
|
+
manual_bump = "${ollamaModels.tertiary}"`);
|
|
179
|
+
} else {
|
|
180
|
+
sections.push(`[lanes.local_report]
|
|
181
|
+
primary = "${ollamaModels.secondary}"
|
|
182
|
+
manual_bump = "${ollamaModels.tertiary}"`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// report_fallback_lowcost — only if remote providers are available
|
|
187
|
+
if (openrouter) {
|
|
188
|
+
sections.push(`[lanes.report_fallback_lowcost]
|
|
189
|
+
primary = "${openrouterModels.primary}"`);
|
|
190
|
+
} else if (openai) {
|
|
191
|
+
sections.push(`[lanes.report_fallback_lowcost]
|
|
192
|
+
primary = "${openaiModels.primary}"`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// job_heavy — only if cloud providers available
|
|
196
|
+
if (openrouter && anthropic) {
|
|
197
|
+
sections.push(`[lanes.job_heavy]
|
|
198
|
+
primary = "${openrouterModels.secondary}"
|
|
199
|
+
fallback = "${anthropicModels.secondary}"`);
|
|
200
|
+
} else if (openrouter) {
|
|
201
|
+
sections.push(`[lanes.job_heavy]
|
|
202
|
+
primary = "${openrouterModels.secondary}"`);
|
|
203
|
+
} else if (anthropic) {
|
|
204
|
+
sections.push(`[lanes.job_heavy]
|
|
205
|
+
primary = "${anthropicModels.secondary}"`);
|
|
206
|
+
} else if (openai) {
|
|
207
|
+
sections.push(`[lanes.job_heavy]
|
|
208
|
+
primary = "${openaiModels.secondary}"`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return sections.join("\n\n") + "\n";
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ── Runtime ──────────────────────────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Generates config/runtime.toml with shipped defaults.
|
|
218
|
+
* Must satisfy RUNTIME_SCHEMA: safety.contextTokens
|
|
219
|
+
*/
|
|
220
|
+
export function runtimeTemplate() {
|
|
221
|
+
return `[safety]
|
|
222
|
+
context_tokens = 32768
|
|
223
|
+
context_pressure_soft_ratio = 0.72
|
|
224
|
+
context_pressure_hard_ratio = 0.9
|
|
225
|
+
fresh_session_on_high_pressure = true
|
|
226
|
+
snapshot_before_compaction = true
|
|
227
|
+
|
|
228
|
+
[concurrency]
|
|
229
|
+
max_concurrent_jobs = 2
|
|
230
|
+
max_concurrent_subagents = 2
|
|
231
|
+
|
|
232
|
+
[retention.runs]
|
|
233
|
+
ttl_days = 30
|
|
234
|
+
max_files_per_bucket = 2000
|
|
235
|
+
|
|
236
|
+
[retention.notifications]
|
|
237
|
+
ttl_days = 14
|
|
238
|
+
max_files_per_bucket = 1000
|
|
239
|
+
|
|
240
|
+
[retention.deliveries]
|
|
241
|
+
ttl_days = 14
|
|
242
|
+
max_files_per_bucket = 1000
|
|
243
|
+
|
|
244
|
+
[retention.transport_inbox]
|
|
245
|
+
ttl_days = 7
|
|
246
|
+
max_files_per_bucket = 1000
|
|
247
|
+
|
|
248
|
+
[retrieval]
|
|
249
|
+
lexical_weight = 0.36
|
|
250
|
+
embedding_weight = 0.3
|
|
251
|
+
recency_weight = 0.14
|
|
252
|
+
salience_weight = 0.14
|
|
253
|
+
type_weight = 0.06
|
|
254
|
+
semantic_rescue_bonus = 0.06
|
|
255
|
+
shadow_snapshot_penalty = 0.12
|
|
256
|
+
|
|
257
|
+
[memory_locks]
|
|
258
|
+
ttl_ms = 15000
|
|
259
|
+
retry_delay_ms = 25
|
|
260
|
+
max_retries = 40
|
|
261
|
+
|
|
262
|
+
[network]
|
|
263
|
+
dns_result_order = "system"
|
|
264
|
+
connect_timeout_ms = 20000
|
|
265
|
+
read_timeout_ms = 60000
|
|
266
|
+
retry_budget = 1
|
|
267
|
+
circuit_breaker_threshold = 3
|
|
268
|
+
|
|
269
|
+
[bootstrap_cache]
|
|
270
|
+
enabled = true
|
|
271
|
+
identity_ttl_ms = 300000
|
|
272
|
+
|
|
273
|
+
[extensions]
|
|
274
|
+
implicit_workspace_autoload = false
|
|
275
|
+
require_explicit_trust = true
|
|
276
|
+
trusted_roots = []
|
|
277
|
+
|
|
278
|
+
[shutdown]
|
|
279
|
+
drain_timeout_ms = 15000
|
|
280
|
+
transport_shutdown_timeout_ms = 5000
|
|
281
|
+
|
|
282
|
+
[maintenance]
|
|
283
|
+
wal_checkpoint_threshold_bytes = 67108864
|
|
284
|
+
prune_on_tick = true
|
|
285
|
+
sweep_pending_handoffs_on_tick = true
|
|
286
|
+
sweep_pending_followups_on_tick = true
|
|
287
|
+
|
|
288
|
+
[delivery]
|
|
289
|
+
prevent_resend_on_uncertain = true
|
|
290
|
+
retry_on_failure = false
|
|
291
|
+
notify_on_failure = true
|
|
292
|
+
|
|
293
|
+
[yields]
|
|
294
|
+
enabled = true
|
|
295
|
+
default_target_surface = "operator_review"
|
|
296
|
+
`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ── Delivery ─────────────────────────────────────────────────────────────────
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Generates a minimal config/delivery.toml with standalone profiles.
|
|
303
|
+
*/
|
|
304
|
+
export function deliveryTemplate() {
|
|
305
|
+
return `default_interactive_profile_standalone = "standalone_operator"
|
|
306
|
+
default_peer_profile_standalone = "standalone_peer"
|
|
307
|
+
|
|
308
|
+
[profiles.shadow_scheduler]
|
|
309
|
+
adapter = "shadow"
|
|
310
|
+
enabled = true
|
|
311
|
+
target = "scheduler_log"
|
|
312
|
+
|
|
313
|
+
[profiles.standalone_operator]
|
|
314
|
+
adapter = "local_file"
|
|
315
|
+
enabled = true
|
|
316
|
+
target = "operator"
|
|
317
|
+
|
|
318
|
+
[profiles.standalone_peer]
|
|
319
|
+
adapter = "local_file"
|
|
320
|
+
enabled = true
|
|
321
|
+
target = "peer_queue"
|
|
322
|
+
`;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ── Peers ────────────────────────────────────────────────────────────────────
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Generates an empty config/peers.toml scaffold.
|
|
329
|
+
*/
|
|
330
|
+
export function peersTemplate() {
|
|
331
|
+
return `# Peer agent registry
|
|
332
|
+
# Add peers here as you connect additional agents.
|
|
333
|
+
# See config/peers.toml in the reference installation for the full format.
|
|
334
|
+
`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ── Output contracts ─────────────────────────────────────────────────────────
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Generates config/output-contracts.toml with shipped profiles.
|
|
341
|
+
*/
|
|
342
|
+
export function outputContractsTemplate() {
|
|
343
|
+
return `[profiles.default]
|
|
344
|
+
require_status = true
|
|
345
|
+
section_style = "freeform"
|
|
346
|
+
require_section_items = false
|
|
347
|
+
template_lines = ["Status: <one-line status>", "<response body>"]
|
|
348
|
+
|
|
349
|
+
[profiles.bulleted_briefing]
|
|
350
|
+
require_status = true
|
|
351
|
+
section_style = "bullets"
|
|
352
|
+
require_section_items = true
|
|
353
|
+
template_lines = ["Status: <one-line status>", "- Calendar: <brief update or None>", "- Issues: <brief update or None>", "- Weather: <brief update or None>"]
|
|
354
|
+
|
|
355
|
+
[profiles.structured_rollup]
|
|
356
|
+
require_status = false
|
|
357
|
+
section_style = "headings"
|
|
358
|
+
require_section_items = true
|
|
359
|
+
template_lines = ["## Inbox", "- <brief update or None>", "", "## Projects", "- <brief update or None>", "", "## Backlog", "- <brief update or None>", "", "## Update", "- <brief update or None>"]
|
|
360
|
+
`;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ── Embeddings ───────────────────────────────────────────────────────────────
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Generates config/embeddings.toml with embeddings disabled by default.
|
|
367
|
+
*/
|
|
368
|
+
export function embeddingsTemplate() {
|
|
369
|
+
return `enabled = true
|
|
370
|
+
provider = "ollama"
|
|
371
|
+
model = "ollama/nomic-embed-text"
|
|
372
|
+
dimensions = 128
|
|
373
|
+
index_on_write = true
|
|
374
|
+
`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ── Improvement targets ──────────────────────────────────────────────────────
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Generates an empty config/improvement-targets.toml scaffold.
|
|
381
|
+
*/
|
|
382
|
+
export function improvementTargetsTemplate() {
|
|
383
|
+
return `# Improvement targets — add entries here to enable the improvement harness.
|
|
384
|
+
# See config/improvement-targets.toml in the reference installation for format.
|
|
385
|
+
`;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ── Provider ─────────────────────────────────────────────────────────────────
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Generates config/providers/{id}.toml
|
|
392
|
+
* Must satisfy PROVIDER_SCHEMA: id, and at least one of adapter/type
|
|
393
|
+
*/
|
|
394
|
+
export function providerTemplate(providerId, config = {}) {
|
|
395
|
+
const {
|
|
396
|
+
adapter = providerId,
|
|
397
|
+
authEnv = `${providerId.toUpperCase()}_API_KEY`,
|
|
398
|
+
authRef = authEnv ? `env:${authEnv}` : "",
|
|
399
|
+
baseUrl = "",
|
|
400
|
+
healthcheck = "",
|
|
401
|
+
timeoutMs = 45000,
|
|
402
|
+
lanes = [],
|
|
403
|
+
models = []
|
|
404
|
+
} = config;
|
|
405
|
+
|
|
406
|
+
let lines = [`id = "${providerId}"`, `adapter = "${adapter}"`];
|
|
407
|
+
|
|
408
|
+
if (authRef) {
|
|
409
|
+
lines.push(`auth_ref = "${authRef}"`);
|
|
410
|
+
}
|
|
411
|
+
if (baseUrl) {
|
|
412
|
+
lines.push(`base_url = "${baseUrl}"`);
|
|
413
|
+
}
|
|
414
|
+
if (healthcheck) {
|
|
415
|
+
lines.push(`healthcheck = "${healthcheck}"`);
|
|
416
|
+
}
|
|
417
|
+
lines.push(`default_timeout_ms = ${timeoutMs}`);
|
|
418
|
+
if (lanes.length > 0) {
|
|
419
|
+
lines.push(`lanes = [${lanes.map((l) => `"${l}"`).join(", ")}]`);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const body = lines.join("\n");
|
|
423
|
+
|
|
424
|
+
const modelSections = models
|
|
425
|
+
.map(({ key, id, role }) => `\n[models.${key}]\nid = "${id}"\nrole = "${role}"`)
|
|
426
|
+
.join("\n");
|
|
427
|
+
|
|
428
|
+
return body + "\n" + modelSections + (modelSections ? "\n" : "");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// ── Job ──────────────────────────────────────────────────────────────────────
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Generates config/jobs/{id}.toml — workspace-health default.
|
|
435
|
+
* Must satisfy JOB_SCHEMA: id, modelLane, and at least one of schedule/trigger
|
|
436
|
+
*/
|
|
437
|
+
export function jobTemplate(jobId, { agentId = "nemo" } = {}) {
|
|
438
|
+
return `id = "${jobId}"
|
|
439
|
+
agent_id = "${agentId}"
|
|
440
|
+
trigger = "hourly"
|
|
441
|
+
task_type = "workspace_health"
|
|
442
|
+
model_lane = "local_report"
|
|
443
|
+
output_target = "state/health"
|
|
444
|
+
idempotency_key = "${jobId}:hour"
|
|
445
|
+
memory_backends = ["file"]
|
|
446
|
+
memory_limit = 6
|
|
447
|
+
|
|
448
|
+
[budget]
|
|
449
|
+
max_tokens = 4000
|
|
450
|
+
max_runtime_seconds = 120
|
|
451
|
+
|
|
452
|
+
[retry]
|
|
453
|
+
max_attempts = 1
|
|
454
|
+
|
|
455
|
+
[stop]
|
|
456
|
+
halt_on_policy_error = true
|
|
457
|
+
halt_on_budget_exceeded = true
|
|
458
|
+
`;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// ── Policy files ─────────────────────────────────────────────────────────────
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Generates default policy file content keyed by filename stem.
|
|
465
|
+
* Returns an object: { [filename]: tomlString }
|
|
466
|
+
*/
|
|
467
|
+
export function policyTemplates() {
|
|
468
|
+
return {
|
|
469
|
+
"memory-default": `id = "default"
|
|
470
|
+
allow_durable_writes = true
|
|
471
|
+
allow_identity_updates = false
|
|
472
|
+
require_source_reference = true
|
|
473
|
+
require_write_reason = true
|
|
474
|
+
max_writes_per_run = 5
|
|
475
|
+
|
|
476
|
+
[categories]
|
|
477
|
+
allowed = ["decision", "preference", "workflow_rule", "artifact_summary"]
|
|
478
|
+
blocked = ["ephemeral_chatter", "raw_tool_output", "unverified_external_claim"]
|
|
479
|
+
`,
|
|
480
|
+
"tools-interactive-safe": `id = "interactive_safe"
|
|
481
|
+
default = "deny"
|
|
482
|
+
allowed = ["read_file", "search_file", "list_dir", "apply_patch", "run_tests"]
|
|
483
|
+
blocked = ["send_email", "post_web", "delete_file", "reset_repo"]
|
|
484
|
+
|
|
485
|
+
[limits]
|
|
486
|
+
max_parallel = 3
|
|
487
|
+
require_approval_for_network = true
|
|
488
|
+
`,
|
|
489
|
+
"tools-ops-bounded": `id = "ops_bounded"
|
|
490
|
+
default = "deny"
|
|
491
|
+
allowed = ["read_file", "search_file", "list_dir", "apply_patch", "check_status", "run_tests"]
|
|
492
|
+
blocked = ["send_email", "post_web", "delete_file", "reset_repo"]
|
|
493
|
+
|
|
494
|
+
[limits]
|
|
495
|
+
max_parallel = 3
|
|
496
|
+
require_approval_for_network = true
|
|
497
|
+
`
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// ── Env file ─────────────────────────────────────────────────────────────────
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Generates .env content from a key-value pairs object.
|
|
505
|
+
* @param {Record<string, string>} keys
|
|
506
|
+
*/
|
|
507
|
+
export function envTemplate(keys = {}) {
|
|
508
|
+
const header = `# nemoris environment variables\n# Generated by onboarding wizard\n`;
|
|
509
|
+
const lines = Object.entries(keys)
|
|
510
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
511
|
+
.join("\n");
|
|
512
|
+
return header + (lines ? lines + "\n" : "");
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// ── Execution layer scaffold ──────────────────────────────────────────────────
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Creates config/tools, config/skills, config/agents, config/policies
|
|
519
|
+
* directories and writes default skill manifests and orchestrator config.
|
|
520
|
+
* Safe to run multiple times (directories use recursive: true).
|
|
521
|
+
*
|
|
522
|
+
* @param {string} installDir Absolute path to the nemoris installation root.
|
|
523
|
+
*/
|
|
524
|
+
export async function scaffoldToolsAndSkills(installDir) {
|
|
525
|
+
const toolsDir = path.join(installDir, "config", "tools");
|
|
526
|
+
const skillsDir = path.join(installDir, "config", "skills");
|
|
527
|
+
const agentsDir = path.join(installDir, "config", "agents");
|
|
528
|
+
const policiesDir = path.join(installDir, "config", "policies");
|
|
529
|
+
|
|
530
|
+
await mkdirAsync(toolsDir, { recursive: true });
|
|
531
|
+
await mkdirAsync(skillsDir, { recursive: true });
|
|
532
|
+
await mkdirAsync(agentsDir, { recursive: true });
|
|
533
|
+
await mkdirAsync(policiesDir, { recursive: true });
|
|
534
|
+
|
|
535
|
+
// Default skills
|
|
536
|
+
await writeFileAsync(path.join(skillsDir, "workspace-monitor.toml"), `[skill]
|
|
537
|
+
id = "workspace_monitor"
|
|
538
|
+
description = "Monitor workspace directory for changes and summarise"
|
|
539
|
+
agent_scope = ["ops", "main"]
|
|
540
|
+
|
|
541
|
+
[skill.context]
|
|
542
|
+
prompt = "You are monitoring a workspace directory for changes. Compare current state against last known checkpoint. Report: new files, modified files, deleted files, key content changes. Keep summary under 200 words."
|
|
543
|
+
|
|
544
|
+
[skill.tools]
|
|
545
|
+
required = ["read_file", "list_dir"]
|
|
546
|
+
optional = ["shell_exec"]
|
|
547
|
+
|
|
548
|
+
[skill.budget]
|
|
549
|
+
max_tokens = 4096
|
|
550
|
+
max_tool_calls = 10
|
|
551
|
+
`);
|
|
552
|
+
|
|
553
|
+
await writeFileAsync(path.join(skillsDir, "self-improvement.toml"), `[skill]
|
|
554
|
+
id = "self_improvement"
|
|
555
|
+
description = "Analyse run artifacts and apply tuning adjustments"
|
|
556
|
+
agent_scope = ["ops", "main"]
|
|
557
|
+
|
|
558
|
+
[skill.context]
|
|
559
|
+
prompt = "You are reviewing a run artifact that scored below threshold. Read the run artifact and current tunings. Identify the root cause. Apply ONE of: prompt refinement, budget adjustment, tool policy change, lane escalation. Write the tuning to state/tunings/<jobId>/ as a JSON file. Explain what you changed and why in under 100 words."
|
|
560
|
+
|
|
561
|
+
[skill.tools]
|
|
562
|
+
required = ["read_file", "write_file", "list_dir"]
|
|
563
|
+
optional = []
|
|
564
|
+
|
|
565
|
+
[skill.budget]
|
|
566
|
+
max_tokens = 4096
|
|
567
|
+
max_tool_calls = 8
|
|
568
|
+
`);
|
|
569
|
+
|
|
570
|
+
// Orchestrator agent
|
|
571
|
+
await writeFileAsync(path.join(agentsDir, "orchestrator.toml"), `id = "orchestrator"
|
|
572
|
+
soul_ref = "config/identity/orchestrator-soul.md"
|
|
573
|
+
purpose_ref = "config/identity/orchestrator-purpose.md"
|
|
574
|
+
primary_lane = "local_cheap"
|
|
575
|
+
fallback_lane = "interactive_fallback"
|
|
576
|
+
tool_policy = "orchestrator"
|
|
577
|
+
memory_policy = "orchestrator"
|
|
578
|
+
|
|
579
|
+
[limits]
|
|
580
|
+
max_tokens_per_turn = 2700
|
|
581
|
+
|
|
582
|
+
[routing.static]
|
|
583
|
+
"heartbeat-check" = "heartbeat"
|
|
584
|
+
|
|
585
|
+
[routing.dynamic]
|
|
586
|
+
enabled = true
|
|
587
|
+
model_lane = "local_cheap"
|
|
588
|
+
max_routing_tokens = 500
|
|
589
|
+
`);
|
|
590
|
+
|
|
591
|
+
// Orchestrator identity files
|
|
592
|
+
const identityDir = path.join(installDir, "config", "identity");
|
|
593
|
+
await mkdirAsync(identityDir, { recursive: true });
|
|
594
|
+
const orchSoulPath = path.join(identityDir, "orchestrator-soul.md");
|
|
595
|
+
const orchPurposePath = path.join(identityDir, "orchestrator-purpose.md");
|
|
596
|
+
// Only write if missing (don't overwrite user edits)
|
|
597
|
+
try { await accessAsync(orchSoulPath); } catch {
|
|
598
|
+
await writeFileAsync(orchSoulPath, `You are the orchestrator — a routing and delegation agent.\nYou do not execute tasks directly. You inspect incoming work, select the best agent, and delegate.\nBe concise. Favour the cheapest capable lane. Escalate only when the task demands it.\n`);
|
|
599
|
+
}
|
|
600
|
+
try { await accessAsync(orchPurposePath); } catch {
|
|
601
|
+
await writeFileAsync(orchPurposePath, `Route incoming jobs to the correct agent and model lane.\nMinimise cost. Maximise task-agent fit. Never execute work yourself.\n`);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Orchestrator tool policy
|
|
605
|
+
await writeFileAsync(path.join(policiesDir, "tools-orchestrator.toml"), `id = "orchestrator"
|
|
606
|
+
default = "deny"
|
|
607
|
+
allowed = ["delegate_agent", "delegate_cli"]
|
|
608
|
+
blocked = ["read_file", "write_file", "shell_exec"]
|
|
609
|
+
|
|
610
|
+
[limits]
|
|
611
|
+
require_approval_for_network = false
|
|
612
|
+
`);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// ── Workspace context files ───────────────────────────────────────────────────
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Generates workspace/SOUL.md — who the agent is.
|
|
619
|
+
* Based on the OpenClaw SOUL.md template pattern.
|
|
620
|
+
*/
|
|
621
|
+
export function workspaceSoulTemplate({ agentName, userName }) {
|
|
622
|
+
return `# SOUL.md - Who You Are
|
|
623
|
+
|
|
624
|
+
*You're not a chatbot. You're becoming someone.*
|
|
625
|
+
|
|
626
|
+
## Core Identity
|
|
627
|
+
|
|
628
|
+
- **Name:** ${agentName}
|
|
629
|
+
- **Role:** Personal AI assistant for ${userName}
|
|
630
|
+
- **Workspace:** This folder is home.
|
|
631
|
+
|
|
632
|
+
## Core Truths
|
|
633
|
+
|
|
634
|
+
**Be genuinely helpful, not performatively helpful.** Skip the filler — just help.
|
|
635
|
+
|
|
636
|
+
**Have opinions.** You're allowed to disagree, prefer things, find things interesting or boring.
|
|
637
|
+
|
|
638
|
+
**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Then ask if you're stuck.
|
|
639
|
+
|
|
640
|
+
**Earn trust through competence.** Be careful with external actions. Be bold with internal ones.
|
|
641
|
+
|
|
642
|
+
## Boundaries
|
|
643
|
+
|
|
644
|
+
- Private things stay private.
|
|
645
|
+
- Ask before acting externally.
|
|
646
|
+
- Never send half-baked replies.
|
|
647
|
+
|
|
648
|
+
## Continuity
|
|
649
|
+
|
|
650
|
+
Each session, you wake up fresh. These workspace files are your memory. Read them. Update them.
|
|
651
|
+
|
|
652
|
+
*Update this file as you learn who you are.*
|
|
653
|
+
`;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Generates workspace/USER.md — about the human.
|
|
658
|
+
*/
|
|
659
|
+
export function workspaceUserTemplate({ userName }) {
|
|
660
|
+
return `# USER.md - About ${userName}
|
|
661
|
+
|
|
662
|
+
*Learn about the person you're helping. Update this as you go.*
|
|
663
|
+
|
|
664
|
+
- **Name:** ${userName}
|
|
665
|
+
- **What to call them:** ${userName}
|
|
666
|
+
- **Pronouns:**
|
|
667
|
+
- **Timezone:**
|
|
668
|
+
- **Notes:**
|
|
669
|
+
|
|
670
|
+
## Context
|
|
671
|
+
|
|
672
|
+
*(What do they care about? What projects are they working on? What annoys them? What makes them laugh? Build this over time.)*
|
|
673
|
+
|
|
674
|
+
---
|
|
675
|
+
|
|
676
|
+
The more you know, the better you can help.
|
|
677
|
+
`;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Generates workspace/MEMORY.md — long-term curated memory.
|
|
682
|
+
*/
|
|
683
|
+
export function workspaceMemoryTemplate({ agentName }) {
|
|
684
|
+
return `# MEMORY.md — ${agentName}'s Long-Term Memory
|
|
685
|
+
|
|
686
|
+
*Curated durable memory. Not raw logs — those live in memory/YYYY-MM-DD.md*
|
|
687
|
+
|
|
688
|
+
## Identity
|
|
689
|
+
|
|
690
|
+
- (Fill in who you are and what you're here for)
|
|
691
|
+
|
|
692
|
+
## Durable Facts
|
|
693
|
+
|
|
694
|
+
- (Key things you've learned about the user)
|
|
695
|
+
|
|
696
|
+
## Active Projects
|
|
697
|
+
|
|
698
|
+
- (Running list of what's in flight)
|
|
699
|
+
|
|
700
|
+
## Decisions
|
|
701
|
+
|
|
702
|
+
- (Recurring decisions and standing rules worth remembering)
|
|
703
|
+
`;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Generates workspace/AGENTS.md — operating manual.
|
|
708
|
+
*/
|
|
709
|
+
export function workspaceAgentsTemplate({ agentName }) {
|
|
710
|
+
return `# AGENTS.md - ${agentName}'s Operating Manual
|
|
711
|
+
|
|
712
|
+
This workspace is home.
|
|
713
|
+
|
|
714
|
+
## Session Startup
|
|
715
|
+
|
|
716
|
+
Before doing anything else:
|
|
717
|
+
|
|
718
|
+
1. Read \`SOUL.md\` — this is who you are
|
|
719
|
+
2. Read \`USER.md\` — this is who you're helping
|
|
720
|
+
3. Check \`memory/YYYY-MM-DD.md\` (today + yesterday) for recent context
|
|
721
|
+
4. In main sessions: also read \`MEMORY.md\` for long-term context
|
|
722
|
+
|
|
723
|
+
Don't ask permission. Just do it.
|
|
724
|
+
|
|
725
|
+
## Memory
|
|
726
|
+
|
|
727
|
+
- **Daily notes:** \`memory/YYYY-MM-DD.md\` — raw logs of what happened
|
|
728
|
+
- **Long-term:** \`MEMORY.md\` — curated memories and durable facts
|
|
729
|
+
|
|
730
|
+
Write things down. If you want to remember something, write it to a file.
|
|
731
|
+
|
|
732
|
+
## Hard Rules
|
|
733
|
+
|
|
734
|
+
- Don't exfiltrate private data.
|
|
735
|
+
- Don't run destructive commands without asking.
|
|
736
|
+
- Ask before any external action (emails, posts, anything public).
|
|
737
|
+
|
|
738
|
+
## Make It Yours
|
|
739
|
+
|
|
740
|
+
This is a starting point. Add your own conventions as you figure out what works.
|
|
741
|
+
`;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Generates workspace/TOOLS.md — local notes about the setup.
|
|
746
|
+
*/
|
|
747
|
+
export function workspaceToolsTemplate() {
|
|
748
|
+
return `# TOOLS.md - Local Notes
|
|
749
|
+
|
|
750
|
+
*This file is for setup-specific notes — things unique to your environment.*
|
|
751
|
+
|
|
752
|
+
## What Goes Here
|
|
753
|
+
|
|
754
|
+
- API endpoints and service URLs
|
|
755
|
+
- SSH hosts and aliases
|
|
756
|
+
- Device names and nicknames
|
|
757
|
+
- Preferred models or voices
|
|
758
|
+
- Anything environment-specific that doesn't belong in config
|
|
759
|
+
|
|
760
|
+
---
|
|
761
|
+
|
|
762
|
+
Add whatever helps you do your job. This is your cheat sheet.
|
|
763
|
+
`;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Writes all workspace context files to workspaceRoot.
|
|
768
|
+
* Skips files that already exist (writeIfMissing pattern).
|
|
769
|
+
*/
|
|
770
|
+
import { existsSync, writeFileSync, mkdirSync } from "node:fs";
|
|
771
|
+
export function writeWorkspaceContextFiles({ workspaceRoot, agentName, userName, agentId }) {
|
|
772
|
+
mkdirSync(workspaceRoot, { recursive: true });
|
|
773
|
+
mkdirSync(path.join(workspaceRoot, "memory"), { recursive: true });
|
|
774
|
+
|
|
775
|
+
const files = [
|
|
776
|
+
{ name: "SOUL.md", content: workspaceSoulTemplate({ agentName, userName }) },
|
|
777
|
+
{ name: "USER.md", content: workspaceUserTemplate({ userName }) },
|
|
778
|
+
{ name: "MEMORY.md", content: workspaceMemoryTemplate({ agentName }) },
|
|
779
|
+
{ name: "AGENTS.md", content: workspaceAgentsTemplate({ agentName }) },
|
|
780
|
+
{ name: "TOOLS.md", content: workspaceToolsTemplate() },
|
|
781
|
+
];
|
|
782
|
+
|
|
783
|
+
const results = [];
|
|
784
|
+
for (const { name, content } of files) {
|
|
785
|
+
const filePath = path.join(workspaceRoot, name);
|
|
786
|
+
if (!existsSync(filePath)) {
|
|
787
|
+
writeFileSync(filePath, content, "utf8");
|
|
788
|
+
results.push({ file: name, status: "created" });
|
|
789
|
+
} else {
|
|
790
|
+
results.push({ file: name, status: "exists" });
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return results;
|
|
794
|
+
}
|