@awak-app/simy-cli 0.7.36 → 0.7.37
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 +36 -36
- package/package.json +1 -1
- package/src/agent.js +1 -1
- package/src/backend-executable.js +1 -1
- package/src/cli-contract.js +1 -1
- package/src/cli-observability.js +1 -1
- package/src/codex-plugins/simy/.codex-plugin/plugin.json +5 -5
- package/src/codex-plugins/simy/hooks/README.md +3 -3
- package/src/codex-plugins/simy/hooks/delivery-loop.mjs +2 -2
- package/src/codex-plugins/simy/skills/continue/SKILL.md +4 -4
- package/src/codex-plugins/simy/skills/continue/agents/openai.yaml +3 -3
- package/src/codex-plugins/simy/skills/diagnose/SKILL.md +2 -2
- package/src/codex-plugins/simy/skills/sqm-loop/SKILL.md +3 -3
- package/src/codex-plugins/simy/skills/sqm-loop/agents/openai.yaml +2 -2
- package/src/console/app.js +7 -7
- package/src/delivery-hooks/claude-workflow.md +1 -1
- package/src/delivery-hooks/command.js +5 -5
- package/src/desktop-host.js +5 -5
- package/src/index.js +1 -1
- package/src/local-task.js +1 -1
- package/src/orchestrator/presentation.js +1 -1
- package/src/runner.js +2 -2
- package/src/session-supervision/automatic-continuation.js +11 -11
- package/src/session-supervision/continuation-analysis.js +1 -1
- package/src/session-supervision/continuation-command.js +3 -3
- package/src/session-supervision/continuation-readiness.js +1 -1
- package/src/session-supervision/conversation-presentation.js +3 -3
- package/src/session-supervision/decision-presentation.js +8 -8
- package/src/session-supervision/index.js +13 -13
- package/src/session-supervision/remote-errors.js +2 -2
- package/src/session-supervision/work-state.js +3 -3
- package/src/sqm/agent-skill.js +3 -3
- package/src/sqm/claude-plugin.js +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ This signs the desktop into SIMY; browser automation uses its own provider conne
|
|
|
8
8
|
|
|
9
9
|
Technical coding failures retain their task and retry after a persisted 5–30 minute
|
|
10
10
|
cooldown. Automatic recovery preserves the original goal, usage limits, provider
|
|
11
|
-
connection and explicit stops.
|
|
11
|
+
connection and explicit stops. Turning on Delivery Loop can also adopt an
|
|
12
12
|
ordinary loop waiting for an external dependency, starting with read-only inspection.
|
|
13
13
|
A damaged provider-link file can be repaired by **Connect SIMY** for that provider;
|
|
14
14
|
the original file is retained and the other provider stays disconnected.
|
|
@@ -36,7 +36,7 @@ for configuration, tests and remaining host integration requirements.
|
|
|
36
36
|
|
|
37
37
|
ローカルDelivery Loopの0.6.24候補を版固定で実行するには、[ローカル候補版のインストール・実行](docs/local-delivery-runtime.md)を参照してください。
|
|
38
38
|
|
|
39
|
-
Local execution agent for SIMY Local Tasks and
|
|
39
|
+
Local execution agent for SIMY Local Tasks and Delivery Loop runs.
|
|
40
40
|
|
|
41
41
|
To supervise an explicitly requested current goal after a finished Codex run, use
|
|
42
42
|
`simy delivery followup --provider codex --session SESSION_ID --previous-run-id RUN_ID --objective "Current goal and expected result"`.
|
|
@@ -75,7 +75,7 @@ segments, and keep lifetime failure counts after event rotation.
|
|
|
75
75
|
|
|
76
76
|
Each AI app card separates **SIMY connection: on / stopped** from CLI, sign-in
|
|
77
77
|
and plugin health. Disconnecting collapses those details and explains that SIMY
|
|
78
|
-
monitoring and
|
|
78
|
+
monitoring and Delivery Loop are stopped. Details can still be opened;
|
|
79
79
|
reconnecting restores the item statuses. Plugin inspection failure never changes
|
|
80
80
|
the connection setting or hides its disconnect action.
|
|
81
81
|
|
|
@@ -116,7 +116,7 @@ The server's durable recovery migration requeues the same failed job and retains
|
|
|
116
116
|
its input, history and attempt count; it is required to recover jobs previously
|
|
117
117
|
held by the server's lifetime retry cap.
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
Delivery Loop preflight accepts the task's existing requirement, acceptance
|
|
120
120
|
criteria, required checks and charter context alongside the repository/provider.
|
|
121
121
|
Its `startup` summary lists all blockers, unverified conditions and recovery
|
|
122
122
|
actions together. Environment-only calls remain supported. Task checks reuse the
|
|
@@ -135,7 +135,7 @@ remain available for history and usage warnings. The Web controller supports
|
|
|
135
135
|
connected local Codex/Claude engines; unsupported cloud execution is refused
|
|
136
136
|
before dispatch.
|
|
137
137
|
|
|
138
|
-
A completed
|
|
138
|
+
A completed Delivery Loop worker's structured result is saved privately before evidence
|
|
139
139
|
collection and the final observation flush. If the controller restarts after that save,
|
|
140
140
|
the authorized daemon recovers the same attempt and collects fresh evidence
|
|
141
141
|
without dispatching the completed worker again. Collector read failures retry up
|
|
@@ -157,14 +157,14 @@ repeated on every monitor tick; explicitly continue the same run to recover.
|
|
|
157
157
|
Older reviews without a saved CI basis can refresh only when their prior evidence
|
|
158
158
|
still establishes the complete reviewed identity; otherwise explicitly continue.
|
|
159
159
|
|
|
160
|
-
For interrupted
|
|
160
|
+
For interrupted Delivery Loop work, `GET /v1/agentic-loop/:id/recovery` provides a
|
|
161
161
|
read-only objective and checkout fingerprint. Reconcile an uncertain side effect
|
|
162
162
|
with `POST` to that endpoint (`action: reconcile`, authoritative
|
|
163
163
|
`operation_status`, `observation`, `evidence_refs`) before resuming. A paused
|
|
164
|
-
|
|
164
|
+
Delivery Loop run retains `POST /v1/agentic-loop/:id/continuation-ceilings` for an
|
|
165
165
|
explicit safety-ceiling extension under the progress policy. Token and retry
|
|
166
166
|
budgets are notification thresholds: they do not interrupt or stop useful work.
|
|
167
|
-
|
|
167
|
+
Delivery Loop checks a separate safety ceiling before each new provider call:
|
|
168
168
|
15,000,000 weighted Provider tokens and 50 executor attempts by default, using
|
|
169
169
|
existing `provider_token_ceiling` and `attempt_ceiling` settings when present.
|
|
170
170
|
The final attempt may still be reviewed if token allowance remains. Active
|
|
@@ -175,7 +175,7 @@ remaining work, explicitly extend the relevant ceiling within the existing
|
|
|
175
175
|
run. A notification-threshold increase alone cannot clear a safety stop; at the
|
|
176
176
|
maximum ceiling, further automatic calls remain blocked. Extension itself never
|
|
177
177
|
resets usage or resumes work. Local Task and managed Delivery behavior is not
|
|
178
|
-
changed by this
|
|
178
|
+
changed by this Delivery Loop safety gate. Both routes require the original authorized
|
|
179
179
|
account, organization and device throughout the request.
|
|
180
180
|
|
|
181
181
|
## Provider reconnection and saved consent
|
|
@@ -244,9 +244,9 @@ after reconnecting permits another probe, with bounded automatic cooldowns.
|
|
|
244
244
|
Conversation checks blocked by **Sign-in required** automatically recheck after
|
|
245
245
|
a fresh Codex or Claude login probe succeeds, including after an app restart.
|
|
246
246
|
An unchanged ready status cannot repeatedly re-run a failed check. Manual retry
|
|
247
|
-
remains available; continuing the conversation still requires **
|
|
247
|
+
remains available; continuing the conversation still requires **Continue automatically**.
|
|
248
248
|
|
|
249
|
-
You can
|
|
249
|
+
You can turn on **Continue automatically** while a conversation is running when its original
|
|
250
250
|
provider configuration is verified. SIMY saves the preference and waits for the
|
|
251
251
|
current turn to finish before checking remaining work. Configuration and runtime
|
|
252
252
|
blockers appear in the conversation view with recovery guidance; turning the
|
|
@@ -316,7 +316,7 @@ missing sources. Provider plugin enablement and hook trust remain provider-owned
|
|
|
316
316
|
|
|
317
317
|
For conversation continuation, select SIMY in the Codex app with `@` and ask
|
|
318
318
|
`continue this task` (Japanese: `この作業を続けて`). The `continue` skill uses the
|
|
319
|
-
running app's existing
|
|
319
|
+
running app's existing Delivery Loop switch (**Continue automatically**) and background coordinator. In
|
|
320
320
|
Codex CLI, use its native `$simy:continue` skill selector; in Claude Code use the
|
|
321
321
|
native `/simy:continue` command. Claude Code does not define `@simy` as a plugin
|
|
322
322
|
command: use the slash command for an explicit invocation. Merely mentioning
|
|
@@ -349,7 +349,7 @@ SIMY**, reopen it, and reconnect; closing the window alone does not restart the
|
|
|
349
349
|
background host. Refresh connections and confirm the conversation list returns.
|
|
350
350
|
Diagnostic `provider connection failed` entries distinguish `reason=update`,
|
|
351
351
|
`organization_change`, and `shutdown` without including credentials or transcripts.
|
|
352
|
-
Reconnection does not sign in to Codex CLI or restore revoked
|
|
352
|
+
Reconnection does not sign in to Codex CLI or restore a revoked Delivery Loop.
|
|
353
353
|
|
|
354
354
|
The plugin also includes a non-blocking connection reminder. At task startup,
|
|
355
355
|
resume, and prompt submission it reads the origin-scoped local discovery record
|
|
@@ -370,7 +370,7 @@ from the same maintained source as Codex; only provider terminology and hook/man
|
|
|
370
370
|
format differ. Installing source files does not imply that either provider has enabled
|
|
371
371
|
the plugin. Existing Claude settings, unrelated marketplaces, and plugins are preserved.
|
|
372
372
|
|
|
373
|
-
State-only feature-improvement collection ships with the generated Claude plugin, but uploads require the paired desktop CLI runtime to refresh device consent and drain its local outbox. Without that paired authenticated runtime, the plugin remains default-off and does not
|
|
373
|
+
State-only feature-improvement collection ships with the generated Claude plugin, but uploads require the paired desktop CLI runtime to refresh device consent and drain its local outbox. Without that paired authenticated runtime, the plugin remains default-off and does not turn on Delivery Loop.
|
|
374
374
|
|
|
375
375
|
After that first installation, the daemon checks installed workflows at startup
|
|
376
376
|
and every hour, including after a CLI automatic upgrade. It updates only existing
|
|
@@ -381,7 +381,7 @@ marketplace still points to SIMY’s generated local source. Plugin versions inc
|
|
|
381
381
|
receive a fresh cache. Use a new Codex task to pick up updated skills; running
|
|
382
382
|
tasks are not restarted. Missing or disabled plugins are not enabled automatically.
|
|
383
383
|
Failed refreshes retry on the next check without blocking daemon startup.
|
|
384
|
-
|
|
384
|
+
**Continue automatically** ON saves consent for the selected original conversation; it does not
|
|
385
385
|
immediately launch a second turn. A verified desktop discovery admission or an
|
|
386
386
|
existing exact admission saves this preference without another provider RPC.
|
|
387
387
|
After the current turn settles, continuation separately rechecks context, the
|
|
@@ -401,7 +401,7 @@ probes remain limited to recent conversations and explicitly retained work;
|
|
|
401
401
|
older rows remain visible with unverified execution state. The macOS work list then prioritizes verified running
|
|
402
402
|
conversations, including families with running children, followed by work needing
|
|
403
403
|
a human response and recent activity. Its All, Running, Needs attention, Completed
|
|
404
|
-
and Archived views combine with search. Tracking and
|
|
404
|
+
and Archived views combine with search. Tracking and Delivery Loop settings never
|
|
405
405
|
prove that an AI is running; observations older than 45 seconds or a failed
|
|
406
406
|
provider refresh display an unknown execution state.
|
|
407
407
|
|
|
@@ -417,7 +417,7 @@ unfinished children or open PRs keep a completed task out of the Completed view.
|
|
|
417
417
|
Codex candidates come from CLI, VS Code and app-server
|
|
418
418
|
conversation sources; non-interactive exec, unknown sources and delegated agents
|
|
419
419
|
are excluded. A hook observation alone cannot restore a filtered conversation.
|
|
420
|
-
Explicitly tracked, monitored,
|
|
420
|
+
Explicitly tracked, monitored, Delivery Loop and unsettled supervision records
|
|
421
421
|
remain visible even during a provider discovery failure. When a retained Codex conversation is
|
|
422
422
|
omitted from the recent provider page, SIMY reads its exact metadata without
|
|
423
423
|
loading turns or taking writer ownership. The same interactive source filter and
|
|
@@ -477,7 +477,7 @@ settings can differ, and project-scoped installations are labeled explicitly.
|
|
|
477
477
|
missing sources; existing sources still receive updates.
|
|
478
478
|
|
|
479
479
|
This is the recommended installation because the CLI can safely install patch
|
|
480
|
-
and minor releases, and the daemon restarts only when no
|
|
480
|
+
and minor releases, and the daemon restarts only when no Delivery Loop task is active. To run
|
|
481
481
|
the interactive chat after installation, use `simy`.
|
|
482
482
|
|
|
483
483
|
For a one-time evaluation or source development, these modes remain supported:
|
|
@@ -503,7 +503,7 @@ injected runtime context. Dated messages outside the configured lookback are
|
|
|
503
503
|
excluded even when their conversation continues today. Legacy transcripts with
|
|
504
504
|
no message timestamps use the file timestamp as a fallback. The local cursor stores only opaque hashes and
|
|
505
505
|
timestamps scoped to the connection, account, and device. Uploads require the
|
|
506
|
-
`user_conversation.v2`
|
|
506
|
+
SIMY service `user_conversation.v2` contract and exact per-chunk
|
|
507
507
|
receipts before the cursor advances. Deploy those companion changes before
|
|
508
508
|
enabling this CLI version; earlier receivers cannot acknowledge v2 history.
|
|
509
509
|
|
|
@@ -530,18 +530,18 @@ cursor is written under `$SIMY_HOME/work-pattern-history-sync/`.
|
|
|
530
530
|
|
|
531
531
|
The CLI starts a loopback HTTP agent on a random available port, keeps a
|
|
532
532
|
48-hour, origin-scoped local session, and opens a native coding chat. The first
|
|
533
|
-
chat message creates the remote audit ledger and starts the local
|
|
533
|
+
chat message creates the remote audit ledger and starts the local Delivery Loop
|
|
534
534
|
state machine; SIMY Web mirrors the lifecycle but is not required as the task
|
|
535
535
|
entry point. The CLI also accepts backend-issued launch challenges for Web-led
|
|
536
536
|
runs. It builds the requirement charter, runs Codex or Claude Code, audits
|
|
537
537
|
structured completion evidence, and re-instructs the executor while verification shows useful progress.
|
|
538
538
|
|
|
539
|
-
Before Web creates an
|
|
539
|
+
Before Web creates an Delivery Loop, it classifies whether the request actually
|
|
540
540
|
needs a managed PR, verification, evidence, and merge lifecycle. A bounded
|
|
541
541
|
ordinary task is sent to CLI 0.2.2 or newer through the one-time direct executor
|
|
542
542
|
instead. CLI independently recomputes the guardrail, restricts read-only work at
|
|
543
543
|
the Provider command boundary, and permits exactly one Provider invocation. It
|
|
544
|
-
does not create an
|
|
544
|
+
does not create an Delivery Loop ledger, audit session, or retry lifecycle for
|
|
545
545
|
that task.
|
|
546
546
|
|
|
547
547
|
## Local Tasks
|
|
@@ -570,13 +570,13 @@ process running as the same OS user.
|
|
|
570
570
|
|
|
571
571
|
|
|
572
572
|
CLI API contract v9 promotes non-coding work to a first-class **Local Task**
|
|
573
|
-
runtime. A Local Task is separate from
|
|
573
|
+
runtime. A Local Task is separate from Delivery Loop: it runs one bounded Codex
|
|
574
574
|
or Claude Code session without Git branches, pull requests, audits, or automatic
|
|
575
575
|
Provider retries. Knowledge-work deliverables use an isolated task sandbox by
|
|
576
576
|
default, so they do not require a repository.
|
|
577
577
|
|
|
578
578
|
`POST /v1/local-tasks/start` accepts JSON or the same verified multipart
|
|
579
|
-
attachment envelope used by
|
|
579
|
+
attachment envelope used by Delivery Loop and requires a stable
|
|
580
580
|
`Idempotency-Key`. Repeating the same request returns the original task; using
|
|
581
581
|
the same key for different content returns
|
|
582
582
|
`LOCAL_TASK_IDEMPOTENCY_CONFLICT`. `GET /v1/local-tasks/reconcile` with the same
|
|
@@ -728,7 +728,7 @@ Auto-update E2E screenshots are generated locally under
|
|
|
728
728
|
|
|
729
729
|
## Executor compatibility
|
|
730
730
|
|
|
731
|
-
SIMY checks the selected executor with `--version` before an
|
|
731
|
+
SIMY checks the selected executor with `--version` before an Delivery Loop can
|
|
732
732
|
start. The currently validated compatibility floors are Codex `0.144.0` and
|
|
733
733
|
Claude Code `2.1.200`. A missing, older, or unversioned executable fails the
|
|
734
734
|
preflight and returns the installed version, required version, and a recovery
|
|
@@ -850,7 +850,7 @@ permissions still require validation in the corresponding environment.
|
|
|
850
850
|
|
|
851
851
|
Normal runs do not require a separate sync command: the CLI
|
|
852
852
|
uses a verified local cache and revalidates it with TTL and ETag semantics.
|
|
853
|
-
The
|
|
853
|
+
The Delivery Loop runs min-check after each code modification and the full check
|
|
854
854
|
before PR readiness. Findings and the redacted signed-proof summary are recorded
|
|
855
855
|
in its audit; source text is never uploaded.
|
|
856
856
|
|
|
@@ -892,7 +892,7 @@ list.
|
|
|
892
892
|
|
|
893
893
|
## Interactive console
|
|
894
894
|
|
|
895
|
-
Running `simy` in a terminal opens the
|
|
895
|
+
Running `simy` in a terminal opens the Delivery Loop chat. It discovers the
|
|
896
896
|
current GitHub repository and branch, selects an available executor, and keeps
|
|
897
897
|
the composer active so a natural-language request can start immediately. Each
|
|
898
898
|
run appears in a selectable list with its lifecycle state, process ID, and a
|
|
@@ -992,7 +992,7 @@ Publishing is handled by GitHub Actions in `.github/workflows/publish.yml`.
|
|
|
992
992
|
|
|
993
993
|
### Repository system scopes
|
|
994
994
|
|
|
995
|
-
Version 0.5.7 supports signed `system_scope` snapshots from
|
|
995
|
+
Version 0.5.7 supports signed `system_scope` snapshots from SQM.
|
|
996
996
|
Each check pins the repository's connected component and graph revision. A new
|
|
997
997
|
check revalidates scoped bundles online; a stale or unavailable scope cannot fall
|
|
998
998
|
back to an older cached component. Minimum and full check proofs retain the
|
|
@@ -1039,13 +1039,13 @@ No twelve-view specification, reconstruction review, traceability matrix or new
|
|
|
1039
1039
|
review package is required. Check actual ease, clarity and accuracy using the
|
|
1040
1040
|
[UX guidance](src/orchestrator/delivery/references/ux-specification.md).
|
|
1041
1041
|
|
|
1042
|
-
## Purpose-driven
|
|
1042
|
+
## Purpose-driven Delivery Loop
|
|
1043
1043
|
|
|
1044
1044
|
The desktop **Continue automatically / 自動で続ける** switch enables purpose-driven
|
|
1045
1045
|
search: research, experiments, minimal implementation and direct outcome checks.
|
|
1046
1046
|
A separate review is used for semantic inspection or when explicitly requested. Failed methods trigger replanning; review findings return to repair.
|
|
1047
1047
|
Independent authorized work can continue while a controller-tracked operation waits.
|
|
1048
|
-
The existing Codex
|
|
1048
|
+
The existing Codex **Continue automatically** switch enables read-only diagnosis for uncertain or
|
|
1049
1049
|
information-waiting conversations, then assessed work. No default time/turn cap is
|
|
1050
1050
|
added; explicitly enrolled purpose budgets and user stops are preserved. Native
|
|
1051
1051
|
provider approval waits still require the original provider boundary to settle.
|
|
@@ -1086,7 +1086,7 @@ model responses or credentials. Historical summaries are not a replacement for
|
|
|
1086
1086
|
signed proof files or delivery acceptance. Records from versions that kept
|
|
1087
1087
|
analysis only in memory cannot be recovered after that process exits.
|
|
1088
1088
|
|
|
1089
|
-
The CLI
|
|
1089
|
+
The CLI Delivery Loop uses [proportionate retry and review decisions](docs/stop-prevention/proportionate-continuation.md): reasoning notes are advisory, and an independent reviewer may explicitly defer or dismiss minor/warning/info findings with a nonempty explanation while retaining them in the receipt. Required acceptance evidence and major safety findings remain protected.
|
|
1090
1090
|
|
|
1091
1091
|
### Local coding attempt result collection
|
|
1092
1092
|
|
|
@@ -1402,8 +1402,8 @@ Search covers the full available catalog, including older conversations. Use
|
|
|
1402
1402
|
Each row identifies its provider; identical IDs in Codex and Claude remain
|
|
1403
1403
|
separate rows. A provider failure preserves the other provider's results with
|
|
1404
1404
|
an explicit incomplete warning (`complete: false` and `warnings` in JSON).
|
|
1405
|
-
This view respects both saved provider connection settings and never
|
|
1406
|
-
|
|
1405
|
+
This view respects both saved provider connection settings and never turns on
|
|
1406
|
+
Delivery Loop. Disconnected providers are not read.
|
|
1407
1407
|
|
|
1408
1408
|
The terminal command reads this host and hosts explicitly registered in
|
|
1409
1409
|
`~/.simy/remote-hosts.json`. Use `--local-only` to skip SSH, and
|
|
@@ -1465,7 +1465,7 @@ unsupported-platform error for Claude native control.
|
|
|
1465
1465
|
|
|
1466
1466
|
Keep Claude available in an unlocked desktop while the operation runs. SIMY
|
|
1467
1467
|
opens the exact Code-tab conversation and uses Claude's own archive/restore
|
|
1468
|
-
control. Running conversations and
|
|
1468
|
+
control. Running conversations and Delivery Loop must be stopped first.
|
|
1469
1469
|
Before opening that conversation, SIMY checks Claude's visible windows for
|
|
1470
1470
|
unsent input, generating responses and dialogs. It leaves the current conversation
|
|
1471
1471
|
in place if any are present or the window/input state cannot be inspected. The
|
|
@@ -1487,7 +1487,7 @@ with `--provider codex`.
|
|
|
1487
1487
|
### Delivery entry routes
|
|
1488
1488
|
|
|
1489
1489
|
Crew owns `sessionLoopOptions` in the distributed loop kernel. Codex enrollment,
|
|
1490
|
-
Continue, follow-up, desktop controls and
|
|
1490
|
+
Continue, follow-up, desktop controls and Delivery Loop use purpose v2
|
|
1491
1491
|
by default; old false/v1 enrollment flags cannot select the retired mode.
|
|
1492
1492
|
Explicit strict/balanced policies and human acceptance criteria remain binding.
|
|
1493
1493
|
Compatible saved tasks migrate without replacing identity, history or budget.
|
|
@@ -1534,7 +1534,7 @@ occupancy without accepting the task. Long-running tasks transmit current
|
|
|
1534
1534
|
recovery context and recent work; the full history remains in durable state
|
|
1535
1535
|
and can be inspected through `delivery status`.
|
|
1536
1536
|
|
|
1537
|
-
### Reusable
|
|
1537
|
+
### Reusable Delivery Loop evaluation inputs
|
|
1538
1538
|
|
|
1539
1539
|
For the 20 versioned conversation regression cases, immutable stopping-protocol
|
|
1540
1540
|
plans, and pinned SWE-bench Pro public-task preparation, see
|
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -3126,7 +3126,7 @@ export async function startAgent({
|
|
|
3126
3126
|
request,
|
|
3127
3127
|
});
|
|
3128
3128
|
const runId = String(remoteRun?.id || "");
|
|
3129
|
-
if (!runId) throw new Error("SIMY Web did not return
|
|
3129
|
+
if (!runId) throw new Error("SIMY Web did not return a Delivery Loop run id.");
|
|
3130
3130
|
const run = createRun({ runId, request, session, apiOrigin, localStateRoot: codingRunStateRoot, sqmHistoryRoot: sessionRoot });
|
|
3131
3131
|
await saveCodingRunSnapshot(codingRunStateRoot, run.snapshot);
|
|
3132
3132
|
registry.create(run);
|
|
@@ -28,7 +28,7 @@ export async function inspectBackendExecutable(
|
|
|
28
28
|
backend,
|
|
29
29
|
{ spawnImpl = spawn, candidates, timeoutMs = 5_000, signal, discoveryOptions = {}, minimumVersion, executionEnvironment } = {},
|
|
30
30
|
) {
|
|
31
|
-
const assertActive = () => { if (signal?.aborted) throw Object.assign(new Error('
|
|
31
|
+
const assertActive = () => { if (signal?.aborted) throw Object.assign(new Error('AI provider inspection was cancelled.'), { code: 'cancelled' }); };
|
|
32
32
|
assertActive();
|
|
33
33
|
const policy = versionPolicy(backend);
|
|
34
34
|
const requiredVersion = minimumVersion || policy.minimumVersion;
|
package/src/cli-contract.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "./durable-local-task-steps-contract.js";
|
|
15
15
|
import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
|
|
16
16
|
|
|
17
|
-
export const CLI_VERSION = "0.7.
|
|
17
|
+
export const CLI_VERSION = "0.7.37";
|
|
18
18
|
export const CLI_API_CONTRACT_VERSION = 10;
|
|
19
19
|
|
|
20
20
|
export function narrativeImageCapabilityMetadata() {
|
package/src/cli-observability.js
CHANGED
|
@@ -7,7 +7,7 @@ import { execFile } from 'node:child_process';
|
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
8
|
import { webApiHeaders, webApiUrl } from './web-api.js';
|
|
9
9
|
// Keep this leaf module package-safe: hooks load it before the full CLI contract.
|
|
10
|
-
const CLI_VERSION = '0.7.
|
|
10
|
+
const CLI_VERSION = '0.7.37';
|
|
11
11
|
|
|
12
12
|
const STORE = 'cli-observability-state.json';
|
|
13
13
|
const LEGACY_CONFIG = 'cli-observability.json';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simy",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"description": "SIMY
|
|
4
|
+
"description": "SIMY Delivery Loop, SQM checks and PR review through the original conversation's existing controls.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Specialist Doctors Inc.",
|
|
7
7
|
"url": "https://github.com/SpecialistDoctors-Inc"
|
|
@@ -17,17 +17,17 @@
|
|
|
17
17
|
"interface": {
|
|
18
18
|
"displayName": "SIMY",
|
|
19
19
|
"shortDescription": "Continue, check and review.",
|
|
20
|
-
"longDescription": "Ask SIMY to continue unfinished work in the original conversation through the running CLI app's existing
|
|
20
|
+
"longDescription": "Ask SIMY to continue unfinished work in the original conversation through the running CLI app's existing Delivery Loop controller (Continue automatically), or request SQM checks and PR reviews. Read the same real work state and next action as the app. Saved delegation is not execution proof. Stops, permission waits and opt-outs are preserved. Retired local delivery agents stay retired.",
|
|
21
21
|
"developerName": "Specialist Doctors Inc.",
|
|
22
22
|
"category": "Developer Tools",
|
|
23
23
|
"capabilities": [
|
|
24
|
-
"
|
|
24
|
+
"Delivery Loop",
|
|
25
25
|
"Quality",
|
|
26
26
|
"Review"
|
|
27
27
|
],
|
|
28
28
|
"defaultPrompt": [
|
|
29
|
-
"
|
|
30
|
-
"Run
|
|
29
|
+
"Continue this conversation automatically with SIMY and show its actual progress.",
|
|
30
|
+
"Run a SIMY SQM check for the current thread.",
|
|
31
31
|
"Review the current pull request with SIMY."
|
|
32
32
|
],
|
|
33
33
|
"composerIcon": "./assets/logo.png",
|
|
@@ -27,7 +27,7 @@ including when SessionStart was missed. No per-project enable command is needed.
|
|
|
27
27
|
preserve it. Existing loops, explicit stops, session opt-outs and revoked provider
|
|
28
28
|
consent remain authoritative. No hook grants provider trust. Stop checks that
|
|
29
29
|
session's checkpoint and returns bounded next-step instructions. Approval/input
|
|
30
|
-
blocks, interruption, completion, failures and missing progress stop
|
|
30
|
+
blocks, interruption, completion, failures and missing progress stop Delivery Loop.
|
|
31
31
|
Provider errors/exits are surfaced to SIMY's desktop; a dead provider is not
|
|
32
32
|
silently restarted. No hook writes trust, permission settings, or SQM evidence.
|
|
33
33
|
|
|
@@ -35,7 +35,7 @@ Private state is under `SIMY_HOME/delivery-hooks` (default `~/.simy/delivery-hoo
|
|
|
35
35
|
keyed by provider and session ID and bound to the canonical working directory.
|
|
36
36
|
Historical hook observations do not prove hooks are still enabled. See the
|
|
37
37
|
CLI delivery command documentation for checkpoint commands and explicit recovery.
|
|
38
|
-
The local Codex delivery-loop skill is retired; request the CLI
|
|
38
|
+
The local Codex delivery-loop skill is retired; request the CLI Delivery Loop
|
|
39
39
|
explicitly for new delivery work. Existing lifecycle hooks do not start it.
|
|
40
40
|
|
|
41
41
|
SIMY does not limit a task's elapsed time, number of turns or provider tokens.
|
|
@@ -47,7 +47,7 @@ criteria cannot manufacture progress, and activity alone does not prove it.
|
|
|
47
47
|
Required approvals, manual stops and unreconciled effects still block dispatch.
|
|
48
48
|
|
|
49
49
|
Context-first tasks without host-checkable acceptance pause rather than fail at
|
|
50
|
-
that limit, so
|
|
50
|
+
that limit, so Delivery Loop stays available after review. An explicit resume,
|
|
51
51
|
answer or approval resets both no-progress attempt counters and the progress clock,
|
|
52
52
|
even before a threshold was reached. Failure counts and verified criterion history
|
|
53
53
|
remain; reusing old evidence cannot count as fresh progress.
|
|
@@ -996,7 +996,7 @@ function hookDeliveryContext(record, automatic = false) {
|
|
|
996
996
|
const identity = `SIMY connected to this ${provider} conversation: ${session}.`;
|
|
997
997
|
if (!run && automatic && !record.automaticStartBlocked && !record.providerArchived && !record.parentSession && !(record.autoContinue === false && record.autoContinueEpoch)) return `${identity} Automatic Delivery Loop start is enabled for this workspace. The next nonempty user prompt starts checkpoint tracking in this conversation; do not issue a duplicate start. Provider permissions and user stops remain authoritative.`;
|
|
998
998
|
if (!run) return `${identity} Delivery Loop has not started. When the user requests it, use simy delivery start --provider ${provider} --session ${session} with the agreed objective and next step. For future conversations in this workspace, the user can enable automatic start once with simy delivery auto on --provider ${provider}. Never enable, trust, resume, or reset a loop without the user's request.`;
|
|
999
|
-
return `${identity} Existing
|
|
999
|
+
return `${identity} Existing Delivery Loop: ${run.state}. ${deliveryAcceptanceContext(run)} ` +
|
|
1000
1000
|
(['active', 'reported_done', 'completed'].includes(run.state) ? `${correctiveFollowupInstruction} ` : '') +
|
|
1001
1001
|
(provider === 'codex' && ['completed', 'reported_done'].includes(run.state) ? `Finished run ID: ${run.id}. Its completion does not supervise later user requests. If the user explicitly requests supervision of the current goal, use simy delivery followup --provider codex --session ${session} --previous-run-id ${run.id} --objective CURRENT_GOAL. A different goal archives the old run; the same ordinary reported_done goal retains identity and budgets for read-only reconciliation. A new prompt alone is not consent to run this command. ` : '') +
|
|
1002
1002
|
(run.state === 'active' && !run.managed ? `Before ending the turn, submit an honest JSON checkpoint with simy delivery checkpoint --provider ${provider} --session ${session} --file -. Use status in_progress with summary, remainingWork and nextStep for remaining work; completed with summary, acceptanceMet:true and evidence for finished work; or blocked with summary and blocker missing_information or authorization. The first completed checkpoint requests an outcome check in this conversation; inspect the actual result and submit a fresh checkpoint. If requested work remains, perform the first authorized step instead of ending with self-evaluation or a plan. Completion is provider-reported, not independently verified. ${informationWaitInstruction} ` : '') +
|
|
@@ -1051,7 +1051,7 @@ export async function startDelivery({ root = rootDirectory(), provider, session,
|
|
|
1051
1051
|
if ((following || reconciling) && (provider !== 'codex' || !admitted || !managedState?.autoRecovery || managedState.capability !== 'codex_app_server' || managedState.initialAction.kind !== 'read' || managedState.contextBootstrap)) throw new Error('Follow-up requires exact-session managed supervision, a concrete goal and read-only reconciliation.');
|
|
1052
1052
|
if (upgrading && (!managedState?.autonomyPolicy || managedState.initialAction.kind !== 'read' || managedState.contextBootstrap)) throw new Error('Upgrade requires read-only reconciliation and frozen acceptance of the original objective.');
|
|
1053
1053
|
if (!managedState && continuationMode(continuationPolicy) === 'progress') throw new Error('Progress continuation requires frozen managed acceptance criteria.');
|
|
1054
|
-
if (followupAutoContinue && !(following || reconciling)) throw new Error('
|
|
1054
|
+
if (followupAutoContinue && !(following || reconciling)) throw new Error('Delivery Loop follow-up requires the exact finished run.');
|
|
1055
1055
|
if (autoContinueEpoch && managedState) managedState.autoContinueEpoch = autoContinueEpoch;
|
|
1056
1056
|
if (managedState && previous.associationTaskId) {
|
|
1057
1057
|
managedState.taskId = previous.associationTaskId;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: continue
|
|
3
|
-
description:
|
|
3
|
+
description: Turn on SIMY Delivery Loop (Continue automatically) for the original conversation when the user explicitly asks SIMY or @simy to take over or continue unfinished work. Also read its real execution status or turn Delivery Loop off for this conversation. This is a controller entry, not an independent delivery agent or an SQM check.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Continue the original conversation with SIMY
|
|
@@ -32,8 +32,8 @@ existing conversation reference before changing anything.
|
|
|
32
32
|
Use this command on the original local host. Preserve a remote selection's host
|
|
33
33
|
and use its existing SIMY remote controls instead of treating it as local.
|
|
34
34
|
|
|
35
|
-
This command delegates to the running SIMY app's existing
|
|
36
|
-
and background coordinator. It preserves the original goal, task history,
|
|
35
|
+
This command delegates to the running SIMY app's existing Delivery Loop control
|
|
36
|
+
(the Continue automatically switch) and background coordinator. It preserves the original goal, task history,
|
|
37
37
|
corrections, permissions, failure budgets and approvals. It does not start a
|
|
38
38
|
second provider process, create a replacement task, change accounts or models,
|
|
39
39
|
trust hooks, or grant native permissions. The original current turn continues
|
|
@@ -51,7 +51,7 @@ result and acceptance. Continue already-authorized work in this conversation.
|
|
|
51
51
|
To inspect again, use the same exact target with `auto-continue status` in place
|
|
52
52
|
of `auto-continue on`. If the response was lost or status reading failed after
|
|
53
53
|
saving, query status; never automatically repeat a mutation to infer success.
|
|
54
|
-
When asked to turn
|
|
54
|
+
When asked to turn Delivery Loop off, use `auto-continue off` for the
|
|
55
55
|
same target. OFF preserves the current native turn; an explicit stop uses the
|
|
56
56
|
existing task Stop control. A stopped, rejected, archived, settled, disconnected
|
|
57
57
|
or unavailable task stays within its existing boundary: report the returned
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
interface:
|
|
2
|
-
display_name: "SIMY
|
|
3
|
-
short_description: "
|
|
4
|
-
default_prompt: "Use $simy:continue to
|
|
2
|
+
display_name: "SIMY Continue automatically"
|
|
3
|
+
short_description: "Turn on Delivery Loop for this conversation"
|
|
4
|
+
default_prompt: "Use $simy:continue to turn on SIMY Delivery Loop for this conversation and report its actual work state."
|
|
5
5
|
policy:
|
|
6
6
|
allow_implicit_invocation: true
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: diagnose
|
|
3
|
-
description:
|
|
3
|
+
description: Check why it stopped (停止理由の確認) for a stopped or unresponsive SIMY task when the user says “SIMY CLIに相談して” or asks why it stopped. Distinguish a read-only check from an explicit request to repair and continue the original work. Environment diagnosis is `simy doctor`, not this entry.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Check why it stopped (停止理由の確認)
|
|
7
7
|
|
|
8
8
|
Use this entry for an explicit consultation, diagnosis, or repair-and-continue request, independently of Delivery Loop enrollment or permission to resume. Prefer `$SIMY_HOME/bin/simy` or `$HOME/.simy/bin/simy`. Inspect `--version` and `delivery --help` to confirm `consult` is available. If only `diagnose` exists, use the legacy command below for diagnosis and report repair as unavailable; never reinterpret a failed consult invocation as execution. An older installed CLI is a compatibility gap, never permission to switch its live runtime.
|
|
9
9
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sqm-loop
|
|
3
|
-
description: Run
|
|
3
|
+
description: Run a SIMY SQM check on the repository and changes discussed in the current Codex thread, on request. Use when the user asks SIMY to start SQM or quality-check the current work now. The opted-in pre-PR gate is the separate simy-sqm-pr-check skill.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# SIMY SQM
|
|
6
|
+
# SIMY SQM check
|
|
7
7
|
|
|
8
8
|
Treat the current Codex thread, its attachments, and the active repository as the request context. Convert that context into explicit quality expectations, then verify the actual repository state; do not merely summarize the conversation.
|
|
9
9
|
|
|
@@ -22,7 +22,7 @@ Investigate and repair authorized reversible setup; preserve real account,
|
|
|
22
22
|
organization, device and provider authority. Do not guess credentials or invent a
|
|
23
23
|
successful scope. A connected scope is not quality acceptance.
|
|
24
24
|
|
|
25
|
-
## Run the
|
|
25
|
+
## Run the SQM check
|
|
26
26
|
|
|
27
27
|
1. Identify the repository root and the user-visible behavior or incident discussed in the current thread.
|
|
28
28
|
2. Preserve unrelated user changes and follow repository instructions.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
interface:
|
|
2
|
-
display_name: "SIMY SQM
|
|
3
|
-
short_description: "Run SQM
|
|
2
|
+
display_name: "SIMY SQM check"
|
|
3
|
+
short_description: "Run an SQM check on the current work"
|
|
4
4
|
default_prompt: "Use $sqm-loop to quality-check the work in this thread."
|
|
5
5
|
policy:
|
|
6
6
|
allow_implicit_invocation: true
|
package/src/console/app.js
CHANGED
|
@@ -176,7 +176,7 @@ export function AgenticLoopConsole({ agent, onQuit = () => {}, remoteConnections
|
|
|
176
176
|
}
|
|
177
177
|
if (!selectedRun) {
|
|
178
178
|
if (command.type !== "guidance") {
|
|
179
|
-
setNotice("That command requires a selected
|
|
179
|
+
setNotice("That command requires a selected Delivery Loop run.");
|
|
180
180
|
return;
|
|
181
181
|
}
|
|
182
182
|
setBusy(true);
|
|
@@ -213,12 +213,12 @@ export function AgenticLoopConsole({ agent, onQuit = () => {}, remoteConnections
|
|
|
213
213
|
setNotice("Guidance queued for the next executor handoff.");
|
|
214
214
|
} else {
|
|
215
215
|
await agent.controls.continue(selectedRun, command.message);
|
|
216
|
-
setNotice("Human guidance accepted. The
|
|
216
|
+
setNotice("Human guidance accepted. The Delivery Loop is continuing.");
|
|
217
217
|
}
|
|
218
218
|
break;
|
|
219
219
|
case "decision":
|
|
220
220
|
await agent.controls.applyDecision(selectedRun, command);
|
|
221
|
-
setNotice("Human decision recorded. The
|
|
221
|
+
setNotice("Human decision recorded. The Delivery Loop is continuing.");
|
|
222
222
|
break;
|
|
223
223
|
case "recheck":
|
|
224
224
|
await agent.controls.recheck(selectedRun);
|
|
@@ -297,7 +297,7 @@ export function AgenticLoopConsole({ agent, onQuit = () => {}, remoteConnections
|
|
|
297
297
|
await agent.controls.selectRepository(selectedRun, match);
|
|
298
298
|
setDetailOpen(true);
|
|
299
299
|
setDetailScrollFromEnd(0);
|
|
300
|
-
setNotice(`Authorized ${match.repository}. The same
|
|
300
|
+
setNotice(`Authorized ${match.repository}. The same Delivery Loop run is continuing.`);
|
|
301
301
|
} else if (selectedRun) {
|
|
302
302
|
setNotice(
|
|
303
303
|
`${selectedRun.request.repository} was not found under ${result.root}. Run /scan <path> for another authorized root.`,
|
|
@@ -328,7 +328,7 @@ export function AgenticLoopConsole({ agent, onQuit = () => {}, remoteConnections
|
|
|
328
328
|
setRepositoryPicker(null);
|
|
329
329
|
setDetailOpen(true);
|
|
330
330
|
setDetailScrollFromEnd(0);
|
|
331
|
-
setNotice(`Authorized ${selected.repository}. The same
|
|
331
|
+
setNotice(`Authorized ${selected.repository}. The same Delivery Loop run is continuing.`);
|
|
332
332
|
} else {
|
|
333
333
|
const verified = await agent.controls.selectRepository(null, selected);
|
|
334
334
|
setDraft((current) => ({
|
|
@@ -510,7 +510,7 @@ export function AgenticLoopConsole({ agent, onQuit = () => {}, remoteConnections
|
|
|
510
510
|
if (confirmStop) void execute("/stop");
|
|
511
511
|
else {
|
|
512
512
|
setConfirmStop(true);
|
|
513
|
-
setNotice("Press x again to stop the selected
|
|
513
|
+
setNotice("Press x again to stop the selected Delivery Loop run.");
|
|
514
514
|
}
|
|
515
515
|
return;
|
|
516
516
|
}
|
|
@@ -641,7 +641,7 @@ function NewTask({ draft, loginUrl, width }) {
|
|
|
641
641
|
dimColor: true,
|
|
642
642
|
wrap: "wrap",
|
|
643
643
|
children:
|
|
644
|
-
"Describe the outcome. SIMY will choose a one-time Local Task or
|
|
644
|
+
"Describe the outcome. SIMY will choose a one-time Local Task or a Delivery Loop.",
|
|
645
645
|
}),
|
|
646
646
|
jsx(Text, { children: " " }),
|
|
647
647
|
jsx(Text, {
|
|
@@ -44,7 +44,7 @@ resume or reset a task. Prompt hooks enroll eligible conversations under CLI aut
|
|
|
44
44
|
Use when the user requests this skill or SIMY Delivery Loop, or current hook context confirms automatic enrollment. Preserve that existing loop and proceed to checkpoints without starting again. Installing this plugin does not opt a task in. Do not run SQM unless separately requested.
|
|
45
45
|
|
|
46
46
|
1. Resolve the exact original provider and session ID from the SIMY hook context or verified existing session admission. If no valid connection exists, report the observed missing capability and the smallest confirmed connection step. Never invent a session ID, change hook trust, bypass permissions, or replace the conversation. Codex settings can be opened at `codex://settings`; a hooks subpage deep link is not guaranteed.
|
|
47
|
-
2. Only start or resume a Delivery Loop when the user requests it. For a new regular Loop, use `simy delivery start --provider <provider> --session <exact-session-id> --objective '<user outcome and acceptance criteria>' --next-step '<concrete next action>'`. For a Loop that must track acceptance, use the existing managed specification with `--managed-file <task.json>` as described below: freeze the original user outcome, selected delivery level when one exists, and necessary applicable acceptance criteria before work. A regular start can only end as `reported_done`; do not present it as a fully verified
|
|
47
|
+
2. Only start or resume a Delivery Loop when the user requests it. For a new regular Loop, use `simy delivery start --provider <provider> --session <exact-session-id> --objective '<user outcome and acceptance criteria>' --next-step '<concrete next action>'`. For a Loop that must track acceptance, use the existing managed specification with `--managed-file <task.json>` as described below: freeze the original user outcome, selected delivery level when one exists, and necessary applicable acceptance criteria before work. A regular start can only end as `reported_done`; do not present it as a fully verified managed Delivery Loop. A verified original `codex_app_server` admission and existing user continuation delegation can use `simy delivery continue --provider codex --session <exact-original-session-id>` without SessionStart when the user requests continuation. Preserve the original objective and acceptance. Never start another loop to reset limits or resume user-stopped work without a user request. Authorized automatic recovery from ordinary blockers is distinct from resuming user-stopped work.
|
|
48
48
|
|
|
49
49
|
3. Work in the current conversation with its normal permissions. Before ending each turn, submit a JSON checkpoint through standard input with `simy delivery checkpoint --provider <provider> --session <id> --file -` (for example, a shell heredoc). This updates SIMY state; it does not require writing a checkpoint file to the user's workspace. A file path remains supported when authorized.
|
|
50
50
|
|
|
@@ -225,18 +225,18 @@ export async function runDeliveryCommand(argv, { root = rootDirectory(), cwd = p
|
|
|
225
225
|
}
|
|
226
226
|
if (action === 'activity') return runActivityCommand(args, { root, output });
|
|
227
227
|
if (!action || action === '--help') {
|
|
228
|
-
output('simy delivery auto-continue on|off|status --provider codex|claude --session UUID —
|
|
229
|
-
output('simy delivery observability status — show the state-only telemetry status for this device. The single default-OFF consent checkbox is in SIMY settings; disabling it stops uploads and discards pending telemetry. It never
|
|
228
|
+
output('simy delivery auto-continue on|off|status --provider codex|claude --session UUID — Continue this conversation automatically: turns Delivery Loop on or off for one exact conversation through the running app’s existing control, and reads its actual work state.');
|
|
229
|
+
output('simy delivery observability status — show the state-only telemetry status for this device. The single default-OFF consent checkbox is in SIMY settings; disabling it stops uploads and discards pending telemetry. It never turns on Delivery Loop or starts a task.');
|
|
230
230
|
output('simy delivery followup --provider codex --session ID --previous-run-id ID --objective TEXT — explicitly supervise the current goal after an exact finished run. A new goal archives the prior run; the same ordinary reported_done goal retains its identity and budgets for read-only reconciliation. Uses the existing supervisor; never reopens stopped, failed or budget-exhausted work.');
|
|
231
|
-
output('simy delivery auto on|off|status --provider codex|claude — inspect or override
|
|
231
|
+
output('simy delivery auto on|off|status --provider codex|claude — Start Delivery Loop automatically in this workspace: inspect or override automatic start for future conversations in this project directory (on by default). The next nonempty prompt in a conversation without a Delivery Loop starts checkpoint tracking. Existing loops are preserved; provider hooks must already be enabled.');
|
|
232
232
|
output('simy delivery redelegate-native --provider codex --session ID --task-id ID --consent-ref REF — explicitly delegate current native local edit/test permissions to an exact legacy task. Preserves enrollment, history, budgets and stops; does not resume or grant external actions.');
|
|
233
233
|
output('simy delivery runtime install|status|workflows — install an immutable local candidate, inspect its version/digest, or refresh existing plugin sources/cache while preserving enabled state and hook trust. Never enables a task.');
|
|
234
234
|
output('simy delivery runtime doctor|desktop-install|desktop-activate|watch — audit live host/state/monitor alignment, or prepare a separate local desktop using the verified fixed runtime. Does not start or resume tasks.');
|
|
235
235
|
output('simy delivery runtime policy-adopt [--task-id ID [--criterion-id ID]] — explicitly delegate decisions within existing permissions. Manual acceptance stays manual unless the user names the exact task and criterion to delegate its verifier.');
|
|
236
236
|
output('simy delivery link_parent --provider codex|claude --session CHILD --parent-provider codex|claude --parent-session PARENT — record an explicit observed navigation relationship without starting work.');
|
|
237
237
|
output('simy delivery supervise [--once] — service already opted-in tasks with this runtime; refuses an active desktop host.');
|
|
238
|
-
output('simy delivery consult [--scope diagnose|repair_continue] [--request-id USER_TURN_ID] [--context-file context.json] [--provider codex|claude --session ID | --task-id ID] —
|
|
239
|
-
output('simy delivery diagnose --provider codex|claude --session ID [--task-id ID] [--mode analyze|evidence] [--issues-file issues.json] [--detail brief|full] — advisory
|
|
238
|
+
output('simy delivery consult [--scope diagnose|repair_continue] [--request-id USER_TURN_ID] [--context-file context.json] [--provider codex|claude --session ID | --task-id ID] — diagnose scope: check why it stopped, using the observed current conversation or selected original work; repair_continue scope never implies completion.');
|
|
239
|
+
output('simy delivery diagnose --provider codex|claude --session ID [--task-id ID] [--mode analyze|evidence] [--issues-file issues.json] [--detail brief|full] — advisory check of why it stopped, in a separate process; never resumes the target. Exact --task-id alone is also supported.');
|
|
240
240
|
output('simy delivery evidence --provider codex|claude --session ID — read-only execution and acceptance receipts.');
|
|
241
241
|
output('simy delivery activity [--watch] [--json] [--provider codex|claude] [--session ID] — local observations every 60 seconds; not productive time.');
|
|
242
242
|
output('A task stops after repeated failures, and pauses for review when no progress is confirmed. Resume starts a new progress window.');
|
package/src/desktop-host.js
CHANGED
|
@@ -118,9 +118,9 @@ function desktopActionError(action, error) {
|
|
|
118
118
|
const messages = {
|
|
119
119
|
session_workspace_changed: "The conversation workspace changed. Refresh and select it again.",
|
|
120
120
|
session_metadata_unavailable: "Conversation details are unavailable. Refresh this provider and select the conversation again.",
|
|
121
|
-
session_model_unavailable: "This provider did not expose the conversation model;
|
|
121
|
+
session_model_unavailable: "This provider did not expose the conversation model; Delivery Loop is unavailable.",
|
|
122
122
|
existing_loop: "This conversation already has a Delivery Loop. Open its existing supervision instead.",
|
|
123
|
-
task_settled: "This task has finished. Start a new conversation before
|
|
123
|
+
task_settled: "This task has finished. Start a new conversation before turning on Delivery Loop.",
|
|
124
124
|
};
|
|
125
125
|
if (messages[error?.code]) return messages[error.code];
|
|
126
126
|
}
|
|
@@ -2011,7 +2011,7 @@ export async function startDesktopHost({ input = process.stdin, output = process
|
|
|
2011
2011
|
await authorizeWork(commandScope);
|
|
2012
2012
|
await actionAssociations.correct(command, analysisManager.project(candidate));
|
|
2013
2013
|
} else if (action === "session_set_auto_continue") {
|
|
2014
|
-
if (typeof command.enabled !== 'boolean') throw new Error('Choose whether
|
|
2014
|
+
if (typeof command.enabled !== 'boolean') throw new Error('Choose whether Delivery Loop is on.');
|
|
2015
2015
|
let admission, target, providerEpoch;
|
|
2016
2016
|
if (command.enabled) {
|
|
2017
2017
|
const candidate = await selectedSession(command, { preferObserved: true });
|
|
@@ -2204,7 +2204,7 @@ export async function startDesktopHost({ input = process.stdin, output = process
|
|
|
2204
2204
|
if (fresh.cwd !== candidate.cwd) throw new Error('The conversation workspace changed. Refresh and retry.');
|
|
2205
2205
|
const record = await readRecord(deliveryRoot, 'claude', command.session);
|
|
2206
2206
|
if (fresh.isRunning || fresh.autoContinue || record?.autoContinue || record?.run && !TERMINAL.has(record.run.state))
|
|
2207
|
-
throw Object.assign(new Error('Stop this conversation and turn off
|
|
2207
|
+
throw Object.assign(new Error('Stop this conversation and turn off Delivery Loop before organizing it.'), { code: 'claude_desktop_running' });
|
|
2208
2208
|
};
|
|
2209
2209
|
await verifySelection();
|
|
2210
2210
|
const operation = new AbortController();
|
|
@@ -2380,7 +2380,7 @@ function sessionActionError(error, action) {
|
|
|
2380
2380
|
if (error instanceof AutoContinueUnavailableError) return error.message.replace(/[\r\n\x00-\x1f]/g, ' ').slice(0, 600);
|
|
2381
2381
|
const message = String(error?.message || "");
|
|
2382
2382
|
if (/already has a Delivery Loop|budgets cannot be reset/i.test(message)) return "This conversation already has a task. Open its existing supervision instead.";
|
|
2383
|
-
if (/model.*unavailable|did not expose.*model/i.test(message)) return "This provider did not expose the conversation model;
|
|
2383
|
+
if (/model.*unavailable|did not expose.*model/i.test(message)) return "This provider did not expose the conversation model; Delivery Loop is unavailable.";
|
|
2384
2384
|
if (/workspace changed|metadata.*unavailable|session.*changed/i.test(message)) return "The conversation identity or workspace changed. Refresh and select it again.";
|
|
2385
2385
|
if (/objective|acceptance|autonomy policy|budget/i.test(message)) return message.replace(/[\r\n\x00-\x1f]/g, " ").slice(0, 240);
|
|
2386
2386
|
if (/question changed|answer is empty|session understanding|context/i.test(message)) return message.replace(/[\r\n\x00-\x1f]/g, " ").slice(0, 240);
|
package/src/index.js
CHANGED
|
@@ -161,7 +161,7 @@ async function runCommand(installMode) {
|
|
|
161
161
|
Options:
|
|
162
162
|
--version, -v Print the installed CLI version and exit
|
|
163
163
|
--daemon, --deamon Run detached in the background
|
|
164
|
-
--no-tui Disable the interactive
|
|
164
|
+
--no-tui Disable the interactive Delivery Loop console
|
|
165
165
|
--no-open Do not open the browser when authorization is required
|
|
166
166
|
--port <port> Bind a specific localhost port
|
|
167
167
|
--host <url> Connect to a SIMY Web origin (default: ${DEFAULT_WEB_ORIGIN})
|
package/src/local-task.js
CHANGED
|
@@ -1257,7 +1257,7 @@ function localTaskProviderInstruction(task) {
|
|
|
1257
1257
|
? "The managed output directory is reserved for final user-requested deliverables. Keep helper scripts, temporary files, caches, and intermediate files outside it (use the task workspace or an OS temporary directory), and remove those helper files before completion."
|
|
1258
1258
|
: null;
|
|
1259
1259
|
return [
|
|
1260
|
-
"You are the local executor for a SIMY Local Task, not
|
|
1260
|
+
"You are the local executor for a SIMY Local Task, not a Delivery Loop.",
|
|
1261
1261
|
buildWorkflowReportingInstruction(),
|
|
1262
1262
|
"Complete this bounded task once. Do not create Git branches, commits, pull requests, audits, or automatic retries.",
|
|
1263
1263
|
`Permission mode: ${task.permission_mode}.`,
|
|
@@ -117,7 +117,7 @@ export function summarizeCodingLoopEvent(snapshot, event) {
|
|
|
117
117
|
};
|
|
118
118
|
case "stopped":
|
|
119
119
|
return {
|
|
120
|
-
summary: "The local human operator stopped this
|
|
120
|
+
summary: "The local human operator stopped this Delivery Loop run.",
|
|
121
121
|
result: "No further executor handoff will occur for this run.",
|
|
122
122
|
next: "Start a new run or explicitly continue this one when ready.",
|
|
123
123
|
};
|
package/src/runner.js
CHANGED
|
@@ -452,7 +452,7 @@ export async function recoverLocalCodingRun(run, dependencies = {}, now = Date.n
|
|
|
452
452
|
}
|
|
453
453
|
|
|
454
454
|
function assertRunCanContinue(run) {
|
|
455
|
-
if (run.operation || run.child) throw new Error("The selected
|
|
455
|
+
if (run.operation || run.child) throw new Error("The selected Delivery Loop run is still active.");
|
|
456
456
|
if (!RESUMABLE_STATES.has(run.status)) {
|
|
457
457
|
throw new Error("Human guidance is available only when the selected run is waiting.");
|
|
458
458
|
}
|
|
@@ -542,7 +542,7 @@ export async function continueLocalCodingRunAfterRepositoryApproval(
|
|
|
542
542
|
if (!approvedRepository || !approvedPath) {
|
|
543
543
|
throw new Error("An approved local repository and path are required.");
|
|
544
544
|
}
|
|
545
|
-
if (run.operation || run.child) throw new Error("The selected
|
|
545
|
+
if (run.operation || run.child) throw new Error("The selected Delivery Loop run is still active.");
|
|
546
546
|
if (run.status !== "waiting_human") {
|
|
547
547
|
throw new Error("Repository approval is available only while the run is waiting.");
|
|
548
548
|
}
|
|
@@ -6,15 +6,15 @@ function continuationFailureReason(error) {
|
|
|
6
6
|
// Provider exceptions can contain private paths/output. Only known codes
|
|
7
7
|
// select actionable UI copy; unrecognized failures retain a safe fallback.
|
|
8
8
|
const reasons = {
|
|
9
|
-
desktop_access_required: 'Sign in to an organization with SQM and Developer access before
|
|
10
|
-
provider_sign_in_required: 'Sign in to the original AI provider, then retry
|
|
11
|
-
unavailable: 'The local provider is unavailable. Open or configure it, then retry
|
|
12
|
-
unsupported: 'Update the original AI provider to a compatible version, then retry
|
|
9
|
+
desktop_access_required: 'Sign in to an organization with SQM and Developer access before Delivery Loop can run.',
|
|
10
|
+
provider_sign_in_required: 'Sign in to the original AI provider, then retry Delivery Loop.',
|
|
11
|
+
unavailable: 'The local provider is unavailable. Open or configure it, then retry Delivery Loop.',
|
|
12
|
+
unsupported: 'Update the original AI provider to a compatible version, then retry Delivery Loop.',
|
|
13
13
|
process_exited: 'The original Claude process has exited. Return to the original conversation, then refresh.',
|
|
14
14
|
session_ended: 'This Claude session has ended. Return to the original conversation, then refresh.',
|
|
15
15
|
hook_missing: 'Waiting for the original Claude hook. SIMY recovers automatically when the next verified prompt or response-end hook arrives. If no notifications arrive, check the SIMY plugin in the original Claude window.',
|
|
16
16
|
};
|
|
17
|
-
return Object.hasOwn(reasons, error?.code) ? reasons[error.code] : 'Could not start
|
|
17
|
+
return Object.hasOwn(reasons, error?.code) ? reasons[error.code] : 'Could not start Delivery Loop. Check the original conversation and retry.';
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// Shared by explicit Continue and automatic enrollment. Analysis alone never
|
|
@@ -24,7 +24,7 @@ export async function continueAnalyzedSession({ candidate, manager, root, settin
|
|
|
24
24
|
const link = await requireProviderLink({ root, provider: candidate.provider, expectedEpoch: settings.expectedProviderEpoch });
|
|
25
25
|
const recommendationOptions = { allowDiagnosis: settings.purposeDriven === true && candidate.autoContinue === true };
|
|
26
26
|
const recommendation = manager.requireRecommendation(candidate, recommendationOptions);
|
|
27
|
-
if (candidate.isRunning || candidate.capabilities?.canContinue !== true) throw new Error('
|
|
27
|
+
if (candidate.isRunning || candidate.capabilities?.canContinue !== true) throw new Error('Delivery Loop is unavailable for this conversation.');
|
|
28
28
|
const context = await readContext(candidate, { signal });
|
|
29
29
|
if (context.awaitingTurn || context.incomplete || !recommendation.contextDigest || context.contextDigest !== recommendation.contextDigest) {
|
|
30
30
|
await manager.invalidate(candidate);
|
|
@@ -34,7 +34,7 @@ export async function continueAnalyzedSession({ candidate, manager, root, settin
|
|
|
34
34
|
await beforeEnroll(candidate);
|
|
35
35
|
await requireProviderLink({ root, provider: candidate.provider, expectedEpoch: link.epoch });
|
|
36
36
|
manager.requireRecommendation(candidate, recommendationOptions);
|
|
37
|
-
if (signal?.aborted) throw new Error('
|
|
37
|
+
if (signal?.aborted) throw new Error('Delivery Loop was cancelled before enrollment.');
|
|
38
38
|
return enroll({ root, provider: candidate.provider, session: candidate.session, ...settings, expectedProviderEpoch: link.epoch });
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -46,7 +46,7 @@ function startingPresentation(candidate, status, reason) {
|
|
|
46
46
|
const canPresent = work && !work.needsAttention && !['running', 'stopped', 'archived', 'approval', 'question'].includes(work.state);
|
|
47
47
|
return { ...candidate, autoContinueStatus: status, autoContinueReason: reason,
|
|
48
48
|
...(canPresent ? { workState: { ...work, state: status,
|
|
49
|
-
label: status === 'starting' ? '
|
|
49
|
+
label: status === 'starting' ? 'Delivery Loop を準備中' : '自動復旧中', reason,
|
|
50
50
|
nextAction: null, needsAttention: false } } : {}) };
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -133,7 +133,7 @@ export function createAutomaticContinuation({ root, getManager, selectCandidate,
|
|
|
133
133
|
retries.set(key, { attempt, count, at: now() + Math.min(60_000, 1000 * 2 ** Math.min(count - 1, 6)) });
|
|
134
134
|
} else retries.delete(key);
|
|
135
135
|
errors.set(key, { epoch: candidate.autoContinueEpoch, recoverable,
|
|
136
|
-
reason: recoverable ? 'The AI connection is temporarily unavailable.
|
|
136
|
+
reason: recoverable ? 'The AI connection is temporarily unavailable. Delivery Loop will retry when the connection is available.' : continuationFailureReason(error) });
|
|
137
137
|
}
|
|
138
138
|
}).finally(() => {
|
|
139
139
|
if (controller.signal.aborted) { attempted.delete(key); retries.delete(key); }
|
|
@@ -152,14 +152,14 @@ export function createAutomaticContinuation({ root, getManager, selectCandidate,
|
|
|
152
152
|
if (candidate.autoContinue === true && candidate.capabilities?.canAutoContinue === true &&
|
|
153
153
|
!['managed', 'pending_takeover'].includes(candidate.binding?.mode)) {
|
|
154
154
|
if (pending.has(key)) return startingPresentation(candidate, 'starting',
|
|
155
|
-
'
|
|
155
|
+
'Delivery Loop is on. Preparing Delivery Loop for this conversation in the background.');
|
|
156
156
|
const failure = errors.get(key);
|
|
157
157
|
if (failure?.recoverable && failure.epoch === candidate.autoContinueEpoch)
|
|
158
158
|
return startingPresentation(candidate, 'recovering', failure.reason);
|
|
159
159
|
}
|
|
160
160
|
if (candidate.autoContinue === true && candidate.isRunning !== true &&
|
|
161
161
|
['unmanaged', 'monitor'].includes(candidate.binding?.mode || 'unmanaged') && candidate.analysis?.state === 'needs_user') return {
|
|
162
|
-
...candidate, autoContinueStatus: 'needs_user', autoContinueReason: candidate.analysis.reason || 'Your decision is needed before
|
|
162
|
+
...candidate, autoContinueStatus: 'needs_user', autoContinueReason: candidate.analysis.reason || 'Your decision is needed before Delivery Loop can proceed.',
|
|
163
163
|
};
|
|
164
164
|
const error = errors.get(`${candidate.provider}:${candidate.session}`);
|
|
165
165
|
if (error && candidate.autoContinue === true && candidate.autoContinueEpoch === error.epoch &&
|
|
@@ -83,7 +83,7 @@ async function unavailableAnalysis(candidate, error, inspectProvider, signal) {
|
|
|
83
83
|
if (signal?.aborted) return uncertain('Conversation analysis was cancelled.', 'cancelled');
|
|
84
84
|
if (error?.code === 'provider_sign_in_required' || status?.state === 'signed_out') {
|
|
85
85
|
const label = candidate.provider === 'claude' ? 'Claude Code' : 'Codex';
|
|
86
|
-
return uncertain(`Sign in to ${label}. SIMY rechecks this conversation automatically when sign-in is detected. You can also retry manually. Unfinished work
|
|
86
|
+
return uncertain(`Sign in to ${label}. SIMY rechecks this conversation automatically when sign-in is detected. You can also retry manually. Unfinished work continues only when "Continue automatically" is on.`, 'provider_sign_in_required');
|
|
87
87
|
}
|
|
88
88
|
return uncertain('The local AI app could not check this turn. Check its connection, then retry.', 'analysis_unavailable');
|
|
89
89
|
}
|
|
@@ -8,7 +8,7 @@ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
8
8
|
export async function runContinuationCommand(args, { root, output, signal,
|
|
9
9
|
request = requestOwnerContinuation } = {}) {
|
|
10
10
|
if (args.includes('--help') || args.includes('-h')) {
|
|
11
|
-
output('simy delivery auto-continue on|off|status --provider codex|claude --session UUID\
|
|
11
|
+
output('simy delivery auto-continue on|off|status --provider codex|claude --session UUID\nContinue this conversation automatically: turns Delivery Loop on or off for one exact conversation. Uses the running local SIMY app and its existing controls. ON saves delegation; only workState describes execution. Explicit stops, approvals, original goals and history remain in force.');
|
|
12
12
|
return 0;
|
|
13
13
|
}
|
|
14
14
|
const [operation, ...flags] = args;
|
|
@@ -34,12 +34,12 @@ export async function runContinuationCommand(args, { root, output, signal,
|
|
|
34
34
|
try {
|
|
35
35
|
const receipt = await request({ root, signal, command: command('session_get_status') });
|
|
36
36
|
output(JSON.stringify({ ...receipt, provider, session, accepted,
|
|
37
|
-
message: receipt?.message || (operation === 'on' && accepted ? '
|
|
37
|
+
message: receipt?.message || (operation === 'on' && accepted ? 'Delivery Loop delegation saved. Read workState and its nextAction for actual progress; this acknowledgement does not prove a provider turn executed.' : undefined) }));
|
|
38
38
|
return receipt?.ok ? 0 : 2;
|
|
39
39
|
} catch (error) {
|
|
40
40
|
if (!accepted) throw error;
|
|
41
41
|
output(JSON.stringify({ ok: false, provider, session, accepted, code: error.code || 'status_unavailable',
|
|
42
|
-
message: 'The setting was saved, but execution status could not be read. Check auto-continue status
|
|
42
|
+
message: 'The setting was saved, but execution status could not be read. Check `simy delivery auto-continue status`; do not repeat ON to infer execution.' }));
|
|
43
43
|
return 2;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -54,5 +54,5 @@ export function projectContinuationWait(candidate, wait, now, noticeAfterMs = CO
|
|
|
54
54
|
const needsAttention = wait.reasonCode === 'analysis_retry_exhausted' || now - wait.since >= noticeAfterMs;
|
|
55
55
|
return { ...candidate, continuationWait: { id: wait.id, since: wait.since, reasonCode: wait.reasonCode, needsAttention },
|
|
56
56
|
...(needsAttention ? { autoContinueStatus: 'needs_user',
|
|
57
|
-
autoContinueReason: '
|
|
57
|
+
autoContinueReason: 'Delivery Loop is on, but work has been waiting for execution conditions. Check the original conversation or refresh its connection.' } : {}) };
|
|
58
58
|
}
|
|
@@ -82,7 +82,7 @@ export function conversationPresentation(candidate = {}, projectedRun, { rawReco
|
|
|
82
82
|
const pendingDecision = ['approval', 'question'].includes(work.state) || field('pendingApproval') && !field('pendingApproval').denied ||
|
|
83
83
|
field('understandingQuestion') || managed.userQuestion;
|
|
84
84
|
if (candidate.autoContinueStatus === 'save_failed') {
|
|
85
|
-
ready = readiness('save_failed', 'danger', reason?.text || 'Could not save
|
|
85
|
+
ready = readiness('save_failed', 'danger', reason?.text || 'Could not save Delivery Loop. Retry.');
|
|
86
86
|
} else if (candidate.autoContinueStatus === 'saving') {
|
|
87
87
|
ready = readiness('saving', 'accent', 'Saving On…');
|
|
88
88
|
} else if (candidate.autoContinue !== true) {
|
|
@@ -116,13 +116,13 @@ export function conversationPresentation(candidate = {}, projectedRun, { rawReco
|
|
|
116
116
|
} else if (terminal) {
|
|
117
117
|
ready = readiness('finished', 'secondary', state === 'reported_done' ? 'The AI reported completion; verification is pending.' : 'No current work to continue.');
|
|
118
118
|
} else if (candidate.provider === 'claude' && candidate.isRunning === false && !['starting', 'recovering'].includes(candidate.autoContinueStatus)) {
|
|
119
|
-
ready = readiness('original_turn_required', 'attention', 'Send one message in the original conversation.
|
|
119
|
+
ready = readiness('original_turn_required', 'attention', 'Send one message in the original conversation. Delivery Loop waits for its next verified hook.', action('open_original', 'Open original conversation'));
|
|
120
120
|
} else if (control && candidate.autoContinueStatus === 'supervising' && ['active', 'verification_pending'].includes(state) &&
|
|
121
121
|
ownership === 'simy_continues' && blocker.state === 'ready' &&
|
|
122
122
|
!field('executionUncertain') && !field('providerWarning') && !managed.nativeWait && !managed.purpose?.nativeBoundary) {
|
|
123
123
|
ready = readiness('ready', 'accent', 'Will continue after this turn');
|
|
124
124
|
} else if (['starting', 'recovering'].includes(candidate.autoContinueStatus)) {
|
|
125
|
-
ready = readiness(candidate.autoContinueStatus, 'accent', reason?.text || 'Checking
|
|
125
|
+
ready = readiness(candidate.autoContinueStatus, 'accent', reason?.text || 'Checking Delivery Loop readiness.');
|
|
126
126
|
} else {
|
|
127
127
|
ready = readiness('waiting', 'secondary', reason?.text || 'Checking whether the next step can be sent.', action('refresh', 'Refresh status'));
|
|
128
128
|
}
|
|
@@ -78,8 +78,8 @@ export function sessionDecisionPresentation(candidate, run) {
|
|
|
78
78
|
const acceptancePending = run.state === 'verification_pending';
|
|
79
79
|
const providerName = candidate.provider === 'claude' ? 'Claude' : candidate.provider === 'codex' ? 'Codex' : 'AI';
|
|
80
80
|
const reasons = {
|
|
81
|
-
session_end: pair(`元の${providerName}
|
|
82
|
-
process_exit: pair('元のAI
|
|
81
|
+
session_end: pair(`元の${providerName}会話が終了し、Delivery Loop が止まっています。`, `The original ${providerName} session ended, stopping Delivery Loop.`),
|
|
82
|
+
process_exit: pair('元のAIが終了したため、Delivery Loop を続けられません。', 'The original AI process exited. Delivery Loop cannot proceed.'),
|
|
83
83
|
provider_error: pair('AIの実行でエラーが発生し、作業が止まっています。', 'An AI execution error stopped the work.'),
|
|
84
84
|
shutdown_cancelled: pair('SIMYの終了時に作業が中断されました。', 'Work was interrupted when SIMY exited.'),
|
|
85
85
|
hook_owner_mismatch: pair('元のAI会話の接続が変わりました。', 'The connection to the original AI session changed.'),
|
|
@@ -91,9 +91,9 @@ export function sessionDecisionPresentation(candidate, run) {
|
|
|
91
91
|
const choices = [];
|
|
92
92
|
if (run.canReplan && frozen) choices.push(choice(pair('AIに状況確認を任せる', 'Ask AI to investigate'), pair('目的・履歴・制限を維持して原因と安全な次の手順を調べます。操作の許可を追加することはありません。', 'Investigate the cause and next steps while keeping the goal, history and limits. No new permission is granted.'), 'delivery_replan', {}, 'primary'));
|
|
93
93
|
const enablesAutoContinue = candidate.provider === 'codex' && run.managed !== true && run.state === 'stopped' && candidate.autoContinue !== true;
|
|
94
|
-
if (run.canResume && frozen) choices.push(choice(pair(enablesAutoContinue ? '
|
|
94
|
+
if (run.canResume && frozen) choices.push(choice(pair(enablesAutoContinue ? 'Delivery Loop をオンにして再開' : acceptancePending ? '未完了の作業を続ける' : '再開を依頼する', enablesAutoContinue ? 'Resume with Delivery Loop' : acceptancePending ? 'Continue remaining work' : 'Request resume'), pair(enablesAutoContinue ? 'Delivery Loop をオンにして元の会話の読み取り専用の状況確認から再開します。元の目的・権限・停止履歴・予算を維持します。' : acceptancePending ? '元の目的・履歴・制限を維持して残作業を確認します。最終確認を済ませたことにはしません。' : '保存した目的と履歴を引き継ぎます。接続や権限が不足する場合は、その内容を案内します。', enablesAutoContinue ? 'Turn on Delivery Loop and resume with a read-only status check in the original conversation. Preserve its goal, permissions, stop history and budgets.' : acceptancePending ? 'Keep the goal, history and limits while checking what remains. This does not accept the result.' : 'Keep the saved goal and history. Any missing connection or permission will be shown.'), 'delivery_resume', {}, choices.length ? 'secondary' : 'primary'));
|
|
95
95
|
choices.push(choice(pair(`元の${providerName}会話を開く`, 'Open original conversation ↗'), pair('接続や権限の確認が必要な場合、元のAI会話で確認できます。会話を開くだけでは作業を再開しません。', 'Check the original conversation. Opening it does not resume work.'), 'open_original', {}, 'link'));
|
|
96
|
-
if (candidate.autoContinue === true) choices.push(choice(pair('
|
|
96
|
+
if (candidate.autoContinue === true) choices.push(choice(pair('Delivery Loop をオフにする', 'Turn off Delivery Loop'), pair('今後の Delivery Loop をオフにします。作業の履歴は残ります。', 'Turn off Delivery Loop for future work and retain the history.'), 'session_set_auto_continue', { ...identity, enabled: false }));
|
|
97
97
|
const actionable = frozen && (run.canReplan || run.canResume);
|
|
98
98
|
return model('recovery', {
|
|
99
99
|
title: pair(acceptancePending ? '最終確認を待ちながら、残作業を続けられます' : '作業が止まっています。対応を選んでください', acceptancePending ? 'Continue work while acceptance is pending' : 'Work stopped. Choose how to proceed'),
|
|
@@ -105,12 +105,12 @@ export function sessionDecisionPresentation(candidate, run) {
|
|
|
105
105
|
candidate.capabilities?.canAutoContinue === true && candidate.autoContinueAvailability?.canEnable !== false) return model('continue', {
|
|
106
106
|
title: pair('続けますか?', 'Continue?'),
|
|
107
107
|
question: pair(clean(candidate.analysis.reason) || '許可済みの残作業があります。', clean(candidate.analysis.reason) || 'Authorized work remains.'),
|
|
108
|
-
context: pair('
|
|
109
|
-
}, { choices: [choice(pair('
|
|
108
|
+
context: pair('Delivery Loop はオフです。オンにすると、元の目的と権限の範囲で残作業を確認します。結果を受け入れる操作ではありません。', 'Delivery Loop is off. Turning it on checks remaining work within the original goal and permissions. It does not accept the result.'),
|
|
109
|
+
}, { choices: [choice(pair('Delivery Loop をオンにする', 'Turn on Delivery Loop'), pair('Delivery Loop の設定を保存します。作業の開始や完了を証明するものではありません。', 'Save consent for Delivery Loop. This does not confirm that work started or finished.'), 'session_set_auto_continue', { ...identity, enabled: true }, 'primary'),
|
|
110
110
|
choice(pair('元の会話を開く', 'Open original conversation ↗'), pair('会話を開くだけでは作業を再開しません。', 'Opening the conversation does not resume work.'), 'open_original', {}, 'link')] });
|
|
111
111
|
if (candidate.continuationWait?.needsAttention === true && candidate.autoContinue === true) return model('continuation_wait', {
|
|
112
|
-
title: pair('
|
|
113
|
-
question: pair('
|
|
112
|
+
title: pair('Delivery Loop の条件を確認してください', 'Check Delivery Loop conditions'),
|
|
113
|
+
question: pair('Delivery Loop はオンのまま、実行条件の確認を待っています。元の会話や接続状態を確認してください。', 'Delivery Loop is still on, waiting for execution conditions. Check the original conversation or its connection.'),
|
|
114
114
|
context: pair('設定・目的・停止指示・承認待ちは維持しています。この通知で作業を再開したり、権限を追加したりしません。', 'The setting, goal, stops and pending approvals are preserved. This notice does not resume work or grant permission.'),
|
|
115
115
|
}, { choices: [choice(pair('状況を再確認する', 'Refresh status'), pair('条件を再確認します。', 'Recheck conditions.'), 'refresh', identity),
|
|
116
116
|
choice(pair('元の会話を開く', 'Open original conversation ↗'), pair('元の会話を確認します。', 'Check the original conversation.'), 'open_original', {}, 'link')] }, 'helper');
|
|
@@ -24,7 +24,7 @@ import { readDispatchGate, rootFromDelivery, sessionDispatchReadiness } from '..
|
|
|
24
24
|
// error sanitizer; native provider errors never acquire this type.
|
|
25
25
|
export class AutoContinueUnavailableError extends Error {
|
|
26
26
|
constructor(availability) {
|
|
27
|
-
super(availability.reason || '
|
|
27
|
+
super(availability.reason || 'Delivery Loop is unavailable for this conversation.');
|
|
28
28
|
this.code = availability.reasonCode;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -54,8 +54,8 @@ function codexContinuationChannel(source, model, issue = null) {
|
|
|
54
54
|
// The saved ON preference is consent, not execution readiness. Each blocker
|
|
55
55
|
// names its own recovery route instead of reading as provider activity.
|
|
56
56
|
const DISPATCH_BLOCKERS = {
|
|
57
|
-
update_recovery_required: '
|
|
58
|
-
writer_reconnect_required: '
|
|
57
|
+
update_recovery_required: 'Delivery Loop is on, but SIMY is recovering from an interrupted update and cannot send work. Open Settings > App & updates and choose Try again.',
|
|
58
|
+
writer_reconnect_required: 'Delivery Loop is on, but this conversation was opened before SIMY recovered from an update. Send one message in the original conversation so SIMY can verify the same process and reconnect it.',
|
|
59
59
|
dispatch_history_quarantined: 'Earlier unconfirmed work in this conversation was quarantined during update recovery and will not be resent. Review the original conversation and start the next request yourself.',
|
|
60
60
|
task_admission_blocked: 'This task needs fresh verified provider permissions before SIMY can send work. Continue it once in the original conversation.',
|
|
61
61
|
};
|
|
@@ -63,7 +63,7 @@ function autoContinueState(record, isRunning, availability, readiness = null) {
|
|
|
63
63
|
if (record?.autoContinue !== true) return { autoContinueStatus: 'off', autoContinueReason: null };
|
|
64
64
|
const state = record.run?.state;
|
|
65
65
|
if (readiness?.state === 'blocked' && !TERMINAL_RUNS.has(state)) return { autoContinueStatus: 'needs_user',
|
|
66
|
-
autoContinueReason: DISPATCH_BLOCKERS[readiness.code] || '
|
|
66
|
+
autoContinueReason: DISPATCH_BLOCKERS[readiness.code] || 'Delivery Loop is on, but SIMY cannot send work for this conversation yet.' };
|
|
67
67
|
if (state === 'budget_exhausted') return { autoContinueStatus: 'needs_user', autoContinueReason: 'Local execution limits have been removed. Resume this same conversation to continue.' };
|
|
68
68
|
if (completionClaimUnbound(record)) return { autoContinueStatus: 'watching', autoContinueReason: 'Verification of the recorded completion remains pending.' };
|
|
69
69
|
if (state === 'reported_done') return { autoContinueStatus: 'watching', autoContinueReason: 'The provider reported completion; the actual result has not yet been confirmed.' };
|
|
@@ -383,7 +383,7 @@ async function discoverSessionCatalog({ root, openClient = openCodexAppServer, l
|
|
|
383
383
|
} catch { /* Missing workspaces cannot grant an admission. */ }
|
|
384
384
|
}
|
|
385
385
|
const availability = archived ? { canEnable: false, reasonCode: 'session_archived',
|
|
386
|
-
reason: 'Restore the original provider conversation before
|
|
386
|
+
reason: 'Restore the original provider conversation before turning on Delivery Loop.' }
|
|
387
387
|
: autoContinueAvailability(run, claudeChannel || codexChannel);
|
|
388
388
|
const upgradeable = canUpgradeDelivery(run);
|
|
389
389
|
const continuationIssue = liveHook && run ? !run.managed && !upgradeable
|
|
@@ -469,7 +469,7 @@ function hasAutoContinueAdmission(record, provider, session) {
|
|
|
469
469
|
(provider !== 'codex' || typeof admission.model === 'string' && admission.model.trim());
|
|
470
470
|
}
|
|
471
471
|
function requireAutoContinueTask(record) {
|
|
472
|
-
if (ownerRenewalPending(record)) throw new AutoContinueUnavailableError({ reasonCode: 'owner_renewal_pending', reason: 'Finish the original conversation connection recovery before
|
|
472
|
+
if (ownerRenewalPending(record)) throw new AutoContinueUnavailableError({ reasonCode: 'owner_renewal_pending', reason: 'Finish the original conversation connection recovery before turning on Delivery Loop.' });
|
|
473
473
|
if (record?.run?.state === 'stopped') throw new AutoContinueUnavailableError({ reasonCode: 'task_stopped', reason: 'This task was stopped. Review the original conversation and explicitly request resumption before continuing.' });
|
|
474
474
|
if (record?.run && !record.run.managed && !canUpgradeDelivery(record.run)) throw Object.assign(new Error('This conversation already has a Delivery Loop. Use its existing controls.'), { code: 'existing_loop' });
|
|
475
475
|
if (TERMINAL_RUNS.has(record?.run?.state)) throw new AutoContinueUnavailableError({ reason: 'This supervised task has settled. Review its existing status before continuing.', reasonCode: 'task_settled' });
|
|
@@ -487,7 +487,7 @@ export async function saveSessionAutoContinuePreference(options) {
|
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
export async function setSessionAutoContinue({ root, provider, session, enabled, continuationPolicy, initialContinuationPolicy, ...options }) {
|
|
490
|
-
if (!['codex', 'claude'].includes(provider) || typeof enabled !== 'boolean') throw new Error('Unsupported
|
|
490
|
+
if (!['codex', 'claude'].includes(provider) || typeof enabled !== 'boolean') throw new Error('Unsupported Delivery Loop request.');
|
|
491
491
|
const link = enabled ? await requireProviderLink({ root, provider, expectedEpoch: options.expectedProviderEpoch }) : null;
|
|
492
492
|
let discoveredAdmission;
|
|
493
493
|
const preferenceOnly = options.preferenceOnly === true;
|
|
@@ -496,7 +496,7 @@ export async function setSessionAutoContinue({ root, provider, session, enabled,
|
|
|
496
496
|
const purposeRules = enabled && provider === 'codex' && !preferenceOnly ? await loadPurposeRuleSet() : null;
|
|
497
497
|
if (enabled && !preferenceOnly) {
|
|
498
498
|
const record = await readRecord(root, provider, session);
|
|
499
|
-
if (record?.providerArchived === true) throw Object.assign(new Error('Restore the original provider conversation before
|
|
499
|
+
if (record?.providerArchived === true) throw Object.assign(new Error('Restore the original provider conversation before turning on Delivery Loop.'), { code: 'session_archived' });
|
|
500
500
|
requireAutoContinueTask(record);
|
|
501
501
|
if (!hasAutoContinueAdmission(record, provider, session)) {
|
|
502
502
|
// Only the host's verified discovery catalog supplies this option; command
|
|
@@ -533,8 +533,8 @@ export async function setSessionAutoContinue({ root, provider, session, enabled,
|
|
|
533
533
|
// identity with the earlier catalog snapshot, including a mismatch.
|
|
534
534
|
previous = previous?.admission ? previous : { ...(previous || { schemaVersion: 1, provider, session, cwd: discoveredAdmission.cwd, run: null }), admission: discoveredAdmission };
|
|
535
535
|
}
|
|
536
|
-
if (!previous) throw new Error('Refresh this conversation before changing
|
|
537
|
-
if (enabled && previous.providerArchived === true) throw Object.assign(new Error('Restore the original provider conversation before
|
|
536
|
+
if (!previous) throw new Error('Refresh this conversation before changing Delivery Loop.');
|
|
537
|
+
if (enabled && previous.providerArchived === true) throw Object.assign(new Error('Restore the original provider conversation before turning on Delivery Loop.'), { code: 'session_archived' });
|
|
538
538
|
if (enabled && !preferenceOnly) {
|
|
539
539
|
const channel = provider === 'claude' ? await liveClaudeChannel(previous,
|
|
540
540
|
{ session, cwd: previous.cwd, observation: { source: previous.admission?.source } }, options.inspectProcess || processIdentity)
|
|
@@ -609,7 +609,7 @@ export async function trackSession({ root, provider, session, tracked = true, op
|
|
|
609
609
|
const cwd = await realpath(metadata.cwd);
|
|
610
610
|
await updateRecord(root, provider, session, async previous => {
|
|
611
611
|
await requireProviderLink({ root, provider, expectedEpoch: link.epoch });
|
|
612
|
-
if (previous?.providerArchived === true) throw Object.assign(new Error('Restore the original provider conversation before
|
|
612
|
+
if (previous?.providerArchived === true) throw Object.assign(new Error('Restore the original provider conversation before turning on Delivery Loop or tracking.'), { code: 'session_archived' });
|
|
613
613
|
if (previous && previous.cwd !== cwd) throw new Error('Session workspace changed; refresh its metadata.');
|
|
614
614
|
return { ...(previous || { schemaVersion: 1, provider, session, cwd, run: null }),
|
|
615
615
|
...(link.consentEpoch ? { providerConsentEpoch: link.consentEpoch } : {}), tracked: true, monitor: true,
|
|
@@ -663,7 +663,7 @@ export async function enrollSession(options) {
|
|
|
663
663
|
await requireClaudeChannel(await readRecord(root, provider, session), metadata, inspectProcess);
|
|
664
664
|
} else {
|
|
665
665
|
metadata = await monitorSession({ root, provider, session, monitor: true, openClient, expectedProviderEpoch: link.epoch, now });
|
|
666
|
-
if (!metadata.model) throw new Error('Codex did not expose this conversation’s model;
|
|
666
|
+
if (!metadata.model) throw new Error('Codex did not expose this conversation’s model; Delivery Loop is unavailable.');
|
|
667
667
|
}
|
|
668
668
|
const cwd = await realpath(metadata.cwd);
|
|
669
669
|
const initialPlan = initialDiagnosisPlan(objective.trim(), contextBootstrap);
|
|
@@ -700,7 +700,7 @@ export async function continueSession(options) {
|
|
|
700
700
|
options = sessionLoopOptions(options);
|
|
701
701
|
const record = await readRecord(options.root, options.provider, options.session);
|
|
702
702
|
if (options.upgradeRunId && (record?.run?.id !== options.upgradeRunId || record.run.managed))
|
|
703
|
-
throw Object.assign(new Error('The original task changed before
|
|
703
|
+
throw Object.assign(new Error('The original task changed before Delivery Loop started.'), { code: 'session_task_changed' });
|
|
704
704
|
if (record?.run && !record.run.managed) {
|
|
705
705
|
// The user asked to continue this task. Adopt its goal and failure budget,
|
|
706
706
|
// rather than enrolling the generic context-bootstrap goal as a new run.
|
|
@@ -6,7 +6,7 @@ const messages = {
|
|
|
6
6
|
claude_desktop_accessibility_required: 'Enable macOS Accessibility for the app running SIMY on the conversation host. Use simy sessions provider-setup --provider claude --host ALIAS --session UUID to open that host’s setting.',
|
|
7
7
|
claude_desktop_ui_unavailable: 'Open the exact Claude Desktop Code conversation on the selected host and retry.',
|
|
8
8
|
claude_desktop_ui_changed: 'The Claude Desktop window changed or is covered. Retry when the selected conversation is visible.',
|
|
9
|
-
claude_desktop_running: 'Stop this conversation and turn off
|
|
9
|
+
claude_desktop_running: 'Stop this conversation and turn off Delivery Loop before organizing it.',
|
|
10
10
|
claude_desktop_unsaved_input: 'Claude Desktop contains unsent input. Preserve it on the conversation host before retrying.',
|
|
11
11
|
claude_desktop_identity_unverified: 'Claude Desktop conversation identity could not be verified. Refresh the exact conversation.',
|
|
12
12
|
claude_desktop_identity_changed: 'Claude Desktop account or conversation changed. Refresh before retrying.',
|
|
@@ -24,7 +24,7 @@ const messages = {
|
|
|
24
24
|
session_model_unavailable: 'Select a model in the original remote conversation, then refresh.',
|
|
25
25
|
model_metadata_unavailable: 'Select a model in the original remote conversation, then refresh.',
|
|
26
26
|
session_workspace_changed: 'The remote workspace changed. Refresh and select the conversation again.',
|
|
27
|
-
session_archived: 'Restore the original remote conversation before
|
|
27
|
+
session_archived: 'Restore the original remote conversation before turning on Delivery Loop.',
|
|
28
28
|
existing_loop: 'Use this remote conversation’s existing task controls.',
|
|
29
29
|
task_stopped: 'This remote task was stopped. Review the original conversation and explicitly request resumption.',
|
|
30
30
|
task_settled: 'The remote task has finished. Refresh and select its explicit follow-up controls.',
|
|
@@ -99,14 +99,14 @@ export function sessionWorkState(candidate, run, { now = Date.now(), provider }
|
|
|
99
99
|
// Saved consent is not execution: name the real dispatch blocker rather than
|
|
100
100
|
// presenting a fenced conversation as waiting for its AI.
|
|
101
101
|
if (candidate.autoContinue === true && candidate.dispatchReadiness?.state === 'blocked' && !['completed', 'stopped'].includes(run?.state))
|
|
102
|
-
return result('blocked', '
|
|
102
|
+
return result('blocked', 'Delivery Loop の作業を送信できません', candidate.autoContinueReason || 'Delivery Loop はオンですが、作業を送信できません。',
|
|
103
103
|
candidate.dispatchReadiness.code === 'update_recovery_required' ? 'update_reconnect' : candidate.dispatchReadiness.code === 'writer_reconnect_required' ? 'reconnect_original' : 'open_original', true);
|
|
104
104
|
if (run?.state === 'verification_pending' &&
|
|
105
105
|
(onlyManualAcceptancePending(run) || candidate.autoContinueStatus === 'needs_user'))
|
|
106
106
|
return result('unfinished', '残りの作業あり', '実際の結果の確認が残っています。', 'open_original', true);
|
|
107
107
|
if (candidate.autoContinue === true && ['starting', 'recovering'].includes(candidate.autoContinueStatus))
|
|
108
|
-
return result(candidate.autoContinueStatus, candidate.autoContinueStatus === 'starting' ? '
|
|
109
|
-
candidate.autoContinueReason || '
|
|
108
|
+
return result(candidate.autoContinueStatus, candidate.autoContinueStatus === 'starting' ? 'Delivery Loop を準備中' : '自動復旧中',
|
|
109
|
+
candidate.autoContinueReason || 'Delivery Loop はオンで保存されています。');
|
|
110
110
|
if ((run?.autoRecovery || run?.managed?.autoRecovery) && ['recovery_required', 'auto_resuming'].includes(run.state)) return result('checking', 'AIが状況確認中', '自動復旧の結果を待っています。');
|
|
111
111
|
if (candidate.autoContinue === true && candidate.autoContinueStatus === 'supervising' && run?.state === 'verification_pending')
|
|
112
112
|
return result('checking', '結果を確認中', '作業の結果を自動で確認しています。');
|
package/src/sqm/agent-skill.js
CHANGED
|
@@ -8,7 +8,7 @@ import { hookRuntimeCommand } from "../hook-runtime.js";
|
|
|
8
8
|
import { inspectClaudePlugin, installClaudePlugin } from "./claude-plugin.js";
|
|
9
9
|
|
|
10
10
|
export const SQM_AGENT_SKILL_ID = "simy-sqm-pr-check";
|
|
11
|
-
export const SQM_AGENT_SKILL_VERSION = "3.
|
|
11
|
+
export const SQM_AGENT_SKILL_VERSION = "3.5.0";
|
|
12
12
|
export const SIMY_CODEX_PLUGIN_ID = "simy";
|
|
13
13
|
export const SIMY_CODEX_PLUGIN_VERSION = "0.1.0";
|
|
14
14
|
|
|
@@ -22,10 +22,10 @@ const LEGACY_SKILL_DIGEST = "c9ab46e855280196dbc93fd36ba849418e0c4e500dd6e4b7b03
|
|
|
22
22
|
|
|
23
23
|
const SKILL_CONTENT = `---
|
|
24
24
|
name: ${SQM_AGENT_SKILL_ID}
|
|
25
|
-
description:
|
|
25
|
+
description: Apply a SIMY SQM check as a gate before creating or proposing a pull request, only after the user opted in with @simy, this SQM skill, or a request to use SQM for the task. An immediate SQM check of the current work uses the plugin sqm-loop entry; Delivery Loop uses the separate continue entry. Ordinary PR creation or git push alone does not activate this skill.
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
# SIMY SQM PR
|
|
28
|
+
# SIMY SQM check before a PR
|
|
29
29
|
|
|
30
30
|
Apply this workflow only when the user has explicitly requested quality checks with \`@simy\`, invoked this SQM skill, or requested SQM for the current task. A request to start or continue the original task uses SIMY's \`continue\` entry; selecting that entry does not request SQM. A quoted mention, repository organization, installed plugin, or ordinary request to create a PR is not opt-in. Honor a later request to stop using SQM.
|
|
31
31
|
|
package/src/sqm/claude-plugin.js
CHANGED
|
@@ -97,7 +97,7 @@ export async function claudePluginFiles() {
|
|
|
97
97
|
for (const [name, contents] of [...files].sort(([a], [b]) => a.localeCompare(b))) hash.update(name).update("\0").update(contents);
|
|
98
98
|
const manifest = {
|
|
99
99
|
name: "simy", version: `${sourceManifest.version.split("+")[0]}+claude.${hash.digest("hex").slice(0, 20)}`,
|
|
100
|
-
description: "SIMY
|
|
100
|
+
description: "SIMY Delivery Loop through the original conversation controller, SQM checks, and PR review for Claude Code.",
|
|
101
101
|
author: sourceManifest.author, repository: sourceManifest.repository,
|
|
102
102
|
license: sourceManifest.license, keywords: sourceManifest.keywords,
|
|
103
103
|
};
|