@remembrance-ai/opencode-plugin 0.1.62 → 0.1.64

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/opencode.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "command": [
7
7
  "npx",
8
8
  "-y",
9
- "@remembrance-ai/mcp-server@0.1.62"
9
+ "@remembrance-ai/mcp-server@0.1.64"
10
10
  ],
11
11
  "enabled": true,
12
12
  "environment": {
@@ -16,6 +16,6 @@
16
16
  }
17
17
  },
18
18
  "plugin": [
19
- "@remembrance-ai/opencode-plugin@0.1.62"
19
+ "@remembrance-ai/opencode-plugin@0.1.64"
20
20
  ]
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remembrance-ai/opencode-plugin",
3
- "version": "0.1.62",
3
+ "version": "0.1.64",
4
4
  "license": "Apache-2.0",
5
5
  "description": "opencode plugin for Remembrance session hooks, tool observation, and contribution nudges.",
6
6
  "type": "module",
@@ -4041,6 +4041,7 @@ export function recordPluginLifecycleHealth(
4041
4041
  hostVersion = null,
4042
4042
  credentialSource = null,
4043
4043
  sessionId = null,
4044
+ hookTrust = null,
4044
4045
  },
4045
4046
  env = process.env,
4046
4047
  ) {
@@ -4072,6 +4073,7 @@ export function recordPluginLifecycleHealth(
4072
4073
  hostVersion,
4073
4074
  credentialSource,
4074
4075
  sessionId: null,
4076
+ hookTrust,
4075
4077
  },
4076
4078
  env,
4077
4079
  );
@@ -4164,6 +4166,10 @@ export function recordPluginLifecycleHealth(
4164
4166
  api_destination_fingerprint: apiDestinationFingerprint,
4165
4167
  evidence_origin: evidenceOrigin,
4166
4168
  release_run_id: releaseRunId || null,
4169
+ hook_trust:
4170
+ sanitizePluginHookTrust(hookTrust) ??
4171
+ sanitizePluginHookTrust(existing.hook_trust) ??
4172
+ null,
4167
4173
  components: {
4168
4174
  ...currentComponents,
4169
4175
  [normalizedComponent]: now,
@@ -4190,6 +4196,73 @@ export function recordPluginLifecycleHealth(
4190
4196
  }
4191
4197
  }
4192
4198
 
4199
+ function sanitizePluginHookTrust(value) {
4200
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
4201
+ const status = String(value.status ?? "")
4202
+ .trim()
4203
+ .toLowerCase();
4204
+ if (!["trusted", "review_required", "unavailable"].includes(status)) {
4205
+ return null;
4206
+ }
4207
+ const allowedEvents = new Set([
4208
+ "SessionStart",
4209
+ "UserPromptSubmit",
4210
+ "PostToolUse",
4211
+ "Stop",
4212
+ ]);
4213
+ const reviewEvents = Array.isArray(value.review_events)
4214
+ ? [...new Set(value.review_events)]
4215
+ .filter((event) => allowedEvents.has(event))
4216
+ .slice(0, 4)
4217
+ : [];
4218
+ const hooks = Array.isArray(value.hooks)
4219
+ ? value.hooks
4220
+ .filter(
4221
+ (entry) =>
4222
+ entry &&
4223
+ typeof entry === "object" &&
4224
+ !Array.isArray(entry) &&
4225
+ allowedEvents.has(entry.event),
4226
+ )
4227
+ .slice(0, 4)
4228
+ .map((entry) => ({
4229
+ event: entry.event,
4230
+ enabled: entry.enabled === true,
4231
+ trust_status: [
4232
+ "managed",
4233
+ "missing",
4234
+ "modified",
4235
+ "trusted",
4236
+ "unknown",
4237
+ "untrusted",
4238
+ ].includes(entry.trust_status)
4239
+ ? entry.trust_status
4240
+ : "unknown",
4241
+ }))
4242
+ : [];
4243
+ return {
4244
+ status,
4245
+ checked_at: safeText(value.checked_at ?? "", 64),
4246
+ review_events: reviewEvents,
4247
+ hooks,
4248
+ reason:
4249
+ status === "unavailable" &&
4250
+ [
4251
+ "app_server_closed",
4252
+ "app_server_initialize_failed",
4253
+ "app_server_unavailable",
4254
+ "check_disabled",
4255
+ "codex_executable_not_found",
4256
+ "hooks_list_failed",
4257
+ "hooks_list_timeout",
4258
+ "hooks_list_too_large",
4259
+ "plugin_hooks_not_listed",
4260
+ ].includes(value.reason)
4261
+ ? value.reason
4262
+ : null,
4263
+ };
4264
+ }
4265
+
4193
4266
  function prunePluginHealthSessionMarkers(surface, keepPath, env) {
4194
4267
  const directory = pluginHealthDir(env);
4195
4268
  try {
@@ -4706,7 +4706,17 @@ var HOST_POLICY_OPERATION_CLASSES = /* @__PURE__ */ new Set([
4706
4706
  "other"
4707
4707
  ]);
4708
4708
  var CODEX_HOOK_REVIEW_COMMAND = `CODEX_CLI="\${CODEX_CLI:-$(command -v codex || true)}"; [ -x "$CODEX_CLI" ] || CODEX_CLI="/Applications/ChatGPT.app/Contents/Resources/codex"; [ -x "$CODEX_CLI" ] || CODEX_CLI="/Applications/Codex.app/Contents/Resources/codex"; [ -x "$CODEX_CLI" ] || { printf '%s\\n' "Codex CLI not found. Install the Codex CLI, or install or update the ChatGPT desktop app on macOS, then try again." >&2; exit 1; }; "$CODEX_CLI"`;
4709
- function codexHookReviewAction(followupTool) {
4709
+ function codexHookReviewAction(followupTool, reviewEvents = []) {
4710
+ const allowedEvents = /* @__PURE__ */ new Set([
4711
+ "SessionStart",
4712
+ "UserPromptSubmit",
4713
+ "PostToolUse",
4714
+ "Stop"
4715
+ ]);
4716
+ const events = [...new Set(reviewEvents)].filter((event) => allowedEvents.has(event)).slice(0, 4);
4717
+ if (events.length > 0) {
4718
+ return `Open Terminal and run the command below. Choose Review hooks, open only the Remembrance ${events.join(", ")} hook${events.length === 1 ? "" : "s"}, and press t to trust ${events.length === 1 ? "it" : "them"}. Then fully quit and reopen Codex, submit one prompt, use one Remembrance tool, complete one turn, and call ${followupTool} again.`;
4719
+ }
4710
4720
  return `Open Terminal and launch Codex with the command below. If Codex shows a Hooks need review screen, choose Review hooks and trust only the Remembrance SessionStart, UserPromptSubmit, PostToolUse, and Stop hooks. If no review screen appears, continue: Codex may be reusing an existing valid trust decision. Fully restart Codex, submit one prompt, use one Remembrance tool, complete one turn, and call ${followupTool} again. If the lifecycle warning remains, update or reinstall Remembrance and repeat this check.`;
4711
4721
  }
4712
4722
  function buildConnectionDoctorReport(input) {
@@ -5000,6 +5010,28 @@ function pluginLifecycleCheck(pluginHealth, issues) {
5000
5010
  "The native plugin startup and prompt lifecycle is active."
5001
5011
  );
5002
5012
  }
5013
+ if (stringValue(pluginHealth.surface) === "codex" && issues.includes("hook_trust_required")) {
5014
+ const hookTrust = record(pluginHealth.hook_trust);
5015
+ const allowedEvents = /* @__PURE__ */ new Set([
5016
+ "SessionStart",
5017
+ "UserPromptSubmit",
5018
+ "PostToolUse",
5019
+ "Stop"
5020
+ ]);
5021
+ const reviewEvents = Array.isArray(hookTrust.review_events) ? hookTrust.review_events.filter(
5022
+ (event) => typeof event === "string" && allowedEvents.has(event)
5023
+ ).slice(0, 4) : [];
5024
+ const eventSummary = reviewEvents.length > 0 ? `the updated Remembrance ${reviewEvents.join(", ")} hook${reviewEvents.length === 1 ? "" : "s"}` : "updated Remembrance hooks";
5025
+ return warning(
5026
+ "plugin_lifecycle",
5027
+ `Codex is waiting for trust approval for ${eventSummary}.`,
5028
+ "review_codex_hook_trust",
5029
+ codexHookReviewAction("run_connection_doctor", reviewEvents),
5030
+ issues,
5031
+ false,
5032
+ CODEX_HOOK_REVIEW_COMMAND
5033
+ );
5034
+ }
5003
5035
  if (pluginHealth.status === "partial") {
5004
5036
  const codex = stringValue(pluginHealth.surface) === "codex";
5005
5037
  return warning(
@@ -10730,13 +10762,23 @@ function localPluginLifecycleHealth(options) {
10730
10762
  });
10731
10763
  }
10732
10764
  const coreLifecycleObserved = components.session_start.observed && components.prompt_hook.observed;
10733
- if (coreLifecycleObserved && !components.tool_observer.observed) {
10765
+ const hookTrust = normalizedPluginHookTrust(marker.hook_trust);
10766
+ const hookTrustReviewEvents = hookTrust?.status === "review_required" ? new Set(hookTrust.review_events) : /* @__PURE__ */ new Set();
10767
+ if (hookTrustReviewEvents.size > 0) {
10768
+ issues.push({
10769
+ code: "hook_trust_required",
10770
+ action: codexHookReviewAction("get_connection_status", [
10771
+ ...hookTrustReviewEvents
10772
+ ])
10773
+ });
10774
+ }
10775
+ if (coreLifecycleObserved && !components.tool_observer.observed && !hookTrustReviewEvents.has("PostToolUse")) {
10734
10776
  issues.push({
10735
10777
  code: "tool_observer_not_observed",
10736
10778
  action: surface === "codex" ? `Use a Remembrance MCP tool, then call get_connection_status again. If this remains missing, ${codexHookReviewAction("get_connection_status")}` : "No native tool observer has run in this session yet. Use a Remembrance MCP tool, then call get_connection_status again."
10737
10779
  });
10738
10780
  }
10739
- if (coreLifecycleObserved && !components.completion_hook.observed) {
10781
+ if (coreLifecycleObserved && !components.completion_hook.observed && !hookTrustReviewEvents.has("Stop")) {
10740
10782
  issues.push({
10741
10783
  code: "completion_hook_not_observed",
10742
10784
  action: surface === "codex" ? `Complete one turn, then call get_connection_status again. If this remains missing, ${codexHookReviewAction("get_connection_status")}` : "No native completion hook has run in this session yet. Complete one turn, then call get_connection_status again."
@@ -10795,11 +10837,49 @@ function localPluginLifecycleHealth(options) {
10795
10837
  matches_mcp: markerDestinationFingerprint && options.apiDestinationFingerprint ? markerDestinationFingerprint === options.apiDestinationFingerprint : null,
10796
10838
  mcp_source: options.apiDestinationSource ?? null
10797
10839
  },
10840
+ hook_trust: hookTrust,
10798
10841
  components,
10799
10842
  issues,
10800
10843
  explanation: "Each host session is tracked independently. SessionStart and the prompt hook must run first; tool and completion observations appear after those lifecycle events become eligible."
10801
10844
  };
10802
10845
  }
10846
+ function normalizedPluginHookTrust(value) {
10847
+ if (!isRecord3(value)) return null;
10848
+ const status = value.status;
10849
+ if (status !== "trusted" && status !== "review_required" && status !== "unavailable") {
10850
+ return null;
10851
+ }
10852
+ const allowedEvents = /* @__PURE__ */ new Set([
10853
+ "SessionStart",
10854
+ "UserPromptSubmit",
10855
+ "PostToolUse",
10856
+ "Stop"
10857
+ ]);
10858
+ const allowedStatuses = /* @__PURE__ */ new Set([
10859
+ "managed",
10860
+ "missing",
10861
+ "modified",
10862
+ "trusted",
10863
+ "unknown",
10864
+ "untrusted"
10865
+ ]);
10866
+ const reviewEvents = Array.isArray(value.review_events) ? [...new Set(value.review_events)].filter(
10867
+ (event) => typeof event === "string" && allowedEvents.has(event)
10868
+ ).slice(0, 4) : [];
10869
+ const hooks = Array.isArray(value.hooks) ? value.hooks.filter(isRecord3).filter(
10870
+ (entry) => typeof entry.event === "string" && allowedEvents.has(entry.event)
10871
+ ).slice(0, 4).map((entry) => ({
10872
+ event: String(entry.event),
10873
+ enabled: entry.enabled === true,
10874
+ trust_status: typeof entry.trust_status === "string" && allowedStatuses.has(entry.trust_status) ? entry.trust_status : "unknown"
10875
+ })) : [];
10876
+ return {
10877
+ status,
10878
+ checked_at: typeof value.checked_at === "string" && Number.isFinite(Date.parse(value.checked_at)) ? new Date(value.checked_at).toISOString() : null,
10879
+ review_events: reviewEvents,
10880
+ hooks
10881
+ };
10882
+ }
10803
10883
  function apiDestinationStatus(baseUrl, source) {
10804
10884
  const normalized = baseUrl.replace(/\/+$/, "");
10805
10885
  return {
@@ -11003,7 +11083,7 @@ async function checkClientUpdate(input) {
11003
11083
  // src/server.ts
11004
11084
  var MAX_REMOTE_RESPONSE_BYTES = 4 * 1024 * 1024;
11005
11085
  var DOCTOR_PROBE_TIMEOUT_MS = 7500;
11006
- var SERVER_VERSION = true ? "0.1.62" : "0.0.0-dev";
11086
+ var SERVER_VERSION = true ? "0.1.64" : "0.0.0-dev";
11007
11087
  var tools = toolDefinitions;
11008
11088
  var doctorCliRequested = process.argv[2] === "doctor";
11009
11089
  var inputBuffer = Buffer.alloc(0);
@@ -11018,6 +11098,7 @@ var PRINCIPAL_SESSION_REQUIRED_TOOLS = /* @__PURE__ */ new Set([
11018
11098
  ]);
11019
11099
  var PRINCIPAL_SESSION_AWAITED_TOOLS = /* @__PURE__ */ new Set([
11020
11100
  ...PRINCIPAL_SESSION_REQUIRED_TOOLS,
11101
+ "get_effective_preferences",
11021
11102
  "report_task_outcome"
11022
11103
  ]);
11023
11104
  function apiAccessKey(access) {