digital-brain 1.1.11 → 1.1.15

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 CHANGED
@@ -84,6 +84,7 @@ node ./bin/digital-brain.js init ./Digital Brain\ Vault
84
84
  - Create AI-readable memory files for future prompts.
85
85
  - Draft WhatsApp sends by default, send with explicit `--yes`, or configure auto-send mode during init.
86
86
  - Run an explicit WhatsApp auto-responder that uses Ollama or a Codex command plus vault memory while the command is running.
87
+ - Choose the WhatsApp auto-reply provider during init: Ollama, Codex app bridge, or Codex CLI.
87
88
  - Enforce an AI-disclosure guard after repeated AI-assisted sends.
88
89
 
89
90
  ## Core Commands
@@ -99,6 +100,7 @@ digital-brain send-whatsapp --to "Name" --message "text"
99
100
  digital-brain auto-whatsapp --allow "Name" --model llama3.1
100
101
  digital-brain auto-whatsapp --contact "+15551234567" --model llama3.1
101
102
  digital-brain auto-whatsapp --allow-all --provider codex --yes
103
+ digital-brain auto-whatsapp --allow-all --provider codex-app --yes
102
104
  ```
103
105
 
104
106
  `init` remembers your vault globally, so `run` works from anywhere. `run` syncs the live local sources you selected, extracts relationships, and writes interpreted memory in one command.
@@ -126,13 +128,14 @@ digital-brain auto-whatsapp --allow "Mom" --model llama3.1 --yes
126
128
  digital-brain auto-whatsapp --allow "Mom" --model llama3.1 --yes --no-process-unread
127
129
  digital-brain auto-whatsapp --contact "+15551234567" --model llama3.1 --yes
128
130
  digital-brain auto-whatsapp --allow-all --provider codex --yes
131
+ digital-brain auto-whatsapp --allow-all --provider codex-app --yes
129
132
  ```
130
133
 
131
134
  If you start without `--allow`, `--contact`, or `--allow-all` in an interactive terminal, Digital Brain asks whether to cover all contacts or select contacts from your WhatsApp chat list. With `--allow-all`, it still asks once before the first AI reply to each new chat and stores the decision in `08 Sources/WhatsApp/Outbound/auto-reply-whitelist.json`. Use `--auto-approve-new-chats` only if you intentionally want unattended first sends.
132
135
 
133
136
  Even with `--allow-all`, likely business, notification, OTP, delivery, bank, and support chats are skipped by default. Use explicit `--allow "Name"` or `--contact "+15551234567"` for trusted personal chats. Pass `--include-businesses` only if you intentionally want those chats included.
134
137
 
135
- The default provider is local Ollama. To use Codex instead:
138
+ The default provider is local Ollama. `--provider codex` uses the Codex CLI. `--provider codex-app` uses a file bridge for the Codex desktop app.
136
139
 
137
140
  ```bash
138
141
  digital-brain auto-whatsapp --allow-all --provider codex --yes
@@ -140,6 +143,16 @@ digital-brain auto-whatsapp --allow-all --provider codex --yes
140
143
 
141
144
  If your Codex CLI needs a custom command, pass `--codex-command "..."` or set `DIGITAL_BRAIN_CODEX_COMMAND`. If the command contains `{promptFile}`, Digital Brain writes the reply prompt to a temp file and substitutes that path; otherwise it pipes the prompt to stdin.
142
145
 
146
+ For the Codex desktop app bridge:
147
+
148
+ ```bash
149
+ digital-brain auto-whatsapp --allow-all --provider codex-app --yes
150
+ ```
151
+
152
+ Digital Brain writes requests to `08 Sources/WhatsApp/Outbound/Codex App Bridge/requests` and waits for matching JSON responses in `responses`.
153
+
154
+ If you select `Codex app bridge` during `digital-brain init`, the vault also gets `Tools/Codex App Bridge Automation.md` with the exact prompt to use in the Codex app.
155
+
143
156
  If Digital Brain has already sent two AI-assisted messages to the same chat in the last 24 hours, the next send must disclose that AI is helping. Once that chat has received an AI disclosure, Digital Brain will not keep repeating it.
144
157
 
145
158
  ## Automation
@@ -49,6 +49,7 @@ async function init(argv, args) {
49
49
  let activeWindow = args["active-window"] || "08:00-12:00";
50
50
  let timezone = args.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || "local";
51
51
  let outboundMode = args["outbound-mode"] || "draft";
52
+ let autoReplyProvider = args["auto-reply-provider"] || args.provider || "ollama";
52
53
  let privacyMode = args["privacy-mode"] || "standard";
53
54
  let sourceMarkdownMode = args["source-markdown-mode"] || "none";
54
55
  let selectedSources = parseList(args.sources || "whatsapp");
@@ -105,6 +106,13 @@ async function init(argv, args) {
105
106
  ["send-with-confirmation", "Send with confirmation", "Can send only after explicit command confirmation.", "✅"],
106
107
  ["auto-send", "Auto-send while running", "Lets auto-whatsapp send from allowlisted chats while it is running.", "🚦"],
107
108
  ], outboundMode);
109
+ if (outboundMode !== "disabled") {
110
+ autoReplyProvider = await select(rl, "WhatsApp auto-reply brain", [
111
+ ["ollama", "Ollama local model", "Runs fully local if Ollama and the model are installed.", "🦙"],
112
+ ["codex-app", "Codex app bridge", "Uses request/response files for a Codex desktop automation or thread.", "🧠"],
113
+ ["codex", "Codex CLI", "Uses a local codex command; only choose this if the CLI works.", "⌨️"],
114
+ ], autoReplyProvider);
115
+ }
108
116
  connectAi = await confirm(rl, "🔗 Add global AI pointers for Codex/Claude/Gemini?", true);
109
117
  responsibilityAccepted = await responsibilityGate(rl, { schedule, outboundMode });
110
118
  if (!responsibilityAccepted && needsResponsibilityGate({ schedule, outboundMode })) {
@@ -132,6 +140,7 @@ async function init(argv, args) {
132
140
  activeWindow,
133
141
  timezone,
134
142
  outboundMode,
143
+ autoReplyProvider,
135
144
  privacyMode,
136
145
  sourceMarkdownMode,
137
146
  selectedSources,
@@ -154,6 +163,7 @@ async function init(argv, args) {
154
163
  writeDefaultVault(vault);
155
164
  writeRefreshScript(vault, config);
156
165
  writeWatchScript(vault, config);
166
+ writeCodexAppBridgeGuide(vault, config);
157
167
 
158
168
  if (connectAi) {
159
169
  addGlobalPointer(path.join(os.homedir(), ".codex", "AGENTS.md"), vault, "Codex");
@@ -166,6 +176,14 @@ async function init(argv, args) {
166
176
  console.log(`Default vault saved: ${vault}`);
167
177
  console.log(`Refresh script: ${path.join(vault, "Tools", "digital-brain-refresh.sh")}`);
168
178
  console.log(`Always-on script: ${path.join(vault, "Tools", "digital-brain-watch.sh")}`);
179
+ if (autoReplyProvider === "codex-app") {
180
+ console.log(`Codex app bridge guide: ${path.join(vault, "Tools", "Codex App Bridge Automation.md")}`);
181
+ if (codexAppLooksAvailable()) {
182
+ console.log("Codex app detected. Add the generated bridge prompt as a Codex automation/thread to answer WhatsApp reply requests.");
183
+ } else {
184
+ console.log("Codex app config was not detected. The bridge guide was still generated for later use.");
185
+ }
186
+ }
169
187
  console.log("Next:");
170
188
  console.log(" digital-brain run");
171
189
  if (schedule === "always-on") console.log(` "${path.join(vault, "Tools", "digital-brain-watch.sh")}"`);
@@ -408,6 +426,48 @@ done
408
426
  fs.chmodSync(scriptPath, 0o755);
409
427
  }
410
428
 
429
+ function writeCodexAppBridgeGuide(vault, config) {
430
+ const toolsDir = path.join(vault, "Tools");
431
+ ensureDir(toolsDir);
432
+ const bridgeDir = path.join(vault, "08 Sources", "WhatsApp", "Outbound", "Codex App Bridge");
433
+ const requestsDir = path.join(bridgeDir, "requests");
434
+ const responsesDir = path.join(bridgeDir, "responses");
435
+ ensureDir(requestsDir);
436
+ ensureDir(responsesDir);
437
+ const prompt = `# Codex App Bridge Automation
438
+
439
+ Use this prompt in a Codex desktop automation or a live Codex thread when Digital Brain is configured with:
440
+
441
+ \`\`\`bash
442
+ digital-brain auto-whatsapp --provider codex-app --yes
443
+ \`\`\`
444
+
445
+ Request folder:
446
+
447
+ \`${requestsDir}\`
448
+
449
+ Response folder:
450
+
451
+ \`${responsesDir}\`
452
+
453
+ Automation prompt:
454
+
455
+ \`\`\`text
456
+ Check for pending Digital Brain WhatsApp reply requests in ${requestsDir}. For each .json request that does not already have its response file present, read the request JSON, use its prompt field to produce exactly one WhatsApp reply as the user, and write JSON to the request's responsePath in the exact shape {"reply":"..."}. Do not send any WhatsApp message yourself. Do not write markdown or explanations in the response file. If a request cannot be answered, write {"error":"short reason"} to responsePath.
457
+ \`\`\`
458
+
459
+ Notes:
460
+
461
+ - Digital Brain sends the WhatsApp message after the response file appears.
462
+ - Keep the automation active while \`digital-brain auto-whatsapp --provider codex-app\` is running.
463
+ - The default wait timeout is 5 minutes. Override with \`--provider-timeout-ms\`.
464
+ `;
465
+ writeFileAtomic(path.join(toolsDir, "Codex App Bridge Automation.md"), prompt);
466
+ if (config.autoReplyProvider === "codex-app") {
467
+ writeFileAtomic(path.join(bridgeDir, "README.md"), prompt);
468
+ }
469
+ }
470
+
411
471
  function addGlobalPointer(file, vault, label) {
412
472
  ensureDir(path.dirname(file));
413
473
  const block = `
@@ -522,6 +582,10 @@ function needsResponsibilityGate({ schedule, outboundMode }) {
522
582
  return schedule === "always-on" || ["send-with-confirmation", "auto-send"].includes(outboundMode);
523
583
  }
524
584
 
585
+ function codexAppLooksAvailable() {
586
+ return fs.existsSync(path.join(os.homedir(), ".codex"));
587
+ }
588
+
525
589
  function letterFor(index) {
526
590
  return String.fromCharCode(65 + index);
527
591
  }
@@ -31,6 +31,7 @@ digital-brain run
31
31
  - refresh interval in minutes for always-on mode, clamped to a minimum of 1
32
32
  - active time window
33
33
  - WhatsApp outbound mode
34
+ - WhatsApp auto-reply provider: Ollama, Codex app bridge, or Codex CLI
34
35
  - whether to add AI adapter pointers
35
36
 
36
37
  Most questions are multiple choice. Pick with `A/B/C`, `1/2/3`, the exact value, or press Enter to use the displayed default.
@@ -45,6 +46,7 @@ Important defaults:
45
46
  - skipped always-on interval uses 5 minutes, with a hard minimum of 1 minute
46
47
  - skipped active window uses `08:00-12:00`
47
48
  - skipped outbound mode uses draft-only
49
+ - skipped auto-reply provider uses Ollama
48
50
  - auto-send mode can be selected during init, but only after the responsibility check
49
51
  - skipped AI pointers are added during the guided quiz
50
52
 
@@ -104,6 +106,7 @@ Draft-only:
104
106
  digital-brain auto-whatsapp --allow "Mom" --model llama3.1
105
107
  digital-brain auto-whatsapp --contact "+15551234567" --model llama3.1
106
108
  digital-brain auto-whatsapp --allow-all --provider codex
109
+ digital-brain auto-whatsapp --allow-all --provider codex-app
107
110
  ```
108
111
 
109
112
  Auto-send while the command is running:
@@ -112,6 +115,7 @@ Auto-send while the command is running:
112
115
  digital-brain auto-whatsapp --allow "Mom" --model llama3.1 --yes
113
116
  digital-brain auto-whatsapp --contact "+15551234567" --model llama3.1 --yes
114
117
  digital-brain auto-whatsapp --allow-all --provider codex --yes
118
+ digital-brain auto-whatsapp --allow-all --provider codex-app --yes
115
119
  ```
116
120
 
117
121
  Broad auto-send for personal chats:
@@ -129,11 +133,16 @@ Provider options:
129
133
  ```bash
130
134
  digital-brain auto-whatsapp --allow "Mom" --provider ollama --model llama3.1 --yes
131
135
  digital-brain auto-whatsapp --allow "Mom" --provider codex --yes
136
+ digital-brain auto-whatsapp --allow "Mom" --provider codex-app --yes
132
137
  digital-brain auto-whatsapp --allow "Mom" --provider codex --codex-command "codex exec --skip-git-repo-check" --yes
133
138
  ```
134
139
 
135
140
  `--provider codex` runs a local Codex command. If `--codex-command` contains `{promptFile}`, Digital Brain writes the prompt to a temp file and substitutes the path; otherwise it pipes the prompt to stdin.
136
141
 
142
+ `--provider codex-app` does not use the Codex CLI. It writes request JSON files to `08 Sources/WhatsApp/Outbound/Codex App Bridge/requests` and waits for response JSON files in `responses`. A Codex desktop automation or live Codex thread must process those request files and write `{"reply":"..."}` to the provided `responsePath`.
143
+
144
+ When `codex-app` is selected during `init`, Digital Brain creates `Tools/Codex App Bridge Automation.md` with the exact Codex automation prompt and bridge folder paths.
145
+
137
146
  If you selected `Auto-send while running` during init, `auto-whatsapp` can send without `--yes` while it is running:
138
147
 
139
148
  ```bash
@@ -145,6 +154,7 @@ Guardrails:
145
154
  - with `--provider ollama`, requires Ollama running locally
146
155
  - with `--provider ollama`, requires the selected model, for example `ollama pull llama3.1`
147
156
  - with `--provider codex`, requires a working local Codex command
157
+ - with `--provider codex-app`, requires a Codex desktop bridge automation/thread that writes response files
148
158
  - requires `--allow "Name"` or `--contact "+15551234567"` unless `--allow-all` is explicitly passed
149
159
  - single-threads reply generation so multiple incoming chats do not trigger overlapping sends
150
160
  - skips likely business, notification, OTP, and service chats unless `--include-businesses` is passed or the chat is explicitly allowlisted by name or contact number
package/docs/PRIVACY.md CHANGED
@@ -7,7 +7,7 @@ Digital Brain is designed for local use.
7
7
  - No cloud API is called by default.
8
8
  - Ollama interpretation is local when enabled.
9
9
  - WhatsApp sending uses a local WhatsApp Web session.
10
- - WhatsApp auto-reply uses local Ollama by default or a configured local Codex command, runs only while the command is active, and requires an allowlist unless explicitly overridden. If init is configured for auto-send mode, it can send without `--yes`.
10
+ - WhatsApp auto-reply uses local Ollama by default, a configured local Codex command, or a Codex desktop file bridge. It runs only while the command is active and requires an allowlist unless explicitly overridden. If init is configured for auto-send mode, it can send without `--yes`.
11
11
  - Raw source data stays under `08 Sources/`; normal AI context should use `06 AI Memory/` and human notes under `04 People/`.
12
12
  - Same-person matching across sources is provisional and file-based; keep source evidence visible when using merged person context.
13
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "digital-brain",
3
- "version": "1.1.11",
3
+ "version": "1.1.15",
4
4
  "description": "Your private digital imprint for AI assistants.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,6 +20,7 @@ const outboundDir = path.join(whatsAppDir, "Outbound");
20
20
  const sessionDir = path.join(whatsAppDir, ".session");
21
21
  const statePath = path.join(outboundDir, "auto-reply-state.json");
22
22
  const whitelistPath = path.join(outboundDir, "auto-reply-whitelist.json");
23
+ const codexAppBridgeDir = path.join(outboundDir, "Codex App Bridge");
23
24
  const config = readConfig(vault);
24
25
  const provider = args.provider || config.autoReplyProvider || "ollama";
25
26
  const model = args.model || config.autoReplyModel || "llama3.1";
@@ -62,8 +63,8 @@ if (!hasInitialScope && !interactiveTerminal) {
62
63
 
63
64
  if (provider === "ollama") {
64
65
  await assertOllamaModel(model);
65
- } else if (!["codex"].includes(provider)) {
66
- throw new Error(`Unsupported auto-reply provider "${provider}". Use "ollama" or "codex".`);
66
+ } else if (!["codex", "codex-app"].includes(provider)) {
67
+ throw new Error(`Unsupported auto-reply provider "${provider}". Use "ollama", "codex", or "codex-app".`);
67
68
  }
68
69
 
69
70
  const client = new Client({
@@ -82,6 +83,7 @@ client.on("ready", async () => {
82
83
  if (!hasInitialScope) await configureInteractiveScope();
83
84
  console.log(runtimeAllowAll ? "Allowlist: all chats, with first-send approval per new chat." : allowlistSummary());
84
85
  if (provider === "codex") console.log(`Codex command: ${codexCommand}`);
86
+ if (provider === "codex-app") console.log(`Codex App bridge: ${codexAppBridgeDir}`);
85
87
  if (!includeBusinesses) console.log("Likely business, notification, OTP, and service chats are skipped by default.");
86
88
  try {
87
89
  if (processUnreadOnStart) {
@@ -344,6 +346,7 @@ function readMemoryContext(chatName) {
344
346
 
345
347
  async function generateReply(prompt) {
346
348
  if (provider === "codex") return generateCodexReply(prompt);
349
+ if (provider === "codex-app") return generateCodexAppReply(prompt);
347
350
  return generateOllamaReply(prompt);
348
351
  }
349
352
 
@@ -367,6 +370,52 @@ async function generateCodexReply(prompt) {
367
370
  return runReplyCommand(codexCommand, prompt, "codex");
368
371
  }
369
372
 
373
+ async function generateCodexAppReply(prompt) {
374
+ const timeoutMs = numberArg("provider-timeout-ms", 300000);
375
+ const request = createCodexAppRequest(prompt);
376
+ console.log(`Waiting for Codex App bridge response: ${request.responsePath}`);
377
+ return await waitForCodexAppResponse(request.responsePath, timeoutMs);
378
+ }
379
+
380
+ function createCodexAppRequest(prompt) {
381
+ const requestId = `${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
382
+ const requestsDir = path.join(codexAppBridgeDir, "requests");
383
+ const responsesDir = path.join(codexAppBridgeDir, "responses");
384
+ fs.mkdirSync(requestsDir, { recursive: true });
385
+ fs.mkdirSync(responsesDir, { recursive: true });
386
+ const requestPath = path.join(requestsDir, `${requestId}.json`);
387
+ const responsePath = path.join(responsesDir, `${requestId}.json`);
388
+ writeJsonAtomic(requestPath, {
389
+ schemaVersion: 1,
390
+ requestId,
391
+ createdAt: new Date().toISOString(),
392
+ responsePath,
393
+ prompt,
394
+ instructions: [
395
+ "Write exactly one WhatsApp reply as the user.",
396
+ "Return JSON only: {\"reply\":\"...\"}.",
397
+ "No markdown, no explanations, no surrounding text.",
398
+ ],
399
+ });
400
+ return { requestId, requestPath, responsePath };
401
+ }
402
+
403
+ async function waitForCodexAppResponse(responsePath, timeoutMs) {
404
+ const startedAt = Date.now();
405
+ while (Date.now() - startedAt < timeoutMs) {
406
+ if (fs.existsSync(responsePath)) {
407
+ const response = parseJsonLine(fs.readFileSync(responsePath, "utf8"));
408
+ if (!response) throw new Error(`Codex App bridge wrote invalid JSON: ${responsePath}`);
409
+ if (response.error) throw new Error(`Codex App bridge error: ${response.error}`);
410
+ const reply = cleanReply(response.reply || "");
411
+ if (!reply) throw new Error(`Codex App bridge wrote an empty reply: ${responsePath}`);
412
+ return reply;
413
+ }
414
+ await sleep(1000);
415
+ }
416
+ throw new Error(`Codex App bridge timed out after ${timeoutMs}ms. No response at ${responsePath}`);
417
+ }
418
+
370
419
  async function runReplyCommand(command, prompt, label) {
371
420
  const timeoutMs = numberArg("provider-timeout-ms", 120000);
372
421
  const usesPromptFile = command.includes("{promptFile}");
@@ -685,6 +734,10 @@ function cleanupPromptFile(file) {
685
734
  }
686
735
  }
687
736
 
737
+ function sleep(ms) {
738
+ return new Promise((resolve) => setTimeout(resolve, ms));
739
+ }
740
+
688
741
  function shellQuote(value) {
689
742
  return `'${String(value).replaceAll("'", "'\\''")}'`;
690
743
  }
@@ -752,6 +805,6 @@ function parseArgs(argv) {
752
805
  }
753
806
 
754
807
  function usage() {
755
- console.error('Usage: digital-brain auto-whatsapp --allow "Name" --contact "+15551234567" --model llama3.1 [--yes] [--allow-all] [--include-groups] [--include-businesses]');
808
+ console.error('Usage: digital-brain auto-whatsapp --allow "Name" --contact "+15551234567" --provider ollama|codex|codex-app --model llama3.1 [--yes] [--allow-all] [--include-groups] [--include-businesses]');
756
809
  process.exit(1);
757
810
  }