mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(agents): one host-routed agent-detection entry point (#27054)
* fix(agents): one host-routed agent-detection entry point
preflight.detectAgents / refreshAgents take an optional { worktreeId } and the
host resolves that workspace's project runtime (WSL included) or SSH target.
Gated by preflight.workspace-scoped.v1; old peers keep the host default.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agents): route detection by the worktree's own host record
A repo id shared by local and SSH rows made the first row pick the detection
host. Resolve the worktree from the host's list and apply the shared
execution-host routing rule; ambiguous rows refuse. Dashboard falls back to the
host-default list until a workspace's own list loads.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agents): refuse an ambiguous workspace instead of probing the host
Only selector_not_found keeps the host default; selector_ambiguous now fails
closed. Adds runtime-level coverage of the lookup.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agents): refuse an ambiguously owned folder workspace
Co-Authored-By: Claude <noreply@anthropic.com>
* test(agents): quick-launch runtime target carries the workspace
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -327,6 +327,11 @@
|
||||
"message": "Couldn't start the agent."
|
||||
}
|
||||
},
|
||||
"742db574bda3": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-9::/w\"}}"
|
||||
},
|
||||
"7cdbda15d605": {
|
||||
"name": "agent.launchReplay#1",
|
||||
"ordinal": 7,
|
||||
@@ -419,38 +424,6 @@
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"9c16efc31903": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"a4273b38df83": {
|
||||
"launched": "unlaunched"
|
||||
},
|
||||
@@ -742,6 +715,38 @@
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"e5392ac00e18": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eaa398d8c3aa": {
|
||||
"launched": {
|
||||
"kind": "unconfirmed",
|
||||
@@ -760,11 +765,6 @@
|
||||
"launched": {
|
||||
"kind": "sent"
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -785,8 +785,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.normal:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -797,8 +797,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.result-absent:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "8638ab90602a"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "8638ab90602a"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -809,8 +809,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.result-null:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "c39600966772"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "c39600966772"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -821,8 +821,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.inner-ok-missing:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "ab12d545c466"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "ab12d545c466"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -833,8 +833,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.inner-false-string-error:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "5b817dac4c7d"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "5b817dac4c7d"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -845,8 +845,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.inner-false-object-error:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "7cdbda15d605"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "7cdbda15d605"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -857,8 +857,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.outer-refused:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "a8f475e8a3a8"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "a8f475e8a3a8"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -869,8 +869,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.outer-refused-no-message:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "4a304acaf84f"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "4a304acaf84f"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -881,8 +881,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.method-not-found:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "b613a82cee3d"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "b613a82cee3d"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -896,14 +896,14 @@
|
||||
"sender": [
|
||||
"3e6a0417d2ee",
|
||||
"02188f091419",
|
||||
"9c16efc31903",
|
||||
"e5392ac00e18",
|
||||
"3b9fb3dfed63",
|
||||
"b81eb9edc2b2"
|
||||
],
|
||||
"payloads": [
|
||||
"e0eef7c9fafe",
|
||||
"1c2564f1daca",
|
||||
"f49d184679d1",
|
||||
"742db574bda3",
|
||||
"28339abecf78",
|
||||
"dca101029f70"
|
||||
],
|
||||
@@ -920,14 +920,14 @@
|
||||
"sender": [
|
||||
"3e6a0417d2ee",
|
||||
"02188f091419",
|
||||
"9c16efc31903",
|
||||
"e5392ac00e18",
|
||||
"675c01bc8b85",
|
||||
"b81eb9edc2b2"
|
||||
],
|
||||
"payloads": [
|
||||
"e0eef7c9fafe",
|
||||
"1c2564f1daca",
|
||||
"f49d184679d1",
|
||||
"742db574bda3",
|
||||
"28339abecf78",
|
||||
"dca101029f70"
|
||||
],
|
||||
|
||||
+305
-305
@@ -41,7 +41,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"0220267a7bca": {
|
||||
"039b2277b75a": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
@@ -52,42 +52,7 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "method_not_found",
|
||||
"message": "Unknown method"
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"084fe2afb0ce": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -110,6 +75,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"1469e05f35f7": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"1963ee2edae9": {
|
||||
"launched": {
|
||||
"kind": "not-started",
|
||||
@@ -228,7 +227,7 @@
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"570f5efcd445": {
|
||||
"67471f63b6f7": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
@@ -239,215 +238,7 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "outer refused"
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"57f451cceda3": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"585dfba86809": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"6dd8724be3b1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"9c16efc31903": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"a4273b38df83": {
|
||||
"launched": "unlaunched"
|
||||
},
|
||||
"a9705081551e": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ac08a3ca5c67": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -471,6 +262,148 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"742db574bda3": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-9::/w\"}}"
|
||||
},
|
||||
"786221fb36b0": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"880c4a7837af": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "method_not_found",
|
||||
"message": "Unknown method"
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"96aa7f51b001": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "transport failure",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"a4273b38df83": {
|
||||
"launched": "unlaunched"
|
||||
},
|
||||
"a55db9667146": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"b13ddf18a62e": {
|
||||
"launched": {
|
||||
"kind": "not-started",
|
||||
@@ -535,12 +468,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"e0eef7c9fafe": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"e1b544c0b6ec": {
|
||||
"bfd08cc3060a": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
@@ -551,7 +479,7 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -562,36 +490,22 @@
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "transport failure",
|
||||
"isRpcDeliveryUnknown": true
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"e35d7003f64c": {
|
||||
"launched": {
|
||||
"kind": "not-started",
|
||||
"message": "transport failure"
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"eb8d47f90956": {
|
||||
"launched": {
|
||||
"kind": "sent"
|
||||
}
|
||||
},
|
||||
"efd2ac432554": {
|
||||
"d85a8cd9155e": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
@@ -602,7 +516,7 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -626,10 +540,96 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"e0eef7c9fafe": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"e35d7003f64c": {
|
||||
"launched": {
|
||||
"kind": "not-started",
|
||||
"message": "transport failure"
|
||||
}
|
||||
},
|
||||
"e5392ac00e18": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"e69d09287c64": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "outer refused"
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"eb8d47f90956": {
|
||||
"launched": {
|
||||
"kind": "sent"
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -650,8 +650,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.normal:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -662,8 +662,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.result-absent:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "585dfba86809"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "a55db9667146"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -674,8 +674,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.result-null:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "6dd8724be3b1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "1469e05f35f7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -686,8 +686,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.inner-ok-missing:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "084fe2afb0ce"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "039b2277b75a"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -698,8 +698,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.inner-false-string-error:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "ac08a3ca5c67"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "67471f63b6f7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -710,8 +710,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.inner-false-object-error:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "a9705081551e"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "bfd08cc3060a"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -722,8 +722,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.outer-refused:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "570f5efcd445"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e69d09287c64"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -734,8 +734,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.outer-refused-no-message:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "efd2ac432554"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "d85a8cd9155e"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -746,8 +746,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.method-not-found:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "0220267a7bca"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "880c4a7837af"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -758,8 +758,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.transport-rejection:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e1b544c0b6ec"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "96aa7f51b001"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -770,8 +770,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.transport-rejection-no-message:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "57f451cceda3"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "786221fb36b0"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
|
||||
@@ -329,6 +329,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"742db574bda3": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-9::/w\"}}"
|
||||
},
|
||||
"747c556da67a": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
@@ -399,38 +404,6 @@
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"9c16efc31903": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"a4273b38df83": {
|
||||
"launched": "unlaunched"
|
||||
},
|
||||
@@ -578,6 +551,38 @@
|
||||
"message": "transport failure"
|
||||
}
|
||||
},
|
||||
"e5392ac00e18": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
@@ -625,11 +630,6 @@
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -650,8 +650,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.normal:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
|
||||
@@ -358,6 +358,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"742db574bda3": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-9::/w\"}}"
|
||||
},
|
||||
"753cc167886c": {
|
||||
"launched": {
|
||||
"kind": "not-started",
|
||||
@@ -440,38 +445,6 @@
|
||||
"message": "Cannot read properties of undefined (reading 'settings')"
|
||||
}
|
||||
},
|
||||
"9c16efc31903": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"a4273b38df83": {
|
||||
"launched": "unlaunched"
|
||||
},
|
||||
@@ -585,6 +558,38 @@
|
||||
"message": "transport failure"
|
||||
}
|
||||
},
|
||||
"e5392ac00e18": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
@@ -598,11 +603,6 @@
|
||||
"kind": "sent"
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
},
|
||||
"fb9f08890f70": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 2,
|
||||
@@ -656,8 +656,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.normal:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -668,8 +668,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.result-absent:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "98bd3a5fda2b", "9c16efc31903"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "98bd3a5fda2b", "e5392ac00e18"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -680,8 +680,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.result-null:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "69518e60477b", "9c16efc31903"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "69518e60477b", "e5392ac00e18"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -692,8 +692,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.inner-ok-missing:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "fb9f08890f70", "9c16efc31903", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "fb9f08890f70", "e5392ac00e18", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -704,8 +704,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.inner-false-string-error:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "86798e4821d1", "9c16efc31903", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "86798e4821d1", "e5392ac00e18", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -716,8 +716,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.inner-false-object-error:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "1c5359ea3f7d", "9c16efc31903", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "1c5359ea3f7d", "e5392ac00e18", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -728,8 +728,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.outer-refused:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "ceb5fe72c101", "9c16efc31903"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "ceb5fe72c101", "e5392ac00e18"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -740,8 +740,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.outer-refused-no-message:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "34f3a5b8bd78", "9c16efc31903"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "34f3a5b8bd78", "e5392ac00e18"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -752,8 +752,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.method-not-found:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "0ba58e9fadf7", "9c16efc31903"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "0ba58e9fadf7", "e5392ac00e18"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -764,8 +764,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.transport-rejection:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "566d8202b582", "9c16efc31903"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "566d8202b582", "e5392ac00e18"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
@@ -776,8 +776,8 @@
|
||||
{
|
||||
"id": "pr-triage-launch.transport-rejection-no-message:launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "0ca529648be4", "9c16efc31903"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["3e6a0417d2ee", "0ca529648be4", "e5392ac00e18"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
|
||||
+297
-297
@@ -41,7 +41,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"0220267a7bca": {
|
||||
"1782d7da7125": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
@@ -52,7 +52,7 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -68,48 +68,14 @@
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "method_not_found",
|
||||
"message": "Unknown method"
|
||||
"code": "refused",
|
||||
"message": ""
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"084fe2afb0ce": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "refused"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"1c2564f1daca": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
@@ -152,6 +118,73 @@
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4b4eff46e2be": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "method_not_found",
|
||||
"message": "Unknown method"
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"4b7de31b7ca7": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex", "claude"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"51d4cb56be85": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 2,
|
||||
@@ -178,137 +211,10 @@
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"570f5efcd445": {
|
||||
"584e8a48c9d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "outer refused"
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"57f451cceda3": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"585dfba86809": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"6dd8724be3b1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-1::/folder\"}}"
|
||||
},
|
||||
"70e0675ab2a9": {
|
||||
"name": "repo.list#1",
|
||||
@@ -351,7 +257,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"89b73b1e13d1": {
|
||||
"8f2549f081ef": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
@@ -362,7 +268,7 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -373,13 +279,13 @@
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex", "claude"]
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -408,7 +314,7 @@
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
},
|
||||
"a9705081551e": {
|
||||
"ad56d2b94b5e": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
@@ -419,7 +325,7 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -430,53 +336,13 @@
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ac08a3ca5c67": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "inner refused",
|
||||
"ok": false
|
||||
}
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "transport failure",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -505,22 +371,7 @@
|
||||
"isRpcDeliveryUnknown": false
|
||||
}
|
||||
},
|
||||
"c7584e82c72f": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
},
|
||||
"e0eef7c9fafe": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"e1b544c0b6ec": {
|
||||
"c0260c98bf87": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
@@ -531,39 +382,7 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "transport failure",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"efd2ac432554": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -580,13 +399,134 @@
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": ""
|
||||
"message": "outer refused"
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"c581f563511c": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "inner refused",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"c7584e82c72f": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
},
|
||||
"d0f8e1b60093": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"e0eef7c9fafe": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"ec9c870f9c3a": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "refused"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"f3b516f62081": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
@@ -597,10 +537,70 @@
|
||||
"isRpcDeliveryUnknown": false
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"fea75e1dbba4": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ff00bccaaf1f": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -621,8 +621,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.normal:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "b27c85677730"
|
||||
},
|
||||
@@ -633,8 +633,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.result-absent:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "585dfba86809"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "ff00bccaaf1f"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "98b298cb3858"
|
||||
},
|
||||
@@ -645,8 +645,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.result-null:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "6dd8724be3b1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "fea75e1dbba4"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "98b298cb3858"
|
||||
},
|
||||
@@ -657,8 +657,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.inner-ok-missing:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "084fe2afb0ce"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "ec9c870f9c3a"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "98b298cb3858"
|
||||
},
|
||||
@@ -669,8 +669,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.inner-false-string-error:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "ac08a3ca5c67"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "c581f563511c"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "98b298cb3858"
|
||||
},
|
||||
@@ -681,8 +681,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.inner-false-object-error:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "a9705081551e"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "d0f8e1b60093"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "98b298cb3858"
|
||||
},
|
||||
@@ -693,8 +693,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.outer-refused:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "570f5efcd445"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "c0260c98bf87"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "32a7c0ae7918"
|
||||
},
|
||||
@@ -705,8 +705,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.outer-refused-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "efd2ac432554"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "1782d7da7125"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "f3b516f62081"
|
||||
},
|
||||
@@ -717,8 +717,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.method-not-found:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "0220267a7bca"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "4b4eff46e2be"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "b948e8307e81"
|
||||
},
|
||||
@@ -729,8 +729,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.transport-rejection:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "e1b544c0b6ec"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "ad56d2b94b5e"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "a947768bc0ed"
|
||||
},
|
||||
@@ -741,8 +741,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.transport-rejection-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "57f451cceda3"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "8f2549f081ef"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "c7584e82c72f"
|
||||
},
|
||||
|
||||
+39
-39
@@ -218,6 +218,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"4b7de31b7ca7": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex", "claude"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"51d4cb56be85": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 2,
|
||||
@@ -244,6 +276,11 @@
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"584e8a48c9d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-1::/folder\"}}"
|
||||
},
|
||||
"624ec4e5082c": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
@@ -351,38 +388,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"89b73b1e13d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex", "claude"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"8a72d3d14d44": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
@@ -596,11 +601,6 @@
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": false
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -621,8 +621,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.normal:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "b27c85677730"
|
||||
},
|
||||
|
||||
+59
-59
@@ -257,6 +257,38 @@
|
||||
]
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4b7de31b7ca7": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex", "claude"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"51d4cb56be85": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 2,
|
||||
@@ -315,6 +347,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"584e8a48c9d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-1::/folder\"}}"
|
||||
},
|
||||
"69518e60477b": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 2,
|
||||
@@ -425,38 +462,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"89b73b1e13d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex", "claude"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
@@ -587,11 +592,6 @@
|
||||
"isRpcDeliveryUnknown": false
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
},
|
||||
"fb9f08890f70": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 2,
|
||||
@@ -645,8 +645,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.normal:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "b27c85677730"
|
||||
},
|
||||
@@ -657,8 +657,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.result-absent:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "98bd3a5fda2b", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "98bd3a5fda2b", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "1c10eabc36a4"
|
||||
},
|
||||
@@ -669,8 +669,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.result-null:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "69518e60477b", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "69518e60477b", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "1a04332d2ee1"
|
||||
},
|
||||
@@ -681,8 +681,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.inner-ok-missing:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "fb9f08890f70", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "fb9f08890f70", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "3c911d72c9be"
|
||||
},
|
||||
@@ -693,8 +693,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.inner-false-string-error:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "86798e4821d1", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "86798e4821d1", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "3c911d72c9be"
|
||||
},
|
||||
@@ -705,8 +705,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.inner-false-object-error:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "1c5359ea3f7d", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "1c5359ea3f7d", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "3c911d72c9be"
|
||||
},
|
||||
@@ -717,8 +717,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.outer-refused:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "ceb5fe72c101", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "ceb5fe72c101", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "32a7c0ae7918"
|
||||
},
|
||||
@@ -729,8 +729,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.outer-refused-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "34f3a5b8bd78", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "34f3a5b8bd78", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "f3b516f62081"
|
||||
},
|
||||
@@ -741,8 +741,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.method-not-found:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "0ba58e9fadf7", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "0ba58e9fadf7", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "b948e8307e81"
|
||||
},
|
||||
@@ -753,8 +753,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.transport-rejection:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "566d8202b582", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "566d8202b582", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "a947768bc0ed"
|
||||
},
|
||||
@@ -765,8 +765,8 @@
|
||||
{
|
||||
"id": "new-tab-local-agents.transport-rejection-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "0ca529648be4", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "0ca529648be4", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "c7584e82c72f"
|
||||
},
|
||||
|
||||
@@ -73,6 +73,38 @@
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4b7de31b7ca7": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-1::/folder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex", "claude"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"51d4cb56be85": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 2,
|
||||
@@ -99,6 +131,11 @@
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"584e8a48c9d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-1::/folder\"}}"
|
||||
},
|
||||
"70e0675ab2a9": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
@@ -140,38 +177,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"89b73b1e13d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex", "claude"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
@@ -195,11 +200,6 @@
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -220,8 +220,8 @@
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "89b73b1e13d1"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1"],
|
||||
"sender": ["70e0675ab2a9", "02188f091419", "4b7de31b7ca7"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "584e8a48c9d1"],
|
||||
"settlements": {
|
||||
"load": "b27c85677730"
|
||||
},
|
||||
|
||||
@@ -89,37 +89,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"9c16efc31903": {
|
||||
"742db574bda3": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-9::/w\"}}"
|
||||
},
|
||||
"c15f33d27bd2": {
|
||||
"name": "agent.launchReplay#1",
|
||||
@@ -171,6 +144,38 @@
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"e5392ac00e18": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eaa398d8c3aa": {
|
||||
"launched": {
|
||||
"kind": "unconfirmed",
|
||||
@@ -184,11 +189,6 @@
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -197,8 +197,8 @@
|
||||
{
|
||||
"id": "unconfirmed",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "c15f33d27bd2"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "c15f33d27bd2"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
|
||||
@@ -141,42 +141,15 @@
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"742db574bda3": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-9::/w\"}}"
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"9c16efc31903": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"a4273b38df83": {
|
||||
"launched": "unlaunched"
|
||||
},
|
||||
@@ -243,6 +216,38 @@
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"e5392ac00e18": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["codex"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
@@ -255,11 +260,6 @@
|
||||
"launched": {
|
||||
"kind": "sent"
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -280,8 +280,8 @@
|
||||
{
|
||||
"id": "launched",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "ba3fac1eb199"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
|
||||
@@ -153,7 +153,17 @@
|
||||
"prompt": "Fix the failing checks"
|
||||
}
|
||||
},
|
||||
"9c16efc31903": {
|
||||
"742db574bda3": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"repo-9::/w\"}}"
|
||||
},
|
||||
"e0eef7c9fafe": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"e5392ac00e18": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
@@ -164,7 +174,7 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"worktreeId": "repo-9::/w"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -185,11 +195,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"e0eef7c9fafe": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}"
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
@@ -197,11 +202,6 @@
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -210,8 +210,8 @@
|
||||
{
|
||||
"id": "prompt-not-sent",
|
||||
"observation": {
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "9c16efc31903", "29436402d701"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "28339abecf78"],
|
||||
"sender": ["3e6a0417d2ee", "02188f091419", "e5392ac00e18", "29436402d701"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "742db574bda3", "28339abecf78"],
|
||||
"settlements": {
|
||||
"launch": "eb79a9b3682a"
|
||||
},
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"2398eeb5ec74": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\",\"params\":{\"worktreeId\":\"workspace-1\"}}"
|
||||
},
|
||||
"5ee9e7b49320": {
|
||||
"name": "agent.launchReplay#1",
|
||||
"ordinal": 8,
|
||||
@@ -91,6 +96,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"bca41ddb4705": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"worktreeId": "workspace-1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["claude"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"bcb5111e6012": {
|
||||
"name": "agent.launchReplay#1",
|
||||
"ordinal": 7,
|
||||
@@ -149,11 +186,6 @@
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"f49d184679d1": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.detectAgents\"}"
|
||||
},
|
||||
"fc1691a7deca": {
|
||||
"actionError": "Workspace not found",
|
||||
"busyAction": {
|
||||
@@ -186,38 +218,6 @@
|
||||
"sendSheet": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"fc3974e714de": {
|
||||
"name": "preflight.detectAgents#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.detectAgents"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": ["claude"]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -226,8 +226,8 @@
|
||||
{
|
||||
"id": "refused",
|
||||
"observation": {
|
||||
"sender": ["9239a4f232b8", "96a9a954e28f", "fc3974e714de", "bcb5111e6012"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "f49d184679d1", "5ee9e7b49320"],
|
||||
"sender": ["9239a4f232b8", "96a9a954e28f", "bca41ddb4705", "bcb5111e6012"],
|
||||
"payloads": ["e0eef7c9fafe", "1c2564f1daca", "2398eeb5ec74", "5ee9e7b49320"],
|
||||
"settlements": {
|
||||
"create-and-send": "eb79a9b3682a"
|
||||
},
|
||||
|
||||
@@ -9268,7 +9268,7 @@
|
||||
{
|
||||
"complete": "preflight.detectAgents#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
"worktreeId": "repo-9::/w"
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
@@ -9361,7 +9361,7 @@
|
||||
{
|
||||
"complete": "preflight.detectAgents#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
"worktreeId": "repo-9::/w"
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
@@ -9454,7 +9454,7 @@
|
||||
{
|
||||
"complete": "preflight.detectAgents#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
"worktreeId": "repo-9::/w"
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
@@ -18538,7 +18538,7 @@
|
||||
{
|
||||
"complete": "preflight.detectAgents#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
"worktreeId": "workspace-1"
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
@@ -23149,7 +23149,7 @@
|
||||
{
|
||||
"complete": "preflight.detectAgents#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
"worktreeId": "repo-1::/folder"
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
|
||||
@@ -89,7 +89,9 @@ async function loadDetectedAgents(
|
||||
repo
|
||||
}
|
||||
: {
|
||||
reply: await preflightDetectAgentsRead.request(client),
|
||||
// Why the workspace: the host resolves its project runtime (a WSL distro on Windows). An
|
||||
// older host discards the params and answers with its own default, as it always has.
|
||||
reply: await preflightDetectAgentsRead.request(client, { worktreeId }),
|
||||
interpret: preflightDetectAgentsRead.interpret,
|
||||
repo
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { ipcMain } from 'electron'
|
||||
import {
|
||||
detectInstalledAgentsWithShellPathHydration,
|
||||
detectRemoteAgents,
|
||||
detectRemoteWindowsTerminalCapabilities,
|
||||
refreshShellPathAndDetectAgents,
|
||||
runPreflightCheck
|
||||
} from '../preflight/agent-detection'
|
||||
import { detectAgentsOnHost, refreshAgentsOnHost } from '../preflight/workspace-agent-detection'
|
||||
import type {
|
||||
PreflightRuntimeContext,
|
||||
PreflightStatus,
|
||||
@@ -30,7 +28,7 @@ export function registerPreflightHandlers(): void {
|
||||
)
|
||||
|
||||
ipcMain.handle('preflight:detectAgents', async (_event, args?: PreflightRuntimeContext) =>
|
||||
detectInstalledAgentsWithShellPathHydration(args)
|
||||
detectAgentsOnHost({ kind: 'local', context: args })
|
||||
)
|
||||
|
||||
// Why here: this is the one place that already answers "what can the installed agent CLIs
|
||||
@@ -40,18 +38,13 @@ export function registerPreflightHandlers(): void {
|
||||
)
|
||||
|
||||
ipcMain.handle('preflight:refreshAgents', async (_event, args?: PreflightRuntimeContext) => {
|
||||
return refreshShellPathAndDetectAgents(args)
|
||||
return refreshAgentsOnHost({ kind: 'local', context: args })
|
||||
})
|
||||
|
||||
// Why: remote worktrees need agent detection on the SSH host, not the local
|
||||
// machine. This handler forwards the same KNOWN_AGENT_COMMANDS list to the
|
||||
// relay's preflight.detectAgents RPC, whose lookup command is selected on
|
||||
// the remote host so native Windows OpenSSH does not require a POSIX shell.
|
||||
ipcMain.handle(
|
||||
'preflight:detectRemoteAgents',
|
||||
async (_event, args: { connectionId: string }): Promise<string[]> => {
|
||||
return detectRemoteAgents(args)
|
||||
}
|
||||
async (_event, args: { connectionId: string }): Promise<string[]> =>
|
||||
detectAgentsOnHost({ kind: 'ssh', connectionId: args.connectionId })
|
||||
)
|
||||
|
||||
ipcMain.handle(
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { GlobalSettings } from '../shared/global-settings-types'
|
||||
import type { Project } from '../shared/project-types'
|
||||
import type { Repo } from '../shared/repo-types'
|
||||
import {
|
||||
getWorkspaceRuntimePreference,
|
||||
resolveProjectExecutionRuntime,
|
||||
type ProjectExecutionRuntimeResolution
|
||||
} from '../shared/project-execution-runtime'
|
||||
@@ -49,7 +50,7 @@ function canResolveProjectRuntimeForWorktreeId(
|
||||
|
||||
function resolveLocalProjectRuntime(
|
||||
store: ResolvableStore,
|
||||
project: Project,
|
||||
project: Pick<Project, 'id' | 'localWindowsRuntimePreference'>,
|
||||
settings: ReturnType<ResolvableStore['getSettings']> = store.getSettings()
|
||||
): ProjectExecutionRuntimeResolution {
|
||||
const wslAvailable = hasCachedWslAvailability()
|
||||
@@ -83,6 +84,29 @@ export function resolveLocalProjectRuntimeForRepo(
|
||||
return resolveLocalProjectRuntime(store, project)
|
||||
}
|
||||
|
||||
/** Same choice the renderer makes for a workspace: the project's runtime, else the distro of a
|
||||
* WSL-share path, else the global default. Works for a repo with no project too. */
|
||||
export function resolveLocalWorkspaceRuntime(
|
||||
store: ProjectRuntimeResolutionStore,
|
||||
repo: Repo,
|
||||
workspacePath: string | null | undefined
|
||||
): ProjectExecutionRuntimeResolution | undefined {
|
||||
if (
|
||||
getRepoExecutionHostId(repo) !== LOCAL_EXECUTION_HOST_ID ||
|
||||
!canResolveProjectRuntimeForRepo(store)
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
const project = store.getProjects().find((entry) => entry.sourceRepoIds.includes(repo.id))
|
||||
return resolveLocalProjectRuntime(store, {
|
||||
id: project?.id ?? repo.id,
|
||||
localWindowsRuntimePreference: getWorkspaceRuntimePreference(
|
||||
project?.localWindowsRuntimePreference,
|
||||
workspacePath
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function resolveLocalProjectRuntimesForRepos(
|
||||
store: ProjectRuntimeResolutionStore,
|
||||
repos: readonly Repo[]
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
import '../runtime/rpc/unused-default-rpc-methods.test-fixture'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { RpcDispatcher } from '../runtime/rpc/dispatcher'
|
||||
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
|
||||
import { PREFLIGHT_METHODS } from '../runtime/rpc/methods/preflight'
|
||||
import type { FolderWorkspace } from '../../shared/folder-workspace-types'
|
||||
import type { GlobalWindowsRuntimeDefault } from '../../shared/project-execution-runtime'
|
||||
import type { Project } from '../../shared/project-types'
|
||||
import type { ProjectGroup } from '../../shared/project-group-types'
|
||||
import type { Repo } from '../../shared/repo-types'
|
||||
import { folderWorkspaceKey, parseWorkspaceKey } from '../../shared/workspace-scope'
|
||||
import { splitWorktreeId } from '../../shared/worktree/id'
|
||||
import {
|
||||
resolveWorkspaceAgentDetectionHost,
|
||||
type WorkspaceAgentDetectionStore
|
||||
} from './workspace-agent-detection'
|
||||
|
||||
const { detectInstalledMock, detectRemoteMock, refreshMock } = vi.hoisted(() => ({
|
||||
detectInstalledMock: vi.fn(),
|
||||
detectRemoteMock: vi.fn(),
|
||||
refreshMock: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('./agent-detection', () => ({
|
||||
detectInstalledAgentsWithShellPathHydration: detectInstalledMock,
|
||||
detectRemoteAgents: detectRemoteMock,
|
||||
refreshShellPathAndDetectAgents: refreshMock,
|
||||
detectRemoteWindowsTerminalCapabilities: vi.fn(),
|
||||
runPreflightCheck: vi.fn()
|
||||
}))
|
||||
|
||||
const WSL_REPO_PATH = '\\\\wsl.localhost\\Ubuntu-24.04\\home\\me\\project\\repo'
|
||||
const originalPlatform = process.platform
|
||||
|
||||
function repo(overrides: Partial<Repo> & Pick<Repo, 'id' | 'path'>): Repo {
|
||||
return { displayName: overrides.id, badgeColor: 'blue', addedAt: 1, ...overrides }
|
||||
}
|
||||
|
||||
function project(
|
||||
id: string,
|
||||
repoIds: string[],
|
||||
preference?: Project['localWindowsRuntimePreference']
|
||||
): Project {
|
||||
return {
|
||||
id,
|
||||
displayName: id,
|
||||
badgeColor: 'blue',
|
||||
sourceRepoIds: repoIds,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
...(preference ? { localWindowsRuntimePreference: preference } : {})
|
||||
}
|
||||
}
|
||||
|
||||
const GROUP: ProjectGroup = {
|
||||
id: 'g1',
|
||||
name: 'g1',
|
||||
parentPath: null,
|
||||
connectionId: null,
|
||||
parentGroupId: null,
|
||||
createdFrom: 'manual',
|
||||
tabOrder: 0,
|
||||
isCollapsed: false,
|
||||
color: null,
|
||||
createdAt: 1,
|
||||
updatedAt: 1
|
||||
}
|
||||
|
||||
const FOLDER: FolderWorkspace = {
|
||||
id: 'f1',
|
||||
projectGroupId: 'g1',
|
||||
name: 'f1',
|
||||
folderPath: WSL_REPO_PATH,
|
||||
linkedTask: null,
|
||||
comment: '',
|
||||
isArchived: false,
|
||||
isUnread: false,
|
||||
isPinned: false,
|
||||
sortOrder: 0,
|
||||
lastActivityAt: 1,
|
||||
createdAt: 1,
|
||||
updatedAt: 1
|
||||
}
|
||||
|
||||
function store(args: {
|
||||
repos: Repo[]
|
||||
projects?: Project[]
|
||||
globalDefault?: GlobalWindowsRuntimeDefault
|
||||
folderWorkspaces?: FolderWorkspace[]
|
||||
projectGroups?: ProjectGroup[]
|
||||
}): WorkspaceAgentDetectionStore {
|
||||
return {
|
||||
getRepos: () => args.repos,
|
||||
getRepo: (id: string) => args.repos.find((entry) => entry.id === id),
|
||||
getProjects: () => args.projects ?? [],
|
||||
getSettings: () => ({ localWindowsRuntimeDefault: args.globalDefault }),
|
||||
getFolderWorkspaces: () => args.folderWorkspaces ?? [],
|
||||
getProjectGroups: () => args.projectGroups ?? []
|
||||
}
|
||||
}
|
||||
|
||||
function wslContext(distro: string, projectId: string, reason: string) {
|
||||
return {
|
||||
projectRuntime: {
|
||||
status: 'resolved',
|
||||
runtime: expect.objectContaining({ kind: 'wsl', distro, projectId, reason })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stands in for the runtime, which reads the worktree's host and path from its own list.
|
||||
function resolveFor(s: WorkspaceAgentDetectionStore, workspaceId: string, hostId?: string) {
|
||||
const scope = parseWorkspaceKey(workspaceId)
|
||||
if (scope?.type === 'folder') {
|
||||
return resolveWorkspaceAgentDetectionHost(s, {
|
||||
kind: 'folder',
|
||||
folderWorkspaceId: scope.folderWorkspaceId
|
||||
})
|
||||
}
|
||||
const parsed = splitWorktreeId(workspaceId)
|
||||
if (!parsed) {
|
||||
throw new Error(`not a worktree id: ${workspaceId}`)
|
||||
}
|
||||
return resolveWorkspaceAgentDetectionHost(s, {
|
||||
kind: 'worktree',
|
||||
repoId: parsed.repoId,
|
||||
path: parsed.worktreePath,
|
||||
hostId
|
||||
})
|
||||
}
|
||||
|
||||
describe('resolveWorkspaceAgentDetectionHost on a Windows host', () => {
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(process, 'platform', { value: 'win32' })
|
||||
})
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, 'platform', { value: originalPlatform })
|
||||
})
|
||||
|
||||
it('keeps the host default for a workspace no repo row owns', () => {
|
||||
const s = store({ repos: [repo({ id: 'r1', path: 'C:\\r1' })] })
|
||||
expect(resolveFor(s, 'missing::C:\\x')).toEqual({ kind: 'local' })
|
||||
})
|
||||
|
||||
it('routes by the worktree owner, not the first row sharing its repo id', () => {
|
||||
const sshRow = repo({ id: 'r1', path: '/srv/r1', executionHostId: 'ssh:build-host' })
|
||||
const localRow = repo({ id: 'r1', path: 'C:\\src\\r1', executionHostId: 'local' })
|
||||
for (const repos of [
|
||||
[sshRow, localRow],
|
||||
[localRow, sshRow]
|
||||
]) {
|
||||
const s = store({ repos })
|
||||
expect(resolveFor(s, 'r1::C:\\src\\r1', 'local')).toEqual({
|
||||
kind: 'local',
|
||||
context: {
|
||||
projectRuntime: {
|
||||
status: 'resolved',
|
||||
runtime: expect.objectContaining({ kind: 'windows-host' })
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(resolveFor(s, 'r1::/srv/r1', 'ssh:build-host')).toEqual({
|
||||
kind: 'ssh',
|
||||
connectionId: 'build-host'
|
||||
})
|
||||
// Rival rows and no worktree host name no single owner: refuse rather than guess.
|
||||
expect(() => resolveFor(s, 'r1::/srv/r1')).toThrow('worktree_execution_host_unresolved')
|
||||
}
|
||||
})
|
||||
|
||||
it('probes inside the distro of a WSL-share workspace with no saved runtime', () => {
|
||||
const s = store({ repos: [repo({ id: 'r1', path: WSL_REPO_PATH })] })
|
||||
expect(resolveFor(s, `r1::${WSL_REPO_PATH}`)).toEqual({
|
||||
kind: 'local',
|
||||
context: wslContext('Ubuntu-24.04', 'r1', 'project-override')
|
||||
})
|
||||
})
|
||||
|
||||
it('follows the global WSL default for a project that inherits it', () => {
|
||||
const s = store({
|
||||
repos: [repo({ id: 'r1', path: 'C:\\src\\r1' })],
|
||||
projects: [project('p1', ['r1'])],
|
||||
globalDefault: { kind: 'wsl', distro: 'Ubuntu' }
|
||||
})
|
||||
expect(resolveFor(s, 'r1::C:\\src\\r1')).toEqual({
|
||||
kind: 'local',
|
||||
context: wslContext('Ubuntu', 'p1', 'global-default')
|
||||
})
|
||||
})
|
||||
|
||||
it("lets the project's own runtime win over a WSL-share path", () => {
|
||||
const s = store({
|
||||
repos: [repo({ id: 'r1', path: WSL_REPO_PATH })],
|
||||
projects: [project('p1', ['r1'], { kind: 'windows-host' })]
|
||||
})
|
||||
expect(resolveFor(s, `r1::${WSL_REPO_PATH}`)).toEqual({
|
||||
kind: 'local',
|
||||
context: {
|
||||
projectRuntime: {
|
||||
status: 'resolved',
|
||||
runtime: expect.objectContaining({ kind: 'windows-host', reason: 'project-override' })
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('routes an SSH workspace to its target, under either ownership spelling', () => {
|
||||
const s = store({
|
||||
repos: [
|
||||
repo({ id: 'legacy', path: '/srv/a', connectionId: 'ssh-a' }),
|
||||
repo({ id: 'unified', path: '/srv/b', executionHostId: 'ssh:ssh-b' })
|
||||
]
|
||||
})
|
||||
expect(resolveFor(s, 'legacy::/srv/a')).toEqual({
|
||||
kind: 'ssh',
|
||||
connectionId: 'ssh-a'
|
||||
})
|
||||
expect(resolveFor(s, 'unified::/srv/b')).toEqual({
|
||||
kind: 'ssh',
|
||||
connectionId: 'ssh-b'
|
||||
})
|
||||
})
|
||||
|
||||
it('resolves a local folder workspace through its only repo', () => {
|
||||
const s = store({
|
||||
repos: [repo({ id: 'r1', path: WSL_REPO_PATH, projectGroupId: 'g1' })],
|
||||
projectGroups: [GROUP],
|
||||
folderWorkspaces: [FOLDER]
|
||||
})
|
||||
expect(resolveFor(s, folderWorkspaceKey('f1'))).toEqual({
|
||||
kind: 'local',
|
||||
context: wslContext('Ubuntu-24.04', 'r1', 'project-override')
|
||||
})
|
||||
})
|
||||
it('refuses a folder whose candidate repos live on different SSH hosts', () => {
|
||||
const s = store({
|
||||
repos: [
|
||||
repo({ id: 'a', path: '/srv/a', executionHostId: 'ssh:one', projectGroupId: 'g1' }),
|
||||
repo({ id: 'b', path: '/srv/b', executionHostId: 'ssh:two', projectGroupId: 'g1' })
|
||||
],
|
||||
projectGroups: [GROUP],
|
||||
folderWorkspaces: [{ ...FOLDER, folderPath: '/srv' }]
|
||||
})
|
||||
expect(() => resolveFor(s, folderWorkspaceKey('f1'))).toThrow(
|
||||
'worktree_execution_host_unresolved'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('preflight.detectAgents resolves the workspace on the host', () => {
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(process, 'platform', { value: 'win32' })
|
||||
detectInstalledMock.mockReset().mockResolvedValue(['claude', 'codex'])
|
||||
})
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, 'platform', { value: originalPlatform })
|
||||
})
|
||||
|
||||
function dispatcherFor(s: WorkspaceAgentDetectionStore) {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: these methods read only resolveAgentDetectionHost and getRuntimeId from the runtime.
|
||||
const runtime = {
|
||||
getRuntimeId: () => 'host',
|
||||
resolveAgentDetectionHost: async (worktreeId?: string) =>
|
||||
worktreeId ? resolveFor(s, worktreeId) : { kind: 'local' }
|
||||
} as unknown as OrcaRuntimeService
|
||||
return new RpcDispatcher({ runtime, methods: PREFLIGHT_METHODS })
|
||||
}
|
||||
|
||||
it('#19885: a paired client naming a WSL-hosted workspace gets the distro probe', async () => {
|
||||
const dispatcher = dispatcherFor(store({ repos: [repo({ id: 'r1', path: WSL_REPO_PATH })] }))
|
||||
const response = await dispatcher.dispatch({
|
||||
id: '1',
|
||||
authToken: 't',
|
||||
method: 'preflight.detectAgents',
|
||||
params: { worktreeId: `r1::${WSL_REPO_PATH}` }
|
||||
})
|
||||
expect(detectInstalledMock).toHaveBeenCalledWith(
|
||||
wslContext('Ubuntu-24.04', 'r1', 'project-override')
|
||||
)
|
||||
expect(response).toMatchObject({ ok: true, result: ['claude', 'codex'] })
|
||||
})
|
||||
|
||||
it('#18666: a project on the global WSL default is probed in that distro', async () => {
|
||||
const dispatcher = dispatcherFor(
|
||||
store({
|
||||
repos: [repo({ id: 'r1', path: 'C:\\src\\r1' })],
|
||||
projects: [project('p1', ['r1'])],
|
||||
globalDefault: { kind: 'wsl', distro: 'Ubuntu' }
|
||||
})
|
||||
)
|
||||
await dispatcher.dispatch({
|
||||
id: '1',
|
||||
authToken: 't',
|
||||
method: 'preflight.refreshAgents',
|
||||
params: { worktreeId: 'r1::C:\\src\\r1' }
|
||||
})
|
||||
expect(refreshMock).toHaveBeenCalledWith(wslContext('Ubuntu', 'p1', 'global-default'))
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,142 @@
|
||||
import {
|
||||
getRepoExecutionHostId,
|
||||
getSshTargetIdForExecutionHost,
|
||||
LOCAL_EXECUTION_HOST_ID
|
||||
} from '../../shared/execution-host'
|
||||
import {
|
||||
findFolderWorkspaceCandidateRepos,
|
||||
resolveFolderWorkspaceHost
|
||||
} from '../../shared/folder-workspace-execution-host'
|
||||
import type { FolderWorkspace } from '../../shared/folder-workspace-types'
|
||||
import type { ProjectGroup } from '../../shared/project-group-types'
|
||||
import type { Repo } from '../../shared/repo-types'
|
||||
import {
|
||||
resolveLocalWorkspaceRuntime,
|
||||
type ProjectRuntimeResolutionStore
|
||||
} from '../local-project-runtime-resolution'
|
||||
import type { PreflightRuntimeContext } from '../ipc/preflight-runtime-target'
|
||||
import { resolveWorktreeHostRouting } from '../runtime/worktree-launch-host-repo'
|
||||
import {
|
||||
detectInstalledAgentsWithShellPathHydration,
|
||||
detectRemoteAgents,
|
||||
refreshShellPathAndDetectAgents,
|
||||
type RefreshAgentsResult
|
||||
} from './agent-detection'
|
||||
|
||||
/** Where agent CLIs are probed: this machine (optionally inside a WSL distro) or an SSH target. */
|
||||
export type AgentDetectionHost =
|
||||
| { kind: 'local'; context?: PreflightRuntimeContext }
|
||||
| { kind: 'ssh'; connectionId: string }
|
||||
|
||||
export type WorkspaceAgentDetectionStore = ProjectRuntimeResolutionStore & {
|
||||
getRepos?: () => Repo[]
|
||||
getFolderWorkspaces?: () => FolderWorkspace[]
|
||||
getProjectGroups?: () => ProjectGroup[]
|
||||
}
|
||||
|
||||
export const HOST_DEFAULT_AGENT_DETECTION: AgentDetectionHost = { kind: 'local' }
|
||||
|
||||
/** A workspace as this host knows it: a folder workspace, or a worktree read from its own list. */
|
||||
export type AgentDetectionWorkspace =
|
||||
| { kind: 'folder'; folderWorkspaceId: string }
|
||||
| { kind: 'worktree'; repoId: string; path: string; hostId?: string | null }
|
||||
|
||||
type ResolvedWorkspace =
|
||||
| { kind: 'ssh'; connectionId: string }
|
||||
| { kind: 'local'; repo: Repo; path: string }
|
||||
|
||||
/**
|
||||
* The host a workspace's agents run on, resolved on this machine from its own project settings.
|
||||
* A workspace this store cannot place keeps the host default older clients rely on.
|
||||
*/
|
||||
export function resolveWorkspaceAgentDetectionHost(
|
||||
store: WorkspaceAgentDetectionStore,
|
||||
workspace: AgentDetectionWorkspace
|
||||
): AgentDetectionHost {
|
||||
const resolved =
|
||||
workspace.kind === 'folder'
|
||||
? resolveFolderWorkspace(store, workspace.folderWorkspaceId)
|
||||
: resolveWorktree(store, workspace)
|
||||
if (!resolved) {
|
||||
return HOST_DEFAULT_AGENT_DETECTION
|
||||
}
|
||||
if (resolved.kind === 'ssh') {
|
||||
return resolved
|
||||
}
|
||||
const projectRuntime = resolveLocalWorkspaceRuntime(store, resolved.repo, resolved.path)
|
||||
return projectRuntime
|
||||
? { kind: 'local', context: { projectRuntime } }
|
||||
: HOST_DEFAULT_AGENT_DETECTION
|
||||
}
|
||||
|
||||
function resolveWorktree(
|
||||
store: WorkspaceAgentDetectionStore,
|
||||
worktree: Extract<AgentDetectionWorkspace, { kind: 'worktree' }>
|
||||
): ResolvedWorkspace | null {
|
||||
// Why the shared rule: one repo id can name rows on several hosts, and the first row is not
|
||||
// necessarily this worktree's owner.
|
||||
const routing = resolveWorktreeHostRouting(store.getRepos?.() ?? [], worktree)
|
||||
if (routing.kind === 'ambiguous') {
|
||||
throw new Error('worktree_execution_host_unresolved')
|
||||
}
|
||||
if (routing.kind === 'unowned') {
|
||||
return null
|
||||
}
|
||||
const sshTargetId = getSshTargetIdForExecutionHost(routing.hostId)
|
||||
if (sshTargetId) {
|
||||
return { kind: 'ssh', connectionId: sshTargetId }
|
||||
}
|
||||
// A worktree another runtime owns is not this host's to probe; keep the host default.
|
||||
return routing.hostId === LOCAL_EXECUTION_HOST_ID && routing.repo
|
||||
? { kind: 'local', repo: routing.repo, path: worktree.path }
|
||||
: null
|
||||
}
|
||||
|
||||
function resolveFolderWorkspace(
|
||||
store: WorkspaceAgentDetectionStore,
|
||||
folderWorkspaceId: string
|
||||
): ResolvedWorkspace | null {
|
||||
const state = {
|
||||
folderWorkspaces: store.getFolderWorkspaces?.() ?? [],
|
||||
projectGroups: store.getProjectGroups?.() ?? [],
|
||||
repos: store.getRepos?.() ?? []
|
||||
}
|
||||
const host = resolveFolderWorkspaceHost(state, folderWorkspaceId)
|
||||
if (host.kind === 'ssh') {
|
||||
return { kind: 'ssh', connectionId: host.targetId }
|
||||
}
|
||||
if (host.kind === 'ambiguous') {
|
||||
throw new Error('worktree_execution_host_unresolved')
|
||||
}
|
||||
const folder = state.folderWorkspaces.find((entry) => entry.id === folderWorkspaceId)
|
||||
const candidates = findFolderWorkspaceCandidateRepos(state, folderWorkspaceId)
|
||||
// Why one candidate: a folder spanning several repos has no single project runtime to pick.
|
||||
const repo = candidates.length === 1 ? candidates[0] : undefined
|
||||
return host.kind === 'local' &&
|
||||
folder &&
|
||||
repo &&
|
||||
getRepoExecutionHostId(repo) === LOCAL_EXECUTION_HOST_ID
|
||||
? { kind: 'local', repo, path: folder.folderPath }
|
||||
: null
|
||||
}
|
||||
|
||||
/** The one agent-detection entry point: IPC, runtime RPC, and SSH all probe through here. */
|
||||
export function detectAgentsOnHost(host: AgentDetectionHost): Promise<string[]> {
|
||||
return host.kind === 'ssh'
|
||||
? detectRemoteAgents({ connectionId: host.connectionId })
|
||||
: detectInstalledAgentsWithShellPathHydration(host.context)
|
||||
}
|
||||
|
||||
export async function refreshAgentsOnHost(host: AgentDetectionHost): Promise<RefreshAgentsResult> {
|
||||
if (host.kind === 'local') {
|
||||
return refreshShellPathAndDetectAgents(host.context)
|
||||
}
|
||||
// Why: an SSH probe already reads the target's fresh login PATH; there is no local PATH to merge.
|
||||
return {
|
||||
agents: await detectRemoteAgents({ connectionId: host.connectionId }),
|
||||
addedPathSegments: [],
|
||||
shellHydrationOk: true,
|
||||
pathSource: 'sync_seed_only',
|
||||
pathFailureReason: 'none'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const electronMocks = vi.hoisted(() => {
|
||||
const ipcMain = {
|
||||
on: vi.fn(() => ipcMain),
|
||||
removeListener: vi.fn(() => ipcMain),
|
||||
emit: vi.fn(() => true)
|
||||
}
|
||||
return {
|
||||
BrowserWindow: { fromId: vi.fn((): unknown => null) },
|
||||
webContents: { fromId: vi.fn((): unknown => null) },
|
||||
ipcMain,
|
||||
app: { getPath: vi.fn(() => '/tmp'), isPackaged: false }
|
||||
}
|
||||
})
|
||||
vi.mock('electron', () => electronMocks)
|
||||
|
||||
import { OrcaRuntimeService } from './orca-runtime'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../shared/constants'
|
||||
import type { Repo } from '../../shared/repo-types'
|
||||
import type { ResolvedWorktree } from './runtime-worktree-path-identity'
|
||||
|
||||
const originalPlatform = process.platform
|
||||
|
||||
function makeRuntime(repos: Repo[]) {
|
||||
const store = {
|
||||
getRepo: (id: string) => repos.find((repo) => repo.id === id),
|
||||
getRepos: () => repos,
|
||||
getProjects: () => [],
|
||||
getSettings: () => ({ localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' } })
|
||||
}
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: detection resolution reads only the repo, project and settings members supplied here.
|
||||
const runtime = new OrcaRuntimeService(store as never)
|
||||
const selector = vi.fn<(selector: string) => Promise<ResolvedWorktree>>()
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: replaces the protected worktree lookup this method awaits; its signature is restated above.
|
||||
;(runtime as unknown as { resolveWorktreeSelector: typeof selector }).resolveWorktreeSelector =
|
||||
selector
|
||||
return { runtime, selector }
|
||||
}
|
||||
|
||||
function worktree(fields: Partial<ResolvedWorktree>): ResolvedWorktree {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the method reads only repoId, path and hostId.
|
||||
return fields as ResolvedWorktree
|
||||
}
|
||||
|
||||
describe('OrcaRuntimeService.resolveAgentDetectionHost', () => {
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(process, 'platform', { value: 'win32' })
|
||||
})
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, 'platform', { value: originalPlatform })
|
||||
})
|
||||
|
||||
const local: Repo = {
|
||||
id: 'r1',
|
||||
path: 'C:\\src\\r1',
|
||||
displayName: 'r1',
|
||||
badgeColor: 'blue',
|
||||
addedAt: 1,
|
||||
executionHostId: 'local'
|
||||
}
|
||||
|
||||
it('keeps the host default for no, floating, or unknown workspaces', async () => {
|
||||
const { runtime, selector } = makeRuntime([local])
|
||||
selector.mockRejectedValue(new Error('selector_not_found'))
|
||||
expect(await runtime.resolveAgentDetectionHost(undefined)).toEqual({ kind: 'local' })
|
||||
expect(await runtime.resolveAgentDetectionHost(FLOATING_TERMINAL_WORKTREE_ID)).toEqual({
|
||||
kind: 'local'
|
||||
})
|
||||
expect(await runtime.resolveAgentDetectionHost('r1::C:\\gone')).toEqual({ kind: 'local' })
|
||||
expect(selector).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('refuses a workspace id two hosts share instead of probing this one', async () => {
|
||||
const { runtime, selector } = makeRuntime([local])
|
||||
selector.mockRejectedValue(new Error('selector_ambiguous'))
|
||||
await expect(runtime.resolveAgentDetectionHost('r1::/srv/r1')).rejects.toThrow(
|
||||
'selector_ambiguous'
|
||||
)
|
||||
})
|
||||
|
||||
it("routes by the host's own worktree record", async () => {
|
||||
const ssh: Repo = { ...local, path: '/srv/r1', executionHostId: 'ssh:build-host' }
|
||||
const { runtime, selector } = makeRuntime([ssh, local])
|
||||
selector.mockResolvedValueOnce(worktree({ repoId: 'r1', path: 'C:\\src\\r1', hostId: 'local' }))
|
||||
expect(await runtime.resolveAgentDetectionHost('r1::C:\\src\\r1')).toEqual({
|
||||
kind: 'local',
|
||||
context: {
|
||||
projectRuntime: {
|
||||
status: 'resolved',
|
||||
runtime: expect.objectContaining({ kind: 'wsl', distro: 'Ubuntu' })
|
||||
}
|
||||
}
|
||||
})
|
||||
expect(selector).toHaveBeenCalledWith('id:r1::C:\\src\\r1')
|
||||
})
|
||||
})
|
||||
@@ -13,6 +13,14 @@ import { RECENT_PTY_OUTPUT_LIMIT, RecentPtyOutputBuffer } from './recent-pty-out
|
||||
import { appendRecentPtyPathCandidates } from './terminal-output-path-candidates'
|
||||
import type { ProjectExecutionRuntimeResolution } from '../../shared/project-execution-runtime'
|
||||
import { resolveLocalProjectRuntimeForWorktreeId } from '../local-project-runtime-resolution'
|
||||
import {
|
||||
HOST_DEFAULT_AGENT_DETECTION,
|
||||
resolveWorkspaceAgentDetectionHost,
|
||||
type AgentDetectionHost
|
||||
} from '../preflight/workspace-agent-detection'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../shared/constants'
|
||||
import { parseWorkspaceKey } from '../../shared/workspace-scope'
|
||||
import type { ResolvedWorktree } from './runtime-worktree-path-identity'
|
||||
import type { RuntimePtyWorktreeRecord } from './runtime-terminal-state-records'
|
||||
import {
|
||||
localOrchestrationCliCommand,
|
||||
@@ -248,6 +256,41 @@ export class OrcaRuntimeWithGetOrchestrationDispatchAuthority extends OrcaRuntim
|
||||
: undefined
|
||||
}
|
||||
|
||||
// Why the host's own worktree record: it carries the owning host and the real path, where the
|
||||
// client's id alone could pick the wrong row for a repo id shared across hosts.
|
||||
async resolveAgentDetectionHost(
|
||||
worktreeId: string | null | undefined
|
||||
): Promise<AgentDetectionHost> {
|
||||
if (!this.store || !worktreeId || worktreeId === FLOATING_TERMINAL_WORKTREE_ID) {
|
||||
return HOST_DEFAULT_AGENT_DETECTION
|
||||
}
|
||||
const store = this.requireStore()
|
||||
const scope = parseWorkspaceKey(worktreeId)
|
||||
if (scope?.type === 'folder') {
|
||||
return resolveWorkspaceAgentDetectionHost(store, {
|
||||
kind: 'folder',
|
||||
folderWorkspaceId: scope.folderWorkspaceId
|
||||
})
|
||||
}
|
||||
let worktree: ResolvedWorktree
|
||||
try {
|
||||
worktree = await this.resolveWorktreeSelector(`id:${worktreeId}`)
|
||||
} catch (error) {
|
||||
// Only an unknown workspace keeps the answer older clients always got; an ambiguous one
|
||||
// has no single host, and probing this one would list another machine's agents.
|
||||
if (error instanceof Error && error.message === 'selector_not_found') {
|
||||
return HOST_DEFAULT_AGENT_DETECTION
|
||||
}
|
||||
throw error
|
||||
}
|
||||
return resolveWorkspaceAgentDetectionHost(store, {
|
||||
kind: 'worktree',
|
||||
repoId: worktree.repoId,
|
||||
path: worktree.path,
|
||||
hostId: worktree.hostId
|
||||
})
|
||||
}
|
||||
|
||||
getOrchestrationFleetAgentStatusSnapshot(): readonly FleetAgentStatusEvidence[] {
|
||||
return readOrchestrationFleetAgentStatusSnapshot(this)
|
||||
}
|
||||
|
||||
@@ -58,14 +58,22 @@ describe('preflight RPC methods', () => {
|
||||
pathSource: 'shell_hydrate',
|
||||
pathFailureReason: 'none'
|
||||
})
|
||||
const runtime = { getRuntimeId: () => 'test-runtime' } as unknown as OrcaRuntimeService
|
||||
const resolveAgentDetectionHost = vi.fn(() => ({ kind: 'local' }) as const)
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: these methods read only resolveAgentDetectionHost and getRuntimeId from the runtime.
|
||||
const runtime = {
|
||||
getRuntimeId: () => 'test-runtime',
|
||||
resolveAgentDetectionHost
|
||||
} as unknown as OrcaRuntimeService
|
||||
const dispatcher = new RpcDispatcher({ runtime, methods: PREFLIGHT_METHODS })
|
||||
|
||||
const detected = await dispatcher.dispatch(makeRequest('preflight.detectAgents'))
|
||||
const refreshed = await dispatcher.dispatch(makeRequest('preflight.refreshAgents'))
|
||||
const refreshed = await dispatcher.dispatch(makeRequest('preflight.refreshAgents', null))
|
||||
|
||||
expect(detectInstalledAgentsWithShellPathHydrationMock).toHaveBeenCalled()
|
||||
expect(refreshShellPathAndDetectAgentsMock).toHaveBeenCalled()
|
||||
// An older client sends no workspace and keeps the host-default probe.
|
||||
expect(resolveAgentDetectionHost).toHaveBeenNthCalledWith(1, undefined)
|
||||
expect(resolveAgentDetectionHost).toHaveBeenNthCalledWith(2, undefined)
|
||||
expect(detectInstalledAgentsWithShellPathHydrationMock).toHaveBeenCalledWith(undefined)
|
||||
expect(refreshShellPathAndDetectAgentsMock).toHaveBeenCalledWith(undefined)
|
||||
expect(detected).toMatchObject({ ok: true, result: ['codex'] })
|
||||
expect(refreshed).toMatchObject({
|
||||
ok: true,
|
||||
@@ -73,6 +81,45 @@ describe('preflight RPC methods', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('probes the runtime the host resolves for the requested workspace (#19885)', async () => {
|
||||
const projectRuntime = {
|
||||
status: 'resolved',
|
||||
runtime: {
|
||||
kind: 'wsl',
|
||||
hostPlatform: 'wsl',
|
||||
projectId: 'p1',
|
||||
distro: 'Ubuntu',
|
||||
reason: 'project-override',
|
||||
cacheKey: 'p1:wsl:Ubuntu'
|
||||
}
|
||||
} as const
|
||||
detectInstalledAgentsWithShellPathHydrationMock.mockResolvedValueOnce(['codex'])
|
||||
detectRemoteAgentsMock.mockResolvedValueOnce(['claude'])
|
||||
const resolveAgentDetectionHost = vi.fn((worktreeId?: string) =>
|
||||
worktreeId === 'repo-ssh::/srv/w'
|
||||
? ({ kind: 'ssh', connectionId: 'ssh-1' } as const)
|
||||
: ({ kind: 'local', context: { projectRuntime } } as const)
|
||||
)
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: these methods read only resolveAgentDetectionHost and getRuntimeId from the runtime.
|
||||
const runtime = {
|
||||
getRuntimeId: () => 'test-runtime',
|
||||
resolveAgentDetectionHost
|
||||
} as unknown as OrcaRuntimeService
|
||||
const dispatcher = new RpcDispatcher({ runtime, methods: PREFLIGHT_METHODS })
|
||||
|
||||
const wsl = await dispatcher.dispatch(
|
||||
makeRequest('preflight.detectAgents', { worktreeId: 'repo-1::\\\\wsl.localhost\\Ubuntu\\w' })
|
||||
)
|
||||
const ssh = await dispatcher.dispatch(
|
||||
makeRequest('preflight.detectAgents', { worktreeId: 'repo-ssh::/srv/w' })
|
||||
)
|
||||
|
||||
expect(detectInstalledAgentsWithShellPathHydrationMock).toHaveBeenCalledWith({ projectRuntime })
|
||||
expect(wsl).toMatchObject({ ok: true, result: ['codex'] })
|
||||
expect(detectRemoteAgentsMock).toHaveBeenCalledWith({ connectionId: 'ssh-1' })
|
||||
expect(ssh).toMatchObject({ ok: true, result: ['claude'] })
|
||||
})
|
||||
|
||||
it('detects agents on remote SSH connections through runtime RPC', async () => {
|
||||
detectRemoteAgentsMock.mockResolvedValueOnce(['claude'])
|
||||
const runtime = { getRuntimeId: () => 'test-runtime' } as unknown as OrcaRuntimeService
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { defineMethod } from '../core'
|
||||
import {
|
||||
detectRemoteAgents,
|
||||
detectRemoteWindowsTerminalCapabilities,
|
||||
detectInstalledAgentsWithShellPathHydration,
|
||||
refreshShellPathAndDetectAgents,
|
||||
runPreflightCheck
|
||||
} from '../../../preflight/agent-detection'
|
||||
import {
|
||||
detectAgentsOnHost,
|
||||
refreshAgentsOnHost
|
||||
} from '../../../preflight/workspace-agent-detection'
|
||||
import {
|
||||
PreflightAgentDetection,
|
||||
PreflightCheck,
|
||||
PreflightDetectRemoteAgents,
|
||||
PreflightDetectRemoteWindowsTerminalCapabilities
|
||||
@@ -22,14 +24,17 @@ export const PREFLIGHT_METHODS = [
|
||||
defineMethod({
|
||||
name: 'preflight.detectAgents',
|
||||
permission: 'workspace',
|
||||
params: null,
|
||||
handler: async () => detectInstalledAgentsWithShellPathHydration()
|
||||
params: PreflightAgentDetection,
|
||||
// Why the host resolves: only it knows the workspace's project runtime, including WSL.
|
||||
handler: async (params, { runtime }) =>
|
||||
detectAgentsOnHost(await runtime.resolveAgentDetectionHost(params.worktreeId))
|
||||
}),
|
||||
defineMethod({
|
||||
name: 'preflight.detectRemoteAgents',
|
||||
permission: 'workspace',
|
||||
params: PreflightDetectRemoteAgents,
|
||||
handler: async (params) => detectRemoteAgents(params)
|
||||
handler: async (params) =>
|
||||
detectAgentsOnHost({ kind: 'ssh', connectionId: params.connectionId })
|
||||
}),
|
||||
defineMethod({
|
||||
name: 'preflight.detectRemoteWindowsTerminalCapabilities',
|
||||
@@ -40,7 +45,8 @@ export const PREFLIGHT_METHODS = [
|
||||
defineMethod({
|
||||
name: 'preflight.refreshAgents',
|
||||
permission: 'workspace',
|
||||
params: null,
|
||||
handler: async () => refreshShellPathAndDetectAgents()
|
||||
params: PreflightAgentDetection,
|
||||
handler: async (params, { runtime }) =>
|
||||
refreshAgentsOnHost(await runtime.resolveAgentDetectionHost(params.worktreeId))
|
||||
})
|
||||
]
|
||||
|
||||
@@ -14,10 +14,7 @@ import {
|
||||
launchHostProvesAgentInFront,
|
||||
nameLocalTypedLineShell
|
||||
} from './agent-launch-typed-line-shell'
|
||||
import {
|
||||
detectInstalledAgentsWithShellPathHydration,
|
||||
detectRemoteAgents
|
||||
} from '../preflight/agent-detection'
|
||||
import { detectAgentsOnHost } from '../preflight/workspace-agent-detection'
|
||||
import type { RuntimeStore } from './runtime-store-contract'
|
||||
import type { RuntimeManagedWorktreeCreateArgs } from './runtime-managed-worktree-create-types'
|
||||
|
||||
@@ -61,9 +58,9 @@ export async function resolveWorktreeStartupDraftAgent(
|
||||
const sshConnectionId = getRepoSshConnectionId(repo)
|
||||
try {
|
||||
// Why: startup-draft fallback can run from sparse runtime launch envs too.
|
||||
detected = sshConnectionId
|
||||
? await detectRemoteAgents({ connectionId: sshConnectionId })
|
||||
: await detectInstalledAgentsWithShellPathHydration()
|
||||
detected = await detectAgentsOnHost(
|
||||
sshConnectionId ? { kind: 'ssh', connectionId: sshConnectionId } : { kind: 'local' }
|
||||
)
|
||||
} catch {
|
||||
detected = []
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from '../../../../shared/tui-agent-selection'
|
||||
import type { TuiAgent } from '../../../../shared/tui-agent'
|
||||
import { parseWorkspaceKey } from '../../../../shared/workspace-scope'
|
||||
import { getRuntimeAgentInventoryKey } from '@/store/slices/runtime-agent-inventory-key'
|
||||
|
||||
export type DashboardLaunchDetectionState = Pick<
|
||||
AppState,
|
||||
@@ -46,6 +47,20 @@ function buildDashboardLaunchCatalog(state: DashboardLaunchOptionState): Dashboa
|
||||
}
|
||||
}
|
||||
|
||||
// Why the host-default fallback: a card can render before any launch surface probed its workspace.
|
||||
function runtimeAgentsForWorkspace(
|
||||
state: DashboardLaunchOptionState,
|
||||
environmentId: string,
|
||||
worktreeId: string
|
||||
): readonly TuiAgent[] {
|
||||
const agents = state.runtimeDetectedAgentIds
|
||||
return (
|
||||
agents?.[getRuntimeAgentInventoryKey(environmentId, worktreeId)] ??
|
||||
agents?.[environmentId] ??
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
function detectedAgentsForWorktree(
|
||||
state: DashboardLaunchOptionState,
|
||||
worktreeId: string,
|
||||
@@ -58,7 +73,7 @@ function detectedAgentsForWorktree(
|
||||
const group = folder ? catalog.groupsById.get(folder.projectGroupId) : undefined
|
||||
const host = parseExecutionHostId(group?.executionHostId)
|
||||
if (host?.kind === 'runtime') {
|
||||
return state.runtimeDetectedAgentIds?.[host.environmentId] ?? []
|
||||
return runtimeAgentsForWorkspace(state, host.environmentId, worktreeId)
|
||||
}
|
||||
const connectionId = folder?.connectionId ?? group?.connectionId
|
||||
return connectionId ? (state.remoteDetectedAgentIds?.[connectionId] ?? []) : []
|
||||
@@ -68,7 +83,7 @@ function detectedAgentsForWorktree(
|
||||
const repo = catalog.reposById.get(worktree?.repoId ?? repoId)
|
||||
const host = parseExecutionHostId(worktree?.hostId ?? repo?.executionHostId)
|
||||
if (host?.kind === 'runtime') {
|
||||
return state.runtimeDetectedAgentIds?.[host.environmentId] ?? []
|
||||
return runtimeAgentsForWorkspace(state, host.environmentId, worktreeId)
|
||||
}
|
||||
const connectionId = host?.kind === 'ssh' ? host.targetId : repo?.connectionId
|
||||
return connectionId
|
||||
|
||||
@@ -159,7 +159,8 @@ describe('QuickLaunchAgentMenuItems', () => {
|
||||
|
||||
expect(useDetectedAgentsMock).toHaveBeenLastCalledWith({
|
||||
kind: 'runtime',
|
||||
environmentId: 'env-1'
|
||||
environmentId: 'env-1',
|
||||
worktreeId: 'worktree-1'
|
||||
})
|
||||
})
|
||||
|
||||
@@ -179,7 +180,8 @@ describe('QuickLaunchAgentMenuItems', () => {
|
||||
|
||||
expect(useDetectedAgentsMock).toHaveBeenLastCalledWith({
|
||||
kind: 'runtime',
|
||||
environmentId: 'env-1'
|
||||
environmentId: 'env-1',
|
||||
worktreeId: 'worktree-1'
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('getAgentDetectionTargetKeyForWorktree', () => {
|
||||
} as Parameters<typeof getAgentDetectionTargetKeyForWorktree>[0]
|
||||
|
||||
expect(getAgentDetectionTargetKeyForWorktree(state, folderWorkspaceKey('runtime-folder'))).toBe(
|
||||
'runtime:owner-env'
|
||||
`runtime:owner-env:${encodeURIComponent(folderWorkspaceKey('runtime-folder'))}`
|
||||
)
|
||||
expect(projectGroupReads).toBe(0)
|
||||
})
|
||||
@@ -116,7 +116,14 @@ describe('getAgentDetectionTargetKeyForWorktree', () => {
|
||||
}
|
||||
} as unknown as Parameters<typeof getAgentDetectionTargetKeyForWorktree>[0]
|
||||
|
||||
expect(getAgentDetectionTargetKeyForWorktree(state, 'repo-1::worktree-1')).toBe('runtime:env-1')
|
||||
const key = getAgentDetectionTargetKeyForWorktree(state, 'repo-1::worktree-1')
|
||||
// The workspace rides along so the paired host resolves its runtime (#19885).
|
||||
expect(key).toBe('runtime:env-1:repo-1%3A%3Aworktree-1')
|
||||
expect(parseAgentDetectionTargetKey(key)).toEqual({
|
||||
kind: 'runtime',
|
||||
environmentId: 'env-1',
|
||||
worktreeId: 'repo-1::worktree-1'
|
||||
})
|
||||
})
|
||||
|
||||
it('builds one owner index per cold worktree and repo snapshot', () => {
|
||||
|
||||
@@ -14,6 +14,11 @@ import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants'
|
||||
|
||||
export const AGENT_DETECTION_LOCAL_TARGET_KEY = 'local'
|
||||
|
||||
// Why the workspace rides along: the paired host resolves that workspace's runtime (e.g. WSL).
|
||||
function getRuntimeAgentDetectionTargetKey(environmentId: string, worktreeId: string): string {
|
||||
return `runtime:${encodeURIComponent(environmentId)}:${encodeURIComponent(worktreeId)}`
|
||||
}
|
||||
|
||||
function getLocalAgentDetectionTargetKey(worktreeId: string): string {
|
||||
return worktreeId === FLOATING_TERMINAL_WORKTREE_ID
|
||||
? `${AGENT_DETECTION_LOCAL_TARGET_KEY}:${encodeURIComponent(worktreeId)}:host`
|
||||
@@ -45,7 +50,7 @@ export function getAgentDetectionTargetKeyForWorktree(
|
||||
worktreeId
|
||||
)
|
||||
if (explicitRuntimeEnvironmentId) {
|
||||
return `runtime:${explicitRuntimeEnvironmentId}`
|
||||
return getRuntimeAgentDetectionTargetKey(explicitRuntimeEnvironmentId, worktreeId)
|
||||
}
|
||||
// Why: a hostless folder can span local and SSH children, so keep the
|
||||
// ambiguity gate before applying its focused-runtime fallback.
|
||||
@@ -62,7 +67,7 @@ export function getAgentDetectionTargetKeyForWorktree(
|
||||
return `ssh:${executionHost.targetId}`
|
||||
}
|
||||
if (executionHost?.kind === 'runtime') {
|
||||
return `runtime:${executionHost.environmentId}`
|
||||
return getRuntimeAgentDetectionTargetKey(executionHost.environmentId, worktreeId)
|
||||
}
|
||||
return getLocalAgentDetectionTargetKey(worktreeId)
|
||||
}
|
||||
@@ -97,7 +102,15 @@ export function parseAgentDetectionTargetKey(
|
||||
return { kind: 'ssh', connectionId: key.slice('ssh:'.length) }
|
||||
}
|
||||
if (key.startsWith('runtime:')) {
|
||||
return { kind: 'runtime', environmentId: key.slice('runtime:'.length) }
|
||||
const [encodedEnvironmentId = '', encodedWorktreeId] = key.slice('runtime:'.length).split(':')
|
||||
try {
|
||||
const environmentId = decodeURIComponent(encodedEnvironmentId)
|
||||
return encodedWorktreeId
|
||||
? { kind: 'runtime', environmentId, worktreeId: decodeURIComponent(encodedWorktreeId) }
|
||||
: { kind: 'runtime', environmentId }
|
||||
} catch {
|
||||
return { kind: 'runtime', environmentId: encodedEnvironmentId }
|
||||
}
|
||||
}
|
||||
return { kind: 'local' }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { useAppStore } from '@/store'
|
||||
import type { TuiAgent } from '../../../shared/tui-agent'
|
||||
import { getRuntimeAgentInventoryKey } from '@/store/slices/runtime-agent-inventory-key'
|
||||
|
||||
export type UseDetectedAgentsResult = {
|
||||
/** Null while detection is in flight on first load. */
|
||||
@@ -19,7 +20,8 @@ export type UseDetectedAgentsResult = {
|
||||
export type AgentDetectionTarget =
|
||||
| { kind: 'local'; worktreeId?: string | null; contextKey?: string }
|
||||
| { kind: 'ssh'; connectionId: string }
|
||||
| { kind: 'runtime'; environmentId: string }
|
||||
// worktreeId: the host resolves that workspace's runtime; absent means the host default.
|
||||
| { kind: 'runtime'; environmentId: string; worktreeId?: string | null }
|
||||
|
||||
function normalizeAgentDetectionTarget(
|
||||
target: AgentDetectionTarget | string | null | undefined
|
||||
@@ -66,12 +68,18 @@ export function useDetectedAgents(
|
||||
? target.environmentId
|
||||
: null
|
||||
const localWorktreeId = target?.kind === 'local' ? target.worktreeId : undefined
|
||||
const runtimeWorktreeId = target?.kind === 'runtime' ? target.worktreeId : undefined
|
||||
// Records for a runtime host are per workspace; ssh/local records keep their own keys.
|
||||
const recordKey =
|
||||
targetKind === 'runtime' && targetId
|
||||
? getRuntimeAgentInventoryKey(targetId, runtimeWorktreeId)
|
||||
: targetId
|
||||
const localContextKey = target?.kind === 'local' ? target.contextKey : undefined
|
||||
const remoteTargetKey =
|
||||
targetKind === 'ssh' && targetId
|
||||
? `ssh:${targetId}`
|
||||
: targetKind === 'runtime' && targetId
|
||||
? `runtime:${targetId}`
|
||||
: targetKind === 'runtime' && recordKey
|
||||
? `runtime:${recordKey}`
|
||||
: null
|
||||
|
||||
const detectedIds = useAppStore((s) => {
|
||||
@@ -81,8 +89,8 @@ export function useDetectedAgents(
|
||||
if (targetKind === 'ssh' && targetId) {
|
||||
return s.remoteDetectedAgentIds[targetId] ?? null
|
||||
}
|
||||
if (targetKind === 'runtime' && targetId) {
|
||||
return s.runtimeDetectedAgentIds[targetId] ?? null
|
||||
if (targetKind === 'runtime' && recordKey) {
|
||||
return s.runtimeDetectedAgentIds[recordKey] ?? null
|
||||
}
|
||||
return localContextKey
|
||||
? (s.localDetectedAgentIdsByContext[localContextKey] ?? null)
|
||||
@@ -95,16 +103,16 @@ export function useDetectedAgents(
|
||||
if (targetKind === 'ssh' && targetId) {
|
||||
return s.isDetectingRemoteAgents[targetId] ?? false
|
||||
}
|
||||
if (targetKind === 'runtime' && targetId) {
|
||||
return s.isDetectingRuntimeAgents[targetId] ?? false
|
||||
if (targetKind === 'runtime' && recordKey) {
|
||||
return s.isDetectingRuntimeAgents[recordKey] ?? false
|
||||
}
|
||||
return localContextKey
|
||||
? (s.isDetectingLocalAgentsByContext[localContextKey] ?? false)
|
||||
: s.isDetectingAgents
|
||||
})
|
||||
const isRefreshing = useAppStore((s) => {
|
||||
if (targetKind === 'runtime' && targetId) {
|
||||
return s.isRefreshingRuntimeAgents[targetId] ?? false
|
||||
if (targetKind === 'runtime' && recordKey) {
|
||||
return s.isRefreshingRuntimeAgents[recordKey] ?? false
|
||||
}
|
||||
if (targetKind === 'ssh' && targetId) {
|
||||
return s.isDetectingRemoteAgents[targetId] ?? false
|
||||
@@ -132,13 +140,13 @@ export function useDetectedAgents(
|
||||
// no-op Zustand subscriptions per hook during unrelated store churn.
|
||||
const state = useAppStore.getState()
|
||||
if (targetKind === 'runtime' && targetId) {
|
||||
return state.refreshRuntimeDetectedAgents(targetId)
|
||||
return state.refreshRuntimeDetectedAgents(targetId, runtimeWorktreeId)
|
||||
}
|
||||
if (targetKind === 'ssh' && targetId) {
|
||||
return state.refreshRemoteDetectedAgents(targetId)
|
||||
}
|
||||
return state.refreshDetectedAgents(localWorktreeId)
|
||||
}, [isUnknown, localWorktreeId, targetKind, targetId])
|
||||
}, [isUnknown, localWorktreeId, runtimeWorktreeId, targetKind, targetId])
|
||||
|
||||
useEffect(() => {
|
||||
if (isUnknown) {
|
||||
@@ -165,14 +173,14 @@ export function useDetectedAgents(
|
||||
}
|
||||
} else if (targetKind === 'runtime' && targetId) {
|
||||
if (detectedIds === null) {
|
||||
void state.ensureRuntimeDetectedAgents(targetId)
|
||||
void state.ensureRuntimeDetectedAgents(targetId, runtimeWorktreeId)
|
||||
} else if (isNewRemoteTarget && detectedIds.length > 0) {
|
||||
// Why: a host can install an agent after its cached list was populated;
|
||||
// refresh once when a new launch surface first observes that host.
|
||||
void state.refreshRuntimeDetectedAgents(targetId)
|
||||
void state.refreshRuntimeDetectedAgents(targetId, runtimeWorktreeId)
|
||||
} else if (isNewRemoteTarget) {
|
||||
// Empty results are intentionally retryable through the normal probe.
|
||||
void state.ensureRuntimeDetectedAgents(targetId)
|
||||
void state.ensureRuntimeDetectedAgents(targetId, runtimeWorktreeId)
|
||||
}
|
||||
} else {
|
||||
if (detectedIds === null) {
|
||||
@@ -186,6 +194,7 @@ export function useDetectedAgents(
|
||||
remoteTargetKey,
|
||||
detectedIds,
|
||||
localWorktreeId,
|
||||
runtimeWorktreeId,
|
||||
localContextKey
|
||||
])
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { AppState } from '@/store/types'
|
||||
import type { AgentDetectionTarget } from '@/hooks/useDetectedAgents'
|
||||
import { getRuntimeAgentInventoryKey } from '@/store/slices/runtime-agent-inventory-key'
|
||||
import type { TuiAgent } from '../../../shared/tui-agent'
|
||||
|
||||
type DetectionReadState = Pick<
|
||||
AppState,
|
||||
| 'detectedAgentIds'
|
||||
| 'localDetectedAgentIdsByContext'
|
||||
| 'remoteDetectedAgentIds'
|
||||
| 'runtimeDetectedAgentIds'
|
||||
>
|
||||
|
||||
type DetectionActionState = Pick<
|
||||
AppState,
|
||||
'ensureDetectedAgents' | 'ensureRemoteDetectedAgents' | 'ensureRuntimeDetectedAgents'
|
||||
>
|
||||
|
||||
/** The agent list a detection target's host last reported; null while it has not loaded. */
|
||||
export function readDetectedAgentsForTarget(
|
||||
state: DetectionReadState,
|
||||
target: AgentDetectionTarget
|
||||
): TuiAgent[] | null {
|
||||
if (target.kind === 'ssh') {
|
||||
return state.remoteDetectedAgentIds[target.connectionId] ?? null
|
||||
}
|
||||
if (target.kind === 'runtime') {
|
||||
const key = getRuntimeAgentInventoryKey(target.environmentId, target.worktreeId)
|
||||
return state.runtimeDetectedAgentIds[key] ?? null
|
||||
}
|
||||
return target.contextKey
|
||||
? (state.localDetectedAgentIdsByContext[target.contextKey] ?? null)
|
||||
: state.detectedAgentIds
|
||||
}
|
||||
|
||||
/** Loads a target's list from the host that owns it. */
|
||||
export function ensureDetectedAgentsForTarget(
|
||||
state: DetectionActionState,
|
||||
target: AgentDetectionTarget
|
||||
): Promise<TuiAgent[]> {
|
||||
if (target.kind === 'ssh') {
|
||||
return state.ensureRemoteDetectedAgents(target.connectionId)
|
||||
}
|
||||
if (target.kind === 'runtime') {
|
||||
return state.ensureRuntimeDetectedAgents(target.environmentId, target.worktreeId)
|
||||
}
|
||||
return state.ensureDetectedAgents(target.worktreeId)
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { isNativeChatEnabled } from '../../../shared/structured-native-chat-launch-route'
|
||||
import { pickTuiAgent } from '../../../shared/tui-agent-selection'
|
||||
import type { TuiAgent } from '../../../shared/tui-agent'
|
||||
import { withTimeout } from '../../../shared/promise-timeout-fallback'
|
||||
import { useAppStore } from '@/store'
|
||||
import type { AppState } from '@/store/types'
|
||||
@@ -9,6 +8,10 @@ import {
|
||||
parseAgentDetectionTargetKey
|
||||
} from '@/hooks/useAgentDetectionTarget'
|
||||
import type { AgentDetectionTarget } from '@/hooks/useDetectedAgents'
|
||||
import {
|
||||
ensureDetectedAgentsForTarget,
|
||||
readDetectedAgentsForTarget
|
||||
} from '@/lib/agent-detection-target-inventory'
|
||||
import { workspaceKindForWorktreeId } from '@/lib/agent-launch-route-input'
|
||||
import { planAgentSessionLaunch } from '@/lib/agent-session-launch-plan'
|
||||
import { newAgentLaunchRequestId } from '@/lib/agent-launch-request-id'
|
||||
@@ -33,24 +36,11 @@ function defaultChatDetectionTarget(
|
||||
return parseAgentDetectionTargetKey(getAgentDetectionTargetKeyForWorktree(state, worktreeId))
|
||||
}
|
||||
|
||||
/** Same host lists the tab bar reads; null means that host's detection has not loaded. */
|
||||
function readDetectedAgents(state: AppState, target: AgentDetectionTarget): TuiAgent[] | null {
|
||||
if (target.kind === 'ssh') {
|
||||
return state.remoteDetectedAgentIds[target.connectionId] ?? null
|
||||
}
|
||||
if (target.kind === 'runtime') {
|
||||
return state.runtimeDetectedAgentIds[target.environmentId] ?? null
|
||||
}
|
||||
return target.contextKey
|
||||
? (state.localDetectedAgentIdsByContext[target.contextKey] ?? null)
|
||||
: state.detectedAgentIds
|
||||
}
|
||||
|
||||
/** True when the default chat could open here but the workspace host's agent list has not loaded. */
|
||||
export function emptyWorkspaceDefaultChatAwaitsDetection(worktreeId: string): boolean {
|
||||
const state = useAppStore.getState()
|
||||
const target = defaultChatDetectionTarget(state, worktreeId)
|
||||
return target !== undefined && readDetectedAgents(state, target) === null
|
||||
return target !== undefined && readDetectedAgentsForTarget(state, target) === null
|
||||
}
|
||||
|
||||
/** Loads the workspace host's agent list; resolves on success, failure, or timeout. */
|
||||
@@ -60,13 +50,11 @@ export async function loadEmptyWorkspaceDefaultChatDetection(worktreeId: string)
|
||||
if (!target) {
|
||||
return
|
||||
}
|
||||
const detection =
|
||||
target.kind === 'ssh'
|
||||
? state.ensureRemoteDetectedAgents(target.connectionId)
|
||||
: target.kind === 'runtime'
|
||||
? state.ensureRuntimeDetectedAgents(target.environmentId)
|
||||
: state.ensureDetectedAgents(target.worktreeId)
|
||||
await withTimeout(detection, DEFAULT_CHAT_DETECTION_TIMEOUT_MS, [])
|
||||
await withTimeout(
|
||||
ensureDetectedAgentsForTarget(state, target),
|
||||
DEFAULT_CHAT_DETECTION_TIMEOUT_MS,
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +73,7 @@ export function openDefaultAgentChatInEmptyWorkspace(
|
||||
}
|
||||
const agent = pickTuiAgent(
|
||||
state.settings?.defaultTuiAgent,
|
||||
readDetectedAgents(state, target) ?? [],
|
||||
readDetectedAgentsForTarget(state, target) ?? [],
|
||||
state.settings?.disabledTuiAgents
|
||||
)
|
||||
if (!agent) {
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function detectAgentSessionContinuationAgents(
|
||||
return connectionId
|
||||
? state.ensureRemoteDetectedAgents(connectionId)
|
||||
: runtimeEnvironmentId
|
||||
? state.ensureRuntimeDetectedAgents(runtimeEnvironmentId)
|
||||
? state.ensureRuntimeDetectedAgents(runtimeEnvironmentId, worktreeId)
|
||||
: state.ensureDetectedAgents(worktreeId)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants'
|
||||
import { parseWslUncPath } from '../../../shared/wsl-paths'
|
||||
import {
|
||||
deriveGlobalWindowsRuntimeDefaultFromLegacySettings,
|
||||
getWorkspaceRuntimePreference,
|
||||
resolveProjectExecutionRuntime,
|
||||
type ProjectExecutionRuntimeResolution
|
||||
} from '../../../shared/project-execution-runtime'
|
||||
@@ -81,11 +82,10 @@ export function getLocalProjectExecutionRuntimeContext(
|
||||
}
|
||||
const projectId = getLocalPreflightProjectId(state, worktreeId)
|
||||
const project = getLocalRuntimeProject(state, projectId, repo.id)
|
||||
const localPath = worktree?.path ?? repo?.path
|
||||
const worktreeWslDistro = getWslDistroFromPath(localPath)
|
||||
const projectRuntimePreference =
|
||||
project?.localWindowsRuntimePreference ??
|
||||
(worktreeWslDistro ? { kind: 'wsl', distro: worktreeWslDistro } : { kind: 'inherit-global' })
|
||||
const projectRuntimePreference = getWorkspaceRuntimePreference(
|
||||
project?.localWindowsRuntimePreference,
|
||||
worktree?.path ?? repo?.path
|
||||
)
|
||||
|
||||
return resolveProjectExecutionRuntime({
|
||||
appPlatform,
|
||||
@@ -140,10 +140,10 @@ export function getLocalRepoProjectExecutionRuntimeContext(
|
||||
}
|
||||
const project = getLocalRuntimeProject(state, repoId, repo.id)
|
||||
const projectId = project?.id ?? repoId
|
||||
const repoWslDistro = getWslDistroFromPath(repo?.path)
|
||||
const projectRuntimePreference =
|
||||
project?.localWindowsRuntimePreference ??
|
||||
(repoWslDistro ? { kind: 'wsl', distro: repoWslDistro } : { kind: 'inherit-global' })
|
||||
const projectRuntimePreference = getWorkspaceRuntimePreference(
|
||||
project?.localWindowsRuntimePreference,
|
||||
repo?.path
|
||||
)
|
||||
|
||||
return resolveProjectExecutionRuntime({
|
||||
appPlatform,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
const WORKSPACE_SEPARATOR = '\u0000'
|
||||
|
||||
/** A paired host's agent list is per workspace: two projects on one Windows host can run in
|
||||
* different runtimes (native vs a WSL distro). No workspace means the host-default list. */
|
||||
export function getRuntimeAgentInventoryKey(
|
||||
environmentId: string,
|
||||
worktreeId?: string | null
|
||||
): string {
|
||||
return worktreeId ? `${environmentId}${WORKSPACE_SEPARATOR}${worktreeId}` : environmentId
|
||||
}
|
||||
|
||||
export function getRuntimeAgentInventoryEnvironmentId(inventoryKey: string): string {
|
||||
const separatorIndex = inventoryKey.indexOf(WORKSPACE_SEPARATOR)
|
||||
return separatorIndex === -1 ? inventoryKey : inventoryKey.slice(0, separatorIndex)
|
||||
}
|
||||
@@ -1,20 +1,37 @@
|
||||
import type { StateCreator } from 'zustand'
|
||||
import type { AppState } from '../types'
|
||||
import type { TuiAgent } from '../../../../shared/tui-agent'
|
||||
import { callRuntimeRpc, RuntimeRpcCallError } from '@/runtime/runtime-rpc-client'
|
||||
import {
|
||||
callRuntimeRpc,
|
||||
RuntimeRpcCallError,
|
||||
runtimeEnvironmentSupportsCapability
|
||||
} from '@/runtime/runtime-rpc-client'
|
||||
import { PREFLIGHT_WORKSPACE_SCOPED_RUNTIME_CAPABILITY } from '../../../../shared/protocol-version'
|
||||
import {
|
||||
getRuntimeAgentInventoryEnvironmentId,
|
||||
getRuntimeAgentInventoryKey
|
||||
} from './runtime-agent-inventory-key'
|
||||
|
||||
// Why: remote runtime hosts are not SSH connections, but their launch surfaces
|
||||
// (tab bar, quick launch, Settings → Agents under an Active Server) still have
|
||||
// to probe the host where the workspace actually runs.
|
||||
// Records are keyed by getRuntimeAgentInventoryKey: the environment alone for
|
||||
// the host-default list, or environment + workspace for a workspace's list.
|
||||
export type RuntimeDetectedAgentsSlice = {
|
||||
runtimeDetectedAgentIds: Record<string, TuiAgent[] | null>
|
||||
isDetectingRuntimeAgents: Record<string, boolean>
|
||||
isRefreshingRuntimeAgents: Record<string, boolean>
|
||||
ensureRuntimeDetectedAgents: (environmentId: string) => Promise<TuiAgent[]>
|
||||
ensureRuntimeDetectedAgents: (
|
||||
environmentId: string,
|
||||
worktreeId?: string | null
|
||||
) => Promise<TuiAgent[]>
|
||||
/** Forces a re-detect on the runtime host via `preflight.refreshAgents`
|
||||
* (login-shell PATH re-read), falling back to `preflight.detectAgents` for
|
||||
* servers that predate the refresh RPC. */
|
||||
refreshRuntimeDetectedAgents: (environmentId: string) => Promise<TuiAgent[]>
|
||||
refreshRuntimeDetectedAgents: (
|
||||
environmentId: string,
|
||||
worktreeId?: string | null
|
||||
) => Promise<TuiAgent[]>
|
||||
clearRuntimeDetectedAgents: (environmentId: string) => void
|
||||
/** Drops runtime detected-agent caches for environments not in the kept set.
|
||||
* Wired into setRuntimeEnvironments so removed environments don't leak their
|
||||
@@ -31,208 +48,201 @@ function isRuntimeMethodNotFoundError(error: unknown): boolean {
|
||||
return error instanceof RuntimeRpcCallError && error.code === 'method_not_found'
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends `preflight.detectAgents` / `refreshAgents` scoped to a workspace. A host without the
|
||||
* workspace-scoped capability would discard the params and answer with its own default, so it is
|
||||
* asked for exactly that. The host default goes out synchronously, as it always has.
|
||||
*/
|
||||
export function callRuntimeAgentDetection<T>(
|
||||
environmentId: string,
|
||||
worktreeId: string | null | undefined,
|
||||
call: (params: { worktreeId: string } | undefined) => Promise<T>
|
||||
): Promise<T> {
|
||||
if (!worktreeId) {
|
||||
return call(undefined)
|
||||
}
|
||||
return runtimeEnvironmentSupportsCapability(
|
||||
environmentId,
|
||||
PREFLIGHT_WORKSPACE_SCOPED_RUNTIME_CAPABILITY
|
||||
).then((supported) => call(supported ? { worktreeId } : undefined))
|
||||
}
|
||||
|
||||
export function _getRuntimeDetectPromiseCountForTest(): number {
|
||||
return runtimeDetectPromises.size
|
||||
}
|
||||
|
||||
function withoutKeys<T>(record: Record<string, T>, drop: (key: string) => boolean) {
|
||||
let changed = false
|
||||
const next: Record<string, T> = {}
|
||||
for (const [key, value] of Object.entries(record)) {
|
||||
if (drop(key)) {
|
||||
changed = true
|
||||
} else {
|
||||
next[key] = value
|
||||
}
|
||||
}
|
||||
return changed ? next : record
|
||||
}
|
||||
|
||||
export const createRuntimeDetectedAgentsSlice: StateCreator<
|
||||
AppState,
|
||||
[],
|
||||
[],
|
||||
RuntimeDetectedAgentsSlice
|
||||
> = (set, get) => ({
|
||||
runtimeDetectedAgentIds: {},
|
||||
isDetectingRuntimeAgents: {},
|
||||
isRefreshingRuntimeAgents: {},
|
||||
|
||||
ensureRuntimeDetectedAgents: (environmentId: string) => {
|
||||
const inflightRefresh = runtimeRefreshPromises.get(environmentId)
|
||||
if (inflightRefresh) {
|
||||
return inflightRefresh
|
||||
}
|
||||
const existing = get().runtimeDetectedAgentIds[environmentId]
|
||||
// Why: an empty result ([]) is truthy, so a prior "no agents found" detection
|
||||
// must not be treated as cached — re-detect so a later install / PATH fix is
|
||||
// picked up without a reconnect. Non-empty results still short-circuit.
|
||||
if (existing?.length) {
|
||||
return Promise.resolve(existing)
|
||||
}
|
||||
const inflight = runtimeDetectPromises.get(environmentId)
|
||||
if (inflight) {
|
||||
return inflight
|
||||
}
|
||||
|
||||
set((s) => ({
|
||||
isDetectingRuntimeAgents: { ...s.isDetectingRuntimeAgents, [environmentId]: true }
|
||||
}))
|
||||
|
||||
const pending = callRuntimeRpc<TuiAgent[]>(
|
||||
{ kind: 'environment', environmentId },
|
||||
'preflight.detectAgents'
|
||||
)
|
||||
.then((ids) => {
|
||||
const typed = ids as TuiAgent[]
|
||||
// Why: skip committing if the environment was removed (retained out)
|
||||
// while the detect was in flight — otherwise it re-adds a stale entry
|
||||
// that retainRuntimeDetectedAgents just pruned.
|
||||
if (runtimeDetectPromises.get(environmentId) === pending) {
|
||||
set((s) => ({
|
||||
runtimeDetectedAgentIds: { ...s.runtimeDetectedAgentIds, [environmentId]: typed },
|
||||
isDetectingRuntimeAgents: { ...s.isDetectingRuntimeAgents, [environmentId]: false }
|
||||
}))
|
||||
> = (set, get) => {
|
||||
// Drops every workspace's inventory for the environments `drop` selects.
|
||||
const dropEnvironments = (drop: (environmentId: string) => boolean): void => {
|
||||
const dropKey = (key: string) => drop(getRuntimeAgentInventoryEnvironmentId(key))
|
||||
for (const promises of [runtimeDetectPromises, runtimeRefreshPromises]) {
|
||||
for (const key of promises.keys()) {
|
||||
if (dropKey(key)) {
|
||||
promises.delete(key)
|
||||
}
|
||||
return typed
|
||||
})
|
||||
.catch(() => {
|
||||
// Why: a remote runtime may be disconnected or version-incompatible.
|
||||
// Keep the menu retryable instead of pinning a failed probe forever.
|
||||
// Same in-flight guard as the .then() above: if the environment was
|
||||
// retained out mid-detect, don't re-add the isDetecting entry that
|
||||
// retainRuntimeDetectedAgents just pruned (and don't clobber a freshly
|
||||
// started detect's spinner).
|
||||
if (runtimeDetectPromises.get(environmentId) === pending) {
|
||||
set((s) => ({
|
||||
isDetectingRuntimeAgents: { ...s.isDetectingRuntimeAgents, [environmentId]: false }
|
||||
}))
|
||||
}
|
||||
return [] as TuiAgent[]
|
||||
})
|
||||
.finally(() => {
|
||||
if (runtimeDetectPromises.get(environmentId) === pending) {
|
||||
runtimeDetectPromises.delete(environmentId)
|
||||
}
|
||||
})
|
||||
|
||||
runtimeDetectPromises.set(environmentId, pending)
|
||||
return pending
|
||||
},
|
||||
|
||||
refreshRuntimeDetectedAgents: (environmentId: string) => {
|
||||
const inflight = runtimeRefreshPromises.get(environmentId)
|
||||
if (inflight) {
|
||||
return inflight
|
||||
}
|
||||
|
||||
// Why: a refresh is newer and authoritative; detach an older detect so its
|
||||
// late result cannot overwrite the freshly hydrated PATH result.
|
||||
runtimeDetectPromises.delete(environmentId)
|
||||
set((s) => ({
|
||||
isRefreshingRuntimeAgents: { ...s.isRefreshingRuntimeAgents, [environmentId]: true }
|
||||
}))
|
||||
|
||||
const pending = callRuntimeRpc<{ agents: TuiAgent[] }>(
|
||||
{ kind: 'environment', environmentId },
|
||||
'preflight.refreshAgents'
|
||||
)
|
||||
.then((result) => result.agents)
|
||||
.catch((error) => {
|
||||
if (!isRuntimeMethodNotFoundError(error)) {
|
||||
throw error
|
||||
}
|
||||
// Why: only older servers need the fallback; retrying disconnects and
|
||||
// runtime failures doubles remote work without any chance of recovery.
|
||||
return callRuntimeRpc<TuiAgent[]>(
|
||||
{ kind: 'environment', environmentId },
|
||||
'preflight.detectAgents'
|
||||
)
|
||||
})
|
||||
.then((ids) => {
|
||||
const typed = ids as TuiAgent[]
|
||||
// Why: same guard as ensureRuntimeDetectedAgents — if the environment
|
||||
// was retained out mid-refresh, don't re-add a pruned entry.
|
||||
if (runtimeRefreshPromises.get(environmentId) === pending) {
|
||||
set((s) => ({
|
||||
runtimeDetectedAgentIds: { ...s.runtimeDetectedAgentIds, [environmentId]: typed },
|
||||
isDetectingRuntimeAgents: {
|
||||
...s.isDetectingRuntimeAgents,
|
||||
[environmentId]: false
|
||||
},
|
||||
isRefreshingRuntimeAgents: { ...s.isRefreshingRuntimeAgents, [environmentId]: false }
|
||||
}))
|
||||
}
|
||||
return typed
|
||||
})
|
||||
.catch(() => {
|
||||
// Why: a disconnected runtime must keep Refresh retryable and must not
|
||||
// wipe the last known agent list.
|
||||
if (runtimeRefreshPromises.get(environmentId) === pending) {
|
||||
set((s) => ({
|
||||
isDetectingRuntimeAgents: {
|
||||
...s.isDetectingRuntimeAgents,
|
||||
[environmentId]: false
|
||||
},
|
||||
isRefreshingRuntimeAgents: { ...s.isRefreshingRuntimeAgents, [environmentId]: false }
|
||||
}))
|
||||
}
|
||||
return get().runtimeDetectedAgentIds[environmentId] ?? []
|
||||
})
|
||||
.finally(() => {
|
||||
if (runtimeRefreshPromises.get(environmentId) === pending) {
|
||||
runtimeRefreshPromises.delete(environmentId)
|
||||
}
|
||||
})
|
||||
|
||||
runtimeRefreshPromises.set(environmentId, pending)
|
||||
return pending
|
||||
},
|
||||
|
||||
clearRuntimeDetectedAgents: (environmentId: string) => {
|
||||
runtimeDetectPromises.delete(environmentId)
|
||||
runtimeRefreshPromises.delete(environmentId)
|
||||
set((s) => {
|
||||
const { [environmentId]: _, ...restAgents } = s.runtimeDetectedAgentIds
|
||||
const { [environmentId]: __, ...restLoading } = s.isDetectingRuntimeAgents
|
||||
const { [environmentId]: ___, ...restRefreshing } = s.isRefreshingRuntimeAgents
|
||||
return {
|
||||
runtimeDetectedAgentIds: restAgents,
|
||||
isDetectingRuntimeAgents: restLoading,
|
||||
isRefreshingRuntimeAgents: restRefreshing
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
retainRuntimeDetectedAgents: (environmentIds: Iterable<string>) => {
|
||||
const keep = new Set(environmentIds)
|
||||
for (const id of runtimeDetectPromises.keys()) {
|
||||
if (!keep.has(id)) {
|
||||
runtimeDetectPromises.delete(id)
|
||||
}
|
||||
}
|
||||
for (const id of runtimeRefreshPromises.keys()) {
|
||||
if (!keep.has(id)) {
|
||||
runtimeRefreshPromises.delete(id)
|
||||
}
|
||||
}
|
||||
set((s) => {
|
||||
let changed = false
|
||||
const nextAgents = { ...s.runtimeDetectedAgentIds }
|
||||
const nextLoading = { ...s.isDetectingRuntimeAgents }
|
||||
const nextRefreshing = { ...s.isRefreshingRuntimeAgents }
|
||||
for (const id of Object.keys(nextAgents)) {
|
||||
if (!keep.has(id)) {
|
||||
delete nextAgents[id]
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
for (const id of Object.keys(nextLoading)) {
|
||||
if (!keep.has(id)) {
|
||||
delete nextLoading[id]
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
for (const id of Object.keys(nextRefreshing)) {
|
||||
if (!keep.has(id)) {
|
||||
delete nextRefreshing[id]
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
return changed
|
||||
? {
|
||||
runtimeDetectedAgentIds: nextAgents,
|
||||
isDetectingRuntimeAgents: nextLoading,
|
||||
isRefreshingRuntimeAgents: nextRefreshing
|
||||
}
|
||||
: s
|
||||
const runtimeDetectedAgentIds = withoutKeys(s.runtimeDetectedAgentIds, dropKey)
|
||||
const isDetectingRuntimeAgents = withoutKeys(s.isDetectingRuntimeAgents, dropKey)
|
||||
const isRefreshingRuntimeAgents = withoutKeys(s.isRefreshingRuntimeAgents, dropKey)
|
||||
return runtimeDetectedAgentIds === s.runtimeDetectedAgentIds &&
|
||||
isDetectingRuntimeAgents === s.isDetectingRuntimeAgents &&
|
||||
isRefreshingRuntimeAgents === s.isRefreshingRuntimeAgents
|
||||
? s
|
||||
: { runtimeDetectedAgentIds, isDetectingRuntimeAgents, isRefreshingRuntimeAgents }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
runtimeDetectedAgentIds: {},
|
||||
isDetectingRuntimeAgents: {},
|
||||
isRefreshingRuntimeAgents: {},
|
||||
|
||||
ensureRuntimeDetectedAgents: (environmentId: string, worktreeId?: string | null) => {
|
||||
const key = getRuntimeAgentInventoryKey(environmentId, worktreeId)
|
||||
const inflightRefresh = runtimeRefreshPromises.get(key)
|
||||
if (inflightRefresh) {
|
||||
return inflightRefresh
|
||||
}
|
||||
const existing = get().runtimeDetectedAgentIds[key]
|
||||
// Why: an empty result ([]) is truthy, so a prior "no agents found" detection
|
||||
// must not be treated as cached — re-detect so a later install / PATH fix is
|
||||
// picked up without a reconnect. Non-empty results still short-circuit.
|
||||
if (existing?.length) {
|
||||
return Promise.resolve(existing)
|
||||
}
|
||||
const inflight = runtimeDetectPromises.get(key)
|
||||
if (inflight) {
|
||||
return inflight
|
||||
}
|
||||
|
||||
set((s) => ({
|
||||
isDetectingRuntimeAgents: { ...s.isDetectingRuntimeAgents, [key]: true }
|
||||
}))
|
||||
|
||||
const target = { kind: 'environment', environmentId } as const
|
||||
const pending = callRuntimeAgentDetection(environmentId, worktreeId, (params) =>
|
||||
callRuntimeRpc<TuiAgent[]>(target, 'preflight.detectAgents', params)
|
||||
)
|
||||
.then((typed) => {
|
||||
// Why: skip committing if the environment was removed (retained out)
|
||||
// while the detect was in flight — otherwise it re-adds a stale entry
|
||||
// that retainRuntimeDetectedAgents just pruned.
|
||||
if (runtimeDetectPromises.get(key) === pending) {
|
||||
set((s) => ({
|
||||
runtimeDetectedAgentIds: { ...s.runtimeDetectedAgentIds, [key]: typed },
|
||||
isDetectingRuntimeAgents: { ...s.isDetectingRuntimeAgents, [key]: false }
|
||||
}))
|
||||
}
|
||||
return typed
|
||||
})
|
||||
.catch((): TuiAgent[] => {
|
||||
// Why: a remote runtime may be disconnected or version-incompatible.
|
||||
// Keep the menu retryable instead of pinning a failed probe forever.
|
||||
// Same in-flight guard as the .then() above.
|
||||
if (runtimeDetectPromises.get(key) === pending) {
|
||||
set((s) => ({
|
||||
isDetectingRuntimeAgents: { ...s.isDetectingRuntimeAgents, [key]: false }
|
||||
}))
|
||||
}
|
||||
return []
|
||||
})
|
||||
.finally(() => {
|
||||
if (runtimeDetectPromises.get(key) === pending) {
|
||||
runtimeDetectPromises.delete(key)
|
||||
}
|
||||
})
|
||||
|
||||
runtimeDetectPromises.set(key, pending)
|
||||
return pending
|
||||
},
|
||||
|
||||
refreshRuntimeDetectedAgents: (environmentId: string, worktreeId?: string | null) => {
|
||||
const key = getRuntimeAgentInventoryKey(environmentId, worktreeId)
|
||||
const inflight = runtimeRefreshPromises.get(key)
|
||||
if (inflight) {
|
||||
return inflight
|
||||
}
|
||||
|
||||
// Why: a refresh is newer and authoritative; detach an older detect so its
|
||||
// late result cannot overwrite the freshly hydrated PATH result.
|
||||
runtimeDetectPromises.delete(key)
|
||||
set((s) => ({
|
||||
isRefreshingRuntimeAgents: { ...s.isRefreshingRuntimeAgents, [key]: true }
|
||||
}))
|
||||
|
||||
const target = { kind: 'environment', environmentId } as const
|
||||
const pending = callRuntimeAgentDetection(environmentId, worktreeId, (params) =>
|
||||
callRuntimeRpc<{ agents: TuiAgent[] }>(target, 'preflight.refreshAgents', params)
|
||||
.then((result) => result.agents)
|
||||
.catch((error) => {
|
||||
if (!isRuntimeMethodNotFoundError(error)) {
|
||||
throw error
|
||||
}
|
||||
// Why: only older servers need the fallback; retrying disconnects and
|
||||
// runtime failures doubles remote work without any chance of recovery.
|
||||
return callRuntimeRpc<TuiAgent[]>(target, 'preflight.detectAgents', params)
|
||||
})
|
||||
)
|
||||
.then((typed) => {
|
||||
// Why: same guard as ensureRuntimeDetectedAgents — if the environment
|
||||
// was retained out mid-refresh, don't re-add a pruned entry.
|
||||
if (runtimeRefreshPromises.get(key) === pending) {
|
||||
set((s) => ({
|
||||
runtimeDetectedAgentIds: { ...s.runtimeDetectedAgentIds, [key]: typed },
|
||||
isDetectingRuntimeAgents: { ...s.isDetectingRuntimeAgents, [key]: false },
|
||||
isRefreshingRuntimeAgents: { ...s.isRefreshingRuntimeAgents, [key]: false }
|
||||
}))
|
||||
}
|
||||
return typed
|
||||
})
|
||||
.catch(() => {
|
||||
// Why: a disconnected runtime must keep Refresh retryable and must not
|
||||
// wipe the last known agent list.
|
||||
if (runtimeRefreshPromises.get(key) === pending) {
|
||||
set((s) => ({
|
||||
isDetectingRuntimeAgents: { ...s.isDetectingRuntimeAgents, [key]: false },
|
||||
isRefreshingRuntimeAgents: { ...s.isRefreshingRuntimeAgents, [key]: false }
|
||||
}))
|
||||
}
|
||||
return get().runtimeDetectedAgentIds[key] ?? []
|
||||
})
|
||||
.finally(() => {
|
||||
if (runtimeRefreshPromises.get(key) === pending) {
|
||||
runtimeRefreshPromises.delete(key)
|
||||
}
|
||||
})
|
||||
|
||||
runtimeRefreshPromises.set(key, pending)
|
||||
return pending
|
||||
},
|
||||
|
||||
clearRuntimeDetectedAgents: (environmentId: string) => {
|
||||
dropEnvironments((id) => id === environmentId)
|
||||
},
|
||||
|
||||
retainRuntimeDetectedAgents: (environmentIds: Iterable<string>) => {
|
||||
const keep = new Set(environmentIds)
|
||||
dropEnvironments((id) => !keep.has(id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { parseWslUncPath } from './wsl-paths'
|
||||
|
||||
export type LocalWindowsRuntimePreference =
|
||||
| { kind: 'inherit-global' }
|
||||
| { kind: 'windows-host' }
|
||||
@@ -107,6 +109,18 @@ export function normalizeProjectRuntimePreference(value: unknown): LocalWindowsR
|
||||
return { kind: 'inherit-global' }
|
||||
}
|
||||
|
||||
/** A project with no saved runtime runs a workspace that lives in a WSL share inside that distro. */
|
||||
export function getWorkspaceRuntimePreference(
|
||||
projectPreference: LocalWindowsRuntimePreference | undefined,
|
||||
workspacePath: string | null | undefined
|
||||
): LocalWindowsRuntimePreference {
|
||||
if (projectPreference) {
|
||||
return projectPreference
|
||||
}
|
||||
const distro = workspacePath ? parseWslUncPath(workspacePath)?.distro : undefined
|
||||
return distro ? { kind: 'wsl', distro } : { kind: 'inherit-global' }
|
||||
}
|
||||
|
||||
export function normalizeGlobalWindowsRuntimeDefault(value: unknown): GlobalWindowsRuntimeDefault {
|
||||
if (!isRecord(value)) {
|
||||
return { kind: 'windows-host' }
|
||||
|
||||
@@ -320,6 +320,10 @@ export const WORKTREE_VISIBILITY_SOURCE_DEFAULTS_RUNTIME_CAPABILITY =
|
||||
// would re-show that row as a normal workspace, so the host leaves such rows out of its listings.
|
||||
export const WORKTREE_BACKGROUND_REMOVAL_RUNTIME_CAPABILITY =
|
||||
'worktree.background-removal.v1' as const
|
||||
// Why: older hosts discard preflight.detectAgents params and probe their own PATH, so a client
|
||||
// must not read that answer as the workspace's (a WSL project on a Windows host differs).
|
||||
export const PREFLIGHT_WORKSPACE_SCOPED_RUNTIME_CAPABILITY =
|
||||
'preflight.workspace-scoped.v1' as const
|
||||
// Hosts without this capability have no notifications.registerPush RPC.
|
||||
export const NOTIFICATIONS_REMOTE_PUSH_RUNTIME_CAPABILITY = 'notifications.remote-push.v1' as const
|
||||
|
||||
@@ -425,6 +429,7 @@ export const RUNTIME_CAPABILITIES = [
|
||||
...SKILL_RUNTIME_CAPABILITIES,
|
||||
...AUTOMATION_RUNTIME_CAPABILITIES,
|
||||
NOTIFICATIONS_REMOTE_PUSH_RUNTIME_CAPABILITY,
|
||||
PREFLIGHT_WORKSPACE_SCOPED_RUNTIME_CAPABILITY,
|
||||
...AGENT_LAUNCH_RUNTIME_CAPABILITIES
|
||||
] as const
|
||||
|
||||
|
||||
@@ -11,3 +11,8 @@ export const PreflightDetectRemoteAgents = z.object({
|
||||
export const PreflightDetectRemoteWindowsTerminalCapabilities = z.object({
|
||||
connectionId: z.string().min(1)
|
||||
})
|
||||
|
||||
/** Why optional: a client that predates it sends nothing and keeps the host-default probe. */
|
||||
export const PreflightAgentDetection = z.object({
|
||||
worktreeId: z.string().min(1).optional()
|
||||
})
|
||||
|
||||
+3
-2
@@ -433,6 +433,7 @@ import {
|
||||
PluginsPanelActionParams
|
||||
} from './plugins-params'
|
||||
import {
|
||||
PreflightAgentDetection,
|
||||
PreflightCheck,
|
||||
PreflightDetectRemoteAgents,
|
||||
PreflightDetectRemoteWindowsTerminalCapabilities
|
||||
@@ -1110,11 +1111,11 @@ export const RPC_PARAMS_BY_METHOD = {
|
||||
'plugins.readPanelEntry': PluginReadPanelEntryParams,
|
||||
'plugins.setEnabled': PluginSetEnabledParams,
|
||||
'preflight.check': PreflightCheck,
|
||||
'preflight.detectAgents': null,
|
||||
'preflight.detectAgents': PreflightAgentDetection,
|
||||
'preflight.detectRemoteAgents': PreflightDetectRemoteAgents,
|
||||
'preflight.detectRemoteWindowsTerminalCapabilities':
|
||||
PreflightDetectRemoteWindowsTerminalCapabilities,
|
||||
'preflight.refreshAgents': null,
|
||||
'preflight.refreshAgents': PreflightAgentDetection,
|
||||
'project.list': null,
|
||||
'project.update': ProjectUpdate,
|
||||
'projectGroup.create': ProjectGroupCreate,
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
// Workspace-scoped agent detection, paired across two builds: current code against the newest
|
||||
// published release.
|
||||
//
|
||||
// `preflight.detectAgents` / `refreshAgents` gained an optional `{ worktreeId }` so the host probes
|
||||
// the workspace's own runtime (a WSL distro on a Windows host). Each skew must keep today's answer:
|
||||
//
|
||||
// - an old client sends nothing, and a new host answers with its own default as it always did;
|
||||
// - a new client asks an old host only for its default, because the capability is absent — and an
|
||||
// old host that is sent the field anyway (mobile does not gate) discards it rather than refusing.
|
||||
//
|
||||
// The old side's capability list is read from its checkout and the capability removed from it, so
|
||||
// this stays exercised after a release ships the capability.
|
||||
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
PREFLIGHT_WORKSPACE_SCOPED_RUNTIME_CAPABILITY,
|
||||
RUNTIME_CAPABILITIES
|
||||
} from '../../../src/shared/protocol-version'
|
||||
import { RPC_PARAMS_BY_METHOD } from '../../../src/shared/rpc-contract/rpc-params-catalog.generated'
|
||||
import { parseRpcRequestParams } from '../../../src/main/runtime/rpc/dispatcher-request-parsing'
|
||||
import type { RpcAnyMethodDeclaration, RpcRequest } from '../../../src/main/runtime/rpc/core'
|
||||
import { getRuntimeAgentInventoryKey } from '../../../src/renderer/src/store/slices/runtime-agent-inventory-key'
|
||||
import {
|
||||
importReleaseCheckoutModule,
|
||||
materializeReleaseCheckout,
|
||||
resolveBaselineReleaseRef
|
||||
} from './release-checkout'
|
||||
|
||||
// Why: a cold CI run extracts the baseline checkout before the first pairing.
|
||||
const SUITE_TIMEOUT_MS = 180_000
|
||||
const WORKTREE = 'repo-1::\\\\wsl.localhost\\Ubuntu\\home\\me\\repo'
|
||||
|
||||
type ParseParams = (
|
||||
request: RpcRequest,
|
||||
method: Pick<RpcAnyMethodDeclaration, 'params'>,
|
||||
meta: unknown
|
||||
) => { value?: unknown; error?: unknown }
|
||||
|
||||
type HostBuild = {
|
||||
capabilities: readonly string[]
|
||||
parse: (method: string, params: unknown) => unknown
|
||||
}
|
||||
|
||||
const wire = vi.hoisted(() => {
|
||||
const state: {
|
||||
host: null | { capabilities: readonly string[]; parse: (m: string, p: unknown) => unknown }
|
||||
sent: { method: string; params: unknown; hostSaw: unknown }[]
|
||||
} = { host: null, sent: [] }
|
||||
return state
|
||||
})
|
||||
|
||||
vi.mock('@/runtime/runtime-rpc-client', async (importOriginal) => {
|
||||
const actual = await importOriginal<Record<string, unknown>>()
|
||||
return {
|
||||
...actual,
|
||||
runtimeEnvironmentSupportsCapability: async (_environmentId: string, capability: string) =>
|
||||
wire.host?.capabilities.includes(capability) ?? false,
|
||||
callRuntimeRpc: async (_target: unknown, method: string, params?: unknown) => {
|
||||
const hostSaw = wire.host?.parse(method, params)
|
||||
wire.sent.push({ method, params, hostSaw })
|
||||
const scoped = typeof hostSaw === 'object' && hostSaw !== null && 'worktreeId' in hostSaw
|
||||
const agents = scoped ? ['claude', 'codex'] : ['claude']
|
||||
return method === 'preflight.refreshAgents' ? { agents } : agents
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// @ts-expect-error -- minimal window.api stub for the store under test
|
||||
globalThis.window = { api: {} }
|
||||
|
||||
const { createTestStore } =
|
||||
await import('../../../src/renderer/src/store/slices/store-test-helpers')
|
||||
|
||||
function member<T>(module: Record<string, unknown>, name: string): T {
|
||||
const value = module[name]
|
||||
if (value === undefined) {
|
||||
throw new Error(`the baseline release exports no ${name}`)
|
||||
}
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: a release module is typed unknown; the name is the same export this build calls, and a missing one throws above.
|
||||
return value as T
|
||||
}
|
||||
|
||||
function hostBuild(
|
||||
capabilities: readonly string[],
|
||||
catalog: Record<string, unknown>,
|
||||
parse: ParseParams
|
||||
): HostBuild {
|
||||
return {
|
||||
capabilities,
|
||||
parse: (method, params) => {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: catalog entries are the Zod schema or null each build's dispatcher reads.
|
||||
const schema = (catalog[method] ?? null) as RpcAnyMethodDeclaration['params']
|
||||
const parsed = parse({ id: '1', authToken: 't', method, params }, { params: schema }, {})
|
||||
if (parsed.error) {
|
||||
throw new Error(`${method} refused ${JSON.stringify(params)}`)
|
||||
}
|
||||
return parsed.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let oldHost: HostBuild
|
||||
let newHost: HostBuild
|
||||
|
||||
beforeAll(async () => {
|
||||
const checkout = await materializeReleaseCheckout(resolveBaselineReleaseRef())
|
||||
const [protocol, catalog, parsing] = await Promise.all([
|
||||
importReleaseCheckoutModule(checkout, 'src/shared/protocol-version.ts'),
|
||||
importReleaseCheckoutModule(
|
||||
checkout,
|
||||
'src/shared/rpc-contract/rpc-params-catalog.generated.ts'
|
||||
),
|
||||
importReleaseCheckoutModule(checkout, 'src/main/runtime/rpc/dispatcher-request-parsing.ts')
|
||||
])
|
||||
const oldCatalog = member<Record<string, unknown>>(catalog, 'RPC_PARAMS_BY_METHOD')
|
||||
oldHost = hostBuild(
|
||||
member<readonly string[]>(protocol, 'RUNTIME_CAPABILITIES').filter(
|
||||
(capability) => capability !== PREFLIGHT_WORKSPACE_SCOPED_RUNTIME_CAPABILITY
|
||||
),
|
||||
// Why null: once a release ships the schema, the old side must still be the pre-schema host.
|
||||
{ ...oldCatalog, 'preflight.detectAgents': null, 'preflight.refreshAgents': null },
|
||||
member<ParseParams>(parsing, 'parseRpcRequestParams')
|
||||
)
|
||||
newHost = hostBuild(RUNTIME_CAPABILITIES, RPC_PARAMS_BY_METHOD, (request, method, meta) =>
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the parser only reads `params` from the declaration.
|
||||
parseRpcRequestParams(request, method as RpcAnyMethodDeclaration, meta as never)
|
||||
)
|
||||
}, SUITE_TIMEOUT_MS)
|
||||
|
||||
beforeEach(() => {
|
||||
wire.sent.length = 0
|
||||
})
|
||||
|
||||
describe('workspace-scoped agent detection across versions', () => {
|
||||
it('a new client asks an old host only for its default list', async () => {
|
||||
wire.host = oldHost
|
||||
const store = createTestStore()
|
||||
|
||||
const detected = await store.getState().ensureRuntimeDetectedAgents('env-1', WORKTREE)
|
||||
const refreshed = await store.getState().refreshRuntimeDetectedAgents('env-1', WORKTREE)
|
||||
|
||||
expect(wire.sent.map(({ method, params }) => ({ method, params }))).toEqual([
|
||||
{ method: 'preflight.detectAgents', params: undefined },
|
||||
{ method: 'preflight.refreshAgents', params: undefined }
|
||||
])
|
||||
expect(detected).toEqual(['claude'])
|
||||
expect(refreshed).toEqual(['claude'])
|
||||
expect(
|
||||
store.getState().runtimeDetectedAgentIds[getRuntimeAgentInventoryKey('env-1', WORKTREE)]
|
||||
).toEqual(['claude'])
|
||||
})
|
||||
|
||||
it('an old host discards the field from an ungated client instead of refusing it', () => {
|
||||
expect(oldHost.parse('preflight.detectAgents', { worktreeId: WORKTREE })).toBeUndefined()
|
||||
expect(oldHost.parse('preflight.refreshAgents', { worktreeId: WORKTREE })).toBeUndefined()
|
||||
})
|
||||
|
||||
it('a new client names the workspace to a new host', async () => {
|
||||
wire.host = newHost
|
||||
const store = createTestStore()
|
||||
|
||||
expect(await store.getState().ensureRuntimeDetectedAgents('env-1', WORKTREE)).toEqual([
|
||||
'claude',
|
||||
'codex'
|
||||
])
|
||||
expect(wire.sent).toEqual([
|
||||
{
|
||||
method: 'preflight.detectAgents',
|
||||
params: { worktreeId: WORKTREE },
|
||||
hostSaw: { worktreeId: WORKTREE }
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('an old client gets the host default from a new host', async () => {
|
||||
wire.host = newHost
|
||||
for (const params of [undefined, null]) {
|
||||
expect(newHost.parse('preflight.detectAgents', params)).toEqual({})
|
||||
expect(newHost.parse('preflight.refreshAgents', params)).toEqual({})
|
||||
}
|
||||
// The host-default list keeps its environment-only key, which existing readers index by.
|
||||
const store = createTestStore()
|
||||
expect(await store.getState().ensureRuntimeDetectedAgents('env-1')).toEqual(['claude'])
|
||||
expect(wire.sent[0]?.params).toBeUndefined()
|
||||
expect(store.getState().runtimeDetectedAgentIds['env-1']).toEqual(['claude'])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user