yaver-feedback-react-native 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +102 -1
  2. package/dist/AuthOverlay.d.ts +1 -14
  3. package/dist/AuthOverlay.js +9 -62
  4. package/dist/Discovery.js +0 -6
  5. package/dist/DogfoodRuntime.d.ts +124 -0
  6. package/dist/DogfoodRuntime.js +273 -0
  7. package/dist/FeedbackModal.js +347 -61
  8. package/dist/LoginScreen.d.ts +1 -5
  9. package/dist/LoginScreen.js +2 -6
  10. package/dist/MachinePickerScreen.d.ts +1 -3
  11. package/dist/MachinePickerScreen.js +6 -18
  12. package/dist/P2PClient.d.ts +116 -3
  13. package/dist/P2PClient.js +273 -3
  14. package/dist/P2PDogfoodDriver.d.ts +12 -0
  15. package/dist/P2PDogfoodDriver.js +118 -0
  16. package/dist/PairDeviceModal.d.ts +2 -3
  17. package/dist/VibeChatScreen.d.ts +11 -1
  18. package/dist/VibeChatScreen.js +200 -41
  19. package/dist/YaverFeedback.d.ts +34 -0
  20. package/dist/YaverFeedback.js +124 -19
  21. package/dist/YaverModeBadge.d.ts +22 -0
  22. package/dist/YaverModeBadge.js +219 -0
  23. package/dist/__tests__/AuthDevices.test.js +1 -48
  24. package/dist/__tests__/DogfoodRuntime.test.d.ts +1 -0
  25. package/dist/__tests__/DogfoodRuntime.test.js +116 -0
  26. package/dist/__tests__/P2PDogfoodDriver.test.d.ts +1 -0
  27. package/dist/__tests__/P2PDogfoodDriver.test.js +64 -0
  28. package/dist/__tests__/ReportIdentity.test.d.ts +25 -1
  29. package/dist/__tests__/ReportIdentity.test.js +34 -22
  30. package/dist/__tests__/YaverFeedback.test.js +13 -3
  31. package/dist/__tests__/deviceDogfood.test.d.ts +1 -0
  32. package/dist/__tests__/deviceDogfood.test.js +86 -0
  33. package/dist/__tests__/dogfoodPolicy.test.d.ts +1 -0
  34. package/dist/__tests__/dogfoodPolicy.test.js +33 -0
  35. package/dist/_core/ansi.d.ts +117 -0
  36. package/dist/_core/ansi.js +468 -0
  37. package/dist/_core/ansi.test.d.ts +1 -0
  38. package/dist/_core/ansi.test.js +225 -0
  39. package/dist/_core/buildFeedbackPrompt.d.ts +4 -9
  40. package/dist/_core/buildFeedbackPrompt.js +18 -72
  41. package/dist/_core/constants.d.ts +19 -6
  42. package/dist/_core/constants.js +20 -7
  43. package/dist/_core/device.d.ts +9 -23
  44. package/dist/_core/device.js +13 -28
  45. package/dist/_core/endpoints.d.ts +0 -7
  46. package/dist/_core/endpoints.js +0 -7
  47. package/dist/_core/index.d.ts +4 -0
  48. package/dist/_core/index.js +4 -0
  49. package/dist/_core/remoteless.d.ts +44 -0
  50. package/dist/_core/remoteless.js +75 -0
  51. package/dist/_core/trace.d.ts +47 -0
  52. package/dist/_core/trace.js +38 -0
  53. package/dist/_core/trace.test.d.ts +1 -0
  54. package/dist/_core/trace.test.js +60 -0
  55. package/dist/auth.d.ts +3 -60
  56. package/dist/auth.js +6 -88
  57. package/dist/deviceDogfood.d.ts +53 -0
  58. package/dist/deviceDogfood.js +137 -0
  59. package/dist/dogfoodPolicy.d.ts +25 -0
  60. package/dist/dogfoodPolicy.js +24 -0
  61. package/dist/index.d.ts +13 -4
  62. package/dist/index.js +24 -8
  63. package/dist/reloadActions.js +2 -2
  64. package/dist/types.d.ts +50 -7
  65. package/package.json +12 -3
  66. package/src/AuthOverlay.tsx +20 -106
  67. package/src/Discovery.ts +0 -6
  68. package/src/DogfoodRuntime.ts +373 -0
  69. package/src/FeedbackModal.tsx +445 -67
  70. package/src/LoginScreen.tsx +2 -22
  71. package/src/MachinePickerScreen.tsx +6 -21
  72. package/src/P2PClient.ts +347 -4
  73. package/src/P2PDogfoodDriver.ts +132 -0
  74. package/src/PairDeviceModal.tsx +2 -3
  75. package/src/VibeChatScreen.tsx +232 -42
  76. package/src/YaverFeedback.ts +133 -22
  77. package/src/YaverModeBadge.tsx +234 -0
  78. package/src/__tests__/AuthDevices.test.ts +1 -52
  79. package/src/__tests__/DogfoodRuntime.test.ts +135 -0
  80. package/src/__tests__/P2PDogfoodDriver.test.ts +80 -0
  81. package/src/__tests__/ReportIdentity.test.ts +36 -27
  82. package/src/__tests__/YaverFeedback.test.ts +15 -3
  83. package/src/__tests__/deviceDogfood.test.ts +77 -0
  84. package/src/__tests__/dogfoodPolicy.test.ts +34 -0
  85. package/src/_core/ansi.test.ts +250 -0
  86. package/src/_core/ansi.ts +475 -0
  87. package/src/_core/buildFeedbackPrompt.ts +18 -95
  88. package/src/_core/constants.ts +20 -6
  89. package/src/_core/device.ts +13 -30
  90. package/src/_core/endpoints.ts +0 -7
  91. package/src/_core/index.ts +4 -0
  92. package/src/_core/remoteless.ts +110 -0
  93. package/src/_core/trace.test.ts +58 -0
  94. package/src/_core/trace.ts +75 -0
  95. package/src/auth.ts +7 -156
  96. package/src/deviceDogfood.ts +171 -0
  97. package/src/dogfoodPolicy.ts +40 -0
  98. package/src/index.ts +40 -11
  99. package/src/reloadActions.ts +2 -2
  100. package/src/types.ts +45 -7
  101. package/dist/GuestOnboardingScreen.d.ts +0 -8
  102. package/dist/GuestOnboardingScreen.js +0 -282
  103. package/src/GuestOnboardingScreen.tsx +0 -307
@@ -1,13 +1,9 @@
1
1
  import React from 'react';
2
2
  export interface YaverLoginScreenProps {
3
3
  /** Invoked once a session token is issued and the user is loaded. */
4
- onLoggedIn: (token: string, opts?: {
5
- inviteCode?: string;
6
- }) => void;
4
+ onLoggedIn: (token: string) => void;
7
5
  /** Optional cancel button shown in header. */
8
6
  onCancel?: () => void;
9
- /** Optional prefilled guest invite code from config / deep link. */
10
- initialInviteCode?: string;
11
7
  }
12
8
  /**
13
9
  * Full-screen in-SDK login. Mirrors the Yaver mobile app login UX: native
@@ -91,7 +91,7 @@ const iconStyles = react_native_1.StyleSheet.create({
91
91
  * Apple Sign-In on iOS, in-app browser OAuth for Google/GitHub/GitLab/
92
92
  * Microsoft (no codes, no leaving the app), and inline email/password.
93
93
  */
94
- const YaverLoginScreen = ({ onLoggedIn, onCancel, initialInviteCode, }) => {
94
+ const YaverLoginScreen = ({ onLoggedIn, onCancel, }) => {
95
95
  const [busyProvider, setBusyProvider] = (0, react_1.useState)(null);
96
96
  const [showEmailForm, setShowEmailForm] = (0, react_1.useState)(false);
97
97
  const [isSignUp, setIsSignUp] = (0, react_1.useState)(false);
@@ -99,7 +99,6 @@ const YaverLoginScreen = ({ onLoggedIn, onCancel, initialInviteCode, }) => {
99
99
  const [email, setEmail] = (0, react_1.useState)('');
100
100
  const [password, setPassword] = (0, react_1.useState)('');
101
101
  const [confirmPassword, setConfirmPassword] = (0, react_1.useState)('');
102
- const [inviteCode, setInviteCode] = (0, react_1.useState)((initialInviteCode ?? '').toUpperCase());
103
102
  const [emailBusy, setEmailBusy] = (0, react_1.useState)(false);
104
103
  const [emailError, setEmailError] = (0, react_1.useState)('');
105
104
  const finish = async (token) => {
@@ -107,8 +106,7 @@ const YaverLoginScreen = ({ onLoggedIn, onCancel, initialInviteCode, }) => {
107
106
  await (0, auth_1.saveToken)(token);
108
107
  if (user)
109
108
  await (0, auth_1.saveUser)(user);
110
- const cleanedInviteCode = inviteCode.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 6);
111
- onLoggedIn(token, cleanedInviteCode ? { inviteCode: cleanedInviteCode } : undefined);
109
+ onLoggedIn(token);
112
110
  };
113
111
  const handleApple = async () => {
114
112
  setBusyProvider('apple');
@@ -218,8 +216,6 @@ const YaverLoginScreen = ({ onLoggedIn, onCancel, initialInviteCode, }) => {
218
216
  <react_native_1.TextInput style={styles.input} placeholder="Email" placeholderTextColor="#666" value={email} onChangeText={setEmail} keyboardType="email-address" autoCapitalize="none" autoCorrect={false}/>
219
217
  <react_native_1.TextInput style={styles.input} placeholder="Password" placeholderTextColor="#666" value={password} onChangeText={setPassword} secureTextEntry autoCapitalize="none"/>
220
218
  {isSignUp && (<react_native_1.TextInput style={styles.input} placeholder="Confirm Password" placeholderTextColor="#666" value={confirmPassword} onChangeText={setConfirmPassword} secureTextEntry/>)}
221
- {isSignUp && (<react_native_1.TextInput style={styles.input} placeholder="Invite Code (optional)" placeholderTextColor="#666" value={inviteCode} onChangeText={(value) => setInviteCode(value.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 6))} autoCapitalize="characters" autoCorrect={false} maxLength={6}/>)}
222
-
223
219
  {emailError ? (<react_native_1.Text style={styles.errorText}>{emailError}</react_native_1.Text>) : null}
224
220
 
225
221
  <react_native_1.Pressable style={({ pressed }) => [
@@ -8,9 +8,7 @@ export interface YaverMachinePickerProps {
8
8
  onCancel?: () => void;
9
9
  }
10
10
  /**
11
- * List of remote dev machines the signed-in user can reach. Split into
12
- * - Owned machines (user is the host)
13
- * - Shared machines (host invited them as a guest)
11
+ * List of remote dev machines owned by the signed-in user.
14
12
  *
15
13
  * Tapping a device persists it to AsyncStorage and invokes `onPick`. The
16
14
  * SDK then uses that device's deviceId for agent discovery (LAN probe +
@@ -39,9 +39,7 @@ const react_native_1 = require("react-native");
39
39
  const auth_1 = require("./auth");
40
40
  const PairDeviceModal_1 = require("./PairDeviceModal");
41
41
  /**
42
- * List of remote dev machines the signed-in user can reach. Split into
43
- * - Owned machines (user is the host)
44
- * - Shared machines (host invited them as a guest)
42
+ * List of remote dev machines owned by the signed-in user.
45
43
  *
46
44
  * Tapping a device persists it to AsyncStorage and invokes `onPick`. The
47
45
  * SDK then uses that device's deviceId for agent discovery (LAN probe +
@@ -51,7 +49,7 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
51
49
  const [loading, setLoading] = (0, react_1.useState)(true);
52
50
  const [refreshing, setRefreshing] = (0, react_1.useState)(false);
53
51
  const [error, setError] = (0, react_1.useState)(null);
54
- const [list, setList] = (0, react_1.useState)({ owned: [], shared: [] });
52
+ const [list, setList] = (0, react_1.useState)({ owned: [] });
55
53
  const [pairingDevice, setPairingDevice] = (0, react_1.useState)(null);
56
54
  const [reachability, setReachability] = (0, react_1.useState)({});
57
55
  const load = (0, react_1.useCallback)(async (silent = false) => {
@@ -63,7 +61,7 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
63
61
  setList(result);
64
62
  setReachability({});
65
63
  void (async () => {
66
- const devices = [...result.owned, ...result.shared];
64
+ const devices = result.owned;
67
65
  const settled = await Promise.allSettled(devices.map(async (device) => ({
68
66
  deviceId: device.deviceId,
69
67
  result: await (0, auth_1.probeDeviceReachability)(device),
@@ -78,8 +76,8 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
78
76
  return next;
79
77
  });
80
78
  })();
81
- if (result.owned.length === 0 && result.shared.length === 0) {
82
- setError('No machines found yet. If you do not have your own computer, redeem a host invite code first. Otherwise run `yaver auth` + `yaver serve` on your machine.');
79
+ if (result.owned.length === 0) {
80
+ setError('No machines found yet. Run `yaver auth` + `yaver serve` on your machine.');
83
81
  }
84
82
  }
85
83
  catch (err) {
@@ -165,14 +163,8 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
165
163
  statusLine = 'Runner down — restart the coding agent on the Mac';
166
164
  }
167
165
  else {
168
- // Happy-path subtitle: platform + optional host/share hint.
166
+ // Happy-path subtitle.
169
167
  statusLine = device.platform;
170
- if (device.isGuest && device.hostEmail) {
171
- statusLine = `${statusLine} • ${device.hostEmail}`;
172
- }
173
- else if (device.accessScope === 'shared-scoped') {
174
- statusLine = `${statusLine} • paylaşılan`;
175
- }
176
168
  }
177
169
  return (<react_native_1.TouchableOpacity key={device.deviceId} style={[styles.deviceRow, selected && styles.deviceSelected]} onPress={() => handlePick(device)}>
178
170
  <react_native_1.View style={[styles.health, { backgroundColor: healthColor }]}/>
@@ -200,10 +192,6 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
200
192
  <react_native_1.Text style={styles.sectionTitle}>Kendi makinelerim</react_native_1.Text>
201
193
  {list.owned.map(renderDevice)}
202
194
  </react_native_1.View>)}
203
- {list.shared.length > 0 && (<react_native_1.View style={styles.section}>
204
- <react_native_1.Text style={styles.sectionTitle}>Paylaşılan (guest)</react_native_1.Text>
205
- {list.shared.map(renderDevice)}
206
- </react_native_1.View>)}
207
195
  {error && <react_native_1.Text style={styles.error}>{error}</react_native_1.Text>}
208
196
  </>)}
209
197
  </react_native_1.ScrollView>
@@ -13,6 +13,54 @@ export interface ReloadAck {
13
13
  nativeChangesDetected?: boolean;
14
14
  changeClass?: string;
15
15
  }
16
+ export interface DogfoodDevServerStatus {
17
+ running?: boolean;
18
+ serving?: boolean;
19
+ starting?: boolean;
20
+ building?: boolean;
21
+ framework?: string;
22
+ workDir?: string;
23
+ bundleUrl?: string;
24
+ previewUrl?: string;
25
+ error?: string;
26
+ capabilityGap?: unknown;
27
+ }
28
+ export interface DogfoodDevEvent {
29
+ type: string;
30
+ framework?: string;
31
+ logLine?: string;
32
+ message?: string;
33
+ phase?: string;
34
+ pct?: number;
35
+ currentFile?: string;
36
+ snapshot?: {
37
+ recentLogs?: string[];
38
+ [key: string]: unknown;
39
+ };
40
+ [key: string]: unknown;
41
+ }
42
+ export interface DogfoodRemoteRuntimeTarget {
43
+ id: string;
44
+ label: string;
45
+ enabled: boolean;
46
+ reason?: string;
47
+ platform?: string;
48
+ surface?: string;
49
+ displaySurface?: string;
50
+ }
51
+ export interface DogfoodRemoteRuntimeCapabilities {
52
+ remoteRuntimeEligible?: boolean;
53
+ targets: DogfoodRemoteRuntimeTarget[];
54
+ }
55
+ export interface DogfoodRemoteRuntimeSession {
56
+ id: string;
57
+ status: string;
58
+ targetId?: string;
59
+ targetLabel?: string;
60
+ transportMode?: string;
61
+ note?: string;
62
+ [key: string]: unknown;
63
+ }
16
64
  /**
17
65
  * Try to resolve `{projectName, bundleId}` for the running app so the
18
66
  * agent can map the reload request to a MobileProject in its scan
@@ -120,6 +168,17 @@ export declare class P2PClient {
120
168
  * RunnerAuthModal.tsx and the Swift YaverRunnerAuthFlowPane. */
121
169
  submitRunnerBrowserAuthCode(sessionId: string, code: string): Promise<RunnerBrowserAuthSession>;
122
170
  getRunnerAuthStatus(): Promise<RunnerAuthStatusRow[]>;
171
+ /** Canonical runner + model catalogue used by Vibing routing controls. */
172
+ getAvailableRunners(): Promise<RunnerAuthStatusRow[]>;
173
+ /** Discovered runnable projects on the selected owner machine. Paths stay
174
+ * on that machine/transport; they are never registered in Yaver's backend. */
175
+ listDogfoodProjects(): Promise<Array<{
176
+ name: string;
177
+ path: string;
178
+ framework?: string;
179
+ frameworks?: string[];
180
+ surfaces?: string[];
181
+ }>>;
123
182
  getOpenCodeConfig(): Promise<OpenCodeConfigSummary | null>;
124
183
  saveOpenCodeConfig(patch: {
125
184
  defaultAgent?: string;
@@ -212,6 +271,35 @@ export declare class P2PClient {
212
271
  * Those are two different problems with two different fixes.
213
272
  */
214
273
  getDevServerStatus(): Promise<DevServerSnapshot | null>;
274
+ /**
275
+ * Start the ordinary Projects runtime for an embedded Dogfood host.
276
+ * Requires a full signed-in-user token because /dev/start can spawn tools;
277
+ * a narrow feedback SDK token intentionally cannot use it.
278
+ */
279
+ startDogfoodDevServer(input: {
280
+ framework: string;
281
+ workDir: string;
282
+ lane: 'browser' | 'hermes';
283
+ }): Promise<DogfoodDevServerStatus>;
284
+ /** Full status for Dogfood startup; unlike the compact feedback snapshot,
285
+ * this retains render URLs and structured startup failures. */
286
+ getDogfoodDevServerStatus(): Promise<DogfoodDevServerStatus | null>;
287
+ getDogfoodRemoteRuntimeCapabilities(workDir: string, framework: string): Promise<DogfoodRemoteRuntimeCapabilities>;
288
+ startDogfoodRemoteRuntime(workDir: string, framework: string, targetId: string): Promise<DogfoodRemoteRuntimeSession>;
289
+ stopDogfoodRemoteRuntime(sessionId: string): Promise<void>;
290
+ /** Stop the runtime this SDK host started. Full user auth, same as start. */
291
+ stopDogfoodDevServer(): Promise<void>;
292
+ /** Resolve an agent-reported /dev/ or /dev-web/ route without putting auth in the URL. */
293
+ resolveDogfoodUrl(path: string): string;
294
+ /**
295
+ * React-Native-safe /dev/events stream. XHR is intentional: Hermes fetch
296
+ * exposes no streaming response body. Reconnects are bounded and every
297
+ * reconnect/loss is surfaced to the host console.
298
+ */
299
+ subscribeDogfoodDevEvents(onEvent: (event: DogfoodDevEvent) => void, onHealth?: (health: {
300
+ kind: 'reattaching' | 'lost';
301
+ message: string;
302
+ } | null) => void): () => void;
215
303
  /**
216
304
  * Trigger a reload with an EXPLICIT fast/full mode — no bundle fallback.
217
305
  *
@@ -222,8 +310,8 @@ export declare class P2PClient {
222
310
  * told. So this method reports the failure instead, with a named cause.
223
311
  *
224
312
  * Auth: the SAME bearer used for the feedback POST. `/dev/reload` is
225
- * registered under `authSDKOrGuest` in desktop/agent/httpserver.go and is
226
- * already in the `guest-reload` SDK-token scope list — nothing widens.
313
+ * registered under `authSDK` in desktop/agent/httpserver.go and is already
314
+ * in the `reload` SDK-token scope list — nothing widens.
227
315
  */
228
316
  reloadWithMode(mode: ReloadWireMode, snapshot?: DevServerSnapshot | null): Promise<ReloadAck>;
229
317
  reloadApp(mode?: 'dev' | 'bundle', opts?: {
@@ -237,7 +325,7 @@ export declare class P2PClient {
237
325
  * the project context plus the user's prompt. Returns the task id the
238
326
  * caller can poll via `/tasks/{id}` if needed.
239
327
  *
240
- * Requires an owner/CLI/paired token — the `/vibing*` routes do not
328
+ * Requires an owner/CLI token — the `/vibing*` routes do not
241
329
  * currently accept SDK-minted tokens. Power users typically drive
242
330
  * vibing from Claude Code / the Yaver mobile app; this method is a
243
331
  * convenience for the SDK's one-tap bug-report-to-vibing path.
@@ -246,6 +334,8 @@ export declare class P2PClient {
246
334
  projectName?: string;
247
335
  bundleId?: string;
248
336
  projectPath?: string;
337
+ runner?: string;
338
+ model?: string;
249
339
  }): Promise<{
250
340
  taskId: string;
251
341
  }>;
@@ -365,6 +455,29 @@ export declare class P2PClient {
365
455
  taskId: string;
366
456
  raw?: unknown;
367
457
  }>;
458
+ /** Existing task history, filtered by the agent to Vibing/feedback topics. */
459
+ listVibeThreads(input?: {
460
+ projectName?: string;
461
+ projectPath?: string;
462
+ }): Promise<Array<{
463
+ id: string;
464
+ title: string;
465
+ status: string;
466
+ createdAt?: string;
467
+ projectName?: string;
468
+ turnCount?: number;
469
+ }>>;
470
+ getVibeThread(taskId: string): Promise<{
471
+ id: string;
472
+ title: string;
473
+ status: string;
474
+ turns?: Array<{
475
+ role: 'user' | 'assistant';
476
+ content: string;
477
+ timestamp?: string;
478
+ }>;
479
+ }>;
480
+ deleteVibeThread(taskId: string): Promise<void>;
368
481
  /**
369
482
  * Subscribe to a task's live stdout/stderr stream. Returns an abort
370
483
  * function — call it to detach. The agent emits NDJSON lines on
package/dist/P2PClient.js CHANGED
@@ -45,6 +45,17 @@ function unrefTimer(timer) {
45
45
  maybeNodeTimer.unref();
46
46
  }
47
47
  }
48
+ async function dogfoodFetch(url, init, timeoutMs) {
49
+ const ctrl = new AbortController();
50
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
51
+ unrefTimer(timer);
52
+ try {
53
+ return await fetch(url, { ...init, signal: ctrl.signal });
54
+ }
55
+ finally {
56
+ clearTimeout(timer);
57
+ }
58
+ }
48
59
  /**
49
60
  * Try to resolve `{projectName, bundleId}` for the running app so the
50
61
  * agent can map the reload request to a MobileProject in its scan
@@ -378,6 +389,29 @@ class P2PClient {
378
389
  const data = await resp.json().catch(() => ({}));
379
390
  return Array.isArray(data.runners) ? data.runners : [];
380
391
  }
392
+ /** Canonical runner + model catalogue used by Vibing routing controls. */
393
+ async getAvailableRunners() {
394
+ const resp = await fetch(`${this.baseUrl}/agent/runners`, {
395
+ headers: this.authHeaders(),
396
+ });
397
+ if (!resp.ok) {
398
+ const text = await resp.text().catch(() => '');
399
+ throw new Error(`getAvailableRunners HTTP ${resp.status}: ${text}`);
400
+ }
401
+ const data = await resp.json().catch(() => ({}));
402
+ return Array.isArray(data.runners) ? data.runners : [];
403
+ }
404
+ /** Discovered runnable projects on the selected owner machine. Paths stay
405
+ * on that machine/transport; they are never registered in Yaver's backend. */
406
+ async listDogfoodProjects() {
407
+ const resp = await fetch(`${this.baseUrl}/projects`, { headers: this.authHeaders() });
408
+ if (!resp.ok) {
409
+ const body = await resp.text().catch(() => '');
410
+ throw new Error(`listDogfoodProjects HTTP ${resp.status}: ${body}`);
411
+ }
412
+ const data = await resp.json().catch(() => ({}));
413
+ return Array.isArray(data.projects) ? data.projects : [];
414
+ }
381
415
  async getOpenCodeConfig() {
382
416
  const resp = await fetch(`${this.baseUrl}/runner/opencode/config`, {
383
417
  headers: this.authHeaders(),
@@ -662,6 +696,200 @@ class P2PClient {
662
696
  return null;
663
697
  }
664
698
  }
699
+ /**
700
+ * Start the ordinary Projects runtime for an embedded Dogfood host.
701
+ * Requires a full signed-in-user token because /dev/start can spawn tools;
702
+ * a narrow feedback SDK token intentionally cannot use it.
703
+ */
704
+ async startDogfoodDevServer(input) {
705
+ if (input.lane === 'hermes') {
706
+ const ack = await this.reloadApp('bundle', { projectPath: input.workDir });
707
+ return { running: ack.ok, framework: input.framework, workDir: input.workDir };
708
+ }
709
+ const ctrl = new AbortController();
710
+ const timer = setTimeout(() => ctrl.abort(), 45000);
711
+ unrefTimer(timer);
712
+ try {
713
+ const response = await fetch(`${this.baseUrl}/dev/start`, {
714
+ method: 'POST',
715
+ headers: this.authHeaders({ 'Content-Type': 'application/json' }),
716
+ body: JSON.stringify({
717
+ framework: input.framework,
718
+ workDir: input.workDir,
719
+ platform: 'web',
720
+ caller: 'sdk',
721
+ }),
722
+ signal: ctrl.signal,
723
+ });
724
+ const data = (await response.json().catch(() => ({})));
725
+ if (!response.ok) {
726
+ const error = new Error(data.error || `Dogfood preview start failed with HTTP ${response.status}`);
727
+ error.code = data.code || `DOGFOOD_DEV_START_HTTP_${response.status}`;
728
+ error.remedy = data.remedy || 'Fix the named dev-server failure, then retry Dogfood.';
729
+ error.retryable = data.retryable !== false;
730
+ error.capabilityGap = data.capabilityGap;
731
+ throw error;
732
+ }
733
+ return data;
734
+ }
735
+ finally {
736
+ clearTimeout(timer);
737
+ }
738
+ }
739
+ /** Full status for Dogfood startup; unlike the compact feedback snapshot,
740
+ * this retains render URLs and structured startup failures. */
741
+ async getDogfoodDevServerStatus() {
742
+ const ctrl = new AbortController();
743
+ const timer = setTimeout(() => ctrl.abort(), 10000);
744
+ unrefTimer(timer);
745
+ try {
746
+ const response = await fetch(`${this.baseUrl}${endpoints_1.AGENT_ENDPOINTS.devStatus}`, {
747
+ headers: this.authHeaders(),
748
+ signal: ctrl.signal,
749
+ });
750
+ if (!response.ok)
751
+ return null;
752
+ return await response.json().catch(() => null);
753
+ }
754
+ catch {
755
+ return null;
756
+ }
757
+ finally {
758
+ clearTimeout(timer);
759
+ }
760
+ }
761
+ async getDogfoodRemoteRuntimeCapabilities(workDir, framework) {
762
+ const query = `?workDir=${encodeURIComponent(workDir)}&framework=${encodeURIComponent(framework)}`;
763
+ const response = await dogfoodFetch(`${this.baseUrl}/remote-runtime/capabilities${query}`, {
764
+ headers: this.authHeaders(),
765
+ }, 20000);
766
+ const data = await response.json().catch(() => ({}));
767
+ if (!response.ok)
768
+ throw new Error(data?.error || `Remote-runtime capabilities failed with HTTP ${response.status}`);
769
+ return { ...data, targets: Array.isArray(data?.targets) ? data.targets : [] };
770
+ }
771
+ async startDogfoodRemoteRuntime(workDir, framework, targetId) {
772
+ const response = await dogfoodFetch(`${this.baseUrl}/remote-runtime/sessions`, {
773
+ method: 'POST',
774
+ headers: this.authHeaders({ 'Content-Type': 'application/json' }),
775
+ body: JSON.stringify({ workDir, framework, targetId, surface: 'sdk' }),
776
+ }, 45000);
777
+ const data = await response.json().catch(() => ({}));
778
+ if (!response.ok)
779
+ throw new Error(data?.error || `Remote-runtime start failed with HTTP ${response.status}`);
780
+ return data;
781
+ }
782
+ async stopDogfoodRemoteRuntime(sessionId) {
783
+ const response = await dogfoodFetch(`${this.baseUrl}/remote-runtime/sessions/${encodeURIComponent(sessionId)}`, {
784
+ method: 'DELETE', headers: this.authHeaders(),
785
+ }, 15000);
786
+ if (!response.ok)
787
+ throw new Error(`Remote-runtime stop failed with HTTP ${response.status}`);
788
+ }
789
+ /** Stop the runtime this SDK host started. Full user auth, same as start. */
790
+ async stopDogfoodDevServer() {
791
+ const ctrl = new AbortController();
792
+ const timer = setTimeout(() => ctrl.abort(), 15000);
793
+ unrefTimer(timer);
794
+ try {
795
+ const response = await fetch(`${this.baseUrl}/dev/stop`, {
796
+ method: 'POST', headers: this.authHeaders(), signal: ctrl.signal,
797
+ });
798
+ if (!response.ok)
799
+ throw new Error(`Dogfood preview stop failed with HTTP ${response.status}`);
800
+ }
801
+ finally {
802
+ clearTimeout(timer);
803
+ }
804
+ }
805
+ /** Resolve an agent-reported /dev/ or /dev-web/ route without putting auth in the URL. */
806
+ resolveDogfoodUrl(path) {
807
+ return new URL(path, `${this.baseUrl.replace(/\/+$/, '')}/`).toString();
808
+ }
809
+ /**
810
+ * React-Native-safe /dev/events stream. XHR is intentional: Hermes fetch
811
+ * exposes no streaming response body. Reconnects are bounded and every
812
+ * reconnect/loss is surfaced to the host console.
813
+ */
814
+ subscribeDogfoodDevEvents(onEvent, onHealth) {
815
+ let disposed = false;
816
+ let xhr = null;
817
+ let retryTimer = null;
818
+ let attempt = 0;
819
+ const open = () => {
820
+ if (disposed)
821
+ return;
822
+ const request = new XMLHttpRequest();
823
+ xhr = request;
824
+ let parsed = 0;
825
+ let carry = '';
826
+ const consume = () => {
827
+ const text = request.responseText || '';
828
+ if (text.length <= parsed)
829
+ return;
830
+ carry += text.slice(parsed);
831
+ parsed = text.length;
832
+ const normalized = carry.replace(/\r\n/g, '\n');
833
+ const frames = normalized.split('\n\n');
834
+ carry = frames.pop() || '';
835
+ for (const frame of frames) {
836
+ const data = frame.split('\n')
837
+ .filter((line) => line.startsWith('data:'))
838
+ .map((line) => line.slice(5).replace(/^ /, ''))
839
+ .join('\n');
840
+ if (!data)
841
+ continue;
842
+ try {
843
+ attempt = 0;
844
+ onHealth?.(null);
845
+ onEvent(JSON.parse(data));
846
+ }
847
+ catch { /* comments and non-JSON keepalives are valid SSE */ }
848
+ }
849
+ };
850
+ const reconnect = (reason) => {
851
+ if (disposed)
852
+ return;
853
+ if (attempt >= 5) {
854
+ onHealth?.({ kind: 'lost', message: `Dev-server logs stopped: ${reason}` });
855
+ return;
856
+ }
857
+ const wait = Math.min(8000, 500 * (2 ** attempt));
858
+ attempt += 1;
859
+ onHealth?.({ kind: 'reattaching', message: `Dev-server logs interrupted; reconnecting (${attempt}/5): ${reason}` });
860
+ retryTimer = setTimeout(open, wait);
861
+ unrefTimer(retryTimer);
862
+ };
863
+ request.open('GET', `${this.baseUrl}/dev/events`, true);
864
+ for (const [key, value] of Object.entries(this.authHeaders({ Accept: 'text/event-stream' }))) {
865
+ try {
866
+ request.setRequestHeader(key, value);
867
+ }
868
+ catch { /* restricted RN header */ }
869
+ }
870
+ request.onprogress = consume;
871
+ request.onload = () => { consume(); reconnect(`HTTP ${request.status || 0}`); };
872
+ request.onerror = () => reconnect('connection failed');
873
+ request.onabort = () => reconnect('connection aborted');
874
+ request.ontimeout = () => reconnect('connection timed out');
875
+ try {
876
+ request.send();
877
+ }
878
+ catch (error) {
879
+ reconnect(error instanceof Error ? error.message : 'connection could not start');
880
+ }
881
+ };
882
+ open();
883
+ return () => {
884
+ disposed = true;
885
+ if (retryTimer)
886
+ clearTimeout(retryTimer);
887
+ try {
888
+ xhr?.abort();
889
+ }
890
+ catch { /* idempotent */ }
891
+ };
892
+ }
665
893
  /**
666
894
  * Trigger a reload with an EXPLICIT fast/full mode — no bundle fallback.
667
895
  *
@@ -672,8 +900,8 @@ class P2PClient {
672
900
  * told. So this method reports the failure instead, with a named cause.
673
901
  *
674
902
  * Auth: the SAME bearer used for the feedback POST. `/dev/reload` is
675
- * registered under `authSDKOrGuest` in desktop/agent/httpserver.go and is
676
- * already in the `guest-reload` SDK-token scope list — nothing widens.
903
+ * registered under `authSDK` in desktop/agent/httpserver.go and is already
904
+ * in the `reload` SDK-token scope list — nothing widens.
677
905
  */
678
906
  async reloadWithMode(mode, snapshot) {
679
907
  if (mode === 'bundle')
@@ -785,7 +1013,7 @@ class P2PClient {
785
1013
  * the project context plus the user's prompt. Returns the task id the
786
1014
  * caller can poll via `/tasks/{id}` if needed.
787
1015
  *
788
- * Requires an owner/CLI/paired token — the `/vibing*` routes do not
1016
+ * Requires an owner/CLI token — the `/vibing*` routes do not
789
1017
  * currently accept SDK-minted tokens. Power users typically drive
790
1018
  * vibing from Claude Code / the Yaver mobile app; this method is a
791
1019
  * convenience for the SDK's one-tap bug-report-to-vibing path.
@@ -807,6 +1035,8 @@ class P2PClient {
807
1035
  projectPath: identity.projectPath ?? opts?.projectPath ?? '',
808
1036
  projectName: identity.projectName,
809
1037
  bundleId: identity.bundleId,
1038
+ runner: opts?.runner,
1039
+ model: opts?.model,
810
1040
  }),
811
1041
  });
812
1042
  if (!response.ok) {
@@ -1047,6 +1277,46 @@ class P2PClient {
1047
1277
  }
1048
1278
  return { taskId: json.taskId, raw: json };
1049
1279
  }
1280
+ /** Existing task history, filtered by the agent to Vibing/feedback topics. */
1281
+ async listVibeThreads(input) {
1282
+ const params = new URLSearchParams();
1283
+ if (input?.projectName)
1284
+ params.set('projectName', input.projectName);
1285
+ if (input?.projectPath)
1286
+ params.set('projectPath', input.projectPath);
1287
+ const query = params.toString();
1288
+ const resp = await fetch(`${this.baseUrl}/vibing/tasks${query ? `?${query}` : ''}`, {
1289
+ headers: this.authHeaders(),
1290
+ });
1291
+ if (!resp.ok)
1292
+ throw new Error(`listVibeThreads HTTP ${resp.status}`);
1293
+ const json = (await resp.json().catch(() => ({})));
1294
+ return (json.tasks || []).map((task) => ({
1295
+ id: String(task.id),
1296
+ title: String(task.title || 'New topic'),
1297
+ status: String(task.status || 'completed'),
1298
+ createdAt: task.createdAt,
1299
+ projectName: task.projectName,
1300
+ turnCount: task.turnCount,
1301
+ }));
1302
+ }
1303
+ async getVibeThread(taskId) {
1304
+ const resp = await fetch(`${this.baseUrl}/vibing/task/${encodeURIComponent(taskId)}`, {
1305
+ headers: this.authHeaders(),
1306
+ });
1307
+ if (!resp.ok)
1308
+ throw new Error(`getVibeThread HTTP ${resp.status}`);
1309
+ const json = (await resp.json().catch(() => ({})));
1310
+ return json.task || json;
1311
+ }
1312
+ async deleteVibeThread(taskId) {
1313
+ const resp = await fetch(`${this.baseUrl}/vibing/task/${encodeURIComponent(taskId)}`, {
1314
+ method: 'DELETE',
1315
+ headers: this.authHeaders(),
1316
+ });
1317
+ if (!resp.ok)
1318
+ throw new Error(`deleteVibeThread HTTP ${resp.status}`);
1319
+ }
1050
1320
  /**
1051
1321
  * Subscribe to a task's live stdout/stderr stream. Returns an abort
1052
1322
  * function — call it to detach. The agent emits NDJSON lines on
@@ -0,0 +1,12 @@
1
+ import type { P2PClient } from './P2PClient';
2
+ import { type DogfoodDriver } from './DogfoodRuntime';
3
+ export interface P2PDogfoodDriverOptions {
4
+ startupTimeoutMs?: number;
5
+ pollIntervalMs?: number;
6
+ }
7
+ /**
8
+ * Ready-to-use driver for app owners who authenticate with Yaver's normal
9
+ * account flow. Their UI owns the trigger and preview surface; this adapter
10
+ * owns the existing Projects endpoints and raw build log stream.
11
+ */
12
+ export declare function createP2PDogfoodDriver(client: P2PClient, options?: P2PDogfoodDriverOptions): DogfoodDriver;