fix(mobile): run a server SSH repo's Tasks checks on that server

F2 lists a server's SSH-backed repos, so Tasks now reads and connects their SSH targets through
the repo's own client and keys the drawer's SSH state by (host, connectionId): the desktop and a
server can each hold a target with the same id. Tests drive the real GitHub list loader.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
Jinwoo-H
2026-10-11 23:15:13 -04:00
parent 443c07880d
commit ce4b125b40
24 changed files with 1624 additions and 1254 deletions
@@ -51,6 +51,22 @@
"ordinal": 8,
"value": "Failed to save sparse preset."
},
"14a22ce0f2f8": {
"presets": [],
"presetsError": "",
"saving": true,
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"15870a254b47": {
"name": "repo.saveSparsePreset#1",
"ordinal": 6,
@@ -88,6 +104,21 @@
}
}
},
"16c82c1c6031": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"1bc7cf6bcbaf": {
"name": "workspaceSparsePresets",
"ordinal": 8,
@@ -99,19 +130,6 @@
}
]
},
"1f453ea83df7": {
"presets": [],
"presetsError": "transport failure",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"2902639ee428": {
"name": "workspaceSparsePresetsError",
"ordinal": 8,
@@ -153,6 +171,22 @@
}
}
},
"35c522d7c1af": {
"presets": [],
"presetsError": "Failed to save sparse preset.",
"saving": false,
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"37c5cc533ab4": {
"name": "workspaceSparseSaving",
"ordinal": 9,
@@ -163,25 +197,6 @@
"ordinal": 10,
"value": "p1"
},
"404305aa2e3a": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"presetsError": "",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"4b7e30ea1543": {
"name": "repo.saveSparsePreset#1",
"ordinal": 6,
@@ -222,6 +237,22 @@
}
}
},
"4b91e2fd3d57": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"530030faa977": {
"name": "workspaceSparsePresetsError",
"ordinal": 8,
@@ -232,17 +263,20 @@
"ordinal": 8,
"value": "The host sent a reply this app could not read (repo.saveSparsePreset)"
},
"6060569496ff": {
"6391b30b46c4": {
"presets": [],
"presetsError": "The host sent a reply this app could not read (repo.saveSparsePreset)",
"presetsError": "outer refused",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
"target": "[null,\"ssh-1\"]"
}
},
"6acbe9247ed7": {
@@ -353,6 +387,28 @@
}
}
},
"8e2076073a5f": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"973f31a4e64b": {
"name": "workspaceSparsePresetsError",
"ordinal": 5,
@@ -519,30 +575,20 @@
}
}
},
"bcfc7df6e4f2": {
"be8311b8f9b8": {
"presets": [],
"presetsError": "",
"saving": true,
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"befb68fa6c76": {
"presets": [],
"presetsError": "Failed to save sparse preset.",
"presetsError": "The host sent a reply this app could not read (repo.saveSparsePreset)",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
"target": "[null,\"ssh-1\"]"
}
},
"c1ae182c7519": {
@@ -550,47 +596,41 @@
"ordinal": 12,
"value": false
},
"c33fee0d8294": {
"presets": [],
"presetsError": "Unknown method",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"c88e803973a6": {
"name": "workspaceSparsePresetsLoaded",
"ordinal": 9,
"value": true
},
"d74bc3778ca8": {
"ca3d2bdfccbb": {
"presets": [],
"presetsError": "outer refused",
"presetsError": "Unknown method",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
"target": "[null,\"ssh-1\"]"
}
},
"de95b19c6158": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": {
"$rpc": "null"
"dd4232c3870b": {
"presets": [],
"presetsError": "transport failure",
"saving": false,
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
"target": "[null,\"ssh-1\"]"
}
},
"df0ecff41880": {
@@ -682,19 +722,6 @@
"$rpc": "undefined"
}
},
"ee3a941d5e9c": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"f54b3364071f": {
"name": "workspaceSparseDraft",
"ordinal": 11,
@@ -714,8 +741,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "ee3a941d5e9c",
"effects": ["de95b19c6158"]
"state": "4b91e2fd3d57",
"effects": ["16c82c1c6031"]
}
},
{
@@ -727,9 +754,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "bcfc7df6e4f2",
"state": "14a22ce0f2f8",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"530030faa977",
@@ -746,9 +773,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "404305aa2e3a",
"state": "8e2076073a5f",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -768,9 +795,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "6060569496ff",
"state": "be8311b8f9b8",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"5c6943c3209d",
@@ -787,9 +814,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "6060569496ff",
"state": "be8311b8f9b8",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"5c6943c3209d",
@@ -806,9 +833,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "befb68fa6c76",
"state": "35c522d7c1af",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"0df082978757",
@@ -825,9 +852,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "befb68fa6c76",
"state": "35c522d7c1af",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"0df082978757",
@@ -844,9 +871,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "befb68fa6c76",
"state": "35c522d7c1af",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"0df082978757",
@@ -863,9 +890,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "d74bc3778ca8",
"state": "6391b30b46c4",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"2902639ee428",
@@ -882,9 +909,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "ee3a941d5e9c",
"state": "4b91e2fd3d57",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"ab2dc4474eeb",
@@ -901,9 +928,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "c33fee0d8294",
"state": "ca3d2bdfccbb",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"75bb22c39c21",
@@ -920,9 +947,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "1f453ea83df7",
"state": "dd4232c3870b",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"9a414627d11a",
@@ -939,9 +966,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "ee3a941d5e9c",
"state": "4b91e2fd3d57",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"ab2dc4474eeb",
@@ -4,30 +4,93 @@
"family": "tasks.workspace-sparse",
"namedDeltas": [],
"values": {
"003266639675": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": "The host sent a reply this app could not read (ssh.getState)",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"02daf4600c50": {
"name": "repo.saveSparsePreset#1",
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"repo.saveSparsePreset\",\"params\":{\"repo\":\"id:repo-1\",\"name\":\"docs\",\"directories\":[\"docs\"]}}"
},
"0ca333d433d1": {
"06d696889cd6": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": "The host sent a reply this app could not read (ssh.getState)",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
"record": {
"error": "outer refused",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"09c187268020": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"record": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"0dd2c888ba7f": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"0f2644514840": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"16c82c1c6031": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"196cd7f8b6a9": {
@@ -109,12 +172,23 @@
}
}
},
"3f5e10171cd3": {
"name": "workspaceSparsePresetId",
"ordinal": 10,
"value": "p1"
"35a90f088ce3": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"404305aa2e3a": {
"3f2bc1cb5443": {
"presets": [
{
"directories": ["docs"],
@@ -125,22 +199,38 @@
"presetsError": "",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
"record": {
"error": "The host sent a reply this app could not read (ssh.getState)",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
"target": "[null,\"ssh-1\"]"
}
},
"47b768ac4603": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": "Unknown method",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
"3f5e10171cd3": {
"name": "workspaceSparsePresetId",
"ordinal": 10,
"value": "p1"
},
"44e440f6d4e1": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": "transport failure",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"4b7e30ea1543": {
@@ -183,77 +273,34 @@
}
}
},
"57be9babbecd": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"presetsError": "",
"saving": false,
"ssh": {
"error": "Unknown method",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"5e69d5600126": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": "transport failure",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"68ca812c8120": {
"4b91e2fd3d57": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
"target": "[null,\"ssh-1\"]"
}
},
"6daeb33f37f8": {
"664dd8ca84c4": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": "transport failure",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"7e5ba73897a1": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": "Unknown method",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"813df5a46a4a": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
"record": {
"error": "transport failure",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"86a756c43eee": {
@@ -358,7 +405,7 @@
}
}
},
"9367b086d487": {
"8e2076073a5f": {
"presets": [
{
"directories": ["docs"],
@@ -369,10 +416,15 @@
"presetsError": "",
"saving": false,
"ssh": {
"error": "outer refused",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"973f31a4e64b": {
@@ -380,45 +432,12 @@
"ordinal": 5,
"value": ""
},
"9a06a8b0c07a": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
}
},
"a11809a84754": {
"name": "ssh.getState#1",
"ordinal": 2,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.getState\",\"params\":{\"targetId\":\"ssh-1\"}}"
},
"a16210531185": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": "outer refused",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"a71abdbfefa8": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"af0d2d6c02df": {
"aabdb7800a81": {
"presets": [
{
"directories": ["docs"],
@@ -429,20 +448,39 @@
"presetsError": "",
"saving": false,
"ssh": {
"error": "The host sent a reply this app could not read (ssh.getState)",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
"record": {
"error": "outer refused",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"b40e13ca49f3": {
"ad1ca4768598": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": "outer refused",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
"record": {
"error": "The host sent a reply this app could not read (ssh.getState)",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"b0815569c2b6": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"record": {
"error": "transport failure",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"b53739a58194": {
@@ -552,17 +590,7 @@
}
}
},
"c1ae182c7519": {
"name": "workspaceSparseSaving",
"ordinal": 12,
"value": false
},
"c88e803973a6": {
"name": "workspaceSparsePresetsLoaded",
"ordinal": 9,
"value": true
},
"cd050477e049": {
"c059de5aa0ac": {
"presets": [
{
"directories": ["docs"],
@@ -573,12 +601,38 @@
"presetsError": "",
"saving": false,
"ssh": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
"record": {
"error": "Unknown method",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"c1a5e6927b9d": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"record": {
"error": "Unknown method",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"c1ae182c7519": {
"name": "workspaceSparseSaving",
"ordinal": 12,
"value": false
},
"c88e803973a6": {
"name": "workspaceSparsePresetsLoaded",
"ordinal": 9,
"value": true
},
"d4293c213b99": {
"name": "ssh.getState#1",
"ordinal": 1,
@@ -613,6 +667,19 @@
}
}
},
"da47c018c893": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"record": {
"error": "outer refused",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"dbf775cf1bdc": {
"name": "ssh.getState#1",
"ordinal": 1,
@@ -648,16 +715,47 @@
}
}
},
"de95b19c6158": {
"e2dd2b2e4f1c": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"e669255c71ee": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": "The host sent a reply this app could not read (ssh.getState)",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"e73b5b4b42db": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": {
"$rpc": "null"
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
"target": "[null,\"ssh-1\"]"
}
},
"ea6e5bf810d9": {
@@ -673,36 +771,6 @@
"$rpc": "undefined"
}
},
"ee3a941d5e9c": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"ef27a7ecb258": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"presetsError": "",
"saving": false,
"ssh": {
"error": "transport failure",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"f0dd78640cf3": {
"name": "ssh.getState#1",
"ordinal": 1,
@@ -745,23 +813,18 @@
"$rpc": "null"
}
},
"f7885da6b9c0": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"fc85056a3639": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
"record": {
"error": "Unknown method",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
"target": "[null,\"ssh-1\"]"
}
}
},
@@ -776,8 +839,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "ee3a941d5e9c",
"effects": ["de95b19c6158"]
"state": "4b91e2fd3d57",
"effects": ["16c82c1c6031"]
}
},
{
@@ -789,9 +852,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "404305aa2e3a",
"state": "8e2076073a5f",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -810,8 +873,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "0ca333d433d1",
"effects": ["003266639675"]
"state": "e669255c71ee",
"effects": ["ad1ca4768598"]
}
},
{
@@ -823,9 +886,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "af0d2d6c02df",
"state": "3f2bc1cb5443",
"effects": [
"003266639675",
"ad1ca4768598",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -844,8 +907,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "0ca333d433d1",
"effects": ["003266639675"]
"state": "e669255c71ee",
"effects": ["ad1ca4768598"]
}
},
{
@@ -857,9 +920,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "af0d2d6c02df",
"state": "3f2bc1cb5443",
"effects": [
"003266639675",
"ad1ca4768598",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -878,8 +941,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "68ca812c8120",
"effects": ["9a06a8b0c07a"]
"state": "35a90f088ce3",
"effects": ["e73b5b4b42db"]
}
},
{
@@ -891,9 +954,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "f7885da6b9c0",
"state": "0f2644514840",
"effects": [
"9a06a8b0c07a",
"e73b5b4b42db",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -912,8 +975,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "68ca812c8120",
"effects": ["9a06a8b0c07a"]
"state": "35a90f088ce3",
"effects": ["e73b5b4b42db"]
}
},
{
@@ -925,9 +988,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "f7885da6b9c0",
"state": "0f2644514840",
"effects": [
"9a06a8b0c07a",
"e73b5b4b42db",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -946,8 +1009,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "68ca812c8120",
"effects": ["9a06a8b0c07a"]
"state": "35a90f088ce3",
"effects": ["e73b5b4b42db"]
}
},
{
@@ -959,9 +1022,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "f7885da6b9c0",
"state": "0f2644514840",
"effects": [
"9a06a8b0c07a",
"e73b5b4b42db",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -980,8 +1043,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "a16210531185",
"effects": ["b40e13ca49f3"]
"state": "06d696889cd6",
"effects": ["da47c018c893"]
}
},
{
@@ -993,9 +1056,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "9367b086d487",
"state": "aabdb7800a81",
"effects": [
"b40e13ca49f3",
"da47c018c893",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -1014,8 +1077,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "813df5a46a4a",
"effects": ["a71abdbfefa8"]
"state": "e2dd2b2e4f1c",
"effects": ["09c187268020"]
}
},
{
@@ -1027,9 +1090,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "cd050477e049",
"state": "0dd2c888ba7f",
"effects": [
"a71abdbfefa8",
"09c187268020",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -1048,8 +1111,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "7e5ba73897a1",
"effects": ["47b768ac4603"]
"state": "fc85056a3639",
"effects": ["c1a5e6927b9d"]
}
},
{
@@ -1061,9 +1124,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "57be9babbecd",
"state": "c059de5aa0ac",
"effects": [
"47b768ac4603",
"c1a5e6927b9d",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -1082,8 +1145,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "6daeb33f37f8",
"effects": ["5e69d5600126"]
"state": "664dd8ca84c4",
"effects": ["b0815569c2b6"]
}
},
{
@@ -1095,9 +1158,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "ef27a7ecb258",
"state": "44e440f6d4e1",
"effects": [
"5e69d5600126",
"b0815569c2b6",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -1116,8 +1179,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "813df5a46a4a",
"effects": ["a71abdbfefa8"]
"state": "e2dd2b2e4f1c",
"effects": ["09c187268020"]
}
},
{
@@ -1129,9 +1192,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "cd050477e049",
"state": "0dd2c888ba7f",
"effects": [
"a71abdbfefa8",
"09c187268020",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -4,6 +4,56 @@
"family": "tasks.workspace-ssh",
"namedDeltas": [],
"values": {
"083fb3c55082": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": {
"command": "pnpm install",
"kind": "prompt",
"setupTrust": {
"contentHash": "hash-1",
"scriptContent": "pnpm install"
},
"source": "repo"
},
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"0a9bf4383712": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": {
"command": "pnpm install",
"kind": "prompt",
"setupTrust": {
"contentHash": "hash-1",
"scriptContent": "pnpm install"
},
"source": "repo"
},
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"0ba2cee4b538": {
"status": "fulfilled",
"startedAt": 0,
@@ -18,6 +68,23 @@
"source": "repo"
}
},
"0f216c7f5907": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": "unresolved",
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"11405363c62d": {
"name": "workspaceAgent",
"ordinal": 1,
@@ -103,6 +170,21 @@
}
}
},
"27794616c8de": {
"name": "workspaceSshState",
"ordinal": 11,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"2bdbcded5f9b": {
"name": "preflight.detectRemoteAgents#1",
"ordinal": 4,
@@ -210,28 +292,6 @@
}
}
},
"43ead075ce12": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": {
"command": "pnpm install",
"kind": "prompt",
"setupTrust": {
"contentHash": "hash-1",
"scriptContent": "pnpm install"
},
"source": "repo"
},
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"50c5123e1f42": {
"name": "preflight.detectRemoteAgents#1",
"ordinal": 4,
@@ -350,52 +410,6 @@
}
}
},
"7a1b524f17d0": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": {
"command": "pnpm install",
"kind": "prompt",
"setupTrust": {
"contentHash": "hash-1",
"scriptContent": "pnpm install"
},
"source": "repo"
},
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"80ffce5fa2b3": {
"name": "workspaceSshState",
"ordinal": 11,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"84149cfb1467": {
"name": "workspaceSshState",
"ordinal": 8,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connecting",
"targetId": "ssh-1"
}
},
"9637248df9cb": {
"name": "workspaceDetectedAgentIds",
"ordinal": 3,
@@ -435,20 +449,6 @@
}
}
},
"9f0676ba0d67": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": "unresolved",
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"9f089dfe58b4": {
"name": "preflight.detectRemoteAgents#1",
"ordinal": 4,
@@ -483,25 +483,26 @@
}
}
},
"a1f755a38636": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": "unresolved",
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"a9ec92743b9e": {
"name": "workspaceAgentOverridden",
"ordinal": 2,
"value": false
},
"bdfd96f57e2c": {
"name": "workspaceSshState",
"ordinal": 8,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connecting",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"bf0c0c00caf9": {
"name": "repo.hooks#1",
"ordinal": 14,
@@ -600,6 +601,23 @@
}
}
},
"e6d3fdebfaa8": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": "unresolved",
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"eb79a9b3682a": {
"status": "fulfilled",
"startedAt": 0,
@@ -674,15 +692,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -697,15 +715,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "43ead075ce12",
"state": "0a9bf4383712",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -731,15 +749,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -754,15 +772,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -788,15 +806,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -811,15 +829,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -845,15 +863,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -868,15 +886,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -902,15 +920,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -925,15 +943,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -959,15 +977,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -982,15 +1000,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1016,15 +1034,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1039,15 +1057,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1073,15 +1091,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1096,15 +1114,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1130,15 +1148,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1153,15 +1171,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1187,15 +1205,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1210,15 +1228,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1244,15 +1262,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "9f0676ba0d67",
"state": "0f216c7f5907",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -1267,15 +1285,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "7a1b524f17d0",
"state": "083fb3c55082",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -15,6 +15,31 @@
"code": "incompatible_reply"
}
},
"0a9bf4383712": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": {
"command": "pnpm install",
"kind": "prompt",
"setupTrust": {
"contentHash": "hash-1",
"scriptContent": "pnpm install"
},
"source": "repo"
},
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"0ba2cee4b538": {
"status": "fulfilled",
"startedAt": 0,
@@ -92,6 +117,21 @@
}
}
},
"27794616c8de": {
"name": "workspaceSshState",
"ordinal": 11,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"2bdbcded5f9b": {
"name": "preflight.detectRemoteAgents#1",
"ordinal": 4,
@@ -139,28 +179,6 @@
"ordinal": 10,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.connect\",\"params\":{\"targetId\":\"ssh-1\"}}"
},
"43ead075ce12": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": {
"command": "pnpm install",
"kind": "prompt",
"setupTrust": {
"contentHash": "hash-1",
"scriptContent": "pnpm install"
},
"source": "repo"
},
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"4983c5c25f98": {
"name": "repo.hooks#1",
"ordinal": 13,
@@ -301,6 +319,26 @@
}
}
},
"6fa0515d683b": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": {
"decision": "inherit",
"kind": "decision"
},
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"7073ec04e595": {
"name": "repo.hooks#1",
"ordinal": 13,
@@ -336,23 +374,6 @@
}
}
},
"7e1d82e5b5ed": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": {
"decision": "inherit",
"kind": "decision"
},
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"7f9fca3450c2": {
"name": "repo.hooks#1",
"ordinal": 13,
@@ -385,30 +406,6 @@
}
}
},
"80ffce5fa2b3": {
"name": "workspaceSshState",
"ordinal": 11,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"84149cfb1467": {
"name": "workspaceSshState",
"ordinal": 8,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connecting",
"targetId": "ssh-1"
}
},
"9637248df9cb": {
"name": "workspaceDetectedAgentIds",
"ordinal": 3,
@@ -416,20 +413,6 @@
"$rpc": "null"
}
},
"a1f755a38636": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": "unresolved",
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"a947768bc0ed": {
"status": "rejected",
"startedAt": 0,
@@ -455,6 +438,21 @@
"isRpcDeliveryUnknown": false
}
},
"bdfd96f57e2c": {
"name": "workspaceSshState",
"ordinal": 8,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connecting",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"bf0c0c00caf9": {
"name": "repo.hooks#1",
"ordinal": 14,
@@ -560,6 +558,23 @@
}
}
},
"e6d3fdebfaa8": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": "unresolved",
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"eb79a9b3682a": {
"status": "fulfilled",
"startedAt": 0,
@@ -711,15 +726,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -734,15 +749,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "43ead075ce12",
"state": "0a9bf4383712",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -757,15 +772,15 @@
"connect": "eb79a9b3682a",
"setup": "06f3dbe600c4"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -780,15 +795,15 @@
"connect": "eb79a9b3682a",
"setup": "06f3dbe600c4"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -803,15 +818,15 @@
"connect": "eb79a9b3682a",
"setup": "1712c415bebf"
},
"state": "7e1d82e5b5ed",
"state": "6fa0515d683b",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -826,15 +841,15 @@
"connect": "eb79a9b3682a",
"setup": "1712c415bebf"
},
"state": "7e1d82e5b5ed",
"state": "6fa0515d683b",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -849,15 +864,15 @@
"connect": "eb79a9b3682a",
"setup": "1712c415bebf"
},
"state": "7e1d82e5b5ed",
"state": "6fa0515d683b",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -872,15 +887,15 @@
"connect": "eb79a9b3682a",
"setup": "32a7c0ae7918"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -895,15 +910,15 @@
"connect": "eb79a9b3682a",
"setup": "f3b516f62081"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -918,15 +933,15 @@
"connect": "eb79a9b3682a",
"setup": "b948e8307e81"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -941,15 +956,15 @@
"connect": "eb79a9b3682a",
"setup": "a947768bc0ed"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -964,15 +979,15 @@
"connect": "eb79a9b3682a",
"setup": "c7584e82c72f"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
File diff suppressed because it is too large Load Diff
@@ -9,6 +9,19 @@
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"repo.saveSparsePreset\",\"params\":{\"repo\":\"id:repo-1\",\"name\":\"docs\",\"directories\":[\"docs\"]}}"
},
"09c187268020": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"record": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"0df082978757": {
"name": "workspaceSparsePresetsError",
"ordinal": 8,
@@ -48,15 +61,18 @@
}
}
},
"193c0bc3cf2a": {
"2a3bfef3eb90": {
"presets": [],
"presetsError": "Failed to save sparse preset.",
"saving": false,
"ssh": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
"record": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"37c5cc533ab4": {
@@ -109,16 +125,6 @@
"ordinal": 2,
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.getState\",\"params\":{\"targetId\":\"ssh-1\"}}"
},
"a71abdbfefa8": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"ea6e5bf810d9": {
"name": "workspaceSparseSaving",
"ordinal": 4,
@@ -145,9 +151,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "193c0bc3cf2a",
"state": "2a3bfef3eb90",
"effects": [
"a71abdbfefa8",
"09c187268020",
"ea6e5bf810d9",
"973f31a4e64b",
"0df082978757",
@@ -9,6 +9,21 @@
"ordinal": 7,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"repo.saveSparsePreset\",\"params\":{\"repo\":\"id:repo-1\",\"name\":\"docs\",\"directories\":[\"docs\"]}}"
},
"16c82c1c6031": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"1bc7cf6bcbaf": {
"name": "workspaceSparsePresets",
"ordinal": 8,
@@ -25,25 +40,6 @@
"ordinal": 10,
"value": "p1"
},
"404305aa2e3a": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"presetsError": "",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"4b7e30ea1543": {
"name": "repo.saveSparsePreset#1",
"ordinal": 6,
@@ -84,6 +80,44 @@
}
}
},
"4b91e2fd3d57": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"8e2076073a5f": {
"presets": [
{
"directories": ["docs"],
"id": "p1",
"name": "docs"
}
],
"presetsError": "",
"saving": false,
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"973f31a4e64b": {
"name": "workspaceSparsePresetsError",
"ordinal": 5,
@@ -145,18 +179,6 @@
"ordinal": 9,
"value": true
},
"de95b19c6158": {
"name": "workspaceSshState",
"ordinal": 3,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"ea6e5bf810d9": {
"name": "workspaceSparseSaving",
"ordinal": 4,
@@ -170,19 +192,6 @@
"$rpc": "undefined"
}
},
"ee3a941d5e9c": {
"presets": [],
"presetsError": "",
"saving": false,
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"f54b3364071f": {
"name": "workspaceSparseDraft",
"ordinal": 11,
@@ -202,8 +211,8 @@
"settlements": {
"mount": "eb79a9b3682a"
},
"state": "ee3a941d5e9c",
"effects": ["de95b19c6158"]
"state": "4b91e2fd3d57",
"effects": ["16c82c1c6031"]
}
},
{
@@ -215,9 +224,9 @@
"mount": "eb79a9b3682a",
"save": "eb79a9b3682a"
},
"state": "404305aa2e3a",
"state": "8e2076073a5f",
"effects": [
"de95b19c6158",
"16c82c1c6031",
"ea6e5bf810d9",
"973f31a4e64b",
"1bc7cf6bcbaf",
@@ -9,16 +9,6 @@
"ordinal": 1,
"value": "claude"
},
"263b165043b0": {
"name": "workspaceSshState",
"ordinal": 11,
"value": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"2d313c57ddf7": {
"status": "fulfilled",
"startedAt": 0,
@@ -36,24 +26,6 @@
"ordinal": 10,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.connect\",\"params\":{\"targetId\":\"ssh-1\"}}"
},
"55904d40a00f": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": {
"decision": "skip",
"kind": "decision",
"setupTrust": {
"$rpc": "undefined"
}
},
"ssh": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
}
},
"5d9791d21090": {
"name": "preflight.detectRemoteAgents#1",
"ordinal": 4,
@@ -91,28 +63,19 @@
"ordinal": 6,
"value": []
},
"84149cfb1467": {
"name": "workspaceSshState",
"ordinal": 8,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connecting",
"targetId": "ssh-1"
}
},
"8509334ad6ae": {
"7890acb5e903": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": "unresolved",
"ssh": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
"record": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"9637248df9cb": {
@@ -162,6 +125,34 @@
}
}
},
"b631ed286026": {
"name": "workspaceSshState",
"ordinal": 11,
"value": {
"record": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"bdfd96f57e2c": {
"name": "workspaceSshState",
"ordinal": 8,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connecting",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"bf0c0c00caf9": {
"name": "repo.hooks#1",
"ordinal": 14,
@@ -228,6 +219,27 @@
"value": {
"$rpc": "undefined"
}
},
"f004fb493cbb": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": {
"decision": "skip",
"kind": "decision",
"setupTrust": {
"$rpc": "undefined"
}
},
"ssh": {
"record": {
"error": "",
"reconnectAttempt": 0,
"status": "error",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
}
},
"recording": {
@@ -242,15 +254,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "8509334ad6ae",
"state": "7890acb5e903",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"263b165043b0",
"bdfd96f57e2c",
"b631ed286026",
"e44d3b580ab0"
]
}
@@ -265,15 +277,15 @@
"connect": "eb79a9b3682a",
"setup": "2d313c57ddf7"
},
"state": "55904d40a00f",
"state": "f004fb493cbb",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"d68ba9a6772a",
"84149cfb1467",
"263b165043b0",
"bdfd96f57e2c",
"b631ed286026",
"e44d3b580ab0"
]
}
@@ -4,6 +4,31 @@
"family": "tasks.workspace-ssh",
"namedDeltas": [],
"values": {
"0a9bf4383712": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": {
"command": "pnpm install",
"kind": "prompt",
"setupTrust": {
"contentHash": "hash-1",
"scriptContent": "pnpm install"
},
"source": "repo"
},
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"0ba2cee4b538": {
"status": "fulfilled",
"startedAt": 0,
@@ -37,6 +62,21 @@
"$rpc": "null"
}
},
"27794616c8de": {
"name": "workspaceSshState",
"ordinal": 11,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"2bdbcded5f9b": {
"name": "preflight.detectRemoteAgents#1",
"ordinal": 4,
@@ -74,52 +114,6 @@
"ordinal": 10,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.connect\",\"params\":{\"targetId\":\"ssh-1\"}}"
},
"43ead075ce12": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": {
"command": "pnpm install",
"kind": "prompt",
"setupTrust": {
"contentHash": "hash-1",
"scriptContent": "pnpm install"
},
"source": "repo"
},
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"80ffce5fa2b3": {
"name": "workspaceSshState",
"ordinal": 11,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"84149cfb1467": {
"name": "workspaceSshState",
"ordinal": 8,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connecting",
"targetId": "ssh-1"
}
},
"9637248df9cb": {
"name": "workspaceDetectedAgentIds",
"ordinal": 3,
@@ -127,25 +121,26 @@
"$rpc": "null"
}
},
"a1f755a38636": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": "unresolved",
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
}
},
"a9ec92743b9e": {
"name": "workspaceAgentOverridden",
"ordinal": 2,
"value": false
},
"bdfd96f57e2c": {
"name": "workspaceSshState",
"ordinal": 8,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connecting",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"bf0c0c00caf9": {
"name": "repo.hooks#1",
"ordinal": 14,
@@ -210,6 +205,23 @@
}
}
},
"e6d3fdebfaa8": {
"agent": "claude",
"connecting": false,
"detected": ["codex"],
"setup": "unresolved",
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "connected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"eb79a9b3682a": {
"status": "fulfilled",
"startedAt": 0,
@@ -284,15 +296,15 @@
"mount": "eb79a9b3682a",
"connect": "eb79a9b3682a"
},
"state": "a1f755a38636",
"state": "e6d3fdebfaa8",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -307,15 +319,15 @@
"connect": "eb79a9b3682a",
"setup": "0ba2cee4b538"
},
"state": "43ead075ce12",
"state": "0a9bf4383712",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"13e6d0c47ca4",
"d68ba9a6772a",
"84149cfb1467",
"80ffce5fa2b3",
"bdfd96f57e2c",
"27794616c8de",
"e44d3b580ab0"
]
}
@@ -69,18 +69,6 @@
"kind": "decision"
}
},
"2b24e6370376": {
"name": "workspaceSshState",
"ordinal": 9,
"value": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
}
},
"630e2f27face": {
"name": "workspaceDetectedAgentIds",
"ordinal": 6,
@@ -91,6 +79,26 @@
"ordinal": 8,
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.getState\",\"params\":{\"targetId\":\"ssh-1\"}}"
},
"8ca63385692b": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": {
"decision": "inherit",
"kind": "decision"
},
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"904676884ccd": {
"name": "repo.hooks#1",
"ordinal": 10,
@@ -134,23 +142,6 @@
"$rpc": "null"
}
},
"9a9cd2877569": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": {
"decision": "inherit",
"kind": "decision"
},
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
}
},
"9cc26a1c5918": {
"name": "repo.hooks#1",
"ordinal": 11,
@@ -161,6 +152,38 @@
"ordinal": 2,
"value": false
},
"b5a28716c180": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": "unresolved",
"ssh": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"b83da77586c2": {
"name": "workspaceSshState",
"ordinal": 9,
"value": {
"record": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
},
"target": "[null,\"ssh-1\"]"
}
},
"bdabc93667d6": {
"name": "preflight.detectRemoteAgents#1",
"ordinal": 4,
@@ -196,20 +219,6 @@
}
}
},
"d3698fc526a8": {
"agent": "claude",
"connecting": false,
"detected": [],
"setup": "unresolved",
"ssh": {
"error": {
"$rpc": "null"
},
"reconnectAttempt": 0,
"status": "disconnected",
"targetId": "ssh-1"
}
},
"d760f20b60ad": {
"name": "preflight.detectRemoteAgents#1",
"ordinal": 5,
@@ -236,13 +245,13 @@
"mount": "eb79a9b3682a",
"ensure": "0f1cf505ed63"
},
"state": "d3698fc526a8",
"state": "b5a28716c180",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"2b24e6370376"
"b83da77586c2"
]
}
},
@@ -256,13 +265,13 @@
"ensure": "0f1cf505ed63",
"setup": "1712c415bebf"
},
"state": "9a9cd2877569",
"state": "8ca63385692b",
"effects": [
"11405363c62d",
"a9ec92743b9e",
"9637248df9cb",
"630e2f27face",
"2b24e6370376"
"b83da77586c2"
]
}
}
+3 -10
View File
@@ -1,9 +1,10 @@
import { useEffect, useRef, type ReactNode } from 'react'
import { ActivityIndicator, StyleSheet, View } from 'react-native'
import { useHostClient } from '../transport/client-context'
import { useHostStatusGates, type HostStatusGates } from '../transport/host-status-gates'
import { useHostStatusGates } from '../transport/host-status-gates'
import { colors } from '../theme/mobile-theme'
import { HostStatusGatesContext, useOptionalHostProtocolGates } from './host-protocol-gates-context'
import { HostStatusGatesContext } from './host-protocol-gates-context'
export { useHostProtocolGates } from './host-protocol-gates-context'
import { ProtocolBlockScreen } from './ProtocolBlockScreen'
type Props = {
@@ -11,14 +12,6 @@ type Props = {
children: ReactNode
}
export function useHostProtocolGates(): HostStatusGates {
const gates = useOptionalHostProtocolGates()
if (!gates) {
throw new Error('useHostProtocolGates must be used inside <HostProtocolGate>')
}
return gates
}
// Why: single choke point above every /h/[hostId] route so a blocked verdict replaces the
// whole host UI (sidebar + detail stack) while the host list and other hosts stay usable.
export function HostProtocolGate({ hostId, children }: Props) {
@@ -8,3 +8,12 @@ export const HostStatusGatesContext = createContext<HostStatusGates | null>(null
export function useOptionalHostProtocolGates(): HostStatusGates | null {
return useContext(HostStatusGatesContext)
}
/** The gates of the host route this screen sits under. */
export function useHostProtocolGates(): HostStatusGates {
const gates = useOptionalHostProtocolGates()
if (!gates) {
throw new Error('useHostProtocolGates must be used inside <HostProtocolGate>')
}
return gates
}
@@ -17,7 +17,7 @@ import {
useState
} from './mobile-tasks-dependencies'
import { newTabRepoListRead } from '../session/mobile-session-read-operations'
import { useOptionalHostProtocolGates } from '../components/host-protocol-gates-context'
import { useHostProtocolGates } from '../components/host-protocol-gates-context'
import { useTaskHostRepos } from './use-task-host-repos'
import {
type ActionableTaskItem,
@@ -47,14 +47,12 @@ import {
} from './mobile-tasks-legacy-foundation'
import { useMobileTasksItemState } from './use-mobile-tasks-item-state'
const NO_CAPABILITIES: readonly string[] = []
export function useMobileTasksRouteAndItemState() {
const { hostId, taskSource } = useLocalSearchParams<{ hostId: string; taskSource?: string }>()
const router = useRouter()
const insets = useSafeAreaInsets()
const { client: desktopClient, state: connState } = useHostClient(hostId)
const hostCapabilities = useOptionalHostProtocolGates()?.hostCapabilities ?? NO_CAPABILITIES
const { hostCapabilities } = useHostProtocolGates()
const reconnectAttempts = useReconnectAttempt(hostId)
const lastConnectedAt = useLastConnectedAt(hostId)
const relayRecovery = useRelayRecoveryStatus(hostId)
@@ -14,6 +14,8 @@ vi.mock('react-native', () => ({ AppState: { currentState: 'active' } }))
vi.mock('./mobile-tasks-dependencies', async () => {
const react = await import('react')
const { useHostRepoList } = await import('./use-host-repo-list')
const workItems = await import('./mobile-work-items')
const sourceErrors = await import('./github-work-item-source-errors')
const { normalizeVisibleTaskProviders } = await import('./mobile-task-providers')
return {
useCallback: react.useCallback,
@@ -22,6 +24,11 @@ vi.mock('./mobile-tasks-dependencies', async () => {
useState: react.useState,
useHostRepoList,
normalizeVisibleTaskProviders,
CROSS_REPO_DISPLAY_LIMIT: workItems.CROSS_REPO_DISPLAY_LIMIT,
PER_REPO_FETCH_LIMIT: workItems.PER_REPO_FETCH_LIMIT,
isGitHubWorkItemsSshRemoteRequiredError: workItems.isGitHubWorkItemsSshRemoteRequiredError,
extractGitHubIssueSourceError: sourceErrors.extractGitHubIssueSourceError,
extractGitHubIssueSourceFallback: sourceErrors.extractGitHubIssueSourceFallback,
useHostClient: () => ({ client: desktopRef.current, state: 'connected' }),
useLocalSearchParams: () => ({ hostId: 'mac' }),
useRouter: () => ({}),
@@ -33,11 +40,15 @@ vi.mock('./mobile-tasks-dependencies', async () => {
})
vi.mock('./mobile-tasks-legacy-foundation', async () => ({
...(await import('./mobile-tasks-item-mapping')),
reconcileTeamSelection: () => new Set(),
DEFAULT_LINEAR_DISPLAY_PROPERTIES: [],
GITHUB_REPO_CONCURRENCY: 3,
groupDetailComments: () => []
}))
const { useMobileTasksRouteAndItemState } = await import('./use-mobile-tasks-route-and-item-state')
const { useMobileTasksProviderLoadActions } =
await import('./use-mobile-tasks-provider-load-actions')
type Sent = { method: string; executionHost?: string }
const sentBy = new WeakMap<FakeSession, Sent[]>()
@@ -72,6 +83,20 @@ const HOSTS = [
{ hostId: 'runtime:old', label: 'ThinkPad', health: 'available', relay: 'update-needed' }
]
function issue(title: string) {
return {
id: title,
type: 'issue',
number: 1,
title,
state: 'open',
url: 'https://github.com/o/r/issues/1',
labels: [],
updatedAt: '2026-10-11T00:00:00Z',
author: null
}
}
/** A desktop relaying to `runtime:vm`, whose repo list `vmRepoList` answers. */
function desktop(
vmRepoList: () => Promise<unknown> = async () => ({ repos: VM_REPOS })
@@ -80,6 +105,9 @@ function desktop(
if (method === 'mobileRelay.hosts.list') {
return reply({ hosts: HOSTS })
}
if (method === 'github.listWorkItems') {
return reply({ items: [issue(executionHost ? 'from the server' : 'from the desktop')] })
}
if (method !== 'repo.list') {
return reply({})
}
@@ -90,7 +118,8 @@ function desktop(
})
}
type Model = ReturnType<typeof useMobileTasksRouteAndItemState>
type Model = ReturnType<typeof useMobileTasksRouteAndItemState> &
ReturnType<typeof useMobileTasksProviderLoadActions>
const mounted: ReactTestRenderer[] = []
afterEach(() => {
for (const renderer of mounted.splice(0)) {
@@ -102,7 +131,13 @@ async function mount(client: FakeSession, hostCapabilities: string[]): Promise<(
desktopRef.current = client
const seen: { model?: Model } = {}
function Harness(): null {
seen.model = useMobileTasksRouteAndItemState()
// The GitHub list's loader, fed the screen's own model as the screen feeds it.
seen.model = useMobileTasksProviderLoadActions(
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the loader reads only stage-1 members (client, repos, query, kind).
useMobileTasksRouteAndItemState() as unknown as Parameters<
typeof useMobileTasksProviderLoadActions
>[0]
)
return null
}
const gates: HostStatusGates = {
@@ -147,6 +182,8 @@ const VM_REPO = {
path: '/srv/api',
executionHostId: 'runtime:vm'
}
// A server's SSH-backed repo keeps its connection, which that server holds.
const VM_SSH_REPO = { ...VM_REPOS[1], executionHostId: 'runtime:vm' }
/** Where each call on the screen's client ran: its `executionHost`, or `desktop`. */
function ranOn(client: FakeSession, method: string): string[] {
@@ -161,8 +198,8 @@ describe('Tasks screen repos on the desktop and its servers', () => {
const model = await mount(client, [MOBILE_DESKTOP_RELAY_RUNTIME_CAPABILITY])
await loadRepos(model)
await settle()
expect(model().repos).toEqual([...MAC_REPOS, VM_REPO])
expect(await loadRepos(model)).toEqual([...MAC_REPOS, VM_REPO])
expect(model().repos).toEqual([...MAC_REPOS, VM_REPO, VM_SSH_REPO])
expect(await loadRepos(model)).toEqual([...MAC_REPOS, VM_REPO, VM_SSH_REPO])
// The server needing an update is never asked.
expect(ranOn(client, 'repo.list').sort()).toEqual(['desktop', 'runtime:vm'])
})
@@ -176,19 +213,22 @@ describe('Tasks screen repos on the desktop and its servers', () => {
expect(model().repos).toEqual(MAC_REPOS)
})
it("runs a server repo's work-item calls on that server and the rest on the desktop", async () => {
it("loads a server repo's GitHub items from that server, as the screen's list does", async () => {
const client = desktop()
const model = await mount(client, [MOBILE_DESKTOP_RELAY_RUNTIME_CAPABILITY])
await settle()
await loadRepos(model)
const screenClient = model().client!
await screenClient.sendRequest('github.listWorkItems', { repo: 'id:vm-repo', limit: 1 })
await screenClient.sendRequest('gitlab.workItemDetails', { repo: 'id:vm-repo', iid: 1 })
await screenClient.sendRequest('github.listWorkItems', { repo: 'id:mac-repo', limit: 1 })
await screenClient.sendRequest('linear.status', undefined)
expect(ranOn(client, 'github.listWorkItems')).toEqual(['runtime:vm', 'desktop'])
expect(ranOn(client, 'gitlab.workItemDetails')).toEqual(['runtime:vm'])
expect(ranOn(client, 'linear.status')).toEqual(['desktop'])
await settle()
const page: { titles?: string[] } = {}
await act(async () => {
const loaded = await model().fetchGitHubItemsPage(model().client!, model().repos)
page.titles = loaded.items.map((item) => item.title).sort()
})
expect(page.titles).toEqual(['from the desktop', 'from the server', 'from the server'])
expect(ranOn(client, 'github.listWorkItems').sort()).toEqual([
'desktop',
'runtime:vm',
'runtime:vm'
])
})
it("names a server repo's host before its path in the repo pickers", async () => {
@@ -196,7 +236,7 @@ describe('Tasks screen repos on the desktop and its servers', () => {
await loadRepos(model)
await settle()
const details = model().repos.map((repo) => getTaskRepoDetail(repo, model().serverLabelById))
expect(details).toEqual(['/mac/app', 'VM · /srv/api'])
expect(details).toEqual(['/mac/app', 'VM · /srv/api', 'VM · /r'])
})
it('keeps the desktop list alone on a desktop that does not relay', async () => {
@@ -1,5 +1,5 @@
import type { RouteAndItemStateModel } from './use-mobile-tasks-route-and-item-state'
import type { WorkspaceSshRecord } from './workspace-ssh-gate'
import type { WorkspaceSshEntry } from './workspace-ssh-gate'
import {
type BaseRefSearchResult,
type GitHubProjectPartialFailure,
@@ -66,7 +66,7 @@ export function useMobileTasksWorkspaceAndProjectState(model: RouteAndItemStateM
const [workspaceDetectedAgentIds, setWorkspaceDetectedAgentIds] = useState<Set<string> | null>(
null
)
const [workspaceSshState, setWorkspaceSshState] = useState<WorkspaceSshRecord | null>(null)
const [workspaceSshState, setWorkspaceSshState] = useState<WorkspaceSshEntry | null>(null)
const [workspaceSshConnecting, setWorkspaceSshConnecting] = useState(false)
const [showWorkspaceAgentPicker, setShowWorkspaceAgentPicker] = useState(false)
const [showWorkspaceCreateRepoPicker, setShowWorkspaceCreateRepoPicker] = useState(false)
@@ -1,4 +1,6 @@
import type { ProjectMetadataLoadingModel } from './use-mobile-tasks-project-metadata-loading'
import { taskRepoServer } from './task-repo-host-routing'
import { workspaceSshTargetKey } from './workspace-ssh-gate'
import {
type BaseRefSearchResult,
MOBILE_TUI_AGENT_AUTO_PICK_ORDER,
@@ -89,9 +91,14 @@ export function useMobileTasksWorkspaceCreateProjection(model: ProjectMetadataLo
[getWorkspaceTargetRepo, workspaceCreateDraft]
)
const workspaceCreateTargetConnectionId = workspaceCreateTargetRepo?.connectionId ?? null
const workspaceCreateTargetSshKey = workspaceSshTargetKey(
taskRepoServer(workspaceCreateTargetRepo ?? undefined),
workspaceCreateTargetConnectionId
)
const workspaceCreateSshGate = deriveWorkspaceSshGate({
connectionId: workspaceCreateTargetConnectionId,
state: workspaceSshState,
state:
workspaceSshState?.target === workspaceCreateTargetSshKey ? workspaceSshState.record : null,
connecting: workspaceSshConnecting
})
const workspaceCreateSshStatus = workspaceCreateSshGate.status
@@ -250,6 +257,7 @@ export function useMobileTasksWorkspaceCreateProjection(model: ProjectMetadataLo
getWorkspaceTargetRepo,
workspaceCreateTargetRepo,
workspaceCreateTargetConnectionId,
workspaceCreateTargetSshKey,
workspaceCreateSshGate,
workspaceCreateSshStatus,
workspaceCreateRequiresSshConnection,
@@ -3,7 +3,8 @@ import { act, create, type ReactTestRenderer } from 'react-test-renderer'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
import type { RpcResponse } from '../transport/types'
import { routeTaskRepoCalls } from './task-repo-host-routing'
import { routeTaskRepoCalls, taskRepoServer } from './task-repo-host-routing'
import { workspaceSshTargetKey, type WorkspaceSshEntry } from './workspace-ssh-gate'
import type { ActionableTaskItem, RepoSummary } from './mobile-tasks-legacy-foundation'
import type { WorkspaceSparseActionsModel } from './use-mobile-tasks-workspace-sparse-actions'
@@ -85,7 +86,10 @@ function desktop(): FakeSession {
if (method === 'worktree.create') {
return reply({ worktree: { id: 'wt-1', displayName: 'fix-the-thing' } })
}
return reply(method === 'preflight.detectAgents' ? [] : {})
if (method === 'ssh.getState') {
return reply({ state: CONNECTED_SSH })
}
return reply(method.startsWith('preflight.detect') ? [] : {})
})
}
@@ -102,13 +106,23 @@ afterEach(() => {
}
})
async function createFrom(repo: RepoSummary) {
const CONNECTED_SSH = { targetId: 'ssh-1', status: 'connected', error: null, reconnectAttempt: 0 }
// The desktop and the server each hold an SSH target named `ssh-1`.
const SERVER_SSH_REPO: RepoSummary = {
id: 'vm-ssh-repo',
displayName: 'remote',
path: '/r',
connectionId: 'ssh-1',
executionHostId: 'runtime:vm'
}
async function createFrom(repo: RepoSummary, workspaceSshState: WorkspaceSshEntry | null = null) {
const client = desktop()
const pushed: string[] = []
const item = issueOn(repo)
const noop = () => {}
const model = {
client: routeTaskRepoCalls(client, () => [DESKTOP_REPO, SERVER_REPO]),
client: routeTaskRepoCalls(client, () => [DESKTOP_REPO, SERVER_REPO, SERVER_SSH_REPO]),
hostId: 'mac',
router: { push: (href: string) => pushed.push(href) },
runtimeTaskSettings: {},
@@ -120,11 +134,15 @@ async function createFrom(repo: RepoSummary) {
workspaceCreateDraft: { item },
workspaceCreateRequiresSshConnection: false,
workspaceCreateSshStatus: 'connected',
workspaceCreateTargetConnectionId: null,
workspaceCreateTargetConnectionId: repo.connectionId ?? null,
workspaceCreateTargetRepo: repo,
workspaceCreateTargetSshKey: workspaceSshTargetKey(
taskRepoServer(repo),
repo.connectionId ?? null
),
workspaceDetectedAgentIds: null,
workspaceLastAutoName: '',
workspaceSshState: null,
workspaceSshState,
getWorkspaceTargetRepo: () => repo,
setActionItem: noop,
setCreatingKey: noop,
@@ -171,4 +189,21 @@ describe('creating a workspace from a task on a server repo', () => {
expect(ranOn(client, 'preflight.detectAgents')).toEqual(['desktop'])
expect(new URL(pushed[0]!, 'orca://x').searchParams.get('executionHost')).toBeNull()
})
it("checks a server SSH repo's connection and agents on that server", async () => {
const { client } = await createFrom(SERVER_SSH_REPO)
expect(ranOn(client, 'ssh.getState')).toEqual(['runtime:vm'])
expect(ranOn(client, 'preflight.detectRemoteAgents')).toEqual(['runtime:vm'])
expect(ranOn(client, 'worktree.create')).toEqual(['runtime:vm'])
})
it("doesn't take the desktop's connected target for the server's target of the same id", async () => {
const desktopConnected = {
target: workspaceSshTargetKey(undefined, 'ssh-1'),
record: { targetId: 'ssh-1', status: 'connected' as const, error: null, reconnectAttempt: 0 }
}
const { client } = await createFrom(SERVER_SSH_REPO, desktopConnected)
// The desktop's record doesn't count, so the server is asked.
expect(ranOn(client, 'ssh.getState')).toEqual(['runtime:vm'])
})
})
@@ -2,6 +2,7 @@ import type { WorkspaceSourceEffectsModel } from './use-mobile-tasks-workspace-s
import { type SparsePreset, useCallback, useEffect } from './mobile-tasks-dependencies'
import { sortSparsePresetsByName } from './mobile-tasks-legacy-foundation'
import { repoSparsePresetSaveRun, sshRepoStateRead } from './mobile-workspace-source-operations'
import { taskRepoClient } from './task-repo-host-routing'
export function useMobileTasksWorkspaceSparseActions(model: WorkspaceSourceEffectsModel) {
const {
@@ -20,6 +21,7 @@ export function useMobileTasksWorkspaceSparseActions(model: WorkspaceSourceEffec
workspaceCreateDraft,
workspaceCreateTargetConnectionId,
workspaceCreateTargetRepo,
workspaceCreateTargetSshKey,
workspaceSparseCheckoutAvailable,
workspaceSparseDraft,
workspaceSparseDraftName,
@@ -117,36 +119,50 @@ export function useMobileTasksWorkspaceSparseActions(model: WorkspaceSourceEffec
])
useEffect(() => {
if (!tasksSupported || !client || !workspaceCreateDraft || !workspaceCreateTargetConnectionId) {
if (
!tasksSupported ||
!client ||
!workspaceCreateDraft ||
!workspaceCreateTargetRepo ||
!workspaceCreateTargetConnectionId
) {
setWorkspaceSshState(null)
setWorkspaceSshConnecting(false)
return
}
let stale = false
const target = workspaceCreateTargetSshKey
// The target's own host answers, as a server holds its SSH targets.
void sshRepoStateRead
.request(client, { targetId: workspaceCreateTargetConnectionId })
.request(taskRepoClient(client, workspaceCreateTargetRepo), {
targetId: workspaceCreateTargetConnectionId
})
.then((reply) => {
if (stale) {
return
}
const state = sshRepoStateRead.interpret(reply) ?? null
setWorkspaceSshState(
state ?? {
setWorkspaceSshState({
target,
record: state ?? {
targetId: workspaceCreateTargetConnectionId,
status: 'disconnected',
error: null,
reconnectAttempt: 0
}
)
})
})
.catch((err) => {
if (!stale) {
setWorkspaceSshState({
targetId: workspaceCreateTargetConnectionId,
status: 'error',
error: err instanceof Error ? err.message : 'Failed to read SSH connection state.',
reconnectAttempt: 0
target,
record: {
targetId: workspaceCreateTargetConnectionId,
status: 'error',
error: err instanceof Error ? err.message : 'Failed to read SSH connection state.',
reconnectAttempt: 0
}
})
}
})
@@ -154,7 +170,7 @@ export function useMobileTasksWorkspaceSparseActions(model: WorkspaceSourceEffec
return () => {
stale = true
}
}, [client, tasksSupported, workspaceCreateDraft, workspaceCreateTargetConnectionId])
}, [client, tasksSupported, workspaceCreateDraft, workspaceCreateTargetSshKey])
return Object.assign(model, {
startNewWorkspaceSparsePreset,
startEditWorkspaceSparsePreset,
@@ -1,5 +1,7 @@
import type { WorkspaceSparseActionsModel } from './use-mobile-tasks-workspace-sparse-actions'
import { taskRepoClient } from './task-repo-host-routing'
import { taskRepoClient, taskRepoServer } from './task-repo-host-routing'
import { workspaceSshTargetKey } from './workspace-ssh-gate'
import type { WorkspaceSshRecord } from './workspace-ssh-gate'
import {
normalizeSetupHookTrust,
pickWorkspaceAgent,
@@ -38,62 +40,64 @@ export function useMobileTasksWorkspaceSshState(model: WorkspaceSparseActionsMod
workspaceCreateSshStatus,
workspaceCreateTargetConnectionId,
workspaceCreateTargetRepo,
workspaceCreateTargetSshKey,
workspaceDetectedAgentIds,
workspaceSshState
} = model
const connectWorkspaceSshRepo = useCallback(async (): Promise<void> => {
if (!client || !tasksSupported || !workspaceCreateTargetConnectionId) {
if (
!client ||
!tasksSupported ||
!workspaceCreateTargetRepo ||
!workspaceCreateTargetConnectionId
) {
return
}
setWorkspaceSshConnecting(true)
setWorkspaceSshState({
const target = workspaceCreateTargetSshKey
const record = (status: WorkspaceSshRecord['status'], error: string | null) => ({
targetId: workspaceCreateTargetConnectionId,
status: 'connecting',
error: null,
status,
error,
reconnectAttempt: 0
})
// A reply lands only while its target is still the one shown, so it never overwrites another's.
const settle = (settled: WorkspaceSshRecord) =>
setWorkspaceSshState((current) =>
current?.target === target ? { target, record: settled } : current
)
setWorkspaceSshConnecting(true)
setWorkspaceSshState({ target, record: record('connecting', null) })
try {
const reply = await sshRepoConnectRun.request(
client,
taskRepoClient(client, workspaceCreateTargetRepo),
{ targetId: workspaceCreateTargetConnectionId },
{ timeoutMs: 120_000 }
)
const state = sshRepoConnectRun.interpret(reply)
setWorkspaceSshState(
state ?? {
targetId: workspaceCreateTargetConnectionId,
status: 'connected',
error: null,
reconnectAttempt: 0
}
)
settle(sshRepoConnectRun.interpret(reply) ?? record('connected', null))
} catch (err) {
setWorkspaceSshState({
targetId: workspaceCreateTargetConnectionId,
status: 'error',
error: err instanceof Error ? err.message : 'Failed to connect to SSH repository.',
reconnectAttempt: 0
})
settle(
record('error', err instanceof Error ? err.message : 'Failed to connect to SSH repository.')
)
} finally {
setWorkspaceSshConnecting(false)
}
}, [client, tasksSupported, workspaceCreateTargetConnectionId])
}, [client, tasksSupported, workspaceCreateTargetRepo, workspaceCreateTargetSshKey])
const ensureWorkspaceSshReady = useCallback(
async (repo: RepoSummary): Promise<void> => {
if (!repo.connectionId || !client || !tasksSupported) {
return
}
if (
workspaceSshState?.targetId === repo.connectionId &&
workspaceSshState.status === 'connected'
) {
const target = workspaceSshTargetKey(taskRepoServer(repo), repo.connectionId)
if (workspaceSshState?.target === target && workspaceSshState.record.status === 'connected') {
return
}
const reply = await sshRepoStateRead.request(client, { targetId: repo.connectionId })
const reply = await sshRepoStateRead.request(taskRepoClient(client, repo), {
targetId: repo.connectionId
})
const state = sshRepoStateRead.interpret(reply) ?? null
if (state) {
setWorkspaceSshState(state)
setWorkspaceSshState({ target, record: state })
}
if (state?.status !== 'connected') {
throw new Error(`Connect ${repo.displayName} before creating a workspace.`)
+3
View File
@@ -26,6 +26,9 @@ export function workspaceSshTargetKey(
return JSON.stringify([server ?? null, connectionId])
}
/** An SSH record with the `workspaceSshTargetKey` it was read for. */
export type WorkspaceSshEntry = { target: string; record: WorkspaceSshRecord }
export type WorkspaceSshGate = {
status: SshConnectionStatus | null
requiresConnection: boolean
@@ -15,6 +15,13 @@ export function taskWorkspaceHookMountAdapters(
): Record<string, MountAdapter> {
// The drawer's SSH hook. `connectionId` picks the arm the detection effect takes: a repo on
// an SSH connection detects remote agents, one without it detects local agents.
const sshTargetKey = (connectionId: string | undefined): string =>
modules
.load<typeof import('../../../tasks/workspace-ssh-gate')>(
'mobile/src/tasks/workspace-ssh-gate.ts'
)
.workspaceSshTargetKey(undefined, connectionId ?? null)
function sshStateAdapter(connectionId: string | undefined): MountAdapter {
return (context) => {
const useSsh = modules.load<
@@ -32,6 +39,7 @@ export function taskWorkspaceHookMountAdapters(
workspaceCreateSshStatus: connectionId ? 'connected' : 'idle',
workspaceCreateTargetConnectionId: connectionId,
workspaceCreateTargetRepo: repo,
workspaceCreateTargetSshKey: sshTargetKey(connectionId),
workspaceDetectedAgentIds: null,
workspaceSshState: null,
workspaceSshConnecting: false
@@ -147,6 +155,7 @@ export function taskWorkspaceHookMountAdapters(
workspaceCreateDraft: { key: 'linear:1' },
workspaceCreateTargetConnectionId: 'ssh-1',
workspaceCreateTargetRepo: { id: REPO, displayName: 'Repo' },
workspaceCreateTargetSshKey: sshTargetKey('ssh-1'),
workspaceSparseCheckoutAvailable: true,
workspaceSparseDraft: { mode: 'new', name: 'docs', directoriesText: 'docs' },
workspaceSparseDraftName: 'docs',
@@ -6,6 +6,7 @@ import { hostClientContextExposure, loadHostClientContext } from '../host-client
import type { OperationExposure, operationModuleLoader } from '../operation-module-loader'
import type { MountAdapter } from '../recording-scenario'
import type { RpcClientContextValue } from '../../../transport/rpc-client-context-contract'
import type { HostStatusGates } from '../../../transport/host-status-gates'
const HOST = 'host-1'
@@ -26,6 +27,19 @@ export function tasksRouteScreenMountAdapters(
'mobile/src/tasks/use-mobile-tasks-route-and-item-state.tsx'
).useMobileTasksRouteAndItemState
const hostClientContext = loadHostClientContext(modules)
// The screen sits under the host route's protocol gate; this one advertises no capabilities.
const gatesContext = modules.load<
typeof import('../../../components/host-protocol-gates-context')
>('mobile/src/components/host-protocol-gates-context.ts').HostStatusGatesContext
const gates: HostStatusGates = {
hostCapabilities: [],
floatingWorkspaceEnabled: false,
desktopAppVersion: null,
compatVerdict: { kind: 'ok' },
hostProtocolWindow: { protocolVersion: undefined, minCompatibleMobileVersion: undefined },
statusPending: false,
statusReadable: true
}
const context = mountFixture<RpcClientContextValue>({
acquire: () => client,
release: () => {},
@@ -48,7 +62,12 @@ export function tasksRouteScreenMountAdapters(
return null
}
const screen = screenMount(
() => createElement(hostClientContext.Provider, { value: context }, createElement(Harness)),
() =>
createElement(
hostClientContext.Provider,
{ value: context },
createElement(gatesContext.Provider, { value: gates }, createElement(Harness))
),
effect
)
const model = (): ReturnType<typeof useRoute> => {
+4 -2
View File
@@ -1,15 +1,17 @@
import type { ExecutionHostId } from '../../../src/shared/execution-host'
import { nativeChatRepoListRead } from '../session/mobile-session-read-operations'
import type { Repo as SharedRepo } from '../../../src/shared/repo-types'
import type { RpcClient } from '../transport/rpc-client'
type ListedRepo = { connectionId?: string | null }
/** A caller's view of the host's `Repo` records: a few of its own fields, never new ones. */
export type ListedRepo = Pick<SharedRepo, 'id'> & Partial<SharedRepo>
/** One host's repos, or null when it refused or failed to answer. */
export async function listHostRepos<Repo extends ListedRepo>(
client: RpcClient
): Promise<Repo[] | null> {
const listed = nativeChatRepoListRead.interpret(await nativeChatRepoListRead.request(client))
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary.
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: repo.list rows are the host's Repo records, and Repo is a view of their fields.
return listed.accepted ? (listed.value as Repo[]) : null
}
+2 -2
View File
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'
import type { ExecutionHostId } from '../../../src/shared/execution-host'
import type { RpcClient } from '../transport/rpc-client'
import { listServerRepos } from './server-repos'
import { listServerRepos, type ListedRepo } from './server-repos'
type ServerRepo<Repo> = Repo & { executionHostId: ExecutionHostId }
@@ -9,7 +9,7 @@ type ServerRepo<Repo> = Repo & { executionHostId: ExecutionHostId }
* Each reachable server's repos by host, filled in as each server answers, so a slow server never
* holds back the desktop's own list or another server's. A new `generation` re-reads them.
*/
export function useServerRepos<Repo extends { connectionId?: string | null }>(
export function useServerRepos<Repo extends ListedRepo>(
serverClients: ReadonlyMap<ExecutionHostId, RpcClient>,
active: boolean,
generation = 0