@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.
Files changed (55) hide show
  1. package/README.md +13 -13
  2. package/changelog.json +21 -0
  3. package/dist/arc/evolution.js +1 -1
  4. package/dist/arc/flight-recorder.js +369 -0
  5. package/dist/arc/obligations.js +24 -2
  6. package/dist/heart/active-work.js +1 -1
  7. package/dist/heart/config-registry.js +14 -5
  8. package/dist/heart/daemon/agent-config-check.js +1 -1
  9. package/dist/heart/daemon/agent-service.js +18 -17
  10. package/dist/heart/daemon/cli-exec.js +134 -15
  11. package/dist/heart/daemon/cli-help.js +21 -2
  12. package/dist/heart/daemon/cli-parse.js +31 -3
  13. package/dist/heart/daemon/daemon-entry.js +1 -1
  14. package/dist/heart/daemon/daemon.js +3 -3
  15. package/dist/heart/daemon/hooks/bundle-meta.js +29 -9
  16. package/dist/heart/daemon/inner-status.js +4 -15
  17. package/dist/heart/daemon/sense-manager.js +16 -1
  18. package/dist/heart/habits/habit-parser.js +64 -1
  19. package/dist/heart/hatch/hatch-flow.js +17 -9
  20. package/dist/heart/hatch/specialist-tools.js +15 -11
  21. package/dist/heart/identity.js +4 -1
  22. package/dist/heart/kept-notes.js +5 -73
  23. package/dist/heart/mailbox/readers/runtime-readers.js +21 -49
  24. package/dist/heart/mcp/mcp-server.js +8 -8
  25. package/dist/heart/sense-truth.js +2 -0
  26. package/dist/heart/session-events.js +1 -31
  27. package/dist/heart/start-of-turn-packet.js +8 -2
  28. package/dist/heart/tool-description.js +15 -3
  29. package/dist/heart/turn-context.js +34 -7
  30. package/dist/heart/work-card.js +386 -0
  31. package/dist/mailbox-ui/assets/{index-9-AxCxuB.js → index-Cbasiy6y.js} +1 -1
  32. package/dist/mailbox-ui/index.html +1 -1
  33. package/dist/mind/bundle-manifest.js +9 -3
  34. package/dist/mind/context.js +1 -2
  35. package/dist/mind/desk-section.js +53 -1
  36. package/dist/mind/diary.js +2 -3
  37. package/dist/mind/note-search.js +36 -106
  38. package/dist/mind/prompt.js +45 -102
  39. package/dist/mind/record-paths.js +312 -0
  40. package/dist/repertoire/bundle-templates.js +4 -5
  41. package/dist/repertoire/tools-bundle.js +1 -1
  42. package/dist/repertoire/tools-evolution.js +4 -4
  43. package/dist/repertoire/tools-notes.js +42 -62
  44. package/dist/repertoire/tools-record.js +16 -11
  45. package/dist/repertoire/tools-session.js +4 -4
  46. package/dist/repertoire/tools.js +1 -1
  47. package/dist/senses/habit-turn-message.js +19 -5
  48. package/dist/senses/inner-dialog-worker.js +58 -9
  49. package/dist/senses/inner-dialog.js +30 -11
  50. package/dist/senses/pipeline.js +135 -1
  51. package/dist/util/frontmatter.js +17 -1
  52. package/package.json +3 -3
  53. package/skills/configure-dev-tools.md +1 -1
  54. package/skills/travel-planning.md +1 -1
  55. package/dist/mind/journal-index.js +0 -162
@@ -73,11 +73,12 @@ const habit_turn_message_1 = require("./habit-turn-message");
73
73
  const await_turn_message_1 = require("./await-turn-message");
74
74
  const await_parser_1 = require("../heart/awaiting/await-parser");
75
75
  const await_runtime_state_1 = require("../heart/awaiting/await-runtime-state");
76
- const journal_index_1 = require("../mind/journal-index");
77
76
  const habit_parser_1 = require("../heart/habits/habit-parser");
78
77
  const habit_runtime_state_1 = require("../heart/habits/habit-runtime-state");
79
78
  const cadence_1 = require("../heart/daemon/cadence");
80
79
  const daemon_health_1 = require("../heart/daemon/daemon-health");
80
+ const flight_recorder_1 = require("../arc/flight-recorder");
81
+ const desk_section_1 = require("../mind/desk-section");
81
82
  const DEFAULT_INNER_DIALOG_INSTINCTS = [
82
83
  {
83
84
  id: "heartbeat_checkin",
@@ -627,6 +628,20 @@ function buildAlsoDueLine(agentRoot, currentHabitName, now) {
627
628
  return "";
628
629
  return `also due: ${alsoDue.join(", ")}`;
629
630
  }
631
+ function buildHabitSurfacePolicy(origin, surface) {
632
+ const lines = ["## habit surface policy"];
633
+ lines.push("this habit runs privately, but it may message outward when it needs input, has a useful answer, is blocked, or should report status.");
634
+ if (surface.family)
635
+ lines.push("- family recipients are allowed by default.");
636
+ if (surface.originator && origin)
637
+ lines.push(`- the originator is allowed: ${origin.friendId} via ${origin.channel}/${origin.key}.`);
638
+ if (surface.originator && !origin)
639
+ lines.push("- originator messaging is enabled, but this habit has no origin metadata.");
640
+ if (surface.extra.length > 0)
641
+ lines.push(`- extra allowed recipients: ${surface.extra.join(", ")}.`);
642
+ lines.push("- use send_message for intentional contact; use surface only for a held return tied to an existing return obligation.");
643
+ return lines.join("\n");
644
+ }
630
645
  async function runInnerDialogTurn(options) {
631
646
  const now = options?.now ?? (() => new Date());
632
647
  const reason = options?.reason ?? "instinct";
@@ -681,6 +696,8 @@ async function runInnerDialogTurn(options) {
681
696
  let habitBody;
682
697
  let habitTitle = habitName;
683
698
  let habitLastRun = null;
699
+ let habitOrigin = null;
700
+ let habitSurface = { family: true, originator: true, extra: [] };
684
701
  try {
685
702
  const habitContent = fs.readFileSync(habitFilePath, "utf-8");
686
703
  const parsed = (0, habit_runtime_state_1.applyHabitRuntimeState)(agentRoot, (0, habit_parser_1.parseHabitFile)(habitContent, habitFilePath));
@@ -688,6 +705,8 @@ async function runInnerDialogTurn(options) {
688
705
  habitTitle = parsed.title || habitName;
689
706
  habitLastRun = parsed.lastRun;
690
707
  habitTools = parsed.tools;
708
+ habitOrigin = parsed.origin;
709
+ habitSurface = parsed.surface;
691
710
  }
692
711
  catch {
693
712
  // Habit file missing or unreadable
@@ -707,6 +726,9 @@ async function runInnerDialogTurn(options) {
707
726
  stalenessMs: nowMs - o.createdAt,
708
727
  }));
709
728
  const alsoDue = buildAlsoDueLine(agentRoot, habitName, now);
729
+ const arcResume = (0, flight_recorder_1.formatFlightRecorderResume)((0, flight_recorder_1.readFlightRecorderResume)(agentRoot));
730
+ const deskOrientation = (0, desk_section_1.deskRecordOrientationSection)(agentRoot, now());
731
+ const surfacePolicy = buildHabitSurfacePolicy(habitOrigin, habitSurface);
710
732
  // Degraded state (best-effort: never crash)
711
733
  let degradedComponents = [];
712
734
  try {
@@ -728,17 +750,11 @@ async function runInnerDialogTurn(options) {
728
750
  staleObligations,
729
751
  parseErrors: options?.parseErrors ?? [],
730
752
  degradedComponents,
753
+ arcResume,
754
+ deskOrientation,
755
+ surfacePolicy,
731
756
  now,
732
757
  });
733
- // Piggyback journal embedding indexing (best-effort, fire-and-forget)
734
- const journalDir = path.join(agentRoot, "journal");
735
- /* v8 ignore start -- journal indexing piggyback: embedding provider may not be available; tested via journal-index unit tests @preserve */
736
- void (0, journal_index_1.indexJournalFiles)(journalDir, path.join(journalDir, ".index.json"), {
737
- embed: async () => [],
738
- }).catch(() => {
739
- // swallowed: indexing failure must never block habit turn
740
- });
741
- /* v8 ignore stop */
742
758
  }
743
759
  }
744
760
  else if (reason === "await" && options?.awaitName) {
@@ -827,7 +843,10 @@ async function runInnerDialogTurn(options) {
827
843
  };
828
844
  }
829
845
  // Fresh session: build system prompt
830
- const systemPrompt = await (0, prompt_1.buildSystem)("inner", { toolChoiceRequired: true });
846
+ const systemPrompt = await (0, prompt_1.buildSystem)("inner", {
847
+ toolChoiceRequired: true,
848
+ flightRecorderResume: (0, flight_recorder_1.readFlightRecorderResume)((0, identity_1.getAgentRoot)()),
849
+ });
831
850
  return {
832
851
  messages: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(systemPrompt) }],
833
852
  sessionPath: sessionFilePath,
@@ -52,6 +52,8 @@ const socket_client_1 = require("../heart/daemon/socket-client");
52
52
  const active_work_1 = require("../heart/active-work");
53
53
  const delegation_1 = require("../heart/delegation");
54
54
  const obligations_1 = require("../arc/obligations");
55
+ const packets_1 = require("../arc/packets");
56
+ const evolution_1 = require("../arc/evolution");
55
57
  const provider_failover_1 = require("../heart/provider-failover");
56
58
  const openai_codex_token_1 = require("../heart/providers/openai-codex-token");
57
59
  const tempo_1 = require("../heart/tempo");
@@ -66,7 +68,16 @@ const episodes_1 = require("../arc/episodes");
66
68
  const turn_context_1 = require("../heart/turn-context");
67
69
  const provider_visibility_1 = require("../heart/provider-visibility");
68
70
  const orientation_frame_1 = require("../heart/orientation-frame");
71
+ const flight_recorder_1 = require("../arc/flight-recorder");
69
72
  const VOICE_PENDING_MAX_AGE_MS = 15 * 60 * 1_000;
73
+ const ACTIVE_FLIGHT_RECORDER_PACKET_STATUSES = new Set([
74
+ "drafting",
75
+ "processing",
76
+ "validating",
77
+ "collaborating",
78
+ "paused",
79
+ "blocked",
80
+ ]);
70
81
  function pendingExpirationReason(channel, message, now) {
71
82
  /* v8 ignore start -- pending expiry edge permutations are covered by the stale voice queue tests; this helper keeps defensive non-voice fallbacks @preserve */
72
83
  if (Number.isFinite(message.expiresAt) && Number(message.expiresAt) <= now)
@@ -198,6 +209,65 @@ function latestUserAuthoredText(messages, continuityIngressTexts) {
198
209
  .filter(Boolean);
199
210
  return userMessages[userMessages.length - 1];
200
211
  }
212
+ function sessionRefForFlightRecorder(session) {
213
+ return `${session.friendId}/${session.channel}/${session.key}`;
214
+ }
215
+ function isTerminalWithoutContinuation(outcome, hasActiveContinuation) {
216
+ return !hasActiveContinuation && (outcome === "settled"
217
+ || outcome === "observed"
218
+ || outcome === "rested"
219
+ || outcome === "superseded");
220
+ }
221
+ function readPostTurnFlightRecorderArcSnapshot(agentRoot) {
222
+ const latest = (0, flight_recorder_1.readFlightRecorderResume)(agentRoot);
223
+ return {
224
+ activeObligationIds: (0, obligations_1.readPendingObligations)(agentRoot).map((obligation) => obligation.id),
225
+ activeReturnObligationIds: (0, obligations_1.listActiveReturnObligationsForRoot)(agentRoot).map((obligation) => obligation.id),
226
+ activePacketIds: (0, packets_1.listPonderPackets)(agentRoot)
227
+ .filter((packet) => ACTIVE_FLIGHT_RECORDER_PACKET_STATUSES.has(packet.status))
228
+ .map((packet) => packet.id),
229
+ openEvolutionCaseIds: (0, evolution_1.listOpenEvolutionCases)(agentRoot).map((evolutionCase) => evolutionCase.id),
230
+ recentClaimIds: latest.recentClaimIds,
231
+ unverifiedClaimIds: latest.unverifiedClaimIds,
232
+ };
233
+ }
234
+ function recordPostTurnFlightRecorderCheckpoint(input) {
235
+ const hasActiveContinuation = input.mustResolveBeforeHandoff
236
+ || input.activeObligationIds.length > 0
237
+ || input.activeReturnObligationIds.length > 0
238
+ || input.activePacketIds.length > 0
239
+ || input.openEvolutionCaseIds.length > 0;
240
+ const blockedBecause = [
241
+ ...(input.outcome === "blocked" ? ["agent reported a blocker in this turn"] : []),
242
+ ...(input.outcome === "errored" ? ["turn errored before a safe continuation was reached"] : []),
243
+ ...(input.outcome === "aborted" ? ["turn aborted before a safe continuation was reached"] : []),
244
+ ...(isTerminalWithoutContinuation(input.outcome, hasActiveContinuation) ? [`turn outcome ${input.outcome}; wait for new input before acting`] : []),
245
+ ];
246
+ const nextSafeAction = input.nextSafeAction
247
+ ?? (blockedBecause.length > 0
248
+ ? "inspect the latest session and wait for new input before acting"
249
+ : "continue the current held work and update Arc/Desk with the next checkpoint");
250
+ (0, flight_recorder_1.recordFlightRecorderEvent)(input.agentRoot, {
251
+ kind: "post_turn_persisted",
252
+ sessionRef: sessionRefForFlightRecorder(input.currentSession),
253
+ summary: `persisted ${input.currentSession.channel}/${input.currentSession.key} turn with outcome ${input.outcome}`,
254
+ currentAsk: input.currentAsk,
255
+ nextSafeAction,
256
+ blockedBecause,
257
+ stopBefore: blockedBecause.length > 0 ? ["acting on stale context"] : [],
258
+ activeObligationIds: input.activeObligationIds,
259
+ activeReturnObligationIds: input.activeReturnObligationIds,
260
+ activePacketIds: input.activePacketIds,
261
+ openEvolutionCaseIds: input.openEvolutionCaseIds,
262
+ recentClaimIds: input.recentClaimIds,
263
+ unverifiedClaimIds: input.unverifiedClaimIds,
264
+ meta: {
265
+ outcome: input.outcome,
266
+ mustResolveBeforeHandoff: input.mustResolveBeforeHandoff,
267
+ sessionPath: input.currentSession.sessionPath,
268
+ },
269
+ });
270
+ }
201
271
  function resolveCurrentFailoverBinding(agentName, lane) {
202
272
  const agentRoot = (0, identity_1.getAgentRoot)();
203
273
  const { config: agentConfig } = (0, auth_flow_1.readAgentConfigForAgent)(agentName, path.dirname(agentRoot));
@@ -643,6 +713,7 @@ async function handleInboundTurn(input) {
643
713
  all: activeWorkFrame.pendingObligations,
644
714
  },
645
715
  currentSessionTiming,
716
+ flightRecorderResume: ctx.flightRecorderResume,
646
717
  });
647
718
  /* v8 ignore next 3 -- syncFailure propagation tested in sync.test.ts @preserve */
648
719
  if (syncFailure) {
@@ -708,7 +779,7 @@ async function handleInboundTurn(input) {
708
779
  senseStatusLines: ctx.senseStatusLines,
709
780
  bundleMeta: ctx.bundleMeta,
710
781
  daemonHealth: ctx.daemonHealth,
711
- journalFiles: ctx.journalFiles,
782
+ flightRecorderResume: ctx.flightRecorderResume,
712
783
  ...(ctx.providerVisibility ? { providerVisibility: ctx.providerVisibility } : {}),
713
784
  toolContext: {
714
785
  /* v8 ignore next -- default no-op signin satisfies interface; real signin injected by sense adapter @preserve */
@@ -738,6 +809,36 @@ async function handleInboundTurn(input) {
738
809
  result.error?.message ?? "unknown error", classification, currentProvider, currentBinding.model, agentName, inventory, {}, { currentLane });
739
810
  input.failoverState.pending = failoverContext;
740
811
  input.postTurn(sessionMessages, session.sessionPath, result.usage);
812
+ try {
813
+ const postTurnArc = readPostTurnFlightRecorderArcSnapshot((0, identity_1.getAgentRoot)());
814
+ recordPostTurnFlightRecorderCheckpoint({
815
+ agentRoot: (0, identity_1.getAgentRoot)(),
816
+ currentSession,
817
+ currentAsk: currentObligation
818
+ ?? activeWorkFrame.primaryObligation?.content?.trim()
819
+ ?? currentUserMessage
820
+ ?? null,
821
+ nextSafeAction: failoverContext.userMessage,
822
+ outcome: "errored",
823
+ mustResolveBeforeHandoff,
824
+ activeObligationIds: postTurnArc.activeObligationIds,
825
+ activeReturnObligationIds: postTurnArc.activeReturnObligationIds,
826
+ activePacketIds: postTurnArc.activePacketIds,
827
+ openEvolutionCaseIds: postTurnArc.openEvolutionCaseIds,
828
+ recentClaimIds: postTurnArc.recentClaimIds,
829
+ unverifiedClaimIds: postTurnArc.unverifiedClaimIds,
830
+ });
831
+ }
832
+ catch (checkpointError) {
833
+ /* v8 ignore next -- best-effort recorder write must not hide provider-failover guidance @preserve */
834
+ (0, runtime_1.emitNervesEvent)({
835
+ level: "warn",
836
+ component: "senses",
837
+ event: "senses.flight_recorder_checkpoint_error",
838
+ message: "failed to record provider-failover flight recorder checkpoint",
839
+ meta: { error: checkpointError instanceof Error ? checkpointError.message : String(checkpointError) },
840
+ });
841
+ }
741
842
  return {
742
843
  resolvedContext,
743
844
  gateResult,
@@ -781,6 +882,39 @@ async function handleInboundTurn(input) {
781
882
  : undefined)
782
883
  : (Object.keys(continuingState).length > 0 ? continuingState : undefined);
783
884
  input.postTurn(sessionMessages, session.sessionPath, result.usage, undefined, nextState);
885
+ try {
886
+ const agentRoot = (0, identity_1.getAgentRoot)();
887
+ const postTurnArc = readPostTurnFlightRecorderArcSnapshot(agentRoot);
888
+ recordPostTurnFlightRecorderCheckpoint({
889
+ agentRoot,
890
+ currentSession,
891
+ currentAsk: currentObligation
892
+ ?? activeWorkFrame.primaryObligation?.content?.trim()
893
+ ?? currentUserMessage
894
+ ?? null,
895
+ nextSafeAction: activeWorkFrame.resumeHandle?.nextAction
896
+ ?? activeWorkFrame.primaryObligation?.nextAction?.trim()
897
+ ?? null,
898
+ outcome: result.outcome ?? "observed",
899
+ mustResolveBeforeHandoff,
900
+ activeObligationIds: postTurnArc.activeObligationIds,
901
+ activeReturnObligationIds: postTurnArc.activeReturnObligationIds,
902
+ activePacketIds: postTurnArc.activePacketIds,
903
+ openEvolutionCaseIds: postTurnArc.openEvolutionCaseIds,
904
+ recentClaimIds: postTurnArc.recentClaimIds,
905
+ unverifiedClaimIds: postTurnArc.unverifiedClaimIds,
906
+ });
907
+ }
908
+ catch (error) {
909
+ /* v8 ignore next -- defensive recorder failures are non-fatal to already-persisted user turns @preserve */
910
+ (0, runtime_1.emitNervesEvent)({
911
+ level: "warn",
912
+ component: "senses",
913
+ event: "senses.flight_recorder_checkpoint_error",
914
+ message: "failed to record post-turn flight recorder checkpoint",
915
+ meta: { error: error instanceof Error ? error.message : String(error) },
916
+ });
917
+ }
784
918
  // Step 7: Token accumulation
785
919
  await input.accumulateFriendTokens(input.friendStore, resolvedContext.friend.id, result.usage);
786
920
  (0, runtime_1.emitNervesEvent)({
@@ -17,6 +17,10 @@ function parseScalar(raw) {
17
17
  const value = raw.trim();
18
18
  if (value === "null")
19
19
  return null;
20
+ if (value === "true")
21
+ return true;
22
+ if (value === "false")
23
+ return false;
20
24
  if (value === "[]")
21
25
  return [];
22
26
  if ((value.startsWith("\"") && value.endsWith("\"")) || (value.startsWith("'") && value.endsWith("'"))) {
@@ -46,7 +50,19 @@ function parseFrontmatter(raw) {
46
50
  items.push(parseScalar(lines[cursor].replace(/^\s*-\s+/, "")));
47
51
  cursor += 1;
48
52
  }
49
- frontmatter[key] = items;
53
+ if (items.length > 0) {
54
+ frontmatter[key] = items;
55
+ idx = cursor - 1;
56
+ continue;
57
+ }
58
+ const nested = {};
59
+ cursor = idx + 1;
60
+ while (cursor < lines.length && /^\s+[A-Za-z0-9_:-]+:\s*/.test(lines[cursor])) {
61
+ const child = /^\s+([A-Za-z0-9_:-]+):\s*(.*)$/.exec(lines[cursor]);
62
+ nested[child[1]] = parseScalar(child[2]);
63
+ cursor += 1;
64
+ }
65
+ frontmatter[key] = Object.keys(nested).length > 0 ? nested : items;
50
66
  idx = cursor - 1;
51
67
  }
52
68
  return frontmatter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.655",
3
+ "version": "0.1.0-alpha.658",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",
@@ -79,12 +79,12 @@
79
79
  "@types/mailparser": "^3.4.6",
80
80
  "@types/semver": "^7.7.1",
81
81
  "@types/smtp-server": "^3.5.13",
82
- "@vitest/coverage-v8": "^4.0.18",
82
+ "@vitest/coverage-v8": "^4.1.8",
83
83
  "eslint": "^10.0.2",
84
84
  "jsdom": "^29.0.2",
85
85
  "typescript": "^5.7.0",
86
86
  "typescript-eslint": "^8.56.1",
87
- "vitest": "^4.0.18"
87
+ "vitest": "^4.1.8"
88
88
  },
89
89
  "overrides": {
90
90
  "@microsoft/teams.apps": {
@@ -64,7 +64,7 @@ Once connected, these tools are available:
64
64
  - **status** -- Get agent's current status and activity
65
65
  - **catchup** -- Get recent activity summary
66
66
  - **get_context** -- Get agent's current working context
67
- - **search_notes** -- Read-only note search for specific topics; missing hits are not evidence that the agent has no belief or preference
67
+ - **search_facts** -- Read-only Desk record fact search for specific topics; missing hits are not evidence that the agent has no belief or preference
68
68
  - **get_task** -- Get details of the agent's current task
69
69
 
70
70
  ## Troubleshooting
@@ -92,7 +92,7 @@ an interactive sign-up flow.
92
92
 
93
93
  ### Post-Booking
94
94
  - Save confirmation details (confirmation number, dates, hotel name, airline, booking reference)
95
- - Save to diary/journal for future reference
95
+ - Save durable travel facts to the Desk record diary or notes for future reference
96
96
  - Set reminders for check-in windows
97
97
  - Note cancellation deadlines
98
98
 
@@ -1,162 +0,0 @@
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.indexJournalFiles = indexJournalFiles;
37
- const fs = __importStar(require("fs"));
38
- const path = __importStar(require("path"));
39
- const session_events_1 = require("../heart/session-events");
40
- const runtime_1 = require("../nerves/runtime");
41
- const TEXT_EXTENSIONS = new Set([".md", ".txt"]);
42
- const PREVIEW_CHAR_LIMIT = 500;
43
- function readExistingIndex(indexPath) {
44
- try {
45
- const raw = fs.readFileSync(indexPath, "utf8");
46
- const parsed = JSON.parse(raw);
47
- if (!Array.isArray(parsed))
48
- return [];
49
- return parsed;
50
- }
51
- catch {
52
- return [];
53
- }
54
- }
55
- function extractPreview(content) {
56
- const trimmed = content.trim();
57
- if (!trimmed)
58
- return "";
59
- return trimmed.split("\n")[0].replace(/^#+\s*/, "").trim();
60
- }
61
- async function indexJournalFiles(journalDir, indexPath, embedProvider) {
62
- // Read existing index
63
- const existingIndex = readExistingIndex(indexPath);
64
- const indexMap = new Map();
65
- for (const entry of existingIndex) {
66
- indexMap.set(entry.filename, entry);
67
- }
68
- // Scan journal dir for text files
69
- let dirEntries;
70
- try {
71
- dirEntries = fs.readdirSync(journalDir, { withFileTypes: true });
72
- }
73
- catch {
74
- (0, runtime_1.emitNervesEvent)({
75
- component: "mind",
76
- event: "mind.journal_index_scan",
77
- message: "journal dir not found or unreadable",
78
- meta: { journalDir },
79
- });
80
- return 0;
81
- }
82
- const textFiles = dirEntries.filter((entry) => {
83
- if (!entry.isFile())
84
- return false;
85
- if (entry.name.startsWith("."))
86
- return false;
87
- const ext = path.extname(entry.name).toLowerCase();
88
- return TEXT_EXTENSIONS.has(ext);
89
- });
90
- if (textFiles.length === 0) {
91
- (0, runtime_1.emitNervesEvent)({
92
- component: "mind",
93
- event: "mind.journal_index_scan",
94
- message: "no text files found in journal",
95
- meta: { journalDir },
96
- });
97
- return 0;
98
- }
99
- let newlyIndexed = 0;
100
- for (const file of textFiles) {
101
- const filePath = path.join(journalDir, file.name);
102
- let stat;
103
- try {
104
- stat = fs.statSync(filePath);
105
- }
106
- catch {
107
- /* v8 ignore next -- filesystem race: file deleted between readdir and stat @preserve */
108
- continue;
109
- }
110
- // Check if already indexed with same mtime
111
- const existing = indexMap.get(file.name);
112
- if (existing && existing.mtime === stat.mtimeMs) {
113
- continue;
114
- }
115
- // Read content for embedding
116
- let content;
117
- try {
118
- content = fs.readFileSync(filePath, "utf8");
119
- }
120
- catch {
121
- /* v8 ignore next -- filesystem race: file deleted between stat and read @preserve */
122
- continue;
123
- }
124
- const preview = (0, session_events_1.capStructuredRecordString)(extractPreview(content));
125
- const embedText = content.slice(0, PREVIEW_CHAR_LIMIT);
126
- // Generate embedding
127
- let embedding;
128
- try {
129
- const vectors = await embedProvider.embed([embedText]);
130
- embedding = vectors[0] ?? [];
131
- }
132
- catch {
133
- (0, runtime_1.emitNervesEvent)({
134
- level: "warn",
135
- component: "mind",
136
- event: "mind.journal_embedding_error",
137
- message: "embedding failed for journal file",
138
- meta: { filename: file.name },
139
- });
140
- embedding = [];
141
- }
142
- indexMap.set(file.name, {
143
- filename: file.name,
144
- embedding,
145
- mtime: stat.mtimeMs,
146
- preview,
147
- });
148
- newlyIndexed++;
149
- }
150
- // Write updated index back
151
- if (newlyIndexed > 0) {
152
- const updatedIndex = Array.from(indexMap.values());
153
- fs.writeFileSync(indexPath, JSON.stringify(updatedIndex, null, 2), "utf8");
154
- }
155
- (0, runtime_1.emitNervesEvent)({
156
- component: "mind",
157
- event: "mind.journal_index_complete",
158
- message: "journal indexing complete",
159
- meta: { journalDir, newlyIndexed, total: indexMap.size },
160
- });
161
- return newlyIndexed;
162
- }