@awak-app/simy-cli 0.5.79 → 0.5.81

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
@@ -181,6 +181,13 @@ app-server session and checks writer ownership before each continuation. Claude
181
181
  can continue through a verified live SIMY hook in its original process; sessions
182
182
  without that hook support monitoring only, and cold takeover remains unavailable. See [session supervision](docs/session-supervision.md).
183
183
 
184
+ Provider discovery skips outdated or unversioned executables while looking for a
185
+ compatible installation. Codex session supervision requires `0.158.0` or newer,
186
+ independently of the lower general execution minimum. On macOS, discovery also
187
+ checks the bundled CLI in ChatGPT and Codex apps in system and user Applications.
188
+ Claude discovery likewise falls back to a compatible native or Desktop bundle
189
+ when an older PATH installation cannot meet its version requirement.
190
+
184
191
  The plugin also provides an opt-in, hook-based Delivery Loop in the user's existing
185
192
  Codex or Claude Code conversation. Install the plugin, review and enable SIMY hooks
186
193
  with `/hooks`, then request `/simy:delivery-loop` for the task. Installing or updating
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awak-app/simy-cli",
3
- "version": "0.5.79",
3
+ "version": "0.5.81",
4
4
  "description": "Local SIMY executor for non-coding Local Tasks and Agentic Loop runs.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -4,8 +4,7 @@ import { readdir } from "node:fs/promises";
4
4
  import { readdirSync, statSync } from "node:fs";
5
5
  import path from "node:path";
6
6
 
7
- const MACOS_CODEX_BINARY = "/Applications/ChatGPT.app/Contents/Resources/codex";
8
- const MACOS_CODEX_BUNDLED_BINARY = "/Applications/ChatGPT.app/Contents/Resources/codex-cli/bin/codex";
7
+ export const CODEX_WRITER_LOCK_MINIMUM_VERSION = "0.158.0";
9
8
 
10
9
  export const BACKEND_VERSION_POLICIES = Object.freeze({
11
10
  codex: Object.freeze({
@@ -27,50 +26,45 @@ export async function resolveBackendExecutable(backend, options = {}) {
27
26
 
28
27
  export async function inspectBackendExecutable(
29
28
  backend,
30
- { spawnImpl = spawn, candidates, timeoutMs = 5_000, signal, discoveryOptions = {} } = {},
29
+ { spawnImpl = spawn, candidates, timeoutMs = 5_000, signal, discoveryOptions = {}, minimumVersion } = {},
31
30
  ) {
32
31
  const assertActive = () => { if (signal?.aborted) throw Object.assign(new Error('Backend inspection was cancelled.'), { code: 'cancelled' }); };
33
32
  assertActive();
34
33
  const policy = versionPolicy(backend);
34
+ const requiredVersion = minimumVersion || policy.minimumVersion;
35
35
  candidates ??= [...backendCandidates(backend, discoveryOptions), ...await bundledClaudeCandidates(backend, discoveryOptions)];
36
+ let fallback = null;
36
37
  for (const candidate of [...new Set(candidates.filter(Boolean))]) {
37
38
  assertActive();
38
39
  const probe = await probeExecutable(candidate, { spawnImpl, timeoutMs, signal });
39
40
  assertActive();
40
41
  if (!probe.available) continue;
41
42
  const installedVersion = parseBackendVersion(probe.output);
42
- if (!installedVersion) {
43
- return {
44
- backend,
45
- executable: candidate,
46
- available: true,
47
- compatible: false,
48
- status: "unknown_version",
49
- installed_version: null,
50
- minimum_version: policy.minimumVersion,
51
- update_command: policy.updateCommand,
52
- };
53
- }
54
- const compatible = compareVersions(installedVersion, policy.minimumVersion) >= 0;
55
- return {
43
+ const compatible = Boolean(installedVersion) && compareVersions(installedVersion, requiredVersion) >= 0;
44
+ const inspection = {
56
45
  backend,
57
46
  executable: candidate,
58
47
  available: true,
59
48
  compatible,
60
- status: compatible ? "compatible" : "outdated",
49
+ status: compatible ? "compatible" : installedVersion ? "outdated" : "unknown_version",
61
50
  installed_version: installedVersion,
62
- minimum_version: policy.minimumVersion,
51
+ minimum_version: requiredVersion,
63
52
  update_command: policy.updateCommand,
64
53
  };
54
+ // An executable that starts may still be too old for the requested feature.
55
+ // Preserve candidate priority among compatible installs, and retain the
56
+ // first runnable failure for actionable diagnostics if none qualifies.
57
+ if (compatible) return inspection;
58
+ fallback ??= inspection;
65
59
  }
66
- return {
60
+ return fallback || {
67
61
  backend,
68
62
  executable: null,
69
63
  available: false,
70
64
  compatible: false,
71
65
  status: "missing",
72
66
  installed_version: null,
73
- minimum_version: policy.minimumVersion,
67
+ minimum_version: requiredVersion,
74
68
  update_command: policy.updateCommand,
75
69
  };
76
70
  }
@@ -94,7 +88,13 @@ export function backendCandidates(backend, {
94
88
  return [
95
89
  environment.SIMY_CODEX_BIN,
96
90
  "codex",
97
- ...(platform === "darwin" ? [MACOS_CODEX_BUNDLED_BINARY, MACOS_CODEX_BINARY] : []),
91
+ ...(platform === "darwin" ? ["/Applications", path.posix.join(home, "Applications")].flatMap(directory =>
92
+ ["ChatGPT.app", "Codex.app"].flatMap(app => {
93
+ const resources = path.posix.join(directory, app, "Contents", "Resources");
94
+ return [path.posix.join(resources, "codex-cli", "bin", "codex"),
95
+ path.posix.join(resources, "codex-cli", "CodexCLI.app", "Contents", "MacOS", "codex"),
96
+ path.posix.join(resources, "codex")];
97
+ })) : []),
98
98
  ];
99
99
  }
100
100
 
@@ -13,7 +13,7 @@ import {
13
13
  } from "./durable-local-task-steps-contract.js";
14
14
  import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
15
15
 
16
- export const CLI_VERSION = "0.5.79";
16
+ export const CLI_VERSION = "0.5.81";
17
17
  export const CLI_API_CONTRACT_VERSION = 10;
18
18
 
19
19
  export function withCliContract(
@@ -1,7 +1,7 @@
1
1
  import { associationContract } from '../codex-plugins/simy/hooks/action-association.mjs';
2
2
  import { spawn } from 'node:child_process';
3
3
  import { realpath } from 'node:fs/promises';
4
- import { inspectBackendExecutable } from '../backend-executable.js';
4
+ import { CODEX_WRITER_LOCK_MINIMUM_VERSION, compareVersions, inspectBackendExecutable } from '../backend-executable.js';
5
5
  import { assessedCheckpointContract, deliveryBudgetState, understandingCheckpointContract } from '../codex-plugins/simy/hooks/delivery-loop.mjs';
6
6
  import { boundConversation, boundProviderGoal, textContent } from './conversation-context.js';
7
7
  import { JsonDataUrlFilter } from './json-data-url-filter.js';
@@ -149,13 +149,13 @@ export class CodexAppServerClient {
149
149
  export function supportsCodexWriterLock(version) {
150
150
  const match = String(version || '').match(/^(\d+)\.(\d+)\.(\d+)/);
151
151
  if (!match) return false;
152
- const parts = match.slice(1).map(Number);
153
- return parts[0] > 0 || parts[1] > 158 || parts[1] === 158 && parts[2] >= 0;
152
+ return compareVersions(version, CODEX_WRITER_LOCK_MINIMUM_VERSION) >= 0;
154
153
  }
155
154
 
156
155
  export async function openCodexAppServer({ inspectExecutable = inspectBackendExecutable, spawnImpl = spawn, signal, maxLineBytes, omitInlineAttachments = false, cancelOnAbort = false } = {}) {
157
156
  if (signal?.aborted) throw new CodexAppServerError('cancelled', 'Codex app-server was cancelled.');
158
- const inspection = await inspectExecutable('codex', cancelOnAbort ? { signal } : undefined);
157
+ const inspection = await inspectExecutable('codex', { minimumVersion: CODEX_WRITER_LOCK_MINIMUM_VERSION,
158
+ ...(cancelOnAbort ? { signal } : {}) });
159
159
  if (!inspection?.available || !inspection.executable) throw new CodexAppServerError('unavailable', 'Codex executable is unavailable.');
160
160
  if (!supportsCodexWriterLock(inspection.installed_version)) throw new CodexAppServerError('unsupported', 'Codex version does not have a verified app-server writer lock.');
161
161
  if (signal?.aborted) throw new CodexAppServerError('cancelled', 'Codex app-server was cancelled.');