@awak-app/simy-cli 0.5.7 → 0.5.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/README.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  Local execution agent for SIMY Local Tasks and Agentic Loop runs.
4
4
 
5
+ ## Apple Silicon Mac app
6
+
7
+ The macOS installer includes a native status window and menu bar app. Open
8
+ SIMY CLI from Applications and choose **Sign in**. The window shows SIMY login,
9
+ the connected Web address and last successful heartbeat, Codex installation and
10
+ login status, tasks, and updates separately. A Codex login check does not prove
11
+ that a task can execute successfully; permissions and service availability are
12
+ checked when work starts.
13
+
14
+ Closing the window keeps the agent running. Open the app again or choose
15
+ **Show SIMY CLI** from its menu bar icon to return. **Quit SIMY CLI** asks before
16
+ interrupting active tasks. The normal `simy` command remains available.
17
+
18
+ Signed patch/minor updates wait for idle and verify the new runtime before
19
+ handoff; failed startup restores the previous installed version. After a runtime
20
+ update, quit and reopen the app to load an updated native interface. The status
21
+ window reports App and runtime versions separately while they differ.
22
+
23
+ For development, `npm run build:macos-installer:dev` creates an unsigned app/DMG;
24
+ production builds require Developer ID signing and notarization. Never publish
25
+ unsigned development artifacts through the stable update manifest.
26
+
5
27
  For normal use, install SIMY globally and keep its background agent running:
6
28
 
7
29
  ```bash
@@ -11,9 +33,12 @@ simy --daemon
11
33
 
12
34
  When an interactive `simy --daemon` starts without managed SIMY workflows,
13
35
  the CLI asks once before installing the Codex plugin
14
- marketplace, Codex/Claude SQM skills, and PR hooks. Existing unrelated plugins
36
+ marketplace and Codex/Claude opt-in SQM skills. Existing unrelated plugins
15
37
  and hooks are preserved. Use `--install-workflows` for explicit non-interactive
16
38
  installation or `--no-workflow-prompt` to start the daemon without asking.
39
+
40
+ SQM checks apply when you explicitly select `@simy`, invoke the SQM skill, or ask to use SQM for the current task. Ordinary PR creation and `git push` do not opt in. Upgrading and restarting the daemon migrates the legacy global PR gate and shipped personal skills; unrelated hooks are preserved. Start a new coding session to reload skill instructions.
41
+
17
42
  After installation, restart Codex and find and install **SIMY** in **Plugins**
18
43
  before using `@simy` in a new task.
19
44
 
@@ -370,6 +395,13 @@ processes, and exercises HIL plus lifecycle controls. It regenerates the
370
395
  screenshots, terminal frames, raw transcript, and SHA-256 manifest under
371
396
  `docs/pr-evidence/cli-agentic-loop-console/`.
372
397
 
398
+ ## Delivery Loop design proposals
399
+
400
+ The [minimum completion plan](docs/delivery-minimum-completion/README.md) describes
401
+ proposed checks for omitted requirements, inactive controls, unsaved results, and
402
+ broken integrations at every delivery level, with workflow, sequence, and state
403
+ diagrams. This is a design proposal, not a shipped completion guarantee.
404
+
373
405
  ## Publishing
374
406
 
375
407
  Publishing is handled by GitHub Actions in `.github/workflows/publish.yml`.
@@ -391,3 +423,24 @@ original snapshot. A scope revision change requires a fresh minimum check.
391
423
  Deploy this CLI and the web capability proxy before enabling the backend scope
392
424
  requirement. Older clients receive an explicit upgrade error, never an expanded
393
425
  organization-wide bundle.
426
+
427
+ ### Submit repaired local delivery evidence to SQM
428
+
429
+ The bundled Delivery Loop maintains an external acceptance checklist with separate
430
+ user-journey and technical-invariant results. It refreshes the table after each
431
+ evaluated action and marks evidence pending when the code or runtime context
432
+ changes. The [acceptance evidence guide](src/codex-plugins/simy/skills/agentic-delivery-loop/references/acceptance-evidence.md)
433
+ documents the offline validator shipped inside the skill. Its result establishes
434
+ record completeness and artifact integrity, not overall delivery or deployment.
435
+ At the start of each new run, the loop recommends and confirms one of five
436
+ [delivery levels](src/codex-plugins/simy/skills/agentic-delivery-loop/references/delivery-levels.md)
437
+ in chat. Risk-based ceilings keep sensitive or consequential changes on the
438
+ required safety level; the selection adjusts verification depth but grants no
439
+ additional authority.
440
+
441
+ `simy sqm submit-evidence --file delivery.json --host https://app-dev.simy.one`
442
+ submits an explicit bounded summary through your existing organization-bound device
443
+ session. `--dry-run` previews the exact document without uploading it. See the
444
+ [local evidence contract](src/codex-plugins/simy/skills/agentic-delivery-loop/references/local-sqm-evidence.md).
445
+ The receipt identifies the source digest and analysis job; it is not a declaration
446
+ that an incident or blocking rule has been published.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awak-app/simy-cli",
3
- "version": "0.5.7",
3
+ "version": "0.5.10",
4
4
  "description": "Local SIMY executor for non-coding Local Tasks and Agentic Loop runs.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -40,8 +40,12 @@
40
40
  "check:auto-update": "node --check ./src/auto-update.js && node --check ./scripts/auto-update-e2e.js && npm run check:sqm",
41
41
  "check:sqm": "node --check ./src/sqm/canonical.js && node --check ./src/sqm/validation.js && node --check ./src/sqm/reliability.js && node --check ./src/sqm/repository.js && node --check ./src/sqm/checkers.js && node --check ./src/sqm/bundle-store.js && node --check ./src/sqm/proof.js && node --check ./src/sqm/evidence-client.js && node --check ./src/sqm/session.js && node --check ./src/sqm/index.js && node --check ./src/sqm/command.js && node --check ./src/sqm/agent-skill.js && node --check ./src/sqm/workflow-onboarding.js && node --check ./src/sqm/workflow-update.js && node --check ./src/sqm/review-worker.js && node --check ./scripts/sqm-complete-loop-e2e.js && node --check ./scripts/sqm-local-review-e2e.js",
42
42
  "check:real-provider-syntax": "node --check ./scripts/real-codex-probe.js && node --check ./scripts/real-claude-probe.js && node --check ./scripts/real-provider-console-fixture.js && node --check ./scripts/real-provider-console-e2e.js",
43
- "check:syntax": "node --check ./src/index.js && node --check ./src/agent.js && node --check ./src/browser.js && node --check ./src/web-api.js && node --check ./src/durable-local-task-worker.js && node --check ./src/durable-local-task-steps-contract.js && node --check ./src/bounded-local-task-subtasks-contract.js && node --check ./src/bounded-local-task-subtask-pool.js && node --check ./src/local-task-artifact-contract.js && node --check ./src/execution-capability-contract.js && node --check ./src/shutdown.js && node --check ./src/run-registry.js && node --check ./src/direct-executor.js && node --check ./src/execution-guardrail.js && node --check ./src/backend-executable.js && node --check ./src/provider-stream.js && node --check ./src/console/index.js && node --check ./src/console/app.js && node --check ./src/console/commands.js && node --check ./src/local-attachments.js && node --check ./src/repository-inventory.js && node --check ./src/session-store.js && node --check ./src/web-origin.js && node --check ./src/workspace-context.js && node --check ./src/orchestrator/index.js && node --check ./src/orchestrator/shared.js && node --check ./src/orchestrator/recovery.js && node --check ./src/orchestrator/risk.js && node --check ./src/orchestrator/contract.js && node --check ./src/orchestrator/completion-contract.js && node --check ./src/orchestrator/instruction.js && node --check ./src/orchestrator/problem-solving.js && node --check ./src/orchestrator/budget.js && node --check ./src/orchestrator/execution-io.js && node --check ./src/orchestrator/presentation.js && node --check ./src/orchestrator/result.js && node --check ./src/orchestrator/retry.js && node --check ./src/orchestrator/evidence.js && node --check ./src/orchestrator/independent-audit.js && node --check ./src/orchestrator/audit.js && node --check ./src/orchestrator/loop.js && node --check ./src/runner.js && node --check ./scripts/fixtures/completion-contract-fixture.js && node --check ./scripts/fixtures/fake-coding-backend.js && node --check ./scripts/e2e-terminal-evidence.js && node --check ./scripts/audit-gate-e2e.js && node --check ./scripts/completion-contract-e2e.js && node --check ./scripts/retry-circuit-e2e.js && node --check ./scripts/follow-up-charter-e2e.js && node --check ./scripts/task-routing-e2e.js && node --check ./scripts/executor-version-preflight-e2e.js && node --check ./scripts/blocked-recovery-e2e.js && node --check ./scripts/process-localization-e2e.js && node --check ./scripts/hypothesis-retries-e2e.js && node --check ./scripts/budget-enforcement-e2e.js && node --check ./scripts/local-task-default-workspace-e2e.js && node --check ./scripts/local-task-idempotency-e2e.js && node --check ./scripts/console-e2e-fixture.js && node --check ./scripts/console-e2e.js && node --check ./scripts/console-recovery-e2e-fixture.js && node --check ./scripts/console-recovery-e2e.js && node --check ./scripts/real-provider-console-fixture.js && node --check ./scripts/real-provider-console-e2e.js && node --check ./scripts/fullstack-cli-smoke.js && node --check ./scripts/check-package-contents.js",
44
- "check:package": "node ./scripts/check-package-contents.js"
43
+ "check:syntax": "node --check ./src/index.js && node --check ./src/install-update.js && node --check ./src/agent.js && node --check ./src/browser.js && node --check ./src/web-api.js && node --check ./src/durable-local-task-worker.js && node --check ./src/durable-local-task-steps-contract.js && node --check ./src/bounded-local-task-subtasks-contract.js && node --check ./src/bounded-local-task-subtask-pool.js && node --check ./src/local-task-artifact-contract.js && node --check ./src/execution-capability-contract.js && node --check ./src/shutdown.js && node --check ./src/run-registry.js && node --check ./src/direct-executor.js && node --check ./src/execution-guardrail.js && node --check ./src/backend-executable.js && node --check ./src/provider-stream.js && node --check ./src/console/index.js && node --check ./src/console/app.js && node --check ./src/console/commands.js && node --check ./src/local-attachments.js && node --check ./src/repository-inventory.js && node --check ./src/session-store.js && node --check ./src/web-origin.js && node --check ./src/workspace-context.js && node --check ./src/orchestrator/index.js && node --check ./src/orchestrator/shared.js && node --check ./src/orchestrator/recovery.js && node --check ./src/orchestrator/risk.js && node --check ./src/orchestrator/contract.js && node --check ./src/orchestrator/completion-contract.js && node --check ./src/orchestrator/instruction.js && node --check ./src/orchestrator/problem-solving.js && node --check ./src/orchestrator/budget.js && node --check ./src/orchestrator/execution-io.js && node --check ./src/orchestrator/presentation.js && node --check ./src/orchestrator/result.js && node --check ./src/orchestrator/retry.js && node --check ./src/orchestrator/evidence.js && node --check ./src/orchestrator/independent-audit.js && node --check ./src/orchestrator/audit.js && node --check ./src/orchestrator/loop.js && node --check ./src/runner.js && node --check ./scripts/fixtures/completion-contract-fixture.js && node --check ./scripts/fixtures/fake-coding-backend.js && node --check ./scripts/e2e-terminal-evidence.js && node --check ./scripts/audit-gate-e2e.js && node --check ./scripts/completion-contract-e2e.js && node --check ./scripts/retry-circuit-e2e.js && node --check ./scripts/follow-up-charter-e2e.js && node --check ./scripts/task-routing-e2e.js && node --check ./scripts/executor-version-preflight-e2e.js && node --check ./scripts/blocked-recovery-e2e.js && node --check ./scripts/process-localization-e2e.js && node --check ./scripts/hypothesis-retries-e2e.js && node --check ./scripts/budget-enforcement-e2e.js && node --check ./scripts/local-task-default-workspace-e2e.js && node --check ./scripts/local-task-idempotency-e2e.js && node --check ./scripts/console-e2e-fixture.js && node --check ./scripts/console-e2e.js && node --check ./scripts/console-recovery-e2e-fixture.js && node --check ./scripts/console-recovery-e2e.js && node --check ./scripts/real-provider-console-fixture.js && node --check ./scripts/real-provider-console-e2e.js && node --check ./scripts/fullstack-cli-smoke.js && node --check ./scripts/check-package-contents.js",
44
+ "check:package": "node ./scripts/check-package-contents.js",
45
+ "build:macos-installer:dev": "bash packaging/build-macos-arm64.sh --unsigned-dev",
46
+ "build:macos-installer": "bash packaging/build-macos-arm64.sh --production",
47
+ "test:macos-installer": "node --test packaging/test-macos-packaging.mjs",
48
+ "build:macos-installer:production": "bash packaging/build-macos-arm64.sh --production"
45
49
  },
46
50
  "engines": {
47
51
  "node": ">=20"
package/src/agent.js CHANGED
@@ -48,6 +48,12 @@ import {
48
48
  inspectDesktopExecutor,
49
49
  normalizeDesktopExecutionTarget,
50
50
  } from "./desktop-executor.js";
51
+ import {
52
+ createCodexStatusInspector,
53
+ createConnectionStatusTracker,
54
+ desktopAuthStatus,
55
+ desktopTaskStatus,
56
+ } from "./desktop-status.js";
51
57
  import { recoveryContractForPreflight } from "./orchestrator/recovery.js";
52
58
  import {
53
59
  DEFAULT_RETRY_BUDGET,
@@ -237,6 +243,25 @@ export async function startAgent({
237
243
  let heartbeatInFlight = null;
238
244
  let durableLocalTaskWorker = null;
239
245
  let sqmLocalReviewWorker = null;
246
+ let loginUrl = null;
247
+ const connectionStatus = createConnectionStatusTracker({ now: dependencies.statusNow });
248
+ const codexStatus = createCodexStatusInspector({
249
+ inspect: dependencies.inspectCodexStatus,
250
+ now: dependencies.statusNowMs,
251
+ });
252
+
253
+ const requestDesktopLogin = () => {
254
+ if (isSessionValid(session, Date.now(), apiOrigin)) return null;
255
+ if (!loginUrl) {
256
+ loginUrl = new URL("/local-cli/connect", apiOrigin);
257
+ loginUrl.searchParams.set("port", String(port));
258
+ loginUrl.searchParams.set("nonce", authNonce);
259
+ loginUrl.searchParams.set("cli_version", CLI_VERSION);
260
+ loginUrl.searchParams.set("api_contract_version", String(CLI_API_CONTRACT_VERSION));
261
+ if (installMode) loginUrl.searchParams.set("install_mode", installMode);
262
+ }
263
+ return loginUrl.toString();
264
+ };
240
265
 
241
266
  const restoreRemoteRuns = async () => {
242
267
  if (!isSessionValid(session, Date.now(), apiOrigin) || !session?.device_id) return;
@@ -268,6 +293,8 @@ export async function startAgent({
268
293
  const heartbeat = () => {
269
294
  if (!isSessionValid(session, Date.now(), apiOrigin)) return Promise.resolve(false);
270
295
  if (heartbeatInFlight) return heartbeatInFlight;
296
+ connectionStatus.attempt();
297
+ let deviceHeartbeatSucceeded = false;
271
298
  heartbeatInFlight = Promise.resolve()
272
299
  .then(() =>
273
300
  heartbeatDevice({
@@ -280,12 +307,26 @@ export async function startAgent({
280
307
  }),
281
308
  )
282
309
  .then(async () => {
310
+ deviceHeartbeatSucceeded = true;
311
+ connectionStatus.success();
283
312
  const sqmResult = sqmLocalReviewWorker
284
313
  ? await sqmLocalReviewWorker.poll()
285
314
  : null;
286
315
  if (!sqmResult && durableLocalTaskWorker) await durableLocalTaskWorker.poll();
287
316
  return true;
288
317
  })
318
+ .catch((error) => {
319
+ if (!deviceHeartbeatSucceeded) {
320
+ const state = connectionStatus.failure(error);
321
+ if (state === "unauthorized") {
322
+ session = null;
323
+ loginUrl = null;
324
+ refreshAvailableCapabilities();
325
+ requestDesktopLogin();
326
+ }
327
+ }
328
+ throw error;
329
+ })
289
330
  .finally(() => {
290
331
  heartbeatInFlight = null;
291
332
  });
@@ -498,7 +539,7 @@ export async function startAgent({
498
539
  const body = await readJson(req);
499
540
  if (body.confirmed !== true) {
500
541
  json(res, 400, {
501
- error: "Explicit confirmation is required before installing the SIMY plugin, agent skills, and PR hooks.",
542
+ error: "Explicit confirmation is required before installing the SIMY plugin and opt-in agent skills.",
502
543
  });
503
544
  return;
504
545
  }
@@ -1530,14 +1571,8 @@ export async function startAgent({
1530
1571
  console.log(`SIMY Web host: ${apiOrigin}`);
1531
1572
  }
1532
1573
 
1533
- let loginUrl = null;
1534
1574
  if (!isSessionValid(session, Date.now(), apiOrigin)) {
1535
- loginUrl = new URL("/local-cli/connect", apiOrigin);
1536
- loginUrl.searchParams.set("port", String(port));
1537
- loginUrl.searchParams.set("nonce", authNonce);
1538
- loginUrl.searchParams.set("cli_version", CLI_VERSION);
1539
- loginUrl.searchParams.set("api_contract_version", String(CLI_API_CONTRACT_VERSION));
1540
- if (installMode) loginUrl.searchParams.set("install_mode", installMode);
1575
+ requestDesktopLogin();
1541
1576
  if (!quiet) {
1542
1577
  console.log(`Sign in to SIMY: ${loginUrl.toString()}`);
1543
1578
  console.log(`Session file: ${sessionPath(apiOrigin, sessionRoot)}`);
@@ -1571,6 +1606,21 @@ export async function startAgent({
1571
1606
  server.on("close", () => {
1572
1607
  void shutdown().catch((error) => reportDurableLocalTaskError(error, quiet));
1573
1608
  });
1609
+ const getDesktopStatus = async () => {
1610
+ const auth = desktopAuthStatus(session, { webOrigin: apiOrigin });
1611
+ return {
1612
+ schemaVersion: 1,
1613
+ sampledAt: new Date().toISOString(),
1614
+ version: CLI_VERSION,
1615
+ webOrigin: apiOrigin,
1616
+ auth,
1617
+ connection: connectionStatus.snapshot({ authenticated: auth.state === "signed_in" }),
1618
+ tasks: desktopTaskStatus([registry, directRegistry, localTaskRegistry]),
1619
+ update: updateManager?.snapshot?.() ?? null,
1620
+ codex: await codexStatus(),
1621
+ loginUrl: auth.state === "signed_in" ? null : requestDesktopLogin(),
1622
+ };
1623
+ };
1574
1624
  return {
1575
1625
  server,
1576
1626
  port,
@@ -1582,8 +1632,17 @@ export async function startAgent({
1582
1632
  shutdown,
1583
1633
  webOrigin: apiOrigin,
1584
1634
  get loginUrl() {
1585
- return loginUrl?.toString() ?? null;
1635
+ return requestDesktopLogin();
1636
+ },
1637
+ requestDesktopLogin,
1638
+ getDesktopStatus,
1639
+ async refreshDesktopStatus() {
1640
+ if (isSessionValid(session, Date.now(), apiOrigin)) {
1641
+ await heartbeat().catch((error) => reportHeartbeatError(error, quiet));
1642
+ }
1643
+ return getDesktopStatus();
1586
1644
  },
1645
+ refreshCodexStatus: () => codexStatus({ force: true }),
1587
1646
  workspace,
1588
1647
  repositoryScanRoot,
1589
1648
  get capabilities() {
@@ -1778,7 +1837,9 @@ export async function heartbeatLocalDevice({
1778
1837
  );
1779
1838
  if (!response.ok) {
1780
1839
  const payload = await response.json().catch(() => null);
1781
- throw new Error(webApiErrorMessage(payload, response.status));
1840
+ const error = new Error(webApiErrorMessage(payload, response.status));
1841
+ error.status = response.status;
1842
+ throw error;
1782
1843
  }
1783
1844
  }
1784
1845