@awak-app/simy-cli 0.7.16 → 0.7.17
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 +180 -26
- package/package.json +3 -2
- package/src/agent.js +128 -7
- package/src/bounded-local-task-subtask-pool.js +32 -6
- package/src/bounded-local-task-subtasks-contract.js +1 -1
- package/src/cli-contract.js +1 -1
- package/src/codex-plugins/simy/.codex-plugin/plugin.json +2 -2
- package/src/codex-plugins/simy/hooks/README.md +8 -3
- package/src/codex-plugins/simy/hooks/delivery-loop.mjs +261 -67
- package/src/codex-plugins/simy/hooks/hooks.json +1 -1
- package/src/codex-plugins/simy/hooks/loop-kernel/conformance.json +136 -0
- package/src/codex-plugins/simy/hooks/loop-kernel/index.mjs +42 -0
- package/src/codex-plugins/simy/hooks/loop-kernel/outcome-review.mjs +31 -0
- package/src/codex-plugins/simy/hooks/loop-kernel/pin.json +6 -0
- package/src/codex-plugins/simy/hooks/loop-kernel/policy.json +141 -0
- package/src/codex-plugins/simy/hooks/loop-kernel/task-pool.mjs +34 -0
- package/src/codex-plugins/simy/hooks/loop-kernel/work-frontier.mjs +132 -0
- package/src/codex-plugins/simy/hooks/loop-kernel.mjs +6 -0
- package/src/codex-plugins/simy/hooks/outcome-progress.mjs +15 -3
- package/src/codex-plugins/simy/hooks/purpose-completion-contract.mjs +10 -0
- package/src/codex-plugins/simy/hooks/purpose-evidence.mjs +8 -6
- package/src/codex-plugins/simy/hooks/result-cycle.mjs +32 -6
- package/src/codex-plugins/simy/hooks/work-frontier.mjs +2 -0
- package/src/codex-plugins/simy/skills/diagnose/SKILL.md +10 -4
- package/src/coding-run-recovery.js +4 -2
- package/src/delivery-hooks/claude-workflow.md +10 -7
- package/src/delivery-hooks/command.js +45 -10
- package/src/delivery-hooks/consult.js +60 -0
- package/src/delivery-hooks/diagnosis.js +2 -2
- package/src/delivery-hooks/legacy-hook-routing.js +46 -8
- package/src/delivery-hooks/local-runtime.js +50 -1
- package/src/delivery-hooks/local-supervisor.js +1 -1
- package/src/delivery-hooks/repair-continuation.js +80 -0
- package/src/delivery-hooks/repair.js +164 -0
- package/src/delivery-hooks/supervisor-service.js +3 -1
- package/src/delivery-hooks/supervisor.js +88 -12
- package/src/desktop-access.js +2 -1
- package/src/desktop-host.js +35 -2
- package/src/durable-local-task-worker.js +372 -58
- package/src/execution-capability-contract.js +15 -1
- package/src/index.js +3 -3
- package/src/local-photos-read.swift +33 -0
- package/src/local-photos.js +47 -0
- package/src/local-task-attachment-store.js +31 -0
- package/src/local-task-destination.js +174 -0
- package/src/local-task.js +119 -7
- package/src/native-delegation-contract.js +68 -0
- package/src/native-provider-identity.js +60 -0
- package/src/orchestrator/budget.js +22 -11
- package/src/orchestrator/independent-audit.js +9 -18
- package/src/orchestrator/loop.js +27 -59
- package/src/orchestrator/recovery.js +32 -2
- package/src/provider-delegation.js +336 -0
- package/src/runner.js +2 -1
- package/src/session-supervision/artifact-observer.js +138 -0
- package/src/session-supervision/codex-app-server.js +19 -5
- package/src/session-supervision/completion-artifacts.js +44 -0
- package/src/session-supervision/consistency-auditor.js +8 -11
- package/src/session-supervision/continuation-analysis.js +46 -28
- package/src/session-supervision/final-response-recovery.js +5 -3
- package/src/session-supervision/index.js +22 -5
- package/src/session-supervision/purpose-recovery.js +12 -3
- package/src/session-supervision/purpose-verification.js +9 -57
- package/src/session-supervision/recovery-frontier.js +55 -0
- package/src/session-supervision/stop-notifications.js +8 -8
- package/src/sqm/agent-skill.js +1 -1
- package/src/sqm/checkers.js +12 -6
- package/src/sqm/claude-plugin-update.js +22 -8
- package/src/sqm/claude-plugin.js +4 -0
- package/src/sqm/local-tests.js +2 -2
- package/src/sqm/plugin-cache.js +67 -0
- package/src/sqm/proof.js +25 -0
- package/src/sqm/report.js +1 -0
- package/src/sqm/workflow-onboarding.js +3 -39
- package/src/sqm/workflow-update.js +17 -6
- package/src/team-status-fence-store.js +32 -0
- package/src/team-threads.js +457 -0
package/README.md
CHANGED
|
@@ -25,6 +25,15 @@ for configuration, tests and remaining host integration requirements.
|
|
|
25
25
|
|
|
26
26
|
Local execution agent for SIMY Local Tasks and Agentic Loop runs.
|
|
27
27
|
|
|
28
|
+
To supervise an explicitly requested current goal after a finished Codex run, use
|
|
29
|
+
`simy delivery followup --provider codex --session SESSION_ID --previous-run-id RUN_ID --objective "Current goal and expected result"`.
|
|
30
|
+
It queues read-only reconciliation for the existing persistent supervisor. A new
|
|
31
|
+
goal archives the old run's evidence; the same ordinary `reported_done` goal keeps
|
|
32
|
+
its run identity and budgets while adopting managed outcome verification. Explicit
|
|
33
|
+
stops, opt-out, pending approvals and unknown effects remain in force. This command
|
|
34
|
+
does not install or activate a supervisor, and ordinary prompts never replace a
|
|
35
|
+
finished run automatically. See [runtime operation](docs/local-delivery-runtime.md).
|
|
36
|
+
|
|
28
37
|
Action candidate search runs once for each authenticated live conversation context,
|
|
29
38
|
and again when that context changes or **Refresh** is selected. Background binding
|
|
30
39
|
renewal uses the lightweight binding endpoint instead of repeating candidate search.
|
|
@@ -125,9 +134,21 @@ For interrupted Agentic work, `GET /v1/agentic-loop/:id/recovery` provides a
|
|
|
125
134
|
read-only objective and checkout fingerprint. Reconcile an uncertain side effect
|
|
126
135
|
with `POST` to that endpoint (`action: reconcile`, authoritative
|
|
127
136
|
`operation_status`, `observation`, `evidence_refs`) before resuming. A paused
|
|
128
|
-
run retains
|
|
129
|
-
|
|
130
|
-
|
|
137
|
+
Agentic run retains `POST /v1/agentic-loop/:id/continuation-ceilings` for an
|
|
138
|
+
explicit safety-ceiling extension under the progress policy. Token and retry
|
|
139
|
+
budgets are notification thresholds: they do not interrupt or stop useful work.
|
|
140
|
+
Agentic Loop checks a separate safety ceiling before each new provider call:
|
|
141
|
+
15,000,000 weighted Provider tokens and 50 executor attempts by default, using
|
|
142
|
+
existing `provider_token_ceiling` and `attempt_ceiling` settings when present.
|
|
143
|
+
The final attempt may still be reviewed if token allowance remains. Active
|
|
144
|
+
processes finish and their results are retained even if usage crosses the ceiling.
|
|
145
|
+
A safety stop preserves run identity, output, usage and approval gates. Review
|
|
146
|
+
remaining work, explicitly extend the relevant ceiling within the existing
|
|
147
|
+
15M-token/100-attempt validation bounds, then request continuation of the same
|
|
148
|
+
run. A notification-threshold increase alone cannot clear a safety stop; at the
|
|
149
|
+
maximum ceiling, further automatic calls remain blocked. Extension itself never
|
|
150
|
+
resets usage or resumes work. Local Task and managed Delivery behavior is not
|
|
151
|
+
changed by this Agentic safety gate. Both routes require the original authorized
|
|
131
152
|
account, organization and device throughout the request.
|
|
132
153
|
|
|
133
154
|
## Provider reconnection and saved consent
|
|
@@ -160,6 +181,10 @@ login status, tasks, and updates separately. A Codex login check does not prove
|
|
|
160
181
|
that a task can execute successfully; permissions and service availability are
|
|
161
182
|
checked when work starts.
|
|
162
183
|
|
|
184
|
+
When SIMY cannot verify a provider connection or plugin status, the connection
|
|
185
|
+
panel shows **Checking connection status** and **Check again**. Check again
|
|
186
|
+
refreshes the status without changing the saved connection setting.
|
|
187
|
+
|
|
163
188
|
The sidebar's **Organization** selector is global across Sessions, Tasks and SQM
|
|
164
189
|
and shows active memberships of the connected SIMY account. Switching is available
|
|
165
190
|
while work is running and opens browser authorization for the selected organization.
|
|
@@ -248,11 +273,13 @@ npm install -g @awak-app/simy-cli
|
|
|
248
273
|
simy --daemon
|
|
249
274
|
```
|
|
250
275
|
|
|
251
|
-
When
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
and hooks are preserved.
|
|
255
|
-
|
|
276
|
+
When `simy --daemon` starts without managed SIMY workflows, the CLI automatically
|
|
277
|
+
prepares the Codex and Claude Code plugin sources, including in non-interactive
|
|
278
|
+
services. Background maintenance also prepares missing sources and refreshes
|
|
279
|
+
existing enabled plugins. Unrelated plugins and hooks are preserved.
|
|
280
|
+
`--install-workflows` remains available for explicit repair;
|
|
281
|
+
`--no-workflow-prompt` skips initial preparation and background installation of
|
|
282
|
+
missing sources. Provider plugin enablement and hook trust remain provider-owned.
|
|
256
283
|
|
|
257
284
|
SQM checks apply when you explicitly select `@simy`, invoke the SQM skill, or ask to use SQM for the current task. Ordinary PR creation and `git push` do not opt in. Upgrading and restarting the daemon migrates the legacy global PR gate and shipped personal skills; unrelated hooks are preserved. Start a new coding session to reload skill instructions.
|
|
258
285
|
|
|
@@ -369,27 +396,23 @@ stops, failures and repeated attempts without new verified outcomes, with task
|
|
|
369
396
|
identity, agent, reasons and outcome timestamps. Missing evidence is shown as
|
|
370
397
|
unknown; open time is never counted as productive time. See [work activity](docs/work-activity.md).
|
|
371
398
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
Claude Code retains its opt-in, hook-based Delivery Loop from an independent
|
|
383
|
-
workflow source. Install the plugin, review and enable SIMY hooks with `/hooks`,
|
|
384
|
-
then request `/simy:delivery-loop` for the task. Installing or updating the plugin
|
|
385
|
-
never approves hook trust. See [hook delivery behavior and limits](docs/delivery-hooks.md).
|
|
399
|
+
The CLI prepares and maintains provider plugin sources automatically. Once the
|
|
400
|
+
user enables the SIMY plugin and permits its hooks, a nonempty prompt starts
|
|
401
|
+
checkpoint tracking in an eligible Codex or Claude conversation. The local Codex
|
|
402
|
+
`delivery-loop` and `agentic-delivery-loop` skills remain retired; prompt hook
|
|
403
|
+
context supplies checkpoint instructions without reinstalling those skills.
|
|
404
|
+
Existing tasks, stops, permission waits and provider/session opt-outs are preserved.
|
|
405
|
+
Use `simy delivery auto off --provider codex` (or `claude`) for a project override.
|
|
406
|
+
Completion in this standard flow is provider-reported, not independently verified.
|
|
407
|
+
See [hook delivery behavior and limits](docs/delivery-hooks.md).
|
|
386
408
|
The Connection tab checks each provider's actual SIMY plugin inventory and
|
|
387
409
|
shows installed/enabled, installed/disabled, missing, or unavailable separately
|
|
388
410
|
from app sign-in. Claude enablement is checked from the home folder; project
|
|
389
411
|
settings can differ, and project-scoped installations are labeled explicitly.
|
|
390
412
|
|
|
391
413
|
`--no-auto-update` or `SIMY_AUTO_UPDATE=0` disables these background updates too;
|
|
392
|
-
`--no-workflow-prompt`
|
|
414
|
+
`--no-workflow-prompt` skips initial preparation and background installation of
|
|
415
|
+
missing sources; existing sources still receive updates.
|
|
393
416
|
|
|
394
417
|
This is the recommended installation because the CLI can safely install patch
|
|
395
418
|
and minor releases, and the daemon restarts only when no Agentic Loop task is active. To run
|
|
@@ -1061,8 +1084,9 @@ that explicitly activated candidate when no live desktop owns the state director
|
|
|
1061
1084
|
A live unknown/mismatched owner is preserved and reported. `runtime doctor` reports
|
|
1062
1085
|
expected versus observed host identity, the all-state consistency monitor, and
|
|
1063
1086
|
remaining task findings. `runtime legacy-hooks` routes only the exact shipped
|
|
1064
|
-
0.6.23/0.6.24 Codex hook entrypoints to the verified pin
|
|
1065
|
-
|
|
1087
|
+
0.6.23/0.6.24 and commit 2052e787 Codex hook entrypoints to the verified pin.
|
|
1088
|
+
All exported helpers use that pin too; saved originals are never imported as
|
|
1089
|
+
a fallback. Customized hooks are reported as `customized_retained`. `runtime workflows` refreshes
|
|
1066
1090
|
existing provider workflows without enabling them.
|
|
1067
1091
|
|
|
1068
1092
|
`runtime doctor` also returns `taskReadiness` for every existing task: its original
|
|
@@ -1141,7 +1165,11 @@ unverified original model nor a different provider is substituted automatically.
|
|
|
1141
1165
|
|
|
1142
1166
|
### Manually consult SIMY about a stopped task
|
|
1143
1167
|
|
|
1144
|
-
Ask “SIMY CLIに相談して” in Codex to use the `simy:diagnose` skill.
|
|
1168
|
+
Ask “SIMY CLIに相談して” or “原因を調べて” in Codex to use the `simy:diagnose` skill for diagnosis only. “直して続きを進めて” explicitly requests repair and continuation within existing permissions. The skill resolves the current conversation or an existing selected/original job reference; from another chat, select the original job or refer to its conversation. Only an ambiguous target requires clarification, and users normally do not need to type internal IDs.
|
|
1169
|
+
|
|
1170
|
+
The skill calls `delivery consult --scope diagnose` or `delivery consult --scope repair_continue --request-id USER_TURN_ID`. It preserves the chosen `--host`. Explicit references override existing selection, which overrides the observed current conversation; a missing or mismatched reference never redirects to another job. Repair delegates to the installed repair adapter, preserving stop, permission, budget and duplicate-work protections. If that adapter is absent, `recovery_unavailable` means no recovery was executed. Repair success is not original-task completion. See [the shared contract](docs/conversation-recovery-contract.md).
|
|
1171
|
+
|
|
1172
|
+
For terminal use and older CLIs, the read-only diagnosis command remains available even when the original conversation is unresponsive:
|
|
1145
1173
|
|
|
1146
1174
|
```sh
|
|
1147
1175
|
~/.simy/bin/simy delivery diagnose --provider codex --session ORIGINAL_SESSION --task-id ORIGINAL_TASK
|
|
@@ -1159,6 +1187,103 @@ Default issue selection is the first eight original acceptance criteria (or `obj
|
|
|
1159
1187
|
|
|
1160
1188
|
Manual diagnosis and delivery reasoning share the scoped SQM knowledge loader. Partial environment overrides cannot mix a token, account, organization or endpoint with saved credentials; unavailable knowledge is reported without blocking the diagnosis. The brief response retains `knowledge.status`, `reason`, `suppliedCount`, and `bundleDigest`, separately from `knowledgeReferenceCount` and test execution (always zero for diagnosis). Truncated evidence is disclosed to both the analyst and the caller.
|
|
1161
1189
|
|
|
1190
|
+
### Local Task delivery to an authorized device folder
|
|
1191
|
+
|
|
1192
|
+
A single-invocation, repository-write durable Local Task can include
|
|
1193
|
+
`execution_spec.local_destination`:
|
|
1194
|
+
|
|
1195
|
+
```json
|
|
1196
|
+
{
|
|
1197
|
+
"device_id": "paired-device-id",
|
|
1198
|
+
"repository": "Organization/repository",
|
|
1199
|
+
"relative_directory": "deliverables"
|
|
1200
|
+
}
|
|
1201
|
+
```
|
|
1202
|
+
|
|
1203
|
+
The repository must match the execution workspace and resolve through the CLI's
|
|
1204
|
+
existing authorized repository inventory. Alternatively, an explicitly delegated
|
|
1205
|
+
native Codex task may specify `authorized_root` instead of `repository`. That
|
|
1206
|
+
absolute directory must be within the existing native session's verified cwd or
|
|
1207
|
+
`sandbox.writableRoots`; the native adapter reports these roots after verifying
|
|
1208
|
+
current account, model, workspace and existing permissions. No new grant is made.
|
|
1209
|
+
The directory must already exist. Read-only tasks and stepped execution do not
|
|
1210
|
+
support delivery. A folder outside existing native roots must be selected through
|
|
1211
|
+
the original provider's workspace permission flow; a repository scan alone does
|
|
1212
|
+
not authorize arbitrary folder writes.
|
|
1213
|
+
|
|
1214
|
+
The worker verifies current output bytes, publishes each file without replacing
|
|
1215
|
+
existing files, and checks size, SHA-256 and path identity through local readback.
|
|
1216
|
+
An identical existing file is a safe replay; a different revision is preserved and
|
|
1217
|
+
delivery fails. Only verified local receipts are persisted in the private task
|
|
1218
|
+
snapshot; the original turn's result summary includes confirmed paths. A folder
|
|
1219
|
+
save is not a claim that an office application rendered or opened the file.
|
|
1220
|
+
|
|
1221
|
+
Offline claims are left to the existing durable lease/reconnect path; no local
|
|
1222
|
+
success or alternate device fallback is synthesized. Stop/lease checks also apply
|
|
1223
|
+
before file publication. Multiple file delivery may be partial if a later file
|
|
1224
|
+
fails; the turn does not report success in that case. Existing local outputs are
|
|
1225
|
+
kept for reconciliation without rerunning an uncertain provider operation.
|
|
1226
|
+
|
|
1227
|
+
Cloud inputs continue through the existing verified attachment store and outputs
|
|
1228
|
+
through the existing artifact contract. This contract does not advertise native
|
|
1229
|
+
browser/Photos automation: `browser_capability` remains unknown until an actual
|
|
1230
|
+
adapter is observed. It does not substitute a server browser for the owner's
|
|
1231
|
+
logged-in browser or grant authority to submit payment/card forms.
|
|
1232
|
+
|
|
1233
|
+
The same destination is accepted on the existing native
|
|
1234
|
+
`POST /v1/local-tasks/start` body as `local_destination`. It is included in the
|
|
1235
|
+
request fingerprint, checked before the native turn, and saved before terminal
|
|
1236
|
+
success. Reconcile returns the same Local Task, original conversation identity
|
|
1237
|
+
and verified destination receipts. A changed destination under the same request
|
|
1238
|
+
key conflicts instead of running another native turn. Direct repository tasks
|
|
1239
|
+
use the same private, ignored output folder layout as durable repository tasks,
|
|
1240
|
+
so outputs stay inside existing native workspace permissions.
|
|
1241
|
+
|
|
1242
|
+
Optional `local_photo_source: {device_id, asset_ids}` is a bounded PhotoKit
|
|
1243
|
+
metadata source for 1–20 explicitly selected asset identifiers. The local adapter
|
|
1244
|
+
requires existing Swift developer tooling (and never launches its installer),
|
|
1245
|
+
checks current Photos authorization, never requests permission or enumerates
|
|
1246
|
+
the library, and returns only selected IDs, dimensions, date and location. It
|
|
1247
|
+
does not export images or claim visual photo recognition. Missing authorization
|
|
1248
|
+
or unavailable selected IDs holds the work before provider invocation; users can
|
|
1249
|
+
supply the photo through the existing attachment flow. No native Photos or
|
|
1250
|
+
browser capability is inferred for a generic model. PhotoKit source reference:
|
|
1251
|
+
[PHPhotoLibrary](https://developer.apple.com/documentation/photos/phphotolibrary).
|
|
1252
|
+
|
|
1253
|
+
Owner-browser execution uses Crew's existing `browser_action` capability and
|
|
1254
|
+
paired Chrome extension queue, independently of CLI/native model execution.
|
|
1255
|
+
P10's companion browser change adds explicit `device_id` and an optional
|
|
1256
|
+
`interaction_scope: "observe_only"` that permits only observation primitives in
|
|
1257
|
+
an existing tab. It cannot type into card fields, click or submit. Its original
|
|
1258
|
+
queue task/device/deadline and result are retained across offline re-entry.
|
|
1259
|
+
|
|
1260
|
+
When the result gateway is offline after file delivery, the existing private task
|
|
1261
|
+
snapshot retains the exact terminal receipt. The same paired device retries only
|
|
1262
|
+
that receipt on reconnect/restart, preserving the original turn and files. It
|
|
1263
|
+
does not claim a new task or re-invoke the provider. A fenced lease remains
|
|
1264
|
+
unconfirmed and never becomes permission to replay the work.
|
|
1265
|
+
|
|
1266
|
+
|
|
1267
|
+
For a paired native Codex write, `workspace_mode: task_sandbox` remains artifact
|
|
1268
|
+
management metadata. It does not move the selected native conversation. After
|
|
1269
|
+
current account/model/cwd/sandbox and lease checks, the CLI places the execution's
|
|
1270
|
+
private output directory under that verified native cwd at
|
|
1271
|
+
`.simy/local-tasks/<execution-id>/outputs` and rebuilds the provider instruction.
|
|
1272
|
+
Both generation and final destination therefore stay inside existing native
|
|
1273
|
+
write permissions. No new sandbox root or cwd override is sent to the provider.
|
|
1274
|
+
Readonly native tasks and Claude's ordinary permission flow do not acquire this
|
|
1275
|
+
Codex-specific writable-root capability.
|
|
1276
|
+
|
|
1277
|
+
Web/Backend admission must preserve the optional destination and selected Photos
|
|
1278
|
+
request in the existing canonical native execution spec and request hash, pinned
|
|
1279
|
+
to the same selected paired device. These fields express intent; no `verified`
|
|
1280
|
+
flag, path in model context, previous receipt or old native root is a grant.
|
|
1281
|
+
Every new execution rechecks current device/account/link/session/lease and actual
|
|
1282
|
+
native roots or PhotoKit authorization. Follow-up preserves the chosen profile;
|
|
1283
|
+
a changed destination requires a fresh explicit admission, never a rewrite of an
|
|
1284
|
+
already-used request key. Revisions use the explicitly requested new artifact
|
|
1285
|
+
name; another revision already present at the same path is preserved and held.
|
|
1286
|
+
|
|
1162
1287
|
Provider diagnostics distinguish `authentication`, `connection`, and `connection_ready`.
|
|
1163
1288
|
If both login and SIMY connection are missing, `actions` lists both remedies in one
|
|
1164
1289
|
report. Connection readiness does not certify model access or task completion.
|
|
@@ -1192,3 +1317,32 @@ Search covers the current recent window (up to 200 active conversations and,
|
|
|
1192
1317
|
with `--include-archived`, up to 200 archived conversations); older entries outside
|
|
1193
1318
|
that window are not searched. This view respects the saved
|
|
1194
1319
|
Codex provider connection setting and never enables automatic continuation.
|
|
1320
|
+
|
|
1321
|
+
The existing per-provider heartbeat now includes `device_io` with
|
|
1322
|
+
`schema_version: local_device_io.v1`. It distinguishes Codex verified native-cwd
|
|
1323
|
+
staging/root delivery, Claude's provider permission flow, and selected Photos
|
|
1324
|
+
metadata requiring existing macOS authorization. It contains no paths or grants;
|
|
1325
|
+
normal provider/device readiness and runtime reauthorization still apply.
|
|
1326
|
+
The ordinary worker also sends `X-SIMY-Device-IO: local_device_io.v1` with its
|
|
1327
|
+
existing native protocol header. Web/gateway should preserve this marker and
|
|
1328
|
+
Backend should require it at claim/invocation when a destination/Photos profile
|
|
1329
|
+
is requested, so an old worker cannot silently ignore those required outputs.
|
|
1330
|
+
|
|
1331
|
+
### Delivery entry routes
|
|
1332
|
+
|
|
1333
|
+
Crew owns `sessionLoopOptions` in the distributed loop kernel. Codex enrollment,
|
|
1334
|
+
Continue, follow-up, desktop controls and automatic continuation use purpose v2
|
|
1335
|
+
by default; old false/v1 enrollment flags cannot select the retired mode.
|
|
1336
|
+
Explicit strict/balanced policies and human acceptance criteria remain binding.
|
|
1337
|
+
Compatible saved tasks migrate without replacing identity, history or budget.
|
|
1338
|
+
|
|
1339
|
+
`~/.simy/bin/simy` is the installed CLI entry. Point any earlier shell alias or
|
|
1340
|
+
PATH symlink to it. `runtime legacy-hooks` switches known installed plugin caches
|
|
1341
|
+
to the same pin; check its output for customized hooks that still need review.
|
|
1342
|
+
`runtime supervisor-watchdog-install` registers the stable Node entry
|
|
1343
|
+
`~/.simy/bin/simy-runtime.mjs`, which resolves the verified runtime pin on each
|
|
1344
|
+
process start, instead of retaining a release-specific service path.
|
|
1345
|
+
When supervision is already live, install the candidate, request
|
|
1346
|
+
`runtime supervisor-handover --release ABSOLUTE_RELEASE`, and wait for activation.
|
|
1347
|
+
The watcher preserves the live supervisor while service registration is updated.
|
|
1348
|
+
No command here enrolls an unrelated conversation or renews an existing budget.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awak-app/simy-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.17",
|
|
4
4
|
"description": "Local SIMY executor for non-coding Local Tasks and Agentic Loop runs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"check:auto-update": "node --check ./src/auto-update.js && node --check ./scripts/auto-update-e2e.js && npm run check:sqm",
|
|
44
44
|
"check:workflow-observation-sender": "node --check ./src/workflow-observation-sender.js",
|
|
45
45
|
"check:sqm": "node --check ./src/sqm/advisory-guidance.js && node --check ./src/sqm/canonical.js && node --check ./src/sqm/validation.js && node --check ./src/sqm/reliability.js && node --check ./src/sqm/repository.js && node --check ./src/sqm/checkers.js && node --check ./src/sqm/bundle-store.js && node --check ./src/sqm/proof.js && node --check ./src/sqm/evidence-client.js && node --check ./src/sqm/session.js && node --check ./src/sqm/index.js && node --check ./src/sqm/command.js && node --check ./src/sqm/agent-skill.js && node --check ./src/sqm/workflow-onboarding.js && node --check ./src/sqm/workflow-update.js && node --check ./src/sqm/history-store.js && node --check ./src/sqm/review-worker.js && node --check ./scripts/sqm-complete-loop-e2e.js && node --check ./scripts/sqm-local-review-e2e.js",
|
|
46
|
-
"check:real-provider-syntax": "node --check ./scripts/real-codex-probe.js && node --check ./scripts/real-claude-probe.js && node --check ./scripts/real-provider-console-fixture.js && node --check ./scripts/real-provider-console-e2e.js && node --check ./scripts/claude-parity-runtime-e2e.js && node --check ./scripts/claude-plugin-runtime-e2e.js",
|
|
46
|
+
"check:real-provider-syntax": "node --check ./scripts/real-codex-probe.js && node --check ./scripts/real-claude-probe.js && node --check ./scripts/real-provider-console-fixture.js && node --check ./scripts/real-provider-console-e2e.js && node --check ./scripts/claude-parity-runtime-e2e.js && node --check ./scripts/claude-plugin-runtime-e2e.js && node --check ./scripts/codex-plugin-runtime-e2e.js",
|
|
47
47
|
"check:syntax": "node --check ./src/purpose-local-proof.js && node --check ./src/device-signing-identity.js && node --check ./src/index.js && node --check ./src/install-update.js && node --check ./src/agent.js && node --check ./src/connection-discovery.js && node --check ./src/connection-diagnostics.js && node --check ./src/launch-authorization.js && node --check ./src/codex-plugins/simy/hooks/connection-reminder.mjs && node --check ./src/codex-plugins/simy/hooks/dev-work.mjs && node --check ./src/dev-work-events.js && node --check ./src/browser.js && node --check ./src/web-api.js && node --check ./src/durable-local-task-worker.js && node --check ./src/durable-local-task-steps-contract.js && node --check ./src/bounded-local-task-subtasks-contract.js && node --check ./src/bounded-local-task-subtask-pool.js && node --check ./src/local-task-artifact-contract.js && node --check ./src/execution-capability-contract.js && node --check ./src/shutdown.js && node --check ./src/run-registry.js && node --check ./src/direct-executor.js && node --check ./src/execution-guardrail.js && node --check ./src/backend-executable.js && node --check ./src/login-shell-path.js && node --check ./src/hook-runtime.js && node --check ./src/provider-stream.js && node --check ./src/console/index.js && node --check ./src/console/app.js && node --check ./src/console/commands.js && node --check ./src/local-attachments.js && node --check ./src/repository-inventory.js && node --check ./src/session-store.js && node --check ./src/web-origin.js && node --check ./src/workspace-context.js && node --check ./src/orchestrator/index.js && node --check ./src/orchestrator/shared.js && node --check ./src/orchestrator/recovery.js && node --check ./src/orchestrator/risk.js && node --check ./src/orchestrator/contract.js && node --check ./src/orchestrator/completion-contract.js && node --check ./src/orchestrator/pr-preparation.js && node --check ./src/orchestrator/minimum-completion.js && node --check ./src/orchestrator/delivery/scripts/check-minimum-completion.mjs && node --check ./src/orchestrator/instruction.js && node --check ./src/orchestrator/problem-solving.js && node --check ./src/orchestrator/budget.js && node --check ./src/orchestrator/execution-io.js && node --check ./src/orchestrator/presentation.js && node --check ./src/orchestrator/result.js && node --check ./src/orchestrator/retry.js && node --check ./src/orchestrator/evidence.js && node --check ./src/orchestrator/independent-audit.js && node --check ./src/orchestrator/audit.js && node --check ./src/orchestrator/loop.js && node --check ./src/runner.js && node --check ./src/coding-run-store.js && node --check ./scripts/fixtures/completion-contract-fixture.js && node --check ./scripts/fixtures/fake-coding-backend.js && node --check ./scripts/e2e-terminal-evidence.js && node --check ./scripts/audit-gate-e2e.js && node --check ./scripts/completion-contract-e2e.js && node --check ./scripts/retry-circuit-e2e.js && node --check ./scripts/follow-up-charter-e2e.js && node --check ./scripts/task-routing-e2e.js && node --check ./scripts/executor-version-preflight-e2e.js && node --check ./scripts/blocked-recovery-e2e.js && node --check ./scripts/process-localization-e2e.js && node --check ./scripts/hypothesis-retries-e2e.js && node --check ./scripts/budget-enforcement-e2e.js && node --check ./scripts/local-task-default-workspace-e2e.js && node --check ./scripts/local-task-idempotency-e2e.js && node --check ./scripts/console-e2e-fixture.js && node --check ./scripts/console-e2e.js && node --check ./scripts/console-recovery-e2e-fixture.js && node --check ./scripts/console-recovery-e2e.js && node --check ./scripts/real-provider-console-fixture.js && node --check ./scripts/real-provider-console-e2e.js && node --check ./scripts/fullstack-cli-smoke.js && node --check ./scripts/check-package-contents.js && node --check ./src/codex-plugins/simy/hooks/delivery-loop.mjs && node --check ./src/delivery-hooks/command.js && node --check ./src/delivery-hooks/desktop.js && node --check ./src/delivery-hooks/supervisor.js && node --check ./src/delivery-hooks/recovery.js && node --check ./src/delivery-hooks/supervisor-owner.js && node --check ./src/delivery-hooks/supervisor-watchdog.js && node --check ./src/delivery-hooks/supervisor-service.js && node --check ./scripts/supervisor-watchdog-live-e2e.js && node --check ./src/codex-plugins/simy/hooks/recovery-incident.mjs && node --check ./src/session-supervision/automatic-continuation.js && node --check ./src/session-supervision/stop-notifications.js",
|
|
48
48
|
"check:package": "node ./scripts/check-package-contents.js",
|
|
49
49
|
"build:macos-installer:dev": "bash packaging/build-macos-arm64.sh --unsigned-dev",
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"build:macos-installer:production": "bash packaging/build-macos-arm64.sh --production",
|
|
53
53
|
"test:e2e:claude-parity": "node ./scripts/claude-parity-runtime-e2e.js",
|
|
54
54
|
"test:e2e:claude-plugin": "node ./scripts/claude-plugin-runtime-e2e.js",
|
|
55
|
+
"test:e2e:codex-plugin": "node ./scripts/codex-plugin-runtime-e2e.js",
|
|
55
56
|
"test:e2e:flexible-delivery": "node --test --test-name-pattern=\"flexible prototype\" test/purpose-search.test.js",
|
|
56
57
|
"test:e2e:sqm-faults": "node ./scripts/sqm-complete-loop-e2e.js"
|
|
57
58
|
},
|
package/src/agent.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { readLocalPhotoSource } from "./local-photos.js";
|
|
2
|
+
import { resolveLocalTaskDestination, saveLocalTaskDestination } from "./local-task-destination.js";
|
|
3
|
+
import { observePairedNativeIdentities } from './native-provider-identity.js';
|
|
4
|
+
import { providerDelegationFromRequest } from './provider-delegation.js';
|
|
1
5
|
import { createLedgerExecutionWorker, discoverLedgerWorkers } from './update-detachment/worker-ledger.js';
|
|
2
6
|
import { normalizeProviderModelSelection } from "./provider-model-selection.js";
|
|
3
7
|
import { assertConnectionScope, connectionScope, connectionDiagnostic } from "./connection-diagnostics.js";
|
|
@@ -147,6 +151,7 @@ import {
|
|
|
147
151
|
import {
|
|
148
152
|
createDurableLocalTaskApi,
|
|
149
153
|
createDurableLocalTaskWorker,
|
|
154
|
+
prepareRepositoryWorkspace,
|
|
150
155
|
} from "./durable-local-task-worker.js";
|
|
151
156
|
import { createLocalTaskAttachmentStore } from "./local-task-attachment-store.js";
|
|
152
157
|
import {
|
|
@@ -297,12 +302,20 @@ export async function startAgent({
|
|
|
297
302
|
if (!link?.connected || expected && link.epoch !== expected.epoch) throw Object.assign(new Error("Reconnect this provider to SIMY before using it."), { code: "provider_link_disconnected" });
|
|
298
303
|
return link;
|
|
299
304
|
};
|
|
305
|
+
let nativeIdentityCapability = null, nativeIdentityActor = null, nativeIdentityObservedAt = 0, nativeIdentityReadInFlight = null;
|
|
306
|
+
const nativeActor = () => JSON.stringify([apiOrigin, session?.account_id, session?.organization_id, session?.device_id]);
|
|
300
307
|
let providerStatuses = { codex: { state: "unknown" }, claude: { state: "unknown" } };
|
|
301
308
|
const providerRecoveryRevisions = { codex: 0, claude: 0 };
|
|
302
309
|
const refreshAvailableCapabilities = () => {
|
|
303
310
|
const features = contractSourceCapabilities.features;
|
|
304
311
|
availableCapabilities = withCliContract({
|
|
305
312
|
...contractSourceCapabilities,
|
|
313
|
+
...(features?.native_delegation === true || dependencies.observePairedNativeIdentities ? {
|
|
314
|
+
native_delegation: nativeIdentityActor === nativeActor() && isSessionValid(session, Date.now(), apiOrigin)
|
|
315
|
+
? { ...nativeIdentityCapability, identities: (nativeIdentityCapability?.identities || []).filter(identity =>
|
|
316
|
+
providerLinks[identity.provider]?.connected && (providerLinks[identity.provider]?.epoch ?? null) === identity.connection_epoch) }
|
|
317
|
+
: { schema_version: 'native_delegation_capability.v1', device_id: session?.device_id || null, identities: [] },
|
|
318
|
+
} : {}),
|
|
306
319
|
...(purposeSigner ? { purpose_verification: purposeSigner } : {}),
|
|
307
320
|
...(features ? { features: { ...features, purpose_host_verification: Boolean(purposeSigner) && isSessionValid(session, Date.now(), apiOrigin), ...(Object.hasOwn(features, "sqm_local_review")
|
|
308
321
|
? { sqm_local_review: features.sqm_local_review === true && sqmEnabled && !pendingOrganization && desktopWorkAllowed() } : {}) } } : {}),
|
|
@@ -489,6 +502,24 @@ export async function startAgent({
|
|
|
489
502
|
notifyDesktopStatusChange();
|
|
490
503
|
}
|
|
491
504
|
})]);
|
|
505
|
+
if ((contractSourceCapabilities.features?.native_delegation === true || dependencies.observePairedNativeIdentities) && session?.device_id && !nativeIdentityReadInFlight && (force || nativeIdentityActor !== nativeActor() || Date.now() - nativeIdentityObservedAt > 60000)) {
|
|
506
|
+
const actor = nativeActor();
|
|
507
|
+
const observation = (async () => {
|
|
508
|
+
const observed = await (dependencies.observePairedNativeIdentities || observePairedNativeIdentities)({
|
|
509
|
+
deviceId: session.device_id, providerStatuses, providerLinks });
|
|
510
|
+
if (!shuttingDown && actor === nativeActor()) {
|
|
511
|
+
nativeIdentityCapability = observed; nativeIdentityActor = actor; nativeIdentityObservedAt = Date.now();
|
|
512
|
+
refreshAvailableCapabilities();
|
|
513
|
+
notifyDesktopStatusChange();
|
|
514
|
+
// Official account observation must not delay the existing desktop
|
|
515
|
+
// startup/status path. Publish its catalog when it is actually ready.
|
|
516
|
+
if (deferStartupSynchronization) void heartbeat().catch(error => reportHeartbeatError(error, quiet));
|
|
517
|
+
}
|
|
518
|
+
})();
|
|
519
|
+
nativeIdentityReadInFlight = observation;
|
|
520
|
+
void observation.finally(() => { if (nativeIdentityReadInFlight === observation) nativeIdentityReadInFlight = null; }).catch(() => {});
|
|
521
|
+
if (!deferStartupSynchronization) await observation;
|
|
522
|
+
}
|
|
492
523
|
const binaryStateChanged = ["codex", "claude"].some((provider) =>
|
|
493
524
|
previous[provider]?.version !== providerStatuses[provider]?.version ||
|
|
494
525
|
previous[provider]?.state !== providerStatuses[provider]?.state);
|
|
@@ -544,7 +575,21 @@ export async function startAgent({
|
|
|
544
575
|
await callback?.(...args);
|
|
545
576
|
await guard();
|
|
546
577
|
};
|
|
547
|
-
return { ...options, sessionRoot,
|
|
578
|
+
return { ...options, sessionRoot, delegationOptions: { supervisionRoot: join(sessionRoot, "delivery-hooks"), pairedDeviceId: session?.device_id, providerLinkEpoch: link?.epoch ?? null, ...options.delegationOptions,
|
|
579
|
+
onWorkspaceAuthorized: async roots => {
|
|
580
|
+
await guard();
|
|
581
|
+
if (task.local_destination?.authorized_root) {
|
|
582
|
+
await resolveLocalTaskDestination({ local_destination: task.local_destination, operation: task.operation,
|
|
583
|
+
permission_mode: task.permission_mode, workspace_mode: task.workspace_mode, repository: task.repository }, {
|
|
584
|
+
deviceId: session?.device_id, resolveRepository: ensureRepositoryIndexed, nativeWritableRoots: roots,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
task.native_writable_roots = [...roots];
|
|
588
|
+
await options.delegationOptions?.onWorkspaceAuthorized?.(roots);
|
|
589
|
+
await guard();
|
|
590
|
+
} },
|
|
591
|
+
beforeCompletion: options.beforeCompletion ? wrap(options.beforeCompletion) : null,
|
|
592
|
+
beforeProviderInvocation: wrap(options.beforeProviderInvocation), beforeSpawn: wrap(options.beforeSpawn) };
|
|
548
593
|
};
|
|
549
594
|
const codingRuntimeOptions = run => {
|
|
550
595
|
const wrap = (custom, marker) => async context => {
|
|
@@ -856,7 +901,7 @@ export async function startAgent({
|
|
|
856
901
|
notifyDesktopStatusChange();
|
|
857
902
|
let deviceHeartbeatSucceeded = false;
|
|
858
903
|
const requestedSession = session;
|
|
859
|
-
const advertisedProviders = JSON.stringify(providerStatuses);
|
|
904
|
+
const advertisedProviders = JSON.stringify([providerStatuses, nativeIdentityCapability]);
|
|
860
905
|
heartbeatInFlight = Promise.resolve()
|
|
861
906
|
.then(async () => { if (enforceDesktopAccess) await desktopAccess.refresh(true); })
|
|
862
907
|
.then(() =>
|
|
@@ -878,7 +923,7 @@ export async function startAgent({
|
|
|
878
923
|
// browser authorization. Publish verified readiness after their probe.
|
|
879
924
|
const publishVerifiedProviders = async () => {
|
|
880
925
|
await readProviderStatuses();
|
|
881
|
-
if (!shuttingDown && JSON.stringify(providerStatuses) !== advertisedProviders) {
|
|
926
|
+
if (!shuttingDown && JSON.stringify([providerStatuses, nativeIdentityCapability]) !== advertisedProviders) {
|
|
882
927
|
await heartbeatDevice({ apiOrigin, apiBaseUrl: session.api_base_url, token: session.token,
|
|
883
928
|
port, capabilities: refreshAvailableCapabilities(), repoInventory: repositoryInventory });
|
|
884
929
|
}
|
|
@@ -1011,6 +1056,8 @@ export async function startAgent({
|
|
|
1011
1056
|
createDurableLocalTaskWorker;
|
|
1012
1057
|
durableLocalTaskWorker = createWorker({
|
|
1013
1058
|
api: durableApi,
|
|
1059
|
+
enableBoundedSubtasks:
|
|
1060
|
+
availableCapabilities.features.bounded_local_task_subtasks === true,
|
|
1014
1061
|
registry: localTaskRegistry,
|
|
1015
1062
|
storageRoot: taskStorageRoot,
|
|
1016
1063
|
workspaceRoot: dependencies.localTaskWorkspaceRoot,
|
|
@@ -1029,6 +1076,7 @@ export async function startAgent({
|
|
|
1029
1076
|
return (dependencies.runLocalTask || startLocalTask)(task, providerRuntimeOptions(task, options, scope, link));
|
|
1030
1077
|
},
|
|
1031
1078
|
attachmentStore: localTaskAttachmentStore,
|
|
1079
|
+
readPhotoSource: dependencies.readLocalPhotoSource || readLocalPhotoSource,
|
|
1032
1080
|
getDeviceId: () => session?.device_id || null,
|
|
1033
1081
|
beginWork: () => acquireUpdateWork(updateManager),
|
|
1034
1082
|
onError: (error) => reportDurableLocalTaskError(error, quiet),
|
|
@@ -1777,12 +1825,17 @@ export async function startAgent({
|
|
|
1777
1825
|
});
|
|
1778
1826
|
return;
|
|
1779
1827
|
}
|
|
1780
|
-
let requestFingerprint;
|
|
1828
|
+
let requestFingerprint, providerDelegation;
|
|
1781
1829
|
try {
|
|
1830
|
+
providerDelegation = providerDelegationFromRequest(body, idempotencyKey);
|
|
1782
1831
|
requestFingerprint = createLocalTaskRequestFingerprint({
|
|
1783
1832
|
instruction,
|
|
1784
1833
|
backend: body.backend,
|
|
1785
1834
|
model: body.model,
|
|
1835
|
+
delegation: providerDelegation,
|
|
1836
|
+
localDestination: body.local_destination,
|
|
1837
|
+
localPhotoSource: body.local_photo_source,
|
|
1838
|
+
nativeIdentity: body.native_identity,
|
|
1786
1839
|
operation: guardrail.operation,
|
|
1787
1840
|
permissionMode: guardrail.permission_mode,
|
|
1788
1841
|
workspaceMode,
|
|
@@ -1882,7 +1935,7 @@ export async function startAgent({
|
|
|
1882
1935
|
workspace = {
|
|
1883
1936
|
...prepared,
|
|
1884
1937
|
id,
|
|
1885
|
-
|
|
1938
|
+
...(await prepareRepositoryWorkspace({ repositoryPath: indexedRepository.local_path, expectedRepository: repository, localTaskId: id, prepareOutputs: body.permission_mode === "workspace_write" })),
|
|
1886
1939
|
};
|
|
1887
1940
|
} else {
|
|
1888
1941
|
const id = createLocalTaskId();
|
|
@@ -1895,6 +1948,19 @@ export async function startAgent({
|
|
|
1895
1948
|
})),
|
|
1896
1949
|
};
|
|
1897
1950
|
}
|
|
1951
|
+
if (body.local_destination?.authorized_root && providerDelegation?.provider !== "codex") {
|
|
1952
|
+
throw Object.assign(new Error("The selected folder requires an already authorized native Codex workspace."), { code: "local_task_destination_unconfirmed" });
|
|
1953
|
+
}
|
|
1954
|
+
let deliveryTask = null;
|
|
1955
|
+
const destinationSpec = { ...body, workspace_mode: workspaceMode, repository };
|
|
1956
|
+
const resolveDestination = () => resolveLocalTaskDestination(destinationSpec, {
|
|
1957
|
+
deviceId: session?.device_id,
|
|
1958
|
+
resolveRepository: ensureRepositoryIndexed,
|
|
1959
|
+
nativeWritableRoots: deliveryTask?.native_writable_roots,
|
|
1960
|
+
});
|
|
1961
|
+
let destination = body.local_destination && !body.local_destination.authorized_root ? await resolveDestination() : null;
|
|
1962
|
+
const photoContext = body.local_photo_source ? await (dependencies.readLocalPhotoSource || readLocalPhotoSource)(body.local_photo_source, { deviceId: session?.device_id }) : null;
|
|
1963
|
+
await authorizeDesktopWork(requestAccessScope);
|
|
1898
1964
|
const stagedAttachments = await stagePreparedRunAttachments({
|
|
1899
1965
|
runId: workspace.id,
|
|
1900
1966
|
prepared: preparedAttachments,
|
|
@@ -1905,6 +1971,10 @@ export async function startAgent({
|
|
|
1905
1971
|
instruction,
|
|
1906
1972
|
backend: body.backend,
|
|
1907
1973
|
model: body.model,
|
|
1974
|
+
delegation: providerDelegation,
|
|
1975
|
+
localDestination: body.local_destination,
|
|
1976
|
+
localPhotoSource: body.local_photo_source,
|
|
1977
|
+
nativeIdentity: body.native_identity,
|
|
1908
1978
|
operation: guardrail.operation,
|
|
1909
1979
|
permissionMode: guardrail.permission_mode,
|
|
1910
1980
|
workspaceMode,
|
|
@@ -1915,6 +1985,7 @@ export async function startAgent({
|
|
|
1915
1985
|
outputsRoot: workspace.outputs_root,
|
|
1916
1986
|
outputsRootExpected: workspace.outputs_root_expected,
|
|
1917
1987
|
attachments: stagedAttachments,
|
|
1988
|
+
localPhotoContext: photoContext,
|
|
1918
1989
|
expectedOutputs: body.expected_outputs,
|
|
1919
1990
|
scenarioPack,
|
|
1920
1991
|
providerTokenBudget: body.provider_token_budget,
|
|
@@ -1927,6 +1998,7 @@ export async function startAgent({
|
|
|
1927
1998
|
workflowStartIntent,
|
|
1928
1999
|
purposeVerification,
|
|
1929
2000
|
});
|
|
2001
|
+
deliveryTask = task;
|
|
1930
2002
|
if (purposeVerification) await preparePurposeVerification(task, { session, apiOrigin, clientRequestId: idempotencyKey, keyPath: purposeKeyPath,
|
|
1931
2003
|
authorize: async () => {
|
|
1932
2004
|
await authorizeDesktopWork(requestAccessScope);
|
|
@@ -1937,7 +2009,27 @@ export async function startAgent({
|
|
|
1937
2009
|
requestClaim.complete(task);
|
|
1938
2010
|
requestClaimCompleted = true;
|
|
1939
2011
|
const runLocalTask = dependencies.runLocalTask || startLocalTask;
|
|
1940
|
-
const runtimeOptions = providerRuntimeOptions(task, { session, apiOrigin,
|
|
2012
|
+
const runtimeOptions = providerRuntimeOptions(task, { session, apiOrigin,
|
|
2013
|
+
beforeCompletion: body.local_destination ? async completed => {
|
|
2014
|
+
await authorizeDesktopWork(requestAccessScope);
|
|
2015
|
+
const current = await resolveDestination();
|
|
2016
|
+
if (destination && (current.root !== destination.root || current.directory !== destination.directory)) {
|
|
2017
|
+
throw Object.assign(new Error("The authorized save folder changed."), { code: "local_task_destination_unconfirmed" });
|
|
2018
|
+
}
|
|
2019
|
+
destination ||= current;
|
|
2020
|
+
completed.local_destination_receipts = [];
|
|
2021
|
+
await saveLocalTaskDestination({ task: completed, destination: current,
|
|
2022
|
+
assertActive: async () => {
|
|
2023
|
+
await authorizeDesktopWork(requestAccessScope);
|
|
2024
|
+
if (completed.stop_requested) throw Object.assign(new Error("Local delivery was stopped."), { code: "local_task_destination_unconfirmed" });
|
|
2025
|
+
},
|
|
2026
|
+
onReceipt: async receipt => {
|
|
2027
|
+
completed.local_destination_receipts.push(receipt);
|
|
2028
|
+
await persistLocalTask(completed);
|
|
2029
|
+
},
|
|
2030
|
+
});
|
|
2031
|
+
} : null,
|
|
2032
|
+
beforeSpawn: async localTask => {
|
|
1941
2033
|
if (purposeWorkspaceBusy(localTask.workspace_path)) throw Object.assign(new Error("Independent verification owns this workspace."), { code: "purpose_verification_workspace_busy" });
|
|
1942
2034
|
if (purposeVerification) {
|
|
1943
2035
|
await authorizeDesktopWork(requestAccessScope);
|
|
@@ -1999,6 +2091,10 @@ export async function startAgent({
|
|
|
1999
2091
|
return;
|
|
2000
2092
|
}
|
|
2001
2093
|
const body = await readJson(req);
|
|
2094
|
+
if (body.delegation || body.delegation_target) {
|
|
2095
|
+
json(res, 409, { error: 'Provider delegation requires the existing Local Task admission.', code: 'delegation_local_task_required' });
|
|
2096
|
+
return;
|
|
2097
|
+
}
|
|
2002
2098
|
const instruction = String(body.instruction || "").trim();
|
|
2003
2099
|
if (!instruction) {
|
|
2004
2100
|
json(res, 400, { error: "instruction is required" });
|
|
@@ -2705,7 +2801,7 @@ export async function startAgent({
|
|
|
2705
2801
|
if (codingRunMonitorTimer) clearInterval(codingRunMonitorTimer);
|
|
2706
2802
|
updateManager?.stop?.();
|
|
2707
2803
|
await Promise.all([stopDesktopTasks(), codexWorkPatternSync?.close?.(),
|
|
2708
|
-
codexWorkPatternSyncInFlight, codingRunMonitorInFlight]);
|
|
2804
|
+
codexWorkPatternSyncInFlight, codingRunMonitorInFlight, nativeIdentityReadInFlight?.catch(() => {})]);
|
|
2709
2805
|
try {
|
|
2710
2806
|
await (dependencies.removeConnectionDiscovery || removeConnectionDiscovery)(
|
|
2711
2807
|
connectionDiscovery,
|
|
@@ -2830,6 +2926,29 @@ export async function startAgent({
|
|
|
2830
2926
|
sqmLocalReviewWorker?.retry?.(); notifyDesktopStatusChange();
|
|
2831
2927
|
},
|
|
2832
2928
|
async retrySqmEnvironment() { await authorizeDesktopWork(); sqmLocalReviewWorker?.retry?.(); notifyDesktopStatusChange(); },
|
|
2929
|
+
getTeamThreadsScope() {
|
|
2930
|
+
return { key: desktopScopeRevision(),
|
|
2931
|
+
fenceKey: createHash('sha256').update(JSON.stringify([apiOrigin, session?.account_id ?? null, session?.organization_id ?? null, session?.device_id ?? null])).digest('hex'),
|
|
2932
|
+
organizationId: session?.organization_id ?? null,
|
|
2933
|
+
authenticated: !shuttingDown && !pendingOrganization && isSessionValid(session, Date.now(), apiOrigin) && Boolean(session?.device_id),
|
|
2934
|
+
allowed: !shuttingDown && desktopWorkAllowed() && desktopAccessReady(desktopAccess.snapshot()) };
|
|
2935
|
+
},
|
|
2936
|
+
async requestTeamThreads(body, signal) {
|
|
2937
|
+
const selected = session;
|
|
2938
|
+
const scope = desktopScopeRevision();
|
|
2939
|
+
const off = body?.operation === 'consent' && body.enabled === false;
|
|
2940
|
+
if (!off) await authorizeDesktopWork(scope);
|
|
2941
|
+
if (shuttingDown || pendingOrganization || !isSessionValid(selected, Date.now(), apiOrigin) || selected !== session) throw new Error('Team sign-in required');
|
|
2942
|
+
const response = await (dependencies.fetch || globalThis.fetch)(webApiUrl('team-threads', { webOrigin: apiOrigin, apiBaseUrl: selected.api_base_url }), {
|
|
2943
|
+
method: body ? 'POST' : 'GET', headers: webApiHeaders({ token: selected.token }, body ? { 'Content-Type': 'application/json' } : {}),
|
|
2944
|
+
...(body ? { body: JSON.stringify(body) } : {}), signal: AbortSignal.any([signal, AbortSignal.timeout(10_000)]), cache: 'no-store', redirect: 'error',
|
|
2945
|
+
});
|
|
2946
|
+
if (scope !== desktopScopeRevision() || selected !== session || signal.aborted) throw new Error('Team sign-in changed');
|
|
2947
|
+
if (!response.ok) throw Object.assign(new Error('Shared conversations unavailable'), { status: response.status });
|
|
2948
|
+
const raw = await response.text();
|
|
2949
|
+
if (raw.length > 1_048_576) throw new Error('Team response too large');
|
|
2950
|
+
return JSON.parse(raw);
|
|
2951
|
+
},
|
|
2833
2952
|
async createDesktopWebSession() {
|
|
2834
2953
|
const scope = await authorizeDesktopWork();
|
|
2835
2954
|
const selected = session;
|
|
@@ -3298,6 +3417,7 @@ function capabilitiesFromInspections(codex, claude) {
|
|
|
3298
3417
|
},
|
|
3299
3418
|
},
|
|
3300
3419
|
features: {
|
|
3420
|
+
team_threads: true,
|
|
3301
3421
|
repository_scan_approval: true,
|
|
3302
3422
|
executor_version_preflight: true,
|
|
3303
3423
|
desktop_executor: true,
|
|
@@ -3308,6 +3428,7 @@ function capabilitiesFromInspections(codex, claude) {
|
|
|
3308
3428
|
local_task_streaming: true,
|
|
3309
3429
|
local_task_idempotency: true,
|
|
3310
3430
|
local_task_model: true,
|
|
3431
|
+
native_delegation: true,
|
|
3311
3432
|
progress_continuation_policy: true,
|
|
3312
3433
|
local_task_reconciliation: true,
|
|
3313
3434
|
local_task_control_plane: true,
|