@geekbeer/minion 2.46.1 → 2.48.3

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.
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  const { execSync } = require('child_process')
11
+ const fs = require('fs')
11
12
  const path = require('path')
12
13
  const os = require('os')
13
14
 
@@ -18,7 +19,6 @@ const os = require('os')
18
19
  */
19
20
  function detectProcessManager() {
20
21
  // Check for user-process mode (PID file or start-agent.ps1 exists)
21
- const fs = require('fs')
22
22
  const dataDir = path.join(os.homedir(), '.minion')
23
23
  const startScript = path.join(dataDir, 'start-agent.ps1')
24
24
  if (fs.existsSync(startScript)) {
@@ -40,6 +40,56 @@ function detectProcessManager() {
40
40
  return 'user-process'
41
41
  }
42
42
 
43
+ /**
44
+ * Generate a temporary PowerShell script that:
45
+ * 1. Stops the running agent process (releasing file locks)
46
+ * 2. Runs npm install -g
47
+ * 3. Restarts the agent
48
+ *
49
+ * This is necessary because node-pty's conpty.node DLL is locked by the
50
+ * running process, causing EBUSY errors if npm tries to overwrite it in-place.
51
+ *
52
+ * @param {string} npmInstallCmd - The npm install command to run
53
+ * @param {string} stopCmd - Command/script block to stop the agent
54
+ * @param {string} startCmd - Command/script block to start the agent
55
+ * @returns {string} - Shell command that writes and launches the update script
56
+ */
57
+ function buildUpdateScript(npmInstallCmd, stopCmd, startCmd) {
58
+ const dataDir = path.join(os.homedir(), '.minion')
59
+ const scriptPath = path.join(dataDir, 'update-agent.ps1')
60
+ const logPath = path.join(dataDir, 'update-agent.log')
61
+
62
+ // PowerShell script content: stop → install → start, with logging
63
+ const ps1 = [
64
+ `$ErrorActionPreference = 'Stop'`,
65
+ `$logFile = '${logPath.replace(/\\/g, '\\\\')}'`,
66
+ `function Log($msg) { "$(Get-Date -Format o) $msg" | Out-File -Append $logFile }`,
67
+ `Log 'Update started'`,
68
+ `try {`,
69
+ ` Log 'Stopping agent...'`,
70
+ ` ${stopCmd}`,
71
+ ` Start-Sleep -Seconds 3`,
72
+ ` Log 'Installing package...'`,
73
+ ` $out = & cmd /c "${npmInstallCmd} 2>&1"`,
74
+ ` Log "npm output: $out"`,
75
+ ` if ($LASTEXITCODE -ne 0) { throw "npm install failed (exit code $LASTEXITCODE)" }`,
76
+ ` Log 'Starting agent...'`,
77
+ ` ${startCmd}`,
78
+ ` Log 'Update completed successfully'`,
79
+ `} catch {`,
80
+ ` Log "Update failed: $_"`,
81
+ ` Log 'Attempting to restart agent anyway...'`,
82
+ ` ${startCmd}`,
83
+ `}`,
84
+ ].join('\n')
85
+
86
+ // Write the script to disk and launch it detached
87
+ try { fs.mkdirSync(dataDir, { recursive: true }) } catch { /* exists */ }
88
+ fs.writeFileSync(scriptPath, ps1, 'utf-8')
89
+
90
+ return `powershell -ExecutionPolicy Bypass -Command "Start-Process powershell -ArgumentList '-ExecutionPolicy Bypass -WindowStyle Hidden -File \\"${scriptPath}\\"' -WindowStyle Hidden"`
91
+ }
92
+
43
93
  /**
44
94
  * Build allowed commands for the detected process manager.
45
95
  * @param {string} procMgr - Process manager type
@@ -53,19 +103,30 @@ function buildAllowedCommands(procMgr) {
53
103
  const pidFile = path.join(dataDir, 'minion-agent.pid')
54
104
  const startScript = path.join(dataDir, 'start-agent.ps1')
55
105
 
106
+ const stopBlock = `$pid = Get-Content '${pidFile}' -ErrorAction SilentlyContinue; if ($pid) { Get-CimInstance Win32_Process -Filter \\"ParentProcessId = $pid\\" -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }; Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue }`
107
+ const startBlock = `Start-Process powershell -ArgumentList '-ExecutionPolicy Bypass -WindowStyle Hidden -File \\"${startScript}\\"' -WindowStyle Hidden`
108
+
56
109
  commands['restart-agent'] = {
57
110
  description: 'Restart the minion agent process',
58
- command: `powershell -Command "$pid = Get-Content '${pidFile}' -ErrorAction SilentlyContinue; if ($pid) { Get-CimInstance Win32_Process -Filter \\"ParentProcessId = $pid\\" -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }; Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue }; Start-Sleep -Seconds 2; Start-Process powershell -ArgumentList '-ExecutionPolicy Bypass -WindowStyle Hidden -File \\"${startScript}\\"' -WindowStyle Hidden"`,
111
+ command: `powershell -Command "${stopBlock}; Start-Sleep -Seconds 2; ${startBlock}"`,
59
112
  deferred: true,
60
113
  }
61
114
  commands['update-agent'] = {
62
115
  description: 'Update @geekbeer/minion to latest version and restart',
63
- command: `npm install -g @geekbeer/minion@latest && powershell -Command "$pid = Get-Content '${pidFile}' -ErrorAction SilentlyContinue; if ($pid) { Get-CimInstance Win32_Process -Filter \\"ParentProcessId = $pid\\" -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }; Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue }; Start-Sleep -Seconds 2; Start-Process powershell -ArgumentList '-ExecutionPolicy Bypass -WindowStyle Hidden -File \\"${startScript}\\"' -WindowStyle Hidden"`,
116
+ command: buildUpdateScript(
117
+ 'npm install -g @geekbeer/minion@latest',
118
+ stopBlock,
119
+ startBlock,
120
+ ),
64
121
  deferred: true,
65
122
  }
66
123
  commands['update-agent-dev'] = {
67
124
  description: 'Update @geekbeer/minion from Verdaccio (dev) and restart',
68
- command: `npm install -g @geekbeer/minion@latest --registry http://verdaccio:4873 && powershell -Command "$pid = Get-Content '${pidFile}' -ErrorAction SilentlyContinue; if ($pid) { Get-CimInstance Win32_Process -Filter \\"ParentProcessId = $pid\\" -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }; Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue }; Start-Sleep -Seconds 2; Start-Process powershell -ArgumentList '-ExecutionPolicy Bypass -WindowStyle Hidden -File \\"${startScript}\\"' -WindowStyle Hidden"`,
125
+ command: buildUpdateScript(
126
+ 'npm install -g @geekbeer/minion@latest --registry http://verdaccio:4873',
127
+ stopBlock,
128
+ startBlock,
129
+ ),
69
130
  deferred: true,
70
131
  }
71
132
  commands['status-services'] = {
@@ -80,12 +141,20 @@ function buildAllowedCommands(procMgr) {
80
141
  }
81
142
  commands['update-agent'] = {
82
143
  description: 'Update @geekbeer/minion to latest version and restart',
83
- command: 'npm install -g @geekbeer/minion@latest && nssm restart minion-agent',
144
+ command: buildUpdateScript(
145
+ 'npm install -g @geekbeer/minion@latest',
146
+ 'nssm stop minion-agent',
147
+ 'nssm start minion-agent',
148
+ ),
84
149
  deferred: true,
85
150
  }
86
151
  commands['update-agent-dev'] = {
87
152
  description: 'Update @geekbeer/minion from Verdaccio (dev) and restart',
88
- command: 'npm install -g @geekbeer/minion@latest --registry http://verdaccio:4873 && nssm restart minion-agent',
153
+ command: buildUpdateScript(
154
+ 'npm install -g @geekbeer/minion@latest --registry http://verdaccio:4873',
155
+ 'nssm stop minion-agent',
156
+ 'nssm start minion-agent',
157
+ ),
89
158
  deferred: true,
90
159
  }
91
160
  commands['restart-display'] = {
@@ -104,12 +173,20 @@ function buildAllowedCommands(procMgr) {
104
173
  }
105
174
  commands['update-agent'] = {
106
175
  description: 'Update @geekbeer/minion to latest version and restart',
107
- command: 'npm install -g @geekbeer/minion@latest && net stop minion-agent & net start minion-agent',
176
+ command: buildUpdateScript(
177
+ 'npm install -g @geekbeer/minion@latest',
178
+ 'net stop minion-agent',
179
+ 'net start minion-agent',
180
+ ),
108
181
  deferred: true,
109
182
  }
110
183
  commands['update-agent-dev'] = {
111
184
  description: 'Update @geekbeer/minion from Verdaccio (dev) and restart',
112
- command: 'npm install -g @geekbeer/minion@latest --registry http://verdaccio:4873 && net stop minion-agent & net start minion-agent',
185
+ command: buildUpdateScript(
186
+ 'npm install -g @geekbeer/minion@latest --registry http://verdaccio:4873',
187
+ 'net stop minion-agent',
188
+ 'net start minion-agent',
189
+ ),
113
190
  deferred: true,
114
191
  }
115
192
  commands['status-services'] = {
@@ -390,9 +390,17 @@ function Invoke-Setup {
390
390
  Write-Step 4 $totalSteps "Creating config directory and .env..."
391
391
  New-Item -Path $DataDir -ItemType Directory -Force | Out-Null
392
392
  New-Item -Path $LogDir -ItemType Directory -Force | Out-Null
393
+ # Detect system timezone (IANA format)
394
+ $sysTz = 'Asia/Tokyo'
395
+ try {
396
+ $tzInfo = Get-TimeZone
397
+ if ($tzInfo.Id) { $sysTz = $tzInfo.Id }
398
+ } catch {}
393
399
  $envValues = @{
394
400
  'AGENT_PORT' = '8080'
395
401
  'MINION_USER' = $env:USERNAME
402
+ 'REFLECTION_TIME' = '03:00'
403
+ 'TIMEZONE' = $sysTz
396
404
  }
397
405
  if ($HqUrl) { $envValues['HQ_URL'] = $HqUrl }
398
406
  if ($ApiToken) { $envValues['API_TOKEN'] = $ApiToken }
@@ -4,11 +4,12 @@
4
4
  * Same as linux/routes/chat.js but with Windows-compatible PATH and shell construction.
5
5
  *
6
6
  * Endpoints:
7
- * POST /api/chat - Send message, get SSE stream
8
- * GET /api/chat/session - Get active session (messages + session_id)
9
- * POST /api/chat/clear - Clear session and start fresh
10
- * POST /api/chat/abort - Kill the active LLM CLI process
11
- * POST /api/chat/reset - Summarize conversation and start fresh session
7
+ * POST /api/chat - Send message, get SSE stream
8
+ * GET /api/chat/session - Get active session (messages + session_id)
9
+ * POST /api/chat/clear - Clear session and start fresh
10
+ * POST /api/chat/abort - Kill the active LLM CLI process
11
+ * POST /api/chat/reset - Summarize conversation and start fresh session
12
+ * POST /api/chat/end-of-day - Generate daily log + extract memories from conversation
12
13
  */
13
14
 
14
15
  const { spawn } = require('child_process')
@@ -17,7 +18,10 @@ const path = require('path')
17
18
  const { verifyToken } = require('../../core/lib/auth')
18
19
  const { config } = require('../../core/config')
19
20
  const chatStore = require('../../core/stores/chat-store')
21
+ const memoryStore = require('../../core/stores/memory-store')
22
+ const dailyLogStore = require('../../core/stores/daily-log-store')
20
23
  const { buildExtendedPath } = require('../../core/lib/platform')
24
+ const { runEndOfDay } = require('../../core/lib/end-of-day')
21
25
 
22
26
  let activeChatChild = null
23
27
 
@@ -34,7 +38,7 @@ async function chatRoutes(fastify) {
34
38
  return { success: false, error: 'message is required' }
35
39
  }
36
40
 
37
- const prompt = context ? buildContextPrefix(message, context) : message
41
+ const prompt = await buildContextPrefix(message, context, session_id)
38
42
  const currentSessionId = session_id || null
39
43
 
40
44
  if (currentSessionId) {
@@ -142,10 +146,45 @@ async function chatRoutes(fastify) {
142
146
  console.log(`[Chat] session reset with summary (${summary?.length || 0} chars)`)
143
147
  return { success: true, summary }
144
148
  })
149
+
150
+ // POST /api/chat/end-of-day - Generate daily log + extract memories
151
+ fastify.post('/api/chat/end-of-day', async (request, reply) => {
152
+ if (!verifyToken(request)) {
153
+ reply.code(401)
154
+ return { success: false, error: 'Unauthorized' }
155
+ }
156
+
157
+ const { clear_session = false } = request.body || {}
158
+
159
+ const result = await runEndOfDay({
160
+ runQuickLlmCall,
161
+ clearSession: clear_session,
162
+ })
163
+
164
+ return { success: true, ...result }
165
+ })
145
166
  }
146
167
 
147
- function buildContextPrefix(message, context) {
168
+ async function buildContextPrefix(message, context, sessionId) {
148
169
  const parts = []
170
+
171
+ // Inject memory + daily logs on new sessions only (not on --resume)
172
+ if (!sessionId) {
173
+ try {
174
+ const memorySnippet = await memoryStore.getContextSnippet(2000)
175
+ const dailyLogSnippet = await dailyLogStore.getContextSnippet(3, 1500)
176
+
177
+ if (memorySnippet) {
178
+ parts.push('[ミニオンメモリ(長期記憶)]', memorySnippet, '')
179
+ }
180
+ if (dailyLogSnippet) {
181
+ parts.push('[最近のデイリーログ]', dailyLogSnippet, '')
182
+ }
183
+ } catch (err) {
184
+ console.error('[Chat] Failed to load memory/daily-log context:', err.message)
185
+ }
186
+ }
187
+
149
188
  if (context) {
150
189
  switch (context.type) {
151
190
  case 'skill':
@@ -445,4 +484,4 @@ function runQuickLlmCall(prompt) {
445
484
  })
446
485
  }
447
486
 
448
- module.exports = { chatRoutes }
487
+ module.exports = { chatRoutes, runQuickLlmCall }
@@ -13,7 +13,12 @@ const { clearLlmCache } = require('../../core/lib/llm-checker')
13
13
  const { config, updateConfig } = require('../../core/config')
14
14
  const { resolveEnvFilePath } = require('../../core/lib/platform')
15
15
 
16
- const ALLOWED_ENV_KEYS = ['LLM_COMMAND']
16
+ const reflectionScheduler = require('../../core/lib/reflection-scheduler')
17
+
18
+ const ALLOWED_ENV_KEYS = ['LLM_COMMAND', 'REFLECTION_TIME', 'TIMEZONE']
19
+
20
+ /** Keys that trigger a reflection scheduler reschedule when changed */
21
+ const REFLECTION_KEYS = ['REFLECTION_TIME', 'TIMEZONE']
17
22
 
18
23
  const BACKUP_PATHS = [
19
24
  '~/.claude',
@@ -183,8 +188,10 @@ function configRoutes(fastify, _opts, done) {
183
188
  return reply.code(403).send({ error: `Key '${key}' is not allowed` })
184
189
  }
185
190
  const envPath = resolveEnvPath()
186
- const value = readEnvKey(envPath, key)
187
- return { key, value: value || '', configured: !!value }
191
+ const fileValue = readEnvKey(envPath, key)
192
+ // Fall back to in-memory config default when .env has no entry
193
+ const value = fileValue !== null ? fileValue : (config[key] ?? '')
194
+ return { key, value, configured: fileValue !== null }
188
195
  })
189
196
 
190
197
  fastify.put('/api/config/env', async (request, reply) => {
@@ -217,6 +224,12 @@ function configRoutes(fastify, _opts, done) {
217
224
  updateConfig(key, value)
218
225
 
219
226
  clearLlmCache()
227
+
228
+ // Reschedule reflection if relevant key changed
229
+ if (REFLECTION_KEYS.includes(key)) {
230
+ reflectionScheduler.reschedule()
231
+ }
232
+
220
233
  return { success: true, restart_required: false }
221
234
  } catch (err) {
222
235
  console.error(`[Config] Failed to update ${key} in ${envPath}:`, err.message)
package/win/server.js CHANGED
@@ -31,12 +31,13 @@ const routineRunner = require('./routine-runner')
31
31
  // Pull-model daemons (from core/)
32
32
  const stepPoller = require('../core/lib/step-poller')
33
33
  const revisionWatcher = require('../core/lib/revision-watcher')
34
+ const reflectionScheduler = require('../core/lib/reflection-scheduler')
34
35
  const { commandRoutes, getProcessManager, getAllowedCommands } = require('./routes/commands')
35
36
  const { terminalRoutes, cleanupSessions } = require('./routes/terminal')
36
37
  const { startTerminalServer, stopTerminalServer } = require('./terminal-server')
37
38
  const { fileRoutes } = require('./routes/files')
38
39
  const { directiveRoutes } = require('./routes/directives')
39
- const { chatRoutes } = require('./routes/chat')
40
+ const { chatRoutes, runQuickLlmCall } = require('./routes/chat')
40
41
  const { configRoutes } = require('./routes/config')
41
42
 
42
43
  // Compatible route modules (reused from Linux)
@@ -47,6 +48,8 @@ const { workflowRoutes } = require('../core/routes/workflows')
47
48
  const { routineRoutes } = require('../core/routes/routines')
48
49
  const { authRoutes } = require('../core/routes/auth')
49
50
  const { variableRoutes } = require('../core/routes/variables')
51
+ const { memoryRoutes } = require('../core/routes/memory')
52
+ const { dailyLogRoutes } = require('../core/routes/daily-logs')
50
53
 
51
54
  // Validate configuration
52
55
  validate()
@@ -81,6 +84,7 @@ async function shutdown(signal) {
81
84
 
82
85
  stepPoller.stop()
83
86
  revisionWatcher.stop()
87
+ reflectionScheduler.stop()
84
88
  workflowRunner.stopAll()
85
89
  routineRunner.stopAll()
86
90
  stopTerminalServer()
@@ -195,6 +199,8 @@ async function registerRoutes(app) {
195
199
  await app.register(routineRoutes, { routineRunner })
196
200
  await app.register(authRoutes)
197
201
  await app.register(variableRoutes)
202
+ await app.register(memoryRoutes)
203
+ await app.register(dailyLogRoutes)
198
204
 
199
205
  // Windows-specific routes
200
206
  await app.register(commandRoutes)
@@ -252,6 +258,9 @@ async function start() {
252
258
  console.error('[Server] Failed to load cached routines:', err.message)
253
259
  }
254
260
 
261
+ // Start reflection scheduler (self-reflection time)
262
+ reflectionScheduler.start(runQuickLlmCall)
263
+
255
264
  if (isHqConfigured()) {
256
265
  console.log(`[Server] HQ URL: ${config.HQ_URL}`)
257
266