memorix 1.9.9 → 1.9.10

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/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ All notable changes to this project will be documented in this file.
6
6
 
7
7
  No unreleased changes.
8
8
 
9
+ ## [1.9.10] - 2026-10-08
10
+
11
+ ### Fixed
12
+ - **OpenCode 1.x regression** -- generated plugins now include the v1
13
+ `server` field alongside the v2 `setup` contract, so OpenCode 1.x can load
14
+ the legacy named export. Lifecycle, file, command, message, and compaction
15
+ events are also forwarded through the v1 generic `event` hook, with a longer
16
+ Windows hook timeout to reduce stale Bun timer failures.
17
+
9
18
  ## [1.9.9] - 2026-10-02
10
19
 
11
20
  ### Added
package/dist/cli/index.js CHANGED
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3232
3232
 
3233
3233
  // src/cli/version.ts
3234
3234
  function getCliVersion() {
3235
- return true ? "1.9.9" : pkg.version;
3235
+ return true ? "1.9.10" : pkg.version;
3236
3236
  }
3237
3237
  var init_version = __esm({
3238
3238
  "src/cli/version.ts"() {
@@ -195509,6 +195509,8 @@ function generateOpenCodePlugin() {
195509
195509
  */
195510
195510
  import { spawnSync } from 'node:child_process';
195511
195511
 
195512
+ const HOOK_TIMEOUT_MS = 30_000;
195513
+
195512
195514
  export const MemorixPlugin = async ({ project, client, $, directory, worktree }) => {
195513
195515
  // Generate a stable session ID for this plugin lifetime
195514
195516
  const sessionId = \`opencode-\${Date.now().toString(36)}-\${Math.random().toString(36).slice(2, 8)}\`;
@@ -195534,14 +195536,14 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
195534
195536
  * - spawnSync is simpler: no stream lifecycle, no writer.close() bugs
195535
195537
  * - stdin pipe via input option is reliable cross-platform
195536
195538
  */
195537
- function runHook(payload) {
195538
- payload.session_id = sessionId;
195539
+ function runHook(payload, activeSessionId = sessionId) {
195540
+ payload.session_id = activeSessionId;
195539
195541
  const data = JSON.stringify(payload);
195540
195542
  const eventName = payload.hook_event_name || 'unknown';
195541
195543
  try {
195542
195544
  const result = spawnSync(hookCommand, ['hook'], {
195543
195545
  input: data,
195544
- timeout: 10_000,
195546
+ timeout: HOOK_TIMEOUT_MS,
195545
195547
  encoding: 'utf-8',
195546
195548
  stdio: ['pipe', 'pipe', 'pipe'],
195547
195549
  shell: process.platform === 'win32',
@@ -195586,7 +195588,87 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
195586
195588
  .trim();
195587
195589
  }
195588
195590
 
195591
+ function flushAssistantResponse(activeSessionId = sessionId) {
195592
+ if (!pendingAssistantResponse?.text) return;
195593
+ const deliveryKey = pendingAssistantResponse.id
195594
+ ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
195595
+ : pendingAssistantResponse.text;
195596
+ if (deliveryKey === lastDeliveredAssistantKey) return;
195597
+ runHook({
195598
+ agent: 'opencode',
195599
+ hook_event_name: 'message.updated',
195600
+ ai_response: pendingAssistantResponse.text,
195601
+ message_id: pendingAssistantResponse.id,
195602
+ cwd: directory,
195603
+ }, activeSessionId);
195604
+ lastDeliveredAssistantKey = deliveryKey;
195605
+ }
195606
+
195607
+ function eventSessionId(event) {
195608
+ const properties = event?.properties ?? {};
195609
+ const value = properties.sessionID ?? properties.sessionId ?? properties.info?.id;
195610
+ return typeof value === 'string' && value ? value : sessionId;
195611
+ }
195612
+
195613
+ async function handleEvent({ event }) {
195614
+ const properties = event?.properties ?? {};
195615
+ const activeSessionId = eventSessionId(event);
195616
+ switch (event?.type) {
195617
+ case 'session.created':
195618
+ runHook({
195619
+ agent: 'opencode',
195620
+ hook_event_name: 'session.created',
195621
+ cwd: directory,
195622
+ }, activeSessionId);
195623
+ break;
195624
+ case 'session.idle':
195625
+ flushAssistantResponse(activeSessionId);
195626
+ runHook({
195627
+ agent: 'opencode',
195628
+ hook_event_name: 'session.idle',
195629
+ cwd: directory,
195630
+ }, activeSessionId);
195631
+ break;
195632
+ case 'file.edited':
195633
+ runHook({
195634
+ agent: 'opencode',
195635
+ hook_event_name: 'file.edited',
195636
+ file_path: properties.file ?? properties.path ?? '',
195637
+ cwd: directory,
195638
+ }, activeSessionId);
195639
+ break;
195640
+ case 'command.executed':
195641
+ runHook({
195642
+ agent: 'opencode',
195643
+ hook_event_name: 'command.executed',
195644
+ command: properties.name ?? properties.command ?? '',
195645
+ cwd: directory,
195646
+ }, activeSessionId);
195647
+ break;
195648
+ case 'message.updated': {
195649
+ const message = properties.info ?? properties.message ?? properties;
195650
+ if (message?.role !== 'assistant') break;
195651
+ const text = extractMessageText(message);
195652
+ if (!text) break;
195653
+ pendingAssistantResponse = { id: message.id, text };
195654
+ break;
195655
+ }
195656
+ case 'session.compacted':
195657
+ runHook({
195658
+ agent: 'opencode',
195659
+ hook_event_name: 'session.compacted',
195660
+ cwd: directory,
195661
+ }, activeSessionId);
195662
+ break;
195663
+ default:
195664
+ break;
195665
+ }
195666
+ }
195667
+
195589
195668
  return {
195669
+ // OpenCode 1.x delivers lifecycle/file/command/message events through this
195670
+ // generic hook. The named keys below remain for older compatible hosts.
195671
+ event: handleEvent,
195590
195672
  /** Session created \u2014 record session start */
195591
195673
  'session.created': async ({ session }) => {
195592
195674
  runHook({
@@ -195598,21 +195680,7 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
195598
195680
 
195599
195681
  /** Session idle \u2014 record session end */
195600
195682
  'session.idle': async ({ session }) => {
195601
- if (pendingAssistantResponse?.text) {
195602
- const deliveryKey = pendingAssistantResponse.id
195603
- ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
195604
- : pendingAssistantResponse.text;
195605
- if (deliveryKey !== lastDeliveredAssistantKey) {
195606
- runHook({
195607
- agent: 'opencode',
195608
- hook_event_name: 'message.updated',
195609
- ai_response: pendingAssistantResponse.text,
195610
- message_id: pendingAssistantResponse.id,
195611
- cwd: directory,
195612
- });
195613
- lastDeliveredAssistantKey = deliveryKey;
195614
- }
195615
- }
195683
+ flushAssistantResponse();
195616
195684
  runHook({
195617
195685
  agent: 'opencode',
195618
195686
  hook_event_name: 'session.idle',
@@ -195720,7 +195788,7 @@ function v2RunHook(payload, directory, sessionId) {
195720
195788
  try {
195721
195789
  const result = spawnSync(v2HookCommand, ['hook'], {
195722
195790
  input: JSON.stringify(nextPayload),
195723
- timeout: 10_000,
195791
+ timeout: HOOK_TIMEOUT_MS,
195724
195792
  encoding: 'utf-8',
195725
195793
  stdio: ['pipe', 'pipe', 'pipe'],
195726
195794
  shell: process.platform === 'win32',
@@ -195844,7 +195912,7 @@ function setup(ctx) {
195844
195912
  return () => controller.abort();
195845
195913
  }
195846
195914
 
195847
- export default { id: 'memorix', setup };
195915
+ export default { id: 'memorix', server: MemorixPlugin, setup };
195848
195916
  `;
195849
195917
  }
195850
195918
  async function installOpenCodeSkills(projectRoot, global2 = false) {
@@ -196670,7 +196738,7 @@ var init_installers = __esm({
196670
196738
  "use strict";
196671
196739
  init_esm_shims();
196672
196740
  init_official_skills();
196673
- OPENCODE_PLUGIN_VERSION = 8;
196741
+ OPENCODE_PLUGIN_VERSION = 9;
196674
196742
  AGENT_SKILL_DIRS = {
196675
196743
  cursor: { project: path31.join(".cursor", "skills"), global: path31.join(".cursor", "skills") },
196676
196744
  windsurf: { project: path31.join(".windsurf", "skills"), global: path31.join(".windsurf", "skills") },
@@ -209197,7 +209265,7 @@ var init_tool_profile = __esm({
209197
209265
  function getMcpServerInfo() {
209198
209266
  return {
209199
209267
  name: "memorix",
209200
- version: true ? "1.9.9" : "1.0.1"
209268
+ version: true ? "1.9.10" : "1.0.1"
209201
209269
  };
209202
209270
  }
209203
209271
  function createMcpDiscoverResult(capabilities, serverInfo) {
package/dist/index.js CHANGED
@@ -160121,7 +160121,7 @@ var init_request_context = __esm({
160121
160121
  function getMcpServerInfo() {
160122
160122
  return {
160123
160123
  name: "memorix",
160124
- version: true ? "1.9.9" : "1.0.1"
160124
+ version: true ? "1.9.10" : "1.0.1"
160125
160125
  };
160126
160126
  }
160127
160127
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -169930,6 +169930,8 @@ function generateOpenCodePlugin() {
169930
169930
  */
169931
169931
  import { spawnSync } from 'node:child_process';
169932
169932
 
169933
+ const HOOK_TIMEOUT_MS = 30_000;
169934
+
169933
169935
  export const MemorixPlugin = async ({ project, client, $, directory, worktree }) => {
169934
169936
  // Generate a stable session ID for this plugin lifetime
169935
169937
  const sessionId = \`opencode-\${Date.now().toString(36)}-\${Math.random().toString(36).slice(2, 8)}\`;
@@ -169955,14 +169957,14 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
169955
169957
  * - spawnSync is simpler: no stream lifecycle, no writer.close() bugs
169956
169958
  * - stdin pipe via input option is reliable cross-platform
169957
169959
  */
169958
- function runHook(payload) {
169959
- payload.session_id = sessionId;
169960
+ function runHook(payload, activeSessionId = sessionId) {
169961
+ payload.session_id = activeSessionId;
169960
169962
  const data = JSON.stringify(payload);
169961
169963
  const eventName = payload.hook_event_name || 'unknown';
169962
169964
  try {
169963
169965
  const result = spawnSync(hookCommand, ['hook'], {
169964
169966
  input: data,
169965
- timeout: 10_000,
169967
+ timeout: HOOK_TIMEOUT_MS,
169966
169968
  encoding: 'utf-8',
169967
169969
  stdio: ['pipe', 'pipe', 'pipe'],
169968
169970
  shell: process.platform === 'win32',
@@ -170007,7 +170009,87 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
170007
170009
  .trim();
170008
170010
  }
170009
170011
 
170012
+ function flushAssistantResponse(activeSessionId = sessionId) {
170013
+ if (!pendingAssistantResponse?.text) return;
170014
+ const deliveryKey = pendingAssistantResponse.id
170015
+ ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
170016
+ : pendingAssistantResponse.text;
170017
+ if (deliveryKey === lastDeliveredAssistantKey) return;
170018
+ runHook({
170019
+ agent: 'opencode',
170020
+ hook_event_name: 'message.updated',
170021
+ ai_response: pendingAssistantResponse.text,
170022
+ message_id: pendingAssistantResponse.id,
170023
+ cwd: directory,
170024
+ }, activeSessionId);
170025
+ lastDeliveredAssistantKey = deliveryKey;
170026
+ }
170027
+
170028
+ function eventSessionId(event) {
170029
+ const properties = event?.properties ?? {};
170030
+ const value = properties.sessionID ?? properties.sessionId ?? properties.info?.id;
170031
+ return typeof value === 'string' && value ? value : sessionId;
170032
+ }
170033
+
170034
+ async function handleEvent({ event }) {
170035
+ const properties = event?.properties ?? {};
170036
+ const activeSessionId = eventSessionId(event);
170037
+ switch (event?.type) {
170038
+ case 'session.created':
170039
+ runHook({
170040
+ agent: 'opencode',
170041
+ hook_event_name: 'session.created',
170042
+ cwd: directory,
170043
+ }, activeSessionId);
170044
+ break;
170045
+ case 'session.idle':
170046
+ flushAssistantResponse(activeSessionId);
170047
+ runHook({
170048
+ agent: 'opencode',
170049
+ hook_event_name: 'session.idle',
170050
+ cwd: directory,
170051
+ }, activeSessionId);
170052
+ break;
170053
+ case 'file.edited':
170054
+ runHook({
170055
+ agent: 'opencode',
170056
+ hook_event_name: 'file.edited',
170057
+ file_path: properties.file ?? properties.path ?? '',
170058
+ cwd: directory,
170059
+ }, activeSessionId);
170060
+ break;
170061
+ case 'command.executed':
170062
+ runHook({
170063
+ agent: 'opencode',
170064
+ hook_event_name: 'command.executed',
170065
+ command: properties.name ?? properties.command ?? '',
170066
+ cwd: directory,
170067
+ }, activeSessionId);
170068
+ break;
170069
+ case 'message.updated': {
170070
+ const message = properties.info ?? properties.message ?? properties;
170071
+ if (message?.role !== 'assistant') break;
170072
+ const text = extractMessageText(message);
170073
+ if (!text) break;
170074
+ pendingAssistantResponse = { id: message.id, text };
170075
+ break;
170076
+ }
170077
+ case 'session.compacted':
170078
+ runHook({
170079
+ agent: 'opencode',
170080
+ hook_event_name: 'session.compacted',
170081
+ cwd: directory,
170082
+ }, activeSessionId);
170083
+ break;
170084
+ default:
170085
+ break;
170086
+ }
170087
+ }
170088
+
170010
170089
  return {
170090
+ // OpenCode 1.x delivers lifecycle/file/command/message events through this
170091
+ // generic hook. The named keys below remain for older compatible hosts.
170092
+ event: handleEvent,
170011
170093
  /** Session created \u2014 record session start */
170012
170094
  'session.created': async ({ session }) => {
170013
170095
  runHook({
@@ -170019,21 +170101,7 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
170019
170101
 
170020
170102
  /** Session idle \u2014 record session end */
170021
170103
  'session.idle': async ({ session }) => {
170022
- if (pendingAssistantResponse?.text) {
170023
- const deliveryKey = pendingAssistantResponse.id
170024
- ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
170025
- : pendingAssistantResponse.text;
170026
- if (deliveryKey !== lastDeliveredAssistantKey) {
170027
- runHook({
170028
- agent: 'opencode',
170029
- hook_event_name: 'message.updated',
170030
- ai_response: pendingAssistantResponse.text,
170031
- message_id: pendingAssistantResponse.id,
170032
- cwd: directory,
170033
- });
170034
- lastDeliveredAssistantKey = deliveryKey;
170035
- }
170036
- }
170104
+ flushAssistantResponse();
170037
170105
  runHook({
170038
170106
  agent: 'opencode',
170039
170107
  hook_event_name: 'session.idle',
@@ -170141,7 +170209,7 @@ function v2RunHook(payload, directory, sessionId) {
170141
170209
  try {
170142
170210
  const result = spawnSync(v2HookCommand, ['hook'], {
170143
170211
  input: JSON.stringify(nextPayload),
170144
- timeout: 10_000,
170212
+ timeout: HOOK_TIMEOUT_MS,
170145
170213
  encoding: 'utf-8',
170146
170214
  stdio: ['pipe', 'pipe', 'pipe'],
170147
170215
  shell: process.platform === 'win32',
@@ -170265,7 +170333,7 @@ function setup(ctx) {
170265
170333
  return () => controller.abort();
170266
170334
  }
170267
170335
 
170268
- export default { id: 'memorix', setup };
170336
+ export default { id: 'memorix', server: MemorixPlugin, setup };
170269
170337
  `;
170270
170338
  }
170271
170339
  async function installOpenCodeSkills(projectRoot, global2 = false) {
@@ -171091,7 +171159,7 @@ var init_installers = __esm({
171091
171159
  "use strict";
171092
171160
  init_esm_shims();
171093
171161
  init_official_skills();
171094
- OPENCODE_PLUGIN_VERSION = 8;
171162
+ OPENCODE_PLUGIN_VERSION = 9;
171095
171163
  AGENT_SKILL_DIRS = {
171096
171164
  cursor: { project: path34.join(".cursor", "skills"), global: path34.join(".cursor", "skills") },
171097
171165
  windsurf: { project: path34.join(".windsurf", "skills"), global: path34.join(".windsurf", "skills") },
@@ -176615,7 +176683,7 @@ var init_serve_shared = __esm({
176615
176683
 
176616
176684
  // src/cli/version.ts
176617
176685
  function getCliVersion() {
176618
- return true ? "1.9.9" : pkg.version;
176686
+ return true ? "1.9.10" : pkg.version;
176619
176687
  }
176620
176688
  var init_version3 = __esm({
176621
176689
  "src/cli/version.ts"() {
@@ -6,6 +6,15 @@ All notable changes to this project will be documented in this file.
6
6
 
7
7
  No unreleased changes.
8
8
 
9
+ ## [1.9.10] - 2026-10-08
10
+
11
+ ### Fixed
12
+ - **OpenCode 1.x regression** -- generated plugins now include the v1
13
+ `server` field alongside the v2 `setup` contract, so OpenCode 1.x can load
14
+ the legacy named export. Lifecycle, file, command, message, and compaction
15
+ events are also forwarded through the v1 generic `event` hook, with a longer
16
+ Windows hook timeout to reduce stale Bun timer failures.
17
+
9
18
  ## [1.9.9] - 2026-10-02
10
19
 
11
20
  ### Added
package/dist/sdk.js CHANGED
@@ -165923,6 +165923,8 @@ function generateOpenCodePlugin() {
165923
165923
  */
165924
165924
  import { spawnSync } from 'node:child_process';
165925
165925
 
165926
+ const HOOK_TIMEOUT_MS = 30_000;
165927
+
165926
165928
  export const MemorixPlugin = async ({ project, client, $, directory, worktree }) => {
165927
165929
  // Generate a stable session ID for this plugin lifetime
165928
165930
  const sessionId = \`opencode-\${Date.now().toString(36)}-\${Math.random().toString(36).slice(2, 8)}\`;
@@ -165948,14 +165950,14 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
165948
165950
  * - spawnSync is simpler: no stream lifecycle, no writer.close() bugs
165949
165951
  * - stdin pipe via input option is reliable cross-platform
165950
165952
  */
165951
- function runHook(payload) {
165952
- payload.session_id = sessionId;
165953
+ function runHook(payload, activeSessionId = sessionId) {
165954
+ payload.session_id = activeSessionId;
165953
165955
  const data = JSON.stringify(payload);
165954
165956
  const eventName = payload.hook_event_name || 'unknown';
165955
165957
  try {
165956
165958
  const result = spawnSync(hookCommand, ['hook'], {
165957
165959
  input: data,
165958
- timeout: 10_000,
165960
+ timeout: HOOK_TIMEOUT_MS,
165959
165961
  encoding: 'utf-8',
165960
165962
  stdio: ['pipe', 'pipe', 'pipe'],
165961
165963
  shell: process.platform === 'win32',
@@ -166000,7 +166002,87 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
166000
166002
  .trim();
166001
166003
  }
166002
166004
 
166005
+ function flushAssistantResponse(activeSessionId = sessionId) {
166006
+ if (!pendingAssistantResponse?.text) return;
166007
+ const deliveryKey = pendingAssistantResponse.id
166008
+ ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
166009
+ : pendingAssistantResponse.text;
166010
+ if (deliveryKey === lastDeliveredAssistantKey) return;
166011
+ runHook({
166012
+ agent: 'opencode',
166013
+ hook_event_name: 'message.updated',
166014
+ ai_response: pendingAssistantResponse.text,
166015
+ message_id: pendingAssistantResponse.id,
166016
+ cwd: directory,
166017
+ }, activeSessionId);
166018
+ lastDeliveredAssistantKey = deliveryKey;
166019
+ }
166020
+
166021
+ function eventSessionId(event) {
166022
+ const properties = event?.properties ?? {};
166023
+ const value = properties.sessionID ?? properties.sessionId ?? properties.info?.id;
166024
+ return typeof value === 'string' && value ? value : sessionId;
166025
+ }
166026
+
166027
+ async function handleEvent({ event }) {
166028
+ const properties = event?.properties ?? {};
166029
+ const activeSessionId = eventSessionId(event);
166030
+ switch (event?.type) {
166031
+ case 'session.created':
166032
+ runHook({
166033
+ agent: 'opencode',
166034
+ hook_event_name: 'session.created',
166035
+ cwd: directory,
166036
+ }, activeSessionId);
166037
+ break;
166038
+ case 'session.idle':
166039
+ flushAssistantResponse(activeSessionId);
166040
+ runHook({
166041
+ agent: 'opencode',
166042
+ hook_event_name: 'session.idle',
166043
+ cwd: directory,
166044
+ }, activeSessionId);
166045
+ break;
166046
+ case 'file.edited':
166047
+ runHook({
166048
+ agent: 'opencode',
166049
+ hook_event_name: 'file.edited',
166050
+ file_path: properties.file ?? properties.path ?? '',
166051
+ cwd: directory,
166052
+ }, activeSessionId);
166053
+ break;
166054
+ case 'command.executed':
166055
+ runHook({
166056
+ agent: 'opencode',
166057
+ hook_event_name: 'command.executed',
166058
+ command: properties.name ?? properties.command ?? '',
166059
+ cwd: directory,
166060
+ }, activeSessionId);
166061
+ break;
166062
+ case 'message.updated': {
166063
+ const message = properties.info ?? properties.message ?? properties;
166064
+ if (message?.role !== 'assistant') break;
166065
+ const text = extractMessageText(message);
166066
+ if (!text) break;
166067
+ pendingAssistantResponse = { id: message.id, text };
166068
+ break;
166069
+ }
166070
+ case 'session.compacted':
166071
+ runHook({
166072
+ agent: 'opencode',
166073
+ hook_event_name: 'session.compacted',
166074
+ cwd: directory,
166075
+ }, activeSessionId);
166076
+ break;
166077
+ default:
166078
+ break;
166079
+ }
166080
+ }
166081
+
166003
166082
  return {
166083
+ // OpenCode 1.x delivers lifecycle/file/command/message events through this
166084
+ // generic hook. The named keys below remain for older compatible hosts.
166085
+ event: handleEvent,
166004
166086
  /** Session created \u2014 record session start */
166005
166087
  'session.created': async ({ session }) => {
166006
166088
  runHook({
@@ -166012,21 +166094,7 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
166012
166094
 
166013
166095
  /** Session idle \u2014 record session end */
166014
166096
  'session.idle': async ({ session }) => {
166015
- if (pendingAssistantResponse?.text) {
166016
- const deliveryKey = pendingAssistantResponse.id
166017
- ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
166018
- : pendingAssistantResponse.text;
166019
- if (deliveryKey !== lastDeliveredAssistantKey) {
166020
- runHook({
166021
- agent: 'opencode',
166022
- hook_event_name: 'message.updated',
166023
- ai_response: pendingAssistantResponse.text,
166024
- message_id: pendingAssistantResponse.id,
166025
- cwd: directory,
166026
- });
166027
- lastDeliveredAssistantKey = deliveryKey;
166028
- }
166029
- }
166097
+ flushAssistantResponse();
166030
166098
  runHook({
166031
166099
  agent: 'opencode',
166032
166100
  hook_event_name: 'session.idle',
@@ -166134,7 +166202,7 @@ function v2RunHook(payload, directory, sessionId) {
166134
166202
  try {
166135
166203
  const result = spawnSync(v2HookCommand, ['hook'], {
166136
166204
  input: JSON.stringify(nextPayload),
166137
- timeout: 10_000,
166205
+ timeout: HOOK_TIMEOUT_MS,
166138
166206
  encoding: 'utf-8',
166139
166207
  stdio: ['pipe', 'pipe', 'pipe'],
166140
166208
  shell: process.platform === 'win32',
@@ -166258,7 +166326,7 @@ function setup(ctx) {
166258
166326
  return () => controller.abort();
166259
166327
  }
166260
166328
 
166261
- export default { id: 'memorix', setup };
166329
+ export default { id: 'memorix', server: MemorixPlugin, setup };
166262
166330
  `;
166263
166331
  }
166264
166332
  async function installOpenCodeSkills(projectRoot, global2 = false) {
@@ -167084,7 +167152,7 @@ var init_installers = __esm({
167084
167152
  "use strict";
167085
167153
  init_esm_shims();
167086
167154
  init_official_skills();
167087
- OPENCODE_PLUGIN_VERSION = 8;
167155
+ OPENCODE_PLUGIN_VERSION = 9;
167088
167156
  AGENT_SKILL_DIRS = {
167089
167157
  cursor: { project: path34.join(".cursor", "skills"), global: path34.join(".cursor", "skills") },
167090
167158
  windsurf: { project: path34.join(".windsurf", "skills"), global: path34.join(".windsurf", "skills") },
@@ -170989,7 +171057,7 @@ var MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
170989
171057
  function getMcpServerInfo() {
170990
171058
  return {
170991
171059
  name: "memorix",
170992
- version: true ? "1.9.9" : "1.0.1"
171060
+ version: true ? "1.9.10" : "1.0.1"
170993
171061
  };
170994
171062
  }
170995
171063
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -13,7 +13,7 @@ Memorix is a TypeScript project built around:
13
13
 
14
14
  ## Current Development Baseline
15
15
 
16
- The current release line is **1.9.9** and package metadata is kept synchronized
16
+ The current release line is **1.9.10** and package metadata is kept synchronized
17
17
  with the root package and MCP Registry manifest.
18
18
 
19
19
  Contributors should assume the following areas are part of the current release line:
@@ -55,7 +55,7 @@ Generated guidance also has scope:
55
55
  | Cursor | `memorix setup --agent cursor --global` | Cursor MCP and rules config | Cursor MCP config, `.cursor/rules/memorix.mdc`, skills, and hook guidance | Reload Cursor after setup so it can pick up project config changes. |
56
56
  | Grok Build | `memorix setup --agent grok --global` | `~/.grok/hooks/memorix.json` + `~/.grok/AGENTS.md` | Lifecycle hooks on SessionStart, SessionEnd, UserPromptSubmit, PostToolUse, PostToolUseFailure, PreCompact, PostCompact, Stop, and StopFailure, plus Grok project/global guidance | Uses Grok's own JSON hook directory. MCP stays host-owned unless you pass `--mcp http`, which writes `[mcp_servers.memorix]` with `url = "http://localhost:3211/mcp"` in Grok `config.toml`. Global hooks are trusted; project hooks require Grok `/hooks-trust`. Set `GROK_HOME` to use a custom Grok home. The command is `memorix hook --agent grok`. |
57
57
  | Gemini CLI | `memorix setup --agent gemini-cli --global` | Gemini CLI extension | Extension under `~/.gemini/extensions/memorix` with MCP, `GEMINI.md` context, hooks, commands, and skills | Antigravity CLI has an official Gemini CLI migration path, but Gemini CLI remains an active standalone Google CLI target. |
58
- | OpenCode | `memorix setup --agent opencode --global` | OpenCode local plugin file | Global setup writes `~/.config/opencode/plugins/memorix.js`, `~/.config/opencode/opencode.json`, skills, and `AGENTS.md` guidance; repo-local setup writes the `.opencode/` equivalents. | The generated plugin keeps the v1 named export and also exposes the v2 default `id`/`setup` contract. v2 event subscriptions use the host session ID when forwarding hooks. |
58
+ | OpenCode | `memorix setup --agent opencode --global` | OpenCode local plugin file | Global setup writes `~/.config/opencode/plugins/memorix.js`, `~/.config/opencode/opencode.json`, skills, and `AGENTS.md` guidance; repo-local setup writes the `.opencode/` equivalents. | The generated plugin keeps the v1 named export, adds the v1 `server` field to its dual default export, and exposes the v2 `id`/`setup` contract. OpenCode 1.x lifecycle/file/command/message events use the generic `event` hook; v2 subscriptions use the host session ID when forwarding hooks. |
59
59
  | OpenClaw | `memorix setup --agent openclaw --global` | OpenClaw compatible bundle | `~/.openclaw/extensions/memorix` with `.mcp.json`, skills, and OpenClaw `HOOK.md`/`handler.ts` hook pack | Setup attempts `openclaw plugins install <path> --force` and `openclaw hooks enable memorix` when OpenClaw is available. |
60
60
  | Hermes Agent | `memorix setup --agent hermes --global` | Hermes plugin | Hermes home (`%LOCALAPPDATA%\hermes` on native Windows, `~/.hermes` elsewhere, or `HERMES_HOME`), plugin enablement in `config.yaml`, and `mcp_servers.memorix` | The plugin registers hooks, a slash command, a CLI command, and skills through Hermes' plugin context. Existing YAML keys are preserved. |
61
61
  | Oh-my-Pi | `memorix setup --agent omp --global` | Oh-my-Pi `omp.extensions` package | `.omp/packages/memorix` or `~/.omp/agent/packages/memorix`, plus `.omp/mcp.json` or `~/.omp/agent/mcp.json` | Setup attempts `omp plugin link <path>`. This target uses Oh-my-Pi's package/extension manifest, not Pi's legacy package manifest. |
package/docs/README.md CHANGED
@@ -126,7 +126,7 @@ Historical/deep-reference documents may describe older designs. If they conflict
126
126
 
127
127
  ## Current Product Line
128
128
 
129
- The current product line is **1.9.9**. Multi-device store sync is opt-in and
129
+ The current product line is **1.9.10**. Multi-device store sync is opt-in and
130
130
  keeps local SQLite canonical; existing users do not upload anything until they
131
131
  configure a relay provider explicitly.
132
132
  The authoritative acceptance contract for the current implementation is
package/docs/SETUP.md CHANGED
@@ -502,7 +502,7 @@ OpenCode can also use a local Memorix plugin generated by setup:
502
502
  memorix setup --agent opencode --global
503
503
  ```
504
504
 
505
- This writes the OpenCode plugin, skills, MCP config, and `AGENTS.md` guidance in the selected scope. The generated plugin exports both the legacy v1 hook function and the v2 default `id`/`setup` contract, so OpenCode v1 and v2 can load the same file during the migration window.
505
+ This writes the OpenCode plugin, skills, MCP config, and `AGENTS.md` guidance in the selected scope. The generated plugin exports the legacy v1 hook function and a dual default object with the v1 `server` field plus the v2 `id`/`setup` contract, so OpenCode v1 and v2 can load the same file during the migration window. OpenCode 1.x events are received through its generic `event` hook.
506
506
 
507
507
  Antigravity setup installs a native plugin at `~/.gemini/config/plugins/memorix` for global setup or `.agents/plugins/memorix` for workspace setup. The plugin includes stdio MCP, official hooks, rules, and skills:
508
508
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "memorix",
3
3
  "mcpName": "io.github.AVIDS2/memorix",
4
- "version": "1.9.9",
4
+ "version": "1.9.10",
5
5
  "description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
6
6
  "type": "module",
7
7
  "sideEffects": false,
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.9"
3
+ "version": "1.9.10"
4
4
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "memorix",
4
- "version": "1.9.9",
4
+ "version": "1.9.10",
5
5
  "description": "Shared workspace memory for Claude Code and other AI coding agents.",
6
6
  "author": {
7
7
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.9",
3
+ "version": "1.9.10",
4
4
  "description": "Local-first project memory for CodeBuddy Code and other coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.9",
3
+ "version": "1.9.10",
4
4
  "description": "Shared workspace memory for Codex and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.9",
3
+ "version": "1.9.10",
4
4
  "description": "Shared local workspace memory for GitHub Copilot CLI and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.9",
3
+ "version": "1.9.10",
4
4
  "mcpServers": {
5
5
  "memorix": {
6
6
  "command": "memorix",
@@ -1,5 +1,5 @@
1
1
  name: memorix
2
- version: "1.9.9"
2
+ version: "1.9.10"
3
3
  description: Shared workspace memory bridge for Hermes Agent.
4
4
  author: AVIDS2
5
5
  license: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix-omp-package",
3
- "version": "1.9.9",
3
+ "version": "1.9.10",
4
4
  "description": "Memorix shared workspace memory package for Oh-my-Pi.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.9",
3
+ "version": "1.9.10",
4
4
  "description": "Shared workspace memory for OpenClaw and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix-pi-package",
3
- "version": "1.9.9",
3
+ "version": "1.9.10",
4
4
  "description": "Memorix shared workspace memory package for Pi coding agent.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
package/server.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "https://github.com/AVIDS2/memorix",
8
8
  "source": "github"
9
9
  },
10
- "version": "1.9.9",
10
+ "version": "1.9.10",
11
11
  "websiteUrl": "https://mem.rglens.com",
12
12
  "icons": [
13
13
  {
@@ -22,7 +22,7 @@
22
22
  {
23
23
  "registryType": "npm",
24
24
  "identifier": "memorix",
25
- "version": "1.9.9",
25
+ "version": "1.9.10",
26
26
  "runtimeHint": "npx",
27
27
  "packageArguments": [
28
28
  {
@@ -466,7 +466,7 @@ function generateKiroHookFiles(): Array<{ filename: string; content: string }> {
466
466
  * protocol used by all agents. spawnSync works in both Node.js and Bun
467
467
  * runtimes (OpenCode may fall back to Node.js on Windows).
468
468
  */
469
- const OPENCODE_PLUGIN_VERSION = 8;
469
+ const OPENCODE_PLUGIN_VERSION = 9;
470
470
 
471
471
  const AGENT_SKILL_DIRS: Partial<Record<AgentName, { project: string; global?: string }>> = {
472
472
  cursor: { project: path.join('.cursor', 'skills'), global: path.join('.cursor', 'skills') },
@@ -537,6 +537,8 @@ function generateOpenCodePlugin(): string {
537
537
  */
538
538
  import { spawnSync } from 'node:child_process';
539
539
 
540
+ const HOOK_TIMEOUT_MS = 30_000;
541
+
540
542
  export const MemorixPlugin = async ({ project, client, $, directory, worktree }) => {
541
543
  // Generate a stable session ID for this plugin lifetime
542
544
  const sessionId = \`opencode-\${Date.now().toString(36)}-\${Math.random().toString(36).slice(2, 8)}\`;
@@ -562,14 +564,14 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
562
564
  * - spawnSync is simpler: no stream lifecycle, no writer.close() bugs
563
565
  * - stdin pipe via input option is reliable cross-platform
564
566
  */
565
- function runHook(payload) {
566
- payload.session_id = sessionId;
567
+ function runHook(payload, activeSessionId = sessionId) {
568
+ payload.session_id = activeSessionId;
567
569
  const data = JSON.stringify(payload);
568
570
  const eventName = payload.hook_event_name || 'unknown';
569
571
  try {
570
572
  const result = spawnSync(hookCommand, ['hook'], {
571
573
  input: data,
572
- timeout: 10_000,
574
+ timeout: HOOK_TIMEOUT_MS,
573
575
  encoding: 'utf-8',
574
576
  stdio: ['pipe', 'pipe', 'pipe'],
575
577
  shell: process.platform === 'win32',
@@ -614,7 +616,87 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
614
616
  .trim();
615
617
  }
616
618
 
619
+ function flushAssistantResponse(activeSessionId = sessionId) {
620
+ if (!pendingAssistantResponse?.text) return;
621
+ const deliveryKey = pendingAssistantResponse.id
622
+ ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
623
+ : pendingAssistantResponse.text;
624
+ if (deliveryKey === lastDeliveredAssistantKey) return;
625
+ runHook({
626
+ agent: 'opencode',
627
+ hook_event_name: 'message.updated',
628
+ ai_response: pendingAssistantResponse.text,
629
+ message_id: pendingAssistantResponse.id,
630
+ cwd: directory,
631
+ }, activeSessionId);
632
+ lastDeliveredAssistantKey = deliveryKey;
633
+ }
634
+
635
+ function eventSessionId(event) {
636
+ const properties = event?.properties ?? {};
637
+ const value = properties.sessionID ?? properties.sessionId ?? properties.info?.id;
638
+ return typeof value === 'string' && value ? value : sessionId;
639
+ }
640
+
641
+ async function handleEvent({ event }) {
642
+ const properties = event?.properties ?? {};
643
+ const activeSessionId = eventSessionId(event);
644
+ switch (event?.type) {
645
+ case 'session.created':
646
+ runHook({
647
+ agent: 'opencode',
648
+ hook_event_name: 'session.created',
649
+ cwd: directory,
650
+ }, activeSessionId);
651
+ break;
652
+ case 'session.idle':
653
+ flushAssistantResponse(activeSessionId);
654
+ runHook({
655
+ agent: 'opencode',
656
+ hook_event_name: 'session.idle',
657
+ cwd: directory,
658
+ }, activeSessionId);
659
+ break;
660
+ case 'file.edited':
661
+ runHook({
662
+ agent: 'opencode',
663
+ hook_event_name: 'file.edited',
664
+ file_path: properties.file ?? properties.path ?? '',
665
+ cwd: directory,
666
+ }, activeSessionId);
667
+ break;
668
+ case 'command.executed':
669
+ runHook({
670
+ agent: 'opencode',
671
+ hook_event_name: 'command.executed',
672
+ command: properties.name ?? properties.command ?? '',
673
+ cwd: directory,
674
+ }, activeSessionId);
675
+ break;
676
+ case 'message.updated': {
677
+ const message = properties.info ?? properties.message ?? properties;
678
+ if (message?.role !== 'assistant') break;
679
+ const text = extractMessageText(message);
680
+ if (!text) break;
681
+ pendingAssistantResponse = { id: message.id, text };
682
+ break;
683
+ }
684
+ case 'session.compacted':
685
+ runHook({
686
+ agent: 'opencode',
687
+ hook_event_name: 'session.compacted',
688
+ cwd: directory,
689
+ }, activeSessionId);
690
+ break;
691
+ default:
692
+ break;
693
+ }
694
+ }
695
+
617
696
  return {
697
+ // OpenCode 1.x delivers lifecycle/file/command/message events through this
698
+ // generic hook. The named keys below remain for older compatible hosts.
699
+ event: handleEvent,
618
700
  /** Session created — record session start */
619
701
  'session.created': async ({ session }) => {
620
702
  runHook({
@@ -626,21 +708,7 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
626
708
 
627
709
  /** Session idle — record session end */
628
710
  'session.idle': async ({ session }) => {
629
- if (pendingAssistantResponse?.text) {
630
- const deliveryKey = pendingAssistantResponse.id
631
- ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
632
- : pendingAssistantResponse.text;
633
- if (deliveryKey !== lastDeliveredAssistantKey) {
634
- runHook({
635
- agent: 'opencode',
636
- hook_event_name: 'message.updated',
637
- ai_response: pendingAssistantResponse.text,
638
- message_id: pendingAssistantResponse.id,
639
- cwd: directory,
640
- });
641
- lastDeliveredAssistantKey = deliveryKey;
642
- }
643
- }
711
+ flushAssistantResponse();
644
712
  runHook({
645
713
  agent: 'opencode',
646
714
  hook_event_name: 'session.idle',
@@ -748,7 +816,7 @@ function v2RunHook(payload, directory, sessionId) {
748
816
  try {
749
817
  const result = spawnSync(v2HookCommand, ['hook'], {
750
818
  input: JSON.stringify(nextPayload),
751
- timeout: 10_000,
819
+ timeout: HOOK_TIMEOUT_MS,
752
820
  encoding: 'utf-8',
753
821
  stdio: ['pipe', 'pipe', 'pipe'],
754
822
  shell: process.platform === 'win32',
@@ -872,7 +940,7 @@ function setup(ctx) {
872
940
  return () => controller.abort();
873
941
  }
874
942
 
875
- export default { id: 'memorix', setup };
943
+ export default { id: 'memorix', server: MemorixPlugin, setup };
876
944
  `;
877
945
  }
878
946