@hzttt/multimodal-rag 0.2.2 → 0.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzttt/multimodal-rag",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "OpenClaw plugin for multimodal RAG - semantic indexing and time-aware search for images and audio using local AI models",
5
5
  "type": "module",
6
6
  "repository": {
package/src/notifier.ts CHANGED
@@ -125,17 +125,86 @@ export class IndexNotifier implements IndexEventCallbacks {
125
125
  }
126
126
 
127
127
  /**
128
- * 触发 agent: enqueueSystemEvent + requestHeartbeatNow
128
+ * 触发 agent: 使用 CLI 注入系统事件并立即唤醒 heartbeat
129
+ *
130
+ * 兼容说明:当前 runtime.system 不提供 requestHeartbeatNow/resolveMainSessionKey。
129
131
  */
130
132
  private triggerAgent(text: string): void {
133
+ const argv = this.buildSystemEventCommand(text);
134
+
135
+ void this.runtime.system
136
+ .runCommandWithTimeout(argv, { timeoutMs: 15000 })
137
+ .then((result) => {
138
+ if (result.code === 0) {
139
+ this.logger.info?.(`Notification triggered: ${text.slice(0, 80)}...`);
140
+ return;
141
+ }
142
+
143
+ const detail = (result.stderr || result.stdout || "no output").trim();
144
+ this.logger.warn?.(
145
+ `Notification wake command failed (code=${String(result.code)}): ${detail}`,
146
+ );
147
+ this.enqueueWithoutWake(text);
148
+ })
149
+ .catch((err) => {
150
+ this.logger.warn?.(`Notification wake command error: ${String(err)}`);
151
+ this.enqueueWithoutWake(text);
152
+ });
153
+ }
154
+
155
+ /**
156
+ * 构建 system event CLI 命令,优先复用当前 openclaw 进程入口
157
+ */
158
+ private buildSystemEventCommand(text: string): string[] {
159
+ const nodeExec = process.execPath;
160
+ const cliEntry = process.argv[1];
161
+
162
+ if (typeof cliEntry === "string" && cliEntry.trim().length > 0) {
163
+ return [
164
+ nodeExec,
165
+ cliEntry,
166
+ "system",
167
+ "event",
168
+ "--text",
169
+ text,
170
+ "--mode",
171
+ "now",
172
+ ];
173
+ }
174
+
175
+ return ["openclaw", "system", "event", "--text", text, "--mode", "now"];
176
+ }
177
+
178
+ /**
179
+ * 降级路径:至少把事件放入队列,避免消息完全丢失
180
+ */
181
+ private enqueueWithoutWake(text: string): void {
131
182
  try {
132
- const sessionKey = this.runtime.system.resolveMainSessionKey(this.openclawConfig);
183
+ const sessionKey = this.resolveMainSessionKey();
133
184
  this.runtime.system.enqueueSystemEvent(text, { sessionKey });
134
- this.runtime.system.requestHeartbeatNow({ reason: "multimodal-rag:notification" });
135
- this.logger.info?.(`Notification triggered: ${text.slice(0, 80)}...`);
185
+ this.logger.warn?.("Notification queued without immediate wake (fallback)");
136
186
  } catch (err) {
137
- this.logger.warn?.(`Failed to trigger agent notification: ${String(err)}`);
187
+ this.logger.warn?.(`Failed to enqueue fallback notification: ${String(err)}`);
188
+ }
189
+ }
190
+
191
+ /**
192
+ * 兼容版 main session key 解析
193
+ */
194
+ private resolveMainSessionKey(): string {
195
+ const configuredMainKey = this.openclawConfig.session?.mainKey?.trim();
196
+ if (configuredMainKey) {
197
+ return configuredMainKey;
138
198
  }
199
+
200
+ const agents = this.openclawConfig.agents?.list ?? [];
201
+ const defaultAgentId =
202
+ agents.find((agent) => agent.default && typeof agent.id === "string" && agent.id.trim())
203
+ ?.id?.trim() ??
204
+ agents.find((agent) => typeof agent.id === "string" && agent.id.trim())?.id?.trim() ??
205
+ "main";
206
+
207
+ return `agent:${defaultAgentId}:main`;
139
208
  }
140
209
 
141
210
  /**
package/src/watcher.ts CHANGED
@@ -237,6 +237,7 @@ export class MediaWatcher {
237
237
  this.logger.warn?.(
238
238
  `Failed to index ${fileName} after 3 attempts: Ollama unavailable`,
239
239
  );
240
+ this.callbacks?.onFileFailed(filePath, "Ollama service unavailable");
240
241
  }
241
242
  return false;
242
243
  }
@@ -250,8 +251,9 @@ export class MediaWatcher {
250
251
  const existing = await this.storage.findByHash(fileHash);
251
252
  if (existing) {
252
253
  this.logger.info?.(`Skipping duplicate: ${fileName}`);
253
- // 索引成功(已存在),清除失败记录
254
+ // 索引成功(已存在),清除失败记录并通知回调
254
255
  this.failedFiles.delete(filePath);
256
+ this.callbacks?.onFileIndexed(filePath, fileType);
255
257
  return true;
256
258
  }
257
259