@ouro.bot/cli 0.1.0-alpha.1 → 0.1.0-alpha.100

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.
Files changed (132) hide show
  1. package/AdoptionSpecialist.ouro/agent.json +70 -9
  2. package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
  3. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  4. package/README.md +147 -205
  5. package/assets/ouroboros.png +0 -0
  6. package/changelog.json +596 -0
  7. package/dist/heart/active-work.js +251 -0
  8. package/dist/heart/bridges/manager.js +358 -0
  9. package/dist/heart/bridges/state-machine.js +135 -0
  10. package/dist/heart/bridges/store.js +123 -0
  11. package/dist/heart/commitments.js +109 -0
  12. package/dist/heart/config.js +102 -23
  13. package/dist/heart/core.js +512 -94
  14. package/dist/heart/cross-chat-delivery.js +146 -0
  15. package/dist/heart/daemon/agent-discovery.js +81 -0
  16. package/dist/heart/daemon/auth-flow.js +430 -0
  17. package/dist/heart/daemon/daemon-cli.js +1935 -185
  18. package/dist/heart/daemon/daemon-entry.js +55 -6
  19. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  20. package/dist/heart/daemon/daemon.js +218 -9
  21. package/dist/heart/daemon/hatch-animation.js +35 -0
  22. package/dist/heart/daemon/hatch-flow.js +10 -83
  23. package/dist/heart/daemon/hatch-specialist.js +6 -1
  24. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  25. package/dist/heart/daemon/launchd.js +159 -0
  26. package/dist/heart/daemon/log-tailer.js +147 -0
  27. package/dist/heart/daemon/message-router.js +17 -8
  28. package/dist/heart/daemon/os-cron.js +260 -0
  29. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  30. package/dist/heart/daemon/ouro-bot-wrapper.js +4 -3
  31. package/dist/heart/daemon/ouro-path-installer.js +260 -0
  32. package/dist/heart/daemon/ouro-uti.js +11 -2
  33. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  34. package/dist/heart/daemon/process-manager.js +32 -2
  35. package/dist/heart/daemon/run-hooks.js +37 -0
  36. package/dist/heart/daemon/runtime-logging.js +61 -14
  37. package/dist/heart/daemon/runtime-metadata.js +219 -0
  38. package/dist/heart/daemon/runtime-mode.js +67 -0
  39. package/dist/heart/daemon/sense-manager.js +307 -0
  40. package/dist/heart/daemon/skill-management-installer.js +94 -0
  41. package/dist/heart/daemon/socket-client.js +202 -0
  42. package/dist/heart/daemon/specialist-orchestrator.js +129 -0
  43. package/dist/heart/daemon/specialist-prompt.js +99 -0
  44. package/dist/heart/daemon/specialist-tools.js +283 -0
  45. package/dist/heart/daemon/staged-restart.js +114 -0
  46. package/dist/heart/daemon/task-scheduler.js +4 -1
  47. package/dist/heart/daemon/thoughts.js +507 -0
  48. package/dist/heart/daemon/update-checker.js +111 -0
  49. package/dist/heart/daemon/update-hooks.js +138 -0
  50. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  51. package/dist/heart/delegation.js +62 -0
  52. package/dist/heart/identity.js +153 -23
  53. package/dist/heart/kicks.js +1 -19
  54. package/dist/heart/model-capabilities.js +48 -0
  55. package/dist/heart/obligations.js +191 -0
  56. package/dist/heart/progress-story.js +42 -0
  57. package/dist/heart/providers/anthropic.js +77 -9
  58. package/dist/heart/providers/azure.js +86 -7
  59. package/dist/heart/providers/github-copilot.js +149 -0
  60. package/dist/heart/providers/minimax.js +4 -0
  61. package/dist/heart/providers/openai-codex.js +12 -3
  62. package/dist/heart/safe-workspace.js +381 -0
  63. package/dist/heart/sense-truth.js +61 -0
  64. package/dist/heart/session-activity.js +169 -0
  65. package/dist/heart/session-recall.js +116 -0
  66. package/dist/heart/streaming.js +103 -22
  67. package/dist/heart/target-resolution.js +123 -0
  68. package/dist/heart/turn-coordinator.js +28 -0
  69. package/dist/mind/associative-recall.js +37 -4
  70. package/dist/mind/bundle-manifest.js +70 -0
  71. package/dist/mind/context.js +141 -11
  72. package/dist/mind/first-impressions.js +16 -2
  73. package/dist/mind/friends/channel.js +43 -0
  74. package/dist/mind/friends/group-context.js +144 -0
  75. package/dist/mind/friends/store-file.js +19 -0
  76. package/dist/mind/friends/trust-explanation.js +74 -0
  77. package/dist/mind/friends/types.js +9 -1
  78. package/dist/mind/memory.js +89 -26
  79. package/dist/mind/obligation-steering.js +31 -0
  80. package/dist/mind/pending.js +160 -0
  81. package/dist/mind/phrases.js +1 -0
  82. package/dist/mind/prompt-refresh.js +20 -0
  83. package/dist/mind/prompt.js +499 -8
  84. package/dist/mind/token-estimate.js +8 -12
  85. package/dist/nerves/cli-logging.js +15 -2
  86. package/dist/nerves/coverage/file-completeness.js +14 -4
  87. package/dist/nerves/coverage/run-artifacts.js +1 -1
  88. package/dist/nerves/index.js +12 -0
  89. package/dist/repertoire/ado-client.js +4 -2
  90. package/dist/repertoire/coding/feedback.js +210 -0
  91. package/dist/repertoire/coding/index.js +4 -1
  92. package/dist/repertoire/coding/manager.js +69 -4
  93. package/dist/repertoire/coding/spawner.js +21 -3
  94. package/dist/repertoire/coding/tools.js +105 -2
  95. package/dist/repertoire/data/ado-endpoints.json +188 -0
  96. package/dist/repertoire/guardrails.js +290 -0
  97. package/dist/repertoire/mcp-client.js +254 -0
  98. package/dist/repertoire/mcp-manager.js +195 -0
  99. package/dist/repertoire/skills.js +3 -26
  100. package/dist/repertoire/tasks/board.js +12 -0
  101. package/dist/repertoire/tasks/index.js +23 -9
  102. package/dist/repertoire/tasks/transitions.js +1 -2
  103. package/dist/repertoire/tools-base.js +770 -213
  104. package/dist/repertoire/tools-bluebubbles.js +93 -0
  105. package/dist/repertoire/tools-teams.js +58 -25
  106. package/dist/repertoire/tools.js +106 -53
  107. package/dist/senses/bluebubbles-client.js +484 -0
  108. package/dist/senses/bluebubbles-entry.js +13 -0
  109. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  110. package/dist/senses/bluebubbles-media.js +339 -0
  111. package/dist/senses/bluebubbles-model.js +261 -0
  112. package/dist/senses/bluebubbles-mutation-log.js +116 -0
  113. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  114. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  115. package/dist/senses/bluebubbles.js +1181 -0
  116. package/dist/senses/cli-layout.js +187 -0
  117. package/dist/senses/cli.js +452 -99
  118. package/dist/senses/continuity.js +94 -0
  119. package/dist/senses/debug-activity.js +154 -0
  120. package/dist/senses/inner-dialog-worker.js +47 -18
  121. package/dist/senses/inner-dialog.js +387 -70
  122. package/dist/senses/pipeline.js +307 -0
  123. package/dist/senses/session-lock.js +119 -0
  124. package/dist/senses/teams.js +574 -129
  125. package/dist/senses/trust-gate.js +112 -2
  126. package/package.json +16 -4
  127. package/subagents/README.md +4 -68
  128. package/dist/heart/daemon/subagent-installer.js +0 -125
  129. package/dist/inner-worker-entry.js +0 -4
  130. package/subagents/work-doer.md +0 -233
  131. package/subagents/work-merger.md +0 -593
  132. package/subagents/work-planner.md +0 -373
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeTrustContext = describeTrustContext;
4
+ const runtime_1 = require("../../nerves/runtime");
5
+ function findRelatedGroupId(friend) {
6
+ return friend.externalIds.find((externalId) => externalId.externalId.startsWith("group:"))?.externalId;
7
+ }
8
+ function resolveLevel(friend) {
9
+ return friend.trustLevel ?? "stranger";
10
+ }
11
+ function describeTrustContext(input) {
12
+ const level = resolveLevel(input.friend);
13
+ const relatedGroupId = findRelatedGroupId(input.friend);
14
+ const explanation = level === "family" || level === "friend"
15
+ ? {
16
+ level,
17
+ basis: "direct",
18
+ summary: level === "family"
19
+ ? "direct family trust"
20
+ : "direct trusted relationship",
21
+ why: "this relationship is directly trusted rather than inferred through a shared group or cold first contact.",
22
+ permits: [
23
+ "local operations when appropriate",
24
+ "proactive follow-through",
25
+ "full collaborative problem solving",
26
+ ],
27
+ constraints: [],
28
+ }
29
+ : level === "acquaintance"
30
+ ? {
31
+ level,
32
+ basis: "shared_group",
33
+ summary: relatedGroupId
34
+ ? "known through the shared project group"
35
+ : "known through a shared group context",
36
+ why: relatedGroupId
37
+ ? `this trust comes from the shared group context ${relatedGroupId}, not from direct endorsement.`
38
+ : "this trust comes from shared group context rather than direct endorsement.",
39
+ permits: [
40
+ "group-safe coordination",
41
+ "normal conversation inside the shared context",
42
+ ],
43
+ constraints: [
44
+ "guarded local actions",
45
+ "do not assume broad private authority",
46
+ ],
47
+ relatedGroupId,
48
+ }
49
+ : {
50
+ level,
51
+ basis: "unknown",
52
+ summary: "truly unknown first-contact context",
53
+ why: "this person is not known through direct trust or a shared group context.",
54
+ permits: [
55
+ "safe first-contact orientation only",
56
+ ],
57
+ constraints: [
58
+ "first contact does not reach the full model on open channels",
59
+ "no local or privileged actions",
60
+ ],
61
+ };
62
+ (0, runtime_1.emitNervesEvent)({
63
+ component: "friends",
64
+ event: "friends.trust_explained",
65
+ message: "built explicit trust explanation",
66
+ meta: {
67
+ channel: input.channel,
68
+ level: explanation.level,
69
+ basis: explanation.basis,
70
+ hasRelatedGroup: Boolean(explanation.relatedGroupId),
71
+ },
72
+ });
73
+ return explanation;
74
+ }
@@ -2,10 +2,12 @@
2
2
  // Context kernel type definitions.
3
3
  // FriendRecord (merged identity + memory), channel capabilities, and resolved context.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.TRUSTED_LEVELS = void 0;
5
6
  exports.isIdentityProvider = isIdentityProvider;
6
7
  exports.isIntegration = isIntegration;
8
+ exports.isTrustedLevel = isTrustedLevel;
7
9
  const runtime_1 = require("../../nerves/runtime");
8
- const IDENTITY_PROVIDERS = new Set(["aad", "local", "teams-conversation"]);
10
+ const IDENTITY_PROVIDERS = new Set(["aad", "local", "teams-conversation", "imessage-handle"]);
9
11
  function isIdentityProvider(value) {
10
12
  (0, runtime_1.emitNervesEvent)({
11
13
  component: "friends",
@@ -19,3 +21,9 @@ const INTEGRATIONS = new Set(["ado", "github", "graph"]);
19
21
  function isIntegration(value) {
20
22
  return typeof value === "string" && INTEGRATIONS.has(value);
21
23
  }
24
+ /** Trust levels that grant full tool access and proactive send capability. */
25
+ exports.TRUSTED_LEVELS = new Set(["family", "friend"]);
26
+ /** Whether a trust level grants full access (family or friend). Defaults to "friend" for legacy records. */
27
+ function isTrustedLevel(trustLevel) {
28
+ return exports.TRUSTED_LEVELS.has(trustLevel ?? "friend");
29
+ }
@@ -33,11 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.__memoryTestUtils = void 0;
37
36
  exports.ensureMemoryStorePaths = ensureMemoryStorePaths;
38
37
  exports.appendFactsWithDedup = appendFactsWithDedup;
39
38
  exports.readMemoryFacts = readMemoryFacts;
40
39
  exports.saveMemoryFact = saveMemoryFact;
40
+ exports.backfillEmbeddings = backfillEmbeddings;
41
41
  exports.searchMemoryFacts = searchMemoryFacts;
42
42
  const fs = __importStar(require("fs"));
43
43
  const path = __importStar(require("path"));
@@ -45,7 +45,9 @@ const crypto_1 = require("crypto");
45
45
  const config_1 = require("../heart/config");
46
46
  const identity_1 = require("../heart/identity");
47
47
  const runtime_1 = require("../nerves/runtime");
48
+ const associative_recall_1 = require("./associative-recall");
48
49
  const DEDUP_THRESHOLD = 0.6;
50
+ const SEMANTIC_DEDUP_THRESHOLD = 0.95;
49
51
  const ENTITY_TOKEN = /[a-z0-9]+/g;
50
52
  const DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
51
53
  class OpenAIEmbeddingProvider {
@@ -119,9 +121,16 @@ function readExistingFacts(factsPath) {
119
121
  const raw = fs.readFileSync(factsPath, "utf8").trim();
120
122
  if (!raw)
121
123
  return [];
122
- return raw
123
- .split("\n")
124
- .map((line) => JSON.parse(line));
124
+ const facts = [];
125
+ for (const line of raw.split("\n")) {
126
+ try {
127
+ facts.push(JSON.parse(line));
128
+ }
129
+ catch {
130
+ // Skip corrupt lines (e.g. partial write from a crash).
131
+ }
132
+ }
133
+ return facts;
125
134
  }
126
135
  function readEntityIndex(entitiesPath) {
127
136
  if (!fs.existsSync(entitiesPath))
@@ -169,13 +178,24 @@ function appendDailyFact(dailyDir, fact) {
169
178
  const dayPath = path.join(dailyDir, `${day}.jsonl`);
170
179
  fs.appendFileSync(dayPath, `${JSON.stringify(fact)}\n`, "utf8");
171
180
  }
172
- function appendFactsWithDedup(stores, incoming) {
181
+ function appendFactsWithDedup(stores, incoming, options) {
173
182
  const existing = readExistingFacts(stores.factsPath);
174
183
  const all = [...existing];
175
184
  let added = 0;
176
185
  let skipped = 0;
186
+ const semanticThreshold = options?.semanticThreshold;
177
187
  for (const fact of incoming) {
178
- const duplicate = all.some((prior) => overlapScore(prior.text, fact.text) > DEDUP_THRESHOLD);
188
+ const duplicate = all.some((prior) => {
189
+ if (overlapScore(prior.text, fact.text) > DEDUP_THRESHOLD)
190
+ return true;
191
+ if (semanticThreshold !== undefined &&
192
+ Array.isArray(fact.embedding) && fact.embedding.length > 0 &&
193
+ Array.isArray(prior.embedding) && prior.embedding.length > 0 &&
194
+ fact.embedding.length === prior.embedding.length) {
195
+ return (0, associative_recall_1.cosineSimilarity)(fact.embedding, prior.embedding) > semanticThreshold;
196
+ }
197
+ return false;
198
+ });
179
199
  if (duplicate) {
180
200
  skipped++;
181
201
  continue;
@@ -194,24 +214,6 @@ function appendFactsWithDedup(stores, incoming) {
194
214
  });
195
215
  return { added, skipped };
196
216
  }
197
- function cosineSimilarity(left, right) {
198
- if (left.length === 0 || right.length === 0 || left.length !== right.length)
199
- return 0;
200
- let dot = 0;
201
- let leftNorm = 0;
202
- let rightNorm = 0;
203
- for (let i = 0; i < left.length; i += 1) {
204
- dot += left[i] * right[i];
205
- leftNorm += left[i] * left[i];
206
- rightNorm += right[i] * right[i];
207
- }
208
- if (leftNorm === 0 || rightNorm === 0)
209
- return 0;
210
- return dot / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm));
211
- }
212
- exports.__memoryTestUtils = {
213
- cosineSimilarity,
214
- };
215
217
  function createDefaultEmbeddingProvider() {
216
218
  const apiKey = (0, config_1.getOpenAIEmbeddingsApiKey)().trim();
217
219
  if (!apiKey)
@@ -263,7 +265,68 @@ async function saveMemoryFact(options) {
263
265
  createdAt: (options.now ?? (() => new Date()))().toISOString(),
264
266
  embedding,
265
267
  };
266
- return appendFactsWithDedup(stores, [fact]);
268
+ return appendFactsWithDedup(stores, [fact], { semanticThreshold: SEMANTIC_DEDUP_THRESHOLD });
269
+ }
270
+ async function backfillEmbeddings(options) {
271
+ const memoryRoot = options?.memoryRoot ?? path.join((0, identity_1.getAgentRoot)(), "psyche", "memory");
272
+ const factsPath = path.join(memoryRoot, "facts.jsonl");
273
+ if (!fs.existsSync(factsPath))
274
+ return { total: 0, backfilled: 0, failed: 0 };
275
+ const facts = readExistingFacts(factsPath);
276
+ const needsEmbedding = facts.filter((f) => !Array.isArray(f.embedding) || f.embedding.length === 0);
277
+ if (needsEmbedding.length === 0)
278
+ return { total: facts.length, backfilled: 0, failed: 0 };
279
+ const provider = options?.embeddingProvider ?? createDefaultEmbeddingProvider();
280
+ if (!provider) {
281
+ (0, runtime_1.emitNervesEvent)({
282
+ level: "warn",
283
+ component: "mind",
284
+ event: "mind.memory_backfill_skipped",
285
+ message: "embedding provider unavailable for backfill",
286
+ meta: { needsEmbedding: needsEmbedding.length },
287
+ });
288
+ return { total: facts.length, backfilled: 0, failed: needsEmbedding.length };
289
+ }
290
+ const batchSize = options?.batchSize ?? 50;
291
+ let backfilled = 0;
292
+ let failed = 0;
293
+ for (let i = 0; i < needsEmbedding.length; i += batchSize) {
294
+ const batch = needsEmbedding.slice(i, i + batchSize);
295
+ try {
296
+ const vectors = await provider.embed(batch.map((f) => f.text));
297
+ for (let j = 0; j < batch.length; j++) {
298
+ batch[j].embedding = vectors[j] ?? [];
299
+ if (batch[j].embedding.length > 0)
300
+ backfilled++;
301
+ else
302
+ failed++;
303
+ }
304
+ }
305
+ catch (error) {
306
+ failed += batch.length;
307
+ (0, runtime_1.emitNervesEvent)({
308
+ level: "warn",
309
+ component: "mind",
310
+ event: "mind.memory_backfill_batch_error",
311
+ message: "embedding backfill batch failed",
312
+ meta: {
313
+ batchStart: i,
314
+ batchSize: batch.length,
315
+ reason: error instanceof Error ? error.message : String(error),
316
+ },
317
+ });
318
+ }
319
+ }
320
+ // Rewrite facts file with updated embeddings
321
+ const lines = facts.map((f) => JSON.stringify(f)).join("\n") + "\n";
322
+ fs.writeFileSync(factsPath, lines, "utf8");
323
+ (0, runtime_1.emitNervesEvent)({
324
+ component: "mind",
325
+ event: "mind.memory_backfill_complete",
326
+ message: "embedding backfill completed",
327
+ meta: { total: facts.length, backfilled, failed },
328
+ });
329
+ return { total: facts.length, backfilled, failed };
267
330
  }
268
331
  function substringMatches(queryLower, facts) {
269
332
  return facts.filter((fact) => fact.text.toLowerCase().includes(queryLower));
@@ -303,7 +366,7 @@ async function searchMemoryFacts(query, facts, embeddingProvider) {
303
366
  .filter((fact) => fact.embedding.length === queryEmbedding.length)
304
367
  .map((fact) => ({
305
368
  fact,
306
- score: cosineSimilarity(queryEmbedding, fact.embedding),
369
+ score: (0, associative_recall_1.cosineSimilarity)(queryEmbedding, fact.embedding),
307
370
  }))
308
371
  .filter((entry) => entry.score > 0)
309
372
  .sort((left, right) => right.score - left.score)
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findActivePersistentObligation = findActivePersistentObligation;
4
+ exports.renderActiveObligationSteering = renderActiveObligationSteering;
5
+ const runtime_1 = require("../nerves/runtime");
6
+ function findActivePersistentObligation(frame) {
7
+ if (!frame)
8
+ return null;
9
+ return (frame.pendingObligations ?? []).find((ob) => ob.status !== "pending" && ob.status !== "fulfilled") ?? null;
10
+ }
11
+ function renderActiveObligationSteering(obligation) {
12
+ (0, runtime_1.emitNervesEvent)({
13
+ component: "mind",
14
+ event: "mind.obligation_steering_rendered",
15
+ message: "rendered active obligation steering",
16
+ meta: {
17
+ hasObligation: Boolean(obligation),
18
+ hasSurface: Boolean(obligation?.currentSurface?.label),
19
+ },
20
+ });
21
+ if (!obligation)
22
+ return "";
23
+ const name = obligation.origin.friendId;
24
+ const surfaceLine = obligation.currentSurface?.label
25
+ ? `\nright now that work is happening in ${obligation.currentSurface.label}.`
26
+ : "";
27
+ return `## where my attention is
28
+ i'm already working on something i owe ${name}.${surfaceLine}
29
+
30
+ i should close that loop before i act like this is a fresh blank turn.`;
31
+ }
@@ -0,0 +1,160 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.INNER_DIALOG_PENDING = void 0;
37
+ exports.getPendingDir = getPendingDir;
38
+ exports.getDeferredReturnDir = getDeferredReturnDir;
39
+ exports.getInnerDialogPendingDir = getInnerDialogPendingDir;
40
+ exports.hasPendingMessages = hasPendingMessages;
41
+ exports.queuePendingMessage = queuePendingMessage;
42
+ exports.enqueueDeferredReturn = enqueueDeferredReturn;
43
+ exports.drainDeferredReturns = drainDeferredReturns;
44
+ exports.drainPending = drainPending;
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ const identity_1 = require("../heart/identity");
48
+ const runtime_1 = require("../nerves/runtime");
49
+ function getPendingDir(agentName, friendId, channel, key) {
50
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "pending", friendId, channel, key);
51
+ }
52
+ function getDeferredReturnDir(agentName, friendId) {
53
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "pending-returns", friendId);
54
+ }
55
+ /** Canonical inner-dialog pending path segments. */
56
+ exports.INNER_DIALOG_PENDING = { friendId: "self", channel: "inner", key: "dialog" };
57
+ /** Returns the pending dir for this agent's inner dialog. */
58
+ function getInnerDialogPendingDir(agentName) {
59
+ return getPendingDir(agentName, exports.INNER_DIALOG_PENDING.friendId, exports.INNER_DIALOG_PENDING.channel, exports.INNER_DIALOG_PENDING.key);
60
+ }
61
+ function hasPendingMessages(pendingDir) {
62
+ if (!fs.existsSync(pendingDir))
63
+ return false;
64
+ try {
65
+ return fs.readdirSync(pendingDir).some((entry) => entry.endsWith(".json") || entry.endsWith(".json.processing"));
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ function writeQueueFile(queueDir, message) {
72
+ fs.mkdirSync(queueDir, { recursive: true });
73
+ const fileName = `${message.timestamp}-${Math.random().toString(36).slice(2, 10)}.json`;
74
+ const filePath = path.join(queueDir, fileName);
75
+ fs.writeFileSync(filePath, JSON.stringify(message, null, 2));
76
+ return filePath;
77
+ }
78
+ function queuePendingMessage(pendingDir, message) {
79
+ writeQueueFile(pendingDir, message);
80
+ }
81
+ function drainQueue(queueDir) {
82
+ if (!fs.existsSync(queueDir))
83
+ return { messages: [], recovered: 0 };
84
+ let entries;
85
+ try {
86
+ entries = fs.readdirSync(queueDir);
87
+ }
88
+ catch {
89
+ return { messages: [], recovered: 0 };
90
+ }
91
+ // Collect both .json (new) and .processing (crash recovery)
92
+ const jsonFiles = entries.filter(f => f.endsWith(".json") && !f.endsWith(".processing"));
93
+ const processingFiles = entries.filter(f => f.endsWith(".json.processing"));
94
+ // Sort by filename (timestamp prefix gives chronological order)
95
+ const allFiles = [
96
+ ...processingFiles.map(f => ({ file: f, needsRename: false })),
97
+ ...jsonFiles.map(f => ({ file: f, needsRename: true })),
98
+ ].sort((a, b) => a.file.localeCompare(b.file));
99
+ const messages = [];
100
+ for (const { file, needsRename } of allFiles) {
101
+ const srcPath = path.join(queueDir, file);
102
+ const processingPath = needsRename
103
+ ? path.join(queueDir, file + ".processing")
104
+ : srcPath;
105
+ try {
106
+ if (needsRename) {
107
+ fs.renameSync(srcPath, processingPath);
108
+ }
109
+ const raw = fs.readFileSync(processingPath, "utf-8");
110
+ const parsed = JSON.parse(raw);
111
+ messages.push(parsed);
112
+ fs.unlinkSync(processingPath);
113
+ }
114
+ catch {
115
+ // Skip unparseable files — still try to clean up
116
+ try {
117
+ fs.unlinkSync(processingPath);
118
+ }
119
+ catch { /* ignore */ }
120
+ }
121
+ }
122
+ return {
123
+ messages,
124
+ recovered: processingFiles.length,
125
+ };
126
+ }
127
+ function enqueueDeferredReturn(agentName, friendId, message) {
128
+ const queueDir = getDeferredReturnDir(agentName, friendId);
129
+ const filePath = writeQueueFile(queueDir, message);
130
+ (0, runtime_1.emitNervesEvent)({
131
+ event: "mind.deferred_return_enqueued",
132
+ component: "mind",
133
+ message: "deferred return queued for later friend delivery",
134
+ meta: { friendId, queueDir },
135
+ });
136
+ return filePath;
137
+ }
138
+ function drainDeferredReturns(agentName, friendId) {
139
+ const queueDir = getDeferredReturnDir(agentName, friendId);
140
+ const { messages } = drainQueue(queueDir);
141
+ (0, runtime_1.emitNervesEvent)({
142
+ event: "mind.deferred_returns_drained",
143
+ component: "mind",
144
+ message: "deferred friend returns drained",
145
+ meta: { friendId, queueDir, count: messages.length },
146
+ });
147
+ return messages;
148
+ }
149
+ function drainPending(pendingDir) {
150
+ if (!fs.existsSync(pendingDir))
151
+ return [];
152
+ const { messages, recovered } = drainQueue(pendingDir);
153
+ (0, runtime_1.emitNervesEvent)({
154
+ event: "mind.pending_drained",
155
+ component: "mind",
156
+ message: "pending queue drained",
157
+ meta: { pendingDir, count: messages.length, recovered },
158
+ });
159
+ return messages;
160
+ }
@@ -16,6 +16,7 @@ function getPhrases() {
16
16
  message: "loading phrase pools",
17
17
  meta: {},
18
18
  });
19
+ (0, identity_1.resetAgentConfigCache)();
19
20
  const phrases = (0, identity_1.loadAgentConfig)().phrases;
20
21
  (0, runtime_1.emitNervesEvent)({
21
22
  event: "repertoire.load_end",
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.refreshSystemPrompt = refreshSystemPrompt;
4
+ const prompt_1 = require("./prompt");
5
+ const runtime_1 = require("../nerves/runtime");
6
+ async function refreshSystemPrompt(messages, channel, options, context) {
7
+ const newSystem = await (0, prompt_1.buildSystem)(channel, options, context);
8
+ if (messages.length > 0 && messages[0].role === "system") {
9
+ messages[0] = { role: "system", content: newSystem };
10
+ }
11
+ else {
12
+ messages.unshift({ role: "system", content: newSystem });
13
+ }
14
+ (0, runtime_1.emitNervesEvent)({
15
+ event: "mind.system_prompt_refreshed",
16
+ component: "mind",
17
+ message: "system prompt refreshed",
18
+ meta: { channel },
19
+ });
20
+ }