@ouro.bot/cli 0.1.0-alpha.655 → 0.1.0-alpha.658
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/README.md +13 -13
- package/changelog.json +21 -0
- package/dist/arc/evolution.js +1 -1
- package/dist/arc/flight-recorder.js +369 -0
- package/dist/arc/obligations.js +24 -2
- package/dist/heart/active-work.js +1 -1
- package/dist/heart/config-registry.js +14 -5
- package/dist/heart/daemon/agent-config-check.js +1 -1
- package/dist/heart/daemon/agent-service.js +18 -17
- package/dist/heart/daemon/cli-exec.js +134 -15
- package/dist/heart/daemon/cli-help.js +21 -2
- package/dist/heart/daemon/cli-parse.js +31 -3
- package/dist/heart/daemon/daemon-entry.js +1 -1
- package/dist/heart/daemon/daemon.js +3 -3
- package/dist/heart/daemon/hooks/bundle-meta.js +29 -9
- package/dist/heart/daemon/inner-status.js +4 -15
- package/dist/heart/daemon/sense-manager.js +16 -1
- package/dist/heart/habits/habit-parser.js +64 -1
- package/dist/heart/hatch/hatch-flow.js +17 -9
- package/dist/heart/hatch/specialist-tools.js +15 -11
- package/dist/heart/identity.js +4 -1
- package/dist/heart/kept-notes.js +5 -73
- package/dist/heart/mailbox/readers/runtime-readers.js +21 -49
- package/dist/heart/mcp/mcp-server.js +8 -8
- package/dist/heart/sense-truth.js +2 -0
- package/dist/heart/session-events.js +1 -31
- package/dist/heart/start-of-turn-packet.js +8 -2
- package/dist/heart/tool-description.js +15 -3
- package/dist/heart/turn-context.js +34 -7
- package/dist/heart/work-card.js +386 -0
- package/dist/mailbox-ui/assets/{index-9-AxCxuB.js → index-Cbasiy6y.js} +1 -1
- package/dist/mailbox-ui/index.html +1 -1
- package/dist/mind/bundle-manifest.js +9 -3
- package/dist/mind/context.js +1 -2
- package/dist/mind/desk-section.js +53 -1
- package/dist/mind/diary.js +2 -3
- package/dist/mind/note-search.js +36 -106
- package/dist/mind/prompt.js +45 -102
- package/dist/mind/record-paths.js +312 -0
- package/dist/repertoire/bundle-templates.js +4 -5
- package/dist/repertoire/tools-bundle.js +1 -1
- package/dist/repertoire/tools-evolution.js +4 -4
- package/dist/repertoire/tools-notes.js +42 -62
- package/dist/repertoire/tools-record.js +16 -11
- package/dist/repertoire/tools-session.js +4 -4
- package/dist/repertoire/tools.js +1 -1
- package/dist/senses/habit-turn-message.js +19 -5
- package/dist/senses/inner-dialog-worker.js +58 -9
- package/dist/senses/inner-dialog.js +30 -11
- package/dist/senses/pipeline.js +135 -1
- package/dist/util/frontmatter.js +17 -1
- package/package.json +3 -3
- package/skills/configure-dev-tools.md +1 -1
- package/skills/travel-planning.md +1 -1
- package/dist/mind/journal-index.js +0 -162
|
@@ -24,7 +24,7 @@ function formatCadence(cadenceMs) {
|
|
|
24
24
|
return `${minutes}m`;
|
|
25
25
|
}
|
|
26
26
|
function buildInnerStatusOutput(input) {
|
|
27
|
-
const { agentName, runtimeState,
|
|
27
|
+
const { agentName, runtimeState, recordSummary, heartbeat, attentionCount, now } = input;
|
|
28
28
|
const lines = [];
|
|
29
29
|
lines.push(`inner dialog status: ${agentName}`);
|
|
30
30
|
// Last turn
|
|
@@ -58,19 +58,7 @@ function buildInnerStatusOutput(input) {
|
|
|
58
58
|
else {
|
|
59
59
|
lines.push(" heartbeat: unknown");
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
if (journalFiles.length === 0) {
|
|
63
|
-
lines.push(" journal: (empty)");
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
lines.push(" journal:");
|
|
67
|
-
const sorted = [...journalFiles].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
68
|
-
for (const file of sorted) {
|
|
69
|
-
const elapsed = now - file.mtimeMs;
|
|
70
|
-
const relativeTime = formatRelativeTime(elapsed);
|
|
71
|
-
lines.push(` - ${file.name} (${relativeTime})`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
61
|
+
lines.push(` Desk record: ${recordSummary.diaryFactCount} diary facts, ${recordSummary.noteCount} notes`);
|
|
74
62
|
// Attention
|
|
75
63
|
const thoughtWord = attentionCount === 1 ? "thought" : "thoughts";
|
|
76
64
|
lines.push(` attention: ${attentionCount} held ${thoughtWord}`);
|
|
@@ -81,7 +69,8 @@ function buildInnerStatusOutput(input) {
|
|
|
81
69
|
meta: {
|
|
82
70
|
agentName,
|
|
83
71
|
status: runtimeState?.status ?? "unknown",
|
|
84
|
-
|
|
72
|
+
diaryFactCount: recordSummary.diaryFactCount,
|
|
73
|
+
noteCount: recordSummary.noteCount,
|
|
85
74
|
attentionCount,
|
|
86
75
|
},
|
|
87
76
|
});
|
|
@@ -57,6 +57,7 @@ function defaultSenses() {
|
|
|
57
57
|
mail: { ...identity_1.DEFAULT_AGENT_SENSES.mail },
|
|
58
58
|
voice: { ...identity_1.DEFAULT_AGENT_SENSES.voice },
|
|
59
59
|
a2a: { ...identity_1.DEFAULT_AGENT_SENSES.a2a },
|
|
60
|
+
workbench: { ...identity_1.DEFAULT_AGENT_SENSES.workbench },
|
|
60
61
|
};
|
|
61
62
|
}
|
|
62
63
|
function readAgentSenses(agentJsonPath) {
|
|
@@ -82,7 +83,7 @@ function readAgentSenses(agentJsonPath) {
|
|
|
82
83
|
if (!rawSenses || typeof rawSenses !== "object" || Array.isArray(rawSenses)) {
|
|
83
84
|
return defaults;
|
|
84
85
|
}
|
|
85
|
-
for (const sense of ["cli", "teams", "bluebubbles", "mail", "voice", "a2a"]) {
|
|
86
|
+
for (const sense of ["cli", "teams", "bluebubbles", "mail", "voice", "a2a", "workbench"]) {
|
|
86
87
|
const rawSense = rawSenses[sense];
|
|
87
88
|
if (!rawSense || typeof rawSense !== "object" || Array.isArray(rawSense)) {
|
|
88
89
|
continue;
|
|
@@ -147,6 +148,7 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
147
148
|
mail: { configured: false, detail: "not enabled in agent.json" },
|
|
148
149
|
voice: { configured: false, detail: "not enabled in agent.json" },
|
|
149
150
|
a2a: { configured: false, detail: "not enabled in agent.json" },
|
|
151
|
+
workbench: { configured: false, detail: "not enabled in agent.json" },
|
|
150
152
|
};
|
|
151
153
|
const payload = runtimeConfig.ok ? runtimeConfig.config : {};
|
|
152
154
|
const unavailableDetail = runtimeConfigUnavailableDetail(agent, runtimeConfig);
|
|
@@ -301,6 +303,12 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
301
303
|
detail: publicUrl ? `${publicUrl}${endpointPath}` : `:${port} ${endpointPath}`,
|
|
302
304
|
};
|
|
303
305
|
}
|
|
306
|
+
if (senses.workbench.enabled) {
|
|
307
|
+
base.workbench = {
|
|
308
|
+
configured: true,
|
|
309
|
+
detail: "native Workbench local control room; MCP registration is stored in agent.json",
|
|
310
|
+
};
|
|
311
|
+
}
|
|
304
312
|
return base;
|
|
305
313
|
}
|
|
306
314
|
function senseRepairHint(agent, sense) {
|
|
@@ -317,6 +325,10 @@ function senseRepairHint(agent, sense) {
|
|
|
317
325
|
if (sense === "a2a") {
|
|
318
326
|
return `Agent-runnable: run 'ouro connect a2a --agent ${agent}', then restart with 'ouro up'.`;
|
|
319
327
|
}
|
|
328
|
+
/* v8 ignore next -- Workbench is deliberately not daemon-managed, so getSenseInventory never asks the daemon manager for a repair hint @preserve */
|
|
329
|
+
if (sense === "workbench") {
|
|
330
|
+
return `Agent-runnable: run 'ouro connect workbench --agent ${agent}' to enable senses.workbench.enabled and mcpServers.ouro_workbench in agent.json.`;
|
|
331
|
+
}
|
|
320
332
|
return `Run 'ouro connect bluebubbles --agent ${agent}' to attach BlueBubbles on this machine; then run 'ouro up' again.`;
|
|
321
333
|
}
|
|
322
334
|
function currentMachineId() {
|
|
@@ -768,6 +780,9 @@ class DaemonSenseManager {
|
|
|
768
780
|
configured: context.facts.a2a.configured,
|
|
769
781
|
...(runtime.get(agent)?.a2a ?? {}),
|
|
770
782
|
},
|
|
783
|
+
workbench: {
|
|
784
|
+
configured: context.facts.workbench.configured,
|
|
785
|
+
},
|
|
771
786
|
};
|
|
772
787
|
const inventory = (0, sense_truth_1.getSenseInventory)({ senses: context.senses }, runtimeInfo);
|
|
773
788
|
return inventory.map((entry) => ({
|
|
@@ -57,6 +57,53 @@ function parseToolsField(raw) {
|
|
|
57
57
|
}
|
|
58
58
|
return undefined;
|
|
59
59
|
}
|
|
60
|
+
function objectRecord(raw) {
|
|
61
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
62
|
+
}
|
|
63
|
+
function stringField(record, key) {
|
|
64
|
+
const value = record[key];
|
|
65
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
66
|
+
}
|
|
67
|
+
function booleanField(record, key, fallback) {
|
|
68
|
+
const value = record[key];
|
|
69
|
+
if (typeof value === "boolean")
|
|
70
|
+
return value;
|
|
71
|
+
if (typeof value === "string") {
|
|
72
|
+
if (value === "true")
|
|
73
|
+
return true;
|
|
74
|
+
if (value === "false")
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return fallback;
|
|
78
|
+
}
|
|
79
|
+
function parseStringArray(raw) {
|
|
80
|
+
if (typeof raw === "string" && raw.startsWith("[") && raw.endsWith("]")) {
|
|
81
|
+
const inner = raw.slice(1, -1);
|
|
82
|
+
if (!inner.trim())
|
|
83
|
+
return [];
|
|
84
|
+
return inner.split(",").map((item) => item.trim()).filter(Boolean);
|
|
85
|
+
}
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
function parseOrigin(raw) {
|
|
89
|
+
const record = objectRecord(raw);
|
|
90
|
+
if (!record)
|
|
91
|
+
return null;
|
|
92
|
+
const friendId = stringField(record, "friendId");
|
|
93
|
+
const channel = stringField(record, "channel");
|
|
94
|
+
const key = stringField(record, "key");
|
|
95
|
+
if (!friendId || !channel || !key)
|
|
96
|
+
return null;
|
|
97
|
+
return { friendId, channel, key };
|
|
98
|
+
}
|
|
99
|
+
function parseSurface(raw) {
|
|
100
|
+
const record = objectRecord(raw);
|
|
101
|
+
return {
|
|
102
|
+
family: record ? booleanField(record, "family", true) : true,
|
|
103
|
+
originator: record ? booleanField(record, "originator", true) : true,
|
|
104
|
+
extra: record ? parseStringArray(record.extra) : [],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
60
107
|
function extractFrontmatterAndBody(content) {
|
|
61
108
|
const lines = content.split(/\r?\n/);
|
|
62
109
|
if (lines[0]?.trim() !== "---") {
|
|
@@ -88,6 +135,8 @@ function parseHabitFile(content, filePath) {
|
|
|
88
135
|
lastRun: null,
|
|
89
136
|
created: null,
|
|
90
137
|
tools: undefined,
|
|
138
|
+
origin: null,
|
|
139
|
+
surface: { family: true, originator: true, extra: [] },
|
|
91
140
|
body: content.trim(),
|
|
92
141
|
};
|
|
93
142
|
}
|
|
@@ -103,6 +152,8 @@ function parseHabitFile(content, filePath) {
|
|
|
103
152
|
const rawCreated = frontmatter.created;
|
|
104
153
|
const created = typeof rawCreated === "string" && rawCreated.length > 0 ? rawCreated : null;
|
|
105
154
|
const tools = parseToolsField(frontmatter.tools);
|
|
155
|
+
const origin = parseOrigin(frontmatter.origin);
|
|
156
|
+
const surface = parseSurface(frontmatter.surface);
|
|
106
157
|
return {
|
|
107
158
|
name: stem,
|
|
108
159
|
title,
|
|
@@ -111,6 +162,8 @@ function parseHabitFile(content, filePath) {
|
|
|
111
162
|
lastRun,
|
|
112
163
|
created,
|
|
113
164
|
tools,
|
|
165
|
+
origin,
|
|
166
|
+
surface,
|
|
114
167
|
body,
|
|
115
168
|
};
|
|
116
169
|
}
|
|
@@ -121,6 +174,16 @@ function formatFrontmatterValue(value) {
|
|
|
121
174
|
return `[${value.join(", ")}]`;
|
|
122
175
|
return String(value);
|
|
123
176
|
}
|
|
177
|
+
function renderFrontmatterLine(lines, key, value) {
|
|
178
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
179
|
+
lines.push(`${key}:`);
|
|
180
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
181
|
+
lines.push(` ${childKey}: ${formatFrontmatterValue(childValue)}`);
|
|
182
|
+
}
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
lines.push(`${key}: ${formatFrontmatterValue(value)}`);
|
|
186
|
+
}
|
|
124
187
|
function renderHabitFile(frontmatter, body) {
|
|
125
188
|
(0, runtime_1.emitNervesEvent)({
|
|
126
189
|
event: "daemon.habit_render",
|
|
@@ -130,7 +193,7 @@ function renderHabitFile(frontmatter, body) {
|
|
|
130
193
|
});
|
|
131
194
|
const lines = ["---"];
|
|
132
195
|
for (const key of Object.keys(frontmatter)) {
|
|
133
|
-
lines
|
|
196
|
+
renderFrontmatterLine(lines, key, frontmatter[key]);
|
|
134
197
|
}
|
|
135
198
|
lines.push("---");
|
|
136
199
|
lines.push("");
|
|
@@ -44,6 +44,8 @@ const runtime_1 = require("../../nerves/runtime");
|
|
|
44
44
|
const auth_flow_1 = require("../auth/auth-flow");
|
|
45
45
|
const provider_models_1 = require("../provider-models");
|
|
46
46
|
const habit_parser_1 = require("../habits/habit-parser");
|
|
47
|
+
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
48
|
+
const record_paths_1 = require("../../mind/record-paths");
|
|
47
49
|
const hatch_specialist_1 = require("./hatch-specialist");
|
|
48
50
|
function requiredCredentialKeys(provider) {
|
|
49
51
|
return identity_1.PROVIDER_CREDENTIALS[provider].required;
|
|
@@ -83,7 +85,7 @@ function writeHeartbeatHabit(bundleRoot, now) {
|
|
|
83
85
|
cadence: "30m",
|
|
84
86
|
status: "active",
|
|
85
87
|
created: now.toISOString(),
|
|
86
|
-
}, "Run a lightweight heartbeat cycle. Review task board and inbox.\nCheck on pending obligations.
|
|
88
|
+
}, "Run a lightweight heartbeat cycle. Review task board and inbox.\nCheck on pending obligations. Write important durable outputs to Arc or Desk record.");
|
|
87
89
|
fs.writeFileSync(filePath, content, "utf-8");
|
|
88
90
|
}
|
|
89
91
|
function writeFriendImprint(bundleRoot, humanName, now) {
|
|
@@ -115,12 +117,15 @@ function writeFriendImprint(bundleRoot, humanName, now) {
|
|
|
115
117
|
};
|
|
116
118
|
fs.writeFileSync(path.join(friendsDir, `${id}.json`), `${JSON.stringify(record, null, 2)}\n`, "utf-8");
|
|
117
119
|
}
|
|
118
|
-
function
|
|
119
|
-
const
|
|
120
|
-
fs.mkdirSync(
|
|
121
|
-
fs.mkdirSync(
|
|
122
|
-
fs.writeFileSync(
|
|
123
|
-
fs.writeFileSync(
|
|
120
|
+
function writeRecordScaffold(bundleRoot) {
|
|
121
|
+
const recordPaths = (0, record_paths_1.resolveDeskRecordPaths)(bundleRoot);
|
|
122
|
+
fs.mkdirSync(recordPaths.diaryDailyDir, { recursive: true });
|
|
123
|
+
fs.mkdirSync(recordPaths.notesRoot, { recursive: true });
|
|
124
|
+
fs.writeFileSync(recordPaths.factsPath, "", "utf-8");
|
|
125
|
+
fs.writeFileSync(recordPaths.entitiesPath, "{}\n", "utf-8");
|
|
126
|
+
fs.mkdirSync(path.join(bundleRoot, "arc", "flight-recorder", "events"), { recursive: true });
|
|
127
|
+
fs.mkdirSync(path.join(bundleRoot, "arc", "flight-recorder", "habit-receipts"), { recursive: true });
|
|
128
|
+
fs.mkdirSync(path.join(bundleRoot, "arc", "claims"), { recursive: true });
|
|
124
129
|
}
|
|
125
130
|
function writeHatchlingAgentConfig(bundleRoot, input) {
|
|
126
131
|
const template = (0, identity_1.buildDefaultAgentTemplate)(input.agentName);
|
|
@@ -156,7 +161,9 @@ async function runHatchFlow(input, deps = {}) {
|
|
|
156
161
|
fs.mkdirSync(bundleRoot, { recursive: true });
|
|
157
162
|
writeReadme(bundleRoot, "Root of this agent bundle.");
|
|
158
163
|
writeReadme(path.join(bundleRoot, "psyche"), "Identity and behavior files.");
|
|
159
|
-
writeReadme(path.join(bundleRoot, "
|
|
164
|
+
writeReadme(path.join(bundleRoot, "arc"), "Live continuity, claims, obligations, and resume state.");
|
|
165
|
+
writeReadme(path.join(bundleRoot, "desk"), "Durable work and maintained record.");
|
|
166
|
+
writeReadme(path.join(bundleRoot, "desk", "_record"), "Desk record: diary facts and maintained reference notes.");
|
|
160
167
|
writeReadme(path.join(bundleRoot, "friends"), "Known friend records.");
|
|
161
168
|
writeReadme(path.join(bundleRoot, "tasks"), "Task files.");
|
|
162
169
|
writeReadme(path.join(bundleRoot, "tasks", "one-shots"), "One-shot tasks.");
|
|
@@ -166,8 +173,9 @@ async function runHatchFlow(input, deps = {}) {
|
|
|
166
173
|
writeReadme(path.join(bundleRoot, "senses"), "Sense-specific config.");
|
|
167
174
|
writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
|
|
168
175
|
writeHatchlingAgentConfig(bundleRoot, input);
|
|
176
|
+
fs.writeFileSync(path.join(bundleRoot, "bundle-meta.json"), `${JSON.stringify((0, bundle_manifest_1.createBundleMeta)(), null, 2)}\n`, "utf-8");
|
|
169
177
|
const credentialPath = await storeHatchlingProviderCredentials(input.agentName, input.provider, input.credentials);
|
|
170
|
-
|
|
178
|
+
writeRecordScaffold(bundleRoot);
|
|
171
179
|
writeFriendImprint(bundleRoot, input.humanName, now);
|
|
172
180
|
writeHeartbeatHabit(bundleRoot, now);
|
|
173
181
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -41,6 +41,7 @@ const tools_base_1 = require("../../repertoire/tools-base");
|
|
|
41
41
|
const hatch_flow_1 = require("./hatch-flow");
|
|
42
42
|
const hatch_animation_1 = require("./hatch-animation");
|
|
43
43
|
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
44
|
+
const record_paths_1 = require("../../mind/record-paths");
|
|
44
45
|
const identity_1 = require("../identity");
|
|
45
46
|
const runtime_1 = require("../../nerves/runtime");
|
|
46
47
|
const vault_setup_1 = require("../../repertoire/vault-setup");
|
|
@@ -107,9 +108,10 @@ function writeReadme(dir, purpose) {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
function scaffoldBundle(bundleRoot) {
|
|
110
|
-
writeReadme(path.join(bundleRoot, "
|
|
111
|
-
writeReadme(path.join(bundleRoot, "
|
|
112
|
-
writeReadme(path.join(bundleRoot, "
|
|
111
|
+
writeReadme(path.join(bundleRoot, "arc"), "Live continuity, claims, obligations, and resume state.");
|
|
112
|
+
writeReadme(path.join(bundleRoot, "arc", "flight-recorder"), "Flight recorder resume and event receipts.");
|
|
113
|
+
writeReadme(path.join(bundleRoot, "desk"), "Durable work and maintained record.");
|
|
114
|
+
writeReadme(path.join(bundleRoot, "desk", "_record"), "Desk record: diary facts and maintained reference notes.");
|
|
113
115
|
writeReadme(path.join(bundleRoot, "friends"), "Known friend records.");
|
|
114
116
|
writeReadme(path.join(bundleRoot, "tasks"), "Task files.");
|
|
115
117
|
writeReadme(path.join(bundleRoot, "tasks", "one-shots"), "One-shot tasks.");
|
|
@@ -118,16 +120,18 @@ function scaffoldBundle(bundleRoot) {
|
|
|
118
120
|
writeReadme(path.join(bundleRoot, "skills"), "Local skill files.");
|
|
119
121
|
writeReadme(path.join(bundleRoot, "senses"), "Sense-specific config.");
|
|
120
122
|
writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const entitiesPath = path.join(notesRoot, "entities.json");
|
|
123
|
+
const recordPaths = (0, record_paths_1.resolveDeskRecordPaths)(bundleRoot);
|
|
124
|
+
fs.mkdirSync(recordPaths.diaryDailyDir, { recursive: true });
|
|
125
|
+
fs.mkdirSync(recordPaths.notesRoot, { recursive: true });
|
|
125
126
|
/* v8 ignore next -- defensive: guard against re-scaffold on existing bundle @preserve */
|
|
126
|
-
if (!fs.existsSync(factsPath))
|
|
127
|
-
fs.writeFileSync(factsPath, "", "utf-8");
|
|
127
|
+
if (!fs.existsSync(recordPaths.factsPath))
|
|
128
|
+
fs.writeFileSync(recordPaths.factsPath, "", "utf-8");
|
|
128
129
|
/* v8 ignore next -- defensive: guard against re-scaffold on existing bundle @preserve */
|
|
129
|
-
if (!fs.existsSync(entitiesPath))
|
|
130
|
-
fs.writeFileSync(entitiesPath, "{}\n", "utf-8");
|
|
130
|
+
if (!fs.existsSync(recordPaths.entitiesPath))
|
|
131
|
+
fs.writeFileSync(recordPaths.entitiesPath, "{}\n", "utf-8");
|
|
132
|
+
fs.mkdirSync(path.join(bundleRoot, "arc", "flight-recorder", "events"), { recursive: true });
|
|
133
|
+
fs.mkdirSync(path.join(bundleRoot, "arc", "flight-recorder", "habit-receipts"), { recursive: true });
|
|
134
|
+
fs.mkdirSync(path.join(bundleRoot, "arc", "claims"), { recursive: true });
|
|
131
135
|
// bundle-meta.json
|
|
132
136
|
const meta = (0, bundle_manifest_1.createBundleMeta)();
|
|
133
137
|
fs.writeFileSync(path.join(bundleRoot, "bundle-meta.json"), JSON.stringify(meta, null, 2) + "\n", "utf-8");
|
package/dist/heart/identity.js
CHANGED
|
@@ -138,6 +138,7 @@ exports.DEFAULT_AGENT_SENSES = {
|
|
|
138
138
|
mail: { enabled: false },
|
|
139
139
|
voice: { enabled: false },
|
|
140
140
|
a2a: { enabled: false },
|
|
141
|
+
workbench: { enabled: false },
|
|
141
142
|
};
|
|
142
143
|
function normalizeSenses(value, configFile) {
|
|
143
144
|
const defaults = {
|
|
@@ -147,6 +148,7 @@ function normalizeSenses(value, configFile) {
|
|
|
147
148
|
mail: { ...exports.DEFAULT_AGENT_SENSES.mail },
|
|
148
149
|
voice: { ...exports.DEFAULT_AGENT_SENSES.voice },
|
|
149
150
|
a2a: { ...exports.DEFAULT_AGENT_SENSES.a2a },
|
|
151
|
+
workbench: { ...exports.DEFAULT_AGENT_SENSES.workbench },
|
|
150
152
|
};
|
|
151
153
|
if (value === undefined) {
|
|
152
154
|
return defaults;
|
|
@@ -162,7 +164,7 @@ function normalizeSenses(value, configFile) {
|
|
|
162
164
|
throw new Error(`agent.json at ${configFile} must include senses as an object when present.`);
|
|
163
165
|
}
|
|
164
166
|
const raw = value;
|
|
165
|
-
const senseNames = ["cli", "teams", "bluebubbles", "mail", "voice", "a2a"];
|
|
167
|
+
const senseNames = ["cli", "teams", "bluebubbles", "mail", "voice", "a2a", "workbench"];
|
|
166
168
|
for (const senseName of senseNames) {
|
|
167
169
|
const rawSense = raw[senseName];
|
|
168
170
|
if (rawSense === undefined) {
|
|
@@ -207,6 +209,7 @@ function buildDefaultAgentTemplate(_agentName) {
|
|
|
207
209
|
mail: { ...exports.DEFAULT_AGENT_SENSES.mail },
|
|
208
210
|
voice: { ...exports.DEFAULT_AGENT_SENSES.voice },
|
|
209
211
|
a2a: { ...exports.DEFAULT_AGENT_SENSES.a2a },
|
|
212
|
+
workbench: { ...exports.DEFAULT_AGENT_SENSES.workbench },
|
|
210
213
|
},
|
|
211
214
|
phrases: {
|
|
212
215
|
thinking: [...exports.DEFAULT_AGENT_PHRASES.thinking],
|
package/dist/heart/kept-notes.js
CHANGED
|
@@ -1,44 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.gatherKeptNotesCandidates = gatherKeptNotesCandidates;
|
|
37
4
|
exports.renderKeptNotesOutcome = renderKeptNotesOutcome;
|
|
38
5
|
exports.injectKeptNotes = injectKeptNotes;
|
|
39
6
|
exports.createKeptNotesJudge = createKeptNotesJudge;
|
|
40
|
-
const fs = __importStar(require("fs"));
|
|
41
|
-
const path = __importStar(require("path"));
|
|
42
7
|
const diary_1 = require("../mind/diary");
|
|
43
8
|
const runtime_1 = require("../nerves/runtime");
|
|
44
9
|
const DEFAULT_TIMEOUT_MS = 2500;
|
|
@@ -82,26 +47,6 @@ function scoreText(queryTerms, text) {
|
|
|
82
47
|
}
|
|
83
48
|
return matches / queryTerms.size;
|
|
84
49
|
}
|
|
85
|
-
function readJournalIndex(journalDir) {
|
|
86
|
-
try {
|
|
87
|
-
const parsed = JSON.parse(fs.readFileSync(path.join(journalDir, ".index.json"), "utf8"));
|
|
88
|
-
if (!Array.isArray(parsed))
|
|
89
|
-
return [];
|
|
90
|
-
return parsed
|
|
91
|
-
.filter((entry) => (typeof entry === "object" &&
|
|
92
|
-
entry !== null &&
|
|
93
|
-
typeof entry.filename === "string" &&
|
|
94
|
-
typeof entry.preview === "string"));
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return [];
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
function journalDirForDiaryRoot(diaryRoot, explicitJournalDir) {
|
|
101
|
-
if (explicitJournalDir)
|
|
102
|
-
return explicitJournalDir;
|
|
103
|
-
return path.join(path.dirname(diaryRoot), "journal");
|
|
104
|
-
}
|
|
105
50
|
function diaryCandidate(fact) {
|
|
106
51
|
return {
|
|
107
52
|
text: fact.text,
|
|
@@ -133,17 +78,7 @@ function gatherKeptNotesCandidates(query, options = {}) {
|
|
|
133
78
|
const diaryCandidates = diaryEntries
|
|
134
79
|
.map((fact) => ({ candidate: diaryCandidate(fact), score: scoreText(queryTerms, fact.text) }))
|
|
135
80
|
.filter((entry) => entry.score > 0);
|
|
136
|
-
|
|
137
|
-
const journalCandidates = readJournalIndex(journalDir)
|
|
138
|
-
.map((entry) => ({
|
|
139
|
-
candidate: {
|
|
140
|
-
text: `${entry.filename}: ${entry.preview}`,
|
|
141
|
-
source: { kind: "journal", label: "journal", ref: entry.filename },
|
|
142
|
-
},
|
|
143
|
-
score: scoreText(queryTerms, `${entry.filename} ${entry.preview}`),
|
|
144
|
-
}))
|
|
145
|
-
.filter((entry) => entry.score > 0);
|
|
146
|
-
return [...diaryCandidates, ...journalCandidates, ...friendNoteCandidates(options.friend, queryTerms)]
|
|
81
|
+
return [...diaryCandidates, ...friendNoteCandidates(options.friend, queryTerms)]
|
|
147
82
|
.sort((left, right) => right.score - left.score)
|
|
148
83
|
.slice(0, MAX_CANDIDATES)
|
|
149
84
|
.map((entry) => entry.candidate);
|
|
@@ -176,18 +111,15 @@ function fuzzyLine(text) {
|
|
|
176
111
|
return trimmed;
|
|
177
112
|
return `I may have kept something related: ${trimmed}`;
|
|
178
113
|
}
|
|
179
|
-
const SOURCE_KIND_ORDER = ["diary", "
|
|
114
|
+
const SOURCE_KIND_ORDER = ["diary", "friend-note"];
|
|
180
115
|
const SOURCE_KIND_LABELS = {
|
|
181
|
-
diary: "my diary",
|
|
182
|
-
journal: "my journal",
|
|
116
|
+
diary: "my Desk record diary",
|
|
183
117
|
"friend-note": "my friend notes",
|
|
184
118
|
};
|
|
185
119
|
function joinLabels(labels) {
|
|
186
120
|
if (labels.length === 1)
|
|
187
121
|
return labels[0];
|
|
188
|
-
|
|
189
|
-
return `${labels[0]} and ${labels[1]}`;
|
|
190
|
-
return `${labels.slice(0, -1).join(", ")}, and ${labels[labels.length - 1]}`;
|
|
122
|
+
return `${labels[0]} and ${labels[1]}`;
|
|
191
123
|
}
|
|
192
124
|
function sourceHeading(sources) {
|
|
193
125
|
const sourceKinds = new Set(sources.map((source) => source.kind));
|
|
@@ -195,7 +127,7 @@ function sourceHeading(sources) {
|
|
|
195
127
|
.filter((kind) => sourceKinds.has(kind))
|
|
196
128
|
.map((kind) => SOURCE_KIND_LABELS[kind]);
|
|
197
129
|
if (labels.length === 0)
|
|
198
|
-
return "## from
|
|
130
|
+
return "## from my kept record";
|
|
199
131
|
return `## from ${joinLabels(labels)}`;
|
|
200
132
|
}
|
|
201
133
|
function renderKeptNotesOutcome(outcome) {
|
|
@@ -53,6 +53,7 @@ const daemon_health_1 = require("../../daemon/daemon-health");
|
|
|
53
53
|
const shared_1 = require("./shared");
|
|
54
54
|
const agent_machine_1 = require("./agent-machine");
|
|
55
55
|
const sessions_1 = require("./sessions");
|
|
56
|
+
const record_paths_1 = require("../../../mind/record-paths");
|
|
56
57
|
const NOTES_VIEW_LIMIT = 20;
|
|
57
58
|
/* v8 ignore start — defensive parsing of on-disk JSON, fallback branches are safety nets */
|
|
58
59
|
function readCodingDeep(agentRoot) {
|
|
@@ -324,71 +325,42 @@ function readDaemonHealthDeep(healthPath) {
|
|
|
324
325
|
}
|
|
325
326
|
/* v8 ignore stop */
|
|
326
327
|
function readNotesView(agentRoot) {
|
|
327
|
-
const
|
|
328
|
-
const effectiveDiaryRoot = fs.existsSync(diaryRoot) ? diaryRoot : null;
|
|
328
|
+
const recordPaths = (0, record_paths_1.resolveDeskRecordPaths)(agentRoot);
|
|
329
329
|
const diaryEntries = [];
|
|
330
|
-
if (effectiveDiaryRoot) {
|
|
331
|
-
const factsPath = path.join(effectiveDiaryRoot, "facts.jsonl");
|
|
332
|
-
try {
|
|
333
|
-
const raw = fs.readFileSync(factsPath, "utf-8");
|
|
334
|
-
for (const line of raw.split("\n")) {
|
|
335
|
-
if (!line.trim())
|
|
336
|
-
continue;
|
|
337
|
-
try {
|
|
338
|
-
const entry = JSON.parse(line);
|
|
339
|
-
if (typeof entry.id === "string" && typeof entry.text === "string") {
|
|
340
|
-
diaryEntries.push({
|
|
341
|
-
id: entry.id,
|
|
342
|
-
text: entry.text,
|
|
343
|
-
source: typeof entry.source === "string" ? entry.source : "",
|
|
344
|
-
createdAt: typeof entry.createdAt === "string" ? entry.createdAt : "",
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
catch {
|
|
349
|
-
// skip unparseable lines
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
catch {
|
|
354
|
-
// no diary facts file
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
diaryEntries.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
358
|
-
const journalDir = path.join(agentRoot, "journal");
|
|
359
|
-
const journalEntries = [];
|
|
360
|
-
const indexPath = path.join(journalDir, ".index.json");
|
|
361
330
|
try {
|
|
362
|
-
const raw = fs.readFileSync(
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
331
|
+
const raw = fs.readFileSync(recordPaths.factsPath, "utf-8");
|
|
332
|
+
for (const line of raw.split("\n")) {
|
|
333
|
+
if (!line.trim())
|
|
334
|
+
continue;
|
|
335
|
+
try {
|
|
336
|
+
const entry = JSON.parse(line);
|
|
337
|
+
if (typeof entry.id === "string" && typeof entry.text === "string") {
|
|
338
|
+
diaryEntries.push({
|
|
339
|
+
id: entry.id,
|
|
340
|
+
text: entry.text,
|
|
341
|
+
source: typeof entry.source === "string" ? entry.source : "",
|
|
342
|
+
createdAt: typeof entry.createdAt === "string" ? entry.createdAt : "",
|
|
371
343
|
});
|
|
372
344
|
}
|
|
373
345
|
}
|
|
346
|
+
catch {
|
|
347
|
+
// skip unparseable lines
|
|
348
|
+
}
|
|
374
349
|
}
|
|
375
350
|
}
|
|
376
351
|
catch {
|
|
377
|
-
// no
|
|
352
|
+
// no diary facts file
|
|
378
353
|
}
|
|
379
|
-
|
|
380
|
-
const canonicalNotes = readCanonicalNotes(
|
|
354
|
+
diaryEntries.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
355
|
+
const canonicalNotes = readCanonicalNotes(recordPaths.notesRoot);
|
|
381
356
|
return {
|
|
382
357
|
diaryEntryCount: diaryEntries.length,
|
|
383
358
|
recentDiaryEntries: diaryEntries.slice(0, NOTES_VIEW_LIMIT),
|
|
384
|
-
journalEntryCount: journalEntries.length,
|
|
385
|
-
recentJournalEntries: journalEntries.slice(0, NOTES_VIEW_LIMIT),
|
|
386
359
|
canonicalNoteCount: canonicalNotes.length,
|
|
387
360
|
recentCanonicalNotes: canonicalNotes.slice(0, NOTES_VIEW_LIMIT),
|
|
388
361
|
};
|
|
389
362
|
}
|
|
390
|
-
function readCanonicalNotes(
|
|
391
|
-
const notesRoot = path.join(agentRoot, "notes");
|
|
363
|
+
function readCanonicalNotes(notesRoot) {
|
|
392
364
|
const notes = [];
|
|
393
365
|
for (const filename of (0, shared_1.safeReaddir)(notesRoot)) {
|
|
394
366
|
if (!filename.endsWith(".md"))
|
|
@@ -123,7 +123,7 @@ const TOOL_TO_COMMAND = {
|
|
|
123
123
|
status: "agent.status",
|
|
124
124
|
catchup: "agent.catchup",
|
|
125
125
|
get_context: "agent.getContext",
|
|
126
|
-
|
|
126
|
+
search_facts: "agent.searchFacts",
|
|
127
127
|
get_task: "agent.getTask",
|
|
128
128
|
};
|
|
129
129
|
const UUID_PREFIX_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
|
|
@@ -326,7 +326,7 @@ function createMcpServer(options) {
|
|
|
326
326
|
status: "handleAgentStatus",
|
|
327
327
|
catchup: "handleAgentCatchup",
|
|
328
328
|
get_context: "handleAgentGetContext",
|
|
329
|
-
|
|
329
|
+
search_facts: "handleAgentSearchFacts",
|
|
330
330
|
get_task: "handleAgentGetTask",
|
|
331
331
|
};
|
|
332
332
|
function stringArg(args, key) {
|
|
@@ -537,7 +537,7 @@ function getToolSchemas() {
|
|
|
537
537
|
return [
|
|
538
538
|
{
|
|
539
539
|
name: "ask",
|
|
540
|
-
description: "Ask the agent a question through a full conversation turn. This has the same identity, tools, and session continuity as send_message; use
|
|
540
|
+
description: "Ask the agent a question through a full conversation turn. This has the same identity, tools, and session continuity as send_message; use search_facts or consult_notes for read-only record lookup.",
|
|
541
541
|
inputSchema: {
|
|
542
542
|
type: "object",
|
|
543
543
|
properties: {
|
|
@@ -548,7 +548,7 @@ function getToolSchemas() {
|
|
|
548
548
|
},
|
|
549
549
|
{
|
|
550
550
|
name: "status",
|
|
551
|
-
description: "Get the agent's current status including active sessions,
|
|
551
|
+
description: "Get the agent's current status including active sessions, Desk record state, and activity level.",
|
|
552
552
|
inputSchema: {
|
|
553
553
|
type: "object",
|
|
554
554
|
properties: {},
|
|
@@ -576,19 +576,19 @@ function getToolSchemas() {
|
|
|
576
576
|
},
|
|
577
577
|
{
|
|
578
578
|
name: "get_context",
|
|
579
|
-
description: "Get the agent's current working context including
|
|
579
|
+
description: "Get the agent's current working context including Desk record summary, active tasks, and relevant state.",
|
|
580
580
|
inputSchema: {
|
|
581
581
|
type: "object",
|
|
582
582
|
properties: {},
|
|
583
583
|
},
|
|
584
584
|
},
|
|
585
585
|
{
|
|
586
|
-
name: "
|
|
587
|
-
description: "Read-only
|
|
586
|
+
name: "search_facts",
|
|
587
|
+
description: "Read-only fact search. Returns matching Desk record diary lines without running an agent turn or treating missing matches as absence of agent belief.",
|
|
588
588
|
inputSchema: {
|
|
589
589
|
type: "object",
|
|
590
590
|
properties: {
|
|
591
|
-
query: { type: "string", description: "Search term to look for in agent
|
|
591
|
+
query: { type: "string", description: "Search term to look for in agent facts" },
|
|
592
592
|
},
|
|
593
593
|
required: ["query"],
|
|
594
594
|
},
|
|
@@ -10,6 +10,7 @@ const SENSES = [
|
|
|
10
10
|
{ sense: "mail", label: "Mail", daemonManaged: true },
|
|
11
11
|
{ sense: "voice", label: "Voice", daemonManaged: true },
|
|
12
12
|
{ sense: "a2a", label: "A2A", daemonManaged: true },
|
|
13
|
+
{ sense: "workbench", label: "Workbench", daemonManaged: false },
|
|
13
14
|
];
|
|
14
15
|
function configuredSenses(senses) {
|
|
15
16
|
const configured = senses ?? {};
|
|
@@ -21,6 +22,7 @@ function configuredSenses(senses) {
|
|
|
21
22
|
mail: configured.mail ?? { ...identity_1.DEFAULT_AGENT_SENSES.mail },
|
|
22
23
|
voice: configured.voice ?? { ...identity_1.DEFAULT_AGENT_SENSES.voice },
|
|
23
24
|
a2a: configured.a2a ?? { ...identity_1.DEFAULT_AGENT_SENSES.a2a },
|
|
25
|
+
workbench: configured.workbench ?? { ...identity_1.DEFAULT_AGENT_SENSES.workbench },
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
function resolveStatus(enabled, daemonManaged, runtimeInfo) {
|