Skip to content

Commit 98b9165

Browse files
committed
fix: surface session state directory
1 parent a91318c commit 98b9165

19 files changed

Lines changed: 105 additions & 34 deletions

skills/dogfood/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ Read current CLI guidance:
2222
agent-device help dogfood
2323
```
2424

25-
Loop: open named session -> snapshot -i + screenshot -> explore flows -> capture evidence per issue -> close.
25+
Loop: open app -> snapshot -i + screenshot -> explore flows -> capture evidence per issue -> close.
2626

2727
Target app is required; infer platform or ask. Findings must come from runtime behavior, not source reads. Let `help dogfood` provide exact report shape, evidence commands, and current workflow guidance.

src/cli.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ export async function runCli(argv: string[], deps: CliDeps = DEFAULT_CLI_DEPS):
215215
currentFlags: CliFlags,
216216
runtime: SessionRuntimeHints | undefined,
217217
): AgentDeviceClientConfig => ({
218-
session: currentFlags.session ?? sessionName,
218+
session: currentFlags.session,
219219
requestId,
220220
stateDir: currentFlags.stateDir,
221221
daemonBaseUrl: currentFlags.daemonBaseUrl,
@@ -231,7 +231,6 @@ export async function runCli(argv: string[], deps: CliDeps = DEFAULT_CLI_DEPS):
231231
lockPolicy: binding.lockPolicy,
232232
lockPlatform: binding.defaultPlatform,
233233
cwd: process.cwd(),
234-
sessionExplicit: currentFlags.session !== undefined,
235234
debug: debugOutputEnabled,
236235
});
237236
let parsedBatchSteps: BatchStep[] | undefined;

src/client-normalizers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ export function buildMeta(options: InternalRequestOptions): DaemonRequest['meta'
333333
return stripUndefined({
334334
requestId: options.requestId,
335335
cwd: options.cwd,
336-
sessionExplicit: options.sessionExplicit ?? options.session !== undefined,
336+
sessionExplicit: options.session !== undefined,
337337
debug: options.debug,
338338
lockPolicy: options.lockPolicy,
339339
lockPlatform: options.lockPlatform,

src/client-shared.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export function serializeOpenResult(result: AppOpenResult): Record<string, unkno
140140
return withSuccessText(
141141
{
142142
session: result.session,
143+
...(result.sessionStateDir ? { sessionStateDir: result.sessionStateDir } : {}),
143144
...(result.appName ? { appName: result.appName } : {}),
144145
...(result.appBundleId ? { appBundleId: result.appBundleId } : {}),
145146
...(result.startup ? { startup: result.startup } : {}),

src/client-types.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ export type AgentDeviceClientConfig = {
5757
leaseBackend?: LeaseBackend;
5858
runtime?: SessionRuntimeHints;
5959
cwd?: string;
60-
sessionExplicit?: boolean;
6160
debug?: boolean;
6261
};
6362

@@ -77,7 +76,6 @@ export type AgentDeviceRequestOverrides = Pick<
7776
| 'leaseId'
7877
| 'leaseBackend'
7978
| 'cwd'
80-
| 'sessionExplicit'
8179
| 'debug'
8280
>;
8381

@@ -186,6 +184,7 @@ export type AppOpenOptions = AgentDeviceRequestOverrides &
186184

187185
export type AppOpenResult = {
188186
session: string;
187+
sessionStateDir?: string;
189188
appName?: string;
190189
appBundleId?: string;
191190
appId?: string;

src/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export function createAgentDeviceClient(
151151
const appId = appBundleId;
152152
return {
153153
session,
154+
sessionStateDir: readOptionalString(data, 'sessionStateDir'),
154155
appName: readOptionalString(data, 'appName'),
155156
appBundleId,
156157
appId,

src/commands/__tests__/client-output.test.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
11
import { describe, expect, test } from 'vitest';
2-
import { recordCliOutput } from '../client-output.ts';
2+
import { openCliOutput, recordCliOutput } from '../client-output.ts';
3+
4+
describe('openCliOutput', () => {
5+
test('prints session state directory on a second line', () => {
6+
const output = openCliOutput({
7+
session: 'default',
8+
sessionStateDir: '/tmp/agent-device/sessions/cwd_123_default',
9+
identifiers: { session: 'default' },
10+
});
11+
12+
expect(output.text).toBe(
13+
['Opened: default', 'Session state: /tmp/agent-device/sessions/cwd_123_default'].join('\n'),
14+
);
15+
expect(output.data).toMatchObject({
16+
session: 'default',
17+
sessionStateDir: '/tmp/agent-device/sessions/cwd_123_default',
18+
});
19+
});
20+
});
321

422
describe('recordCliOutput', () => {
23+
test('prints session state directory for record-created sessions', () => {
24+
const output = recordCliOutput({
25+
recording: 'started',
26+
outPath: '/tmp/recording.mp4',
27+
sessionStateDir: '/tmp/agent-device/sessions/cwd_123_default',
28+
});
29+
30+
expect(output.text).toBe(
31+
['/tmp/recording.mp4', 'Session state: /tmp/agent-device/sessions/cwd_123_default'].join(
32+
'\n',
33+
),
34+
);
35+
});
36+
537
test('prints chunked Android recording paths clearly for human stdout', () => {
638
const output = recordCliOutput({
739
recording: 'stopped',

src/commands/client-output.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ export function sessionCliOutput(result: { sessions: AgentDeviceSession[] }): Cl
5656
}
5757

5858
export function openCliOutput(result: AppOpenResult): CliOutput {
59-
return messageOutput(serializeOpenResult(result));
59+
const data = serializeOpenResult(result);
60+
const lines = [readCommandMessage(data)].filter((line): line is string => Boolean(line));
61+
if (typeof data.sessionStateDir === 'string') {
62+
lines.push(`Session state: ${data.sessionStateDir}`);
63+
}
64+
return { data, text: lines.join('\n') || null };
6065
}
6166

6267
export function closeCliOutput(result: AppCloseResult | SessionCloseResult): CliOutput {
@@ -207,6 +212,8 @@ function defaultCommandCliOutput(result: CommandRequestResult): CliOutput {
207212
function formatRecordSingleOutput(data: Record<string, unknown>, outPath: string): string {
208213
const lines: string[] = [];
209214
if (outPath) lines.push(outPath);
215+
if (typeof data.sessionStateDir === 'string')
216+
lines.push(`Session state: ${data.sessionStateDir}`);
210217
if (typeof data.warning === 'string') lines.push(`Warning: ${data.warning}`);
211218
if (typeof data.overlayWarning === 'string')
212219
lines.push(`Overlay warning: ${data.overlayWarning}`);

src/daemon/__tests__/request-router-open.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { test, expect, vi, beforeEach } from 'vitest';
2+
import fs from 'node:fs';
23
import os from 'node:os';
34
import path from 'node:path';
45
import { getResolveTargetDeviceMock } from './request-router-dispatch-mocks.ts';
@@ -57,6 +58,23 @@ beforeEach(() => {
5758
mockEnsureDeviceReady.mockResolvedValue(undefined);
5859
});
5960

61+
test('open returns and creates the session state directory', async () => {
62+
const sessionStore = makeSessionStore('agent-device-router-open-');
63+
const device = makeIosDevice('SIM-STATE');
64+
mockResolveTargetDevice.mockResolvedValue(device);
65+
66+
const handler = createOpenHandler(sessionStore);
67+
68+
const response = await handler(openRequest('session-a', { platform: 'ios' }, 'req-open-state'));
69+
70+
expect(response.ok).toBe(true);
71+
if (response.ok) {
72+
expect(response.data?.session).toBe('session-a');
73+
expect(response.data?.sessionStateDir).toEqual(expect.stringContaining('session-a'));
74+
expect(fs.existsSync(String(response.data?.sessionStateDir))).toBe(true);
75+
}
76+
});
77+
6078
test('router serializes same-device open requests before first session creation finishes', async () => {
6179
const sessionStore = makeSessionStore('agent-device-router-open-');
6280
const sameDevice = makeIosDevice('SIM-001');

src/daemon/handlers/record-trace-recording.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ async function startRecording(params: {
286286

287287
activeSession.recording = recording;
288288
sessionStore.set(sessionName, activeSession);
289+
const sessionStateDir = sessionStore.ensureSessionDir(sessionName);
289290
sessionStore.recordAction(activeSession, {
290291
command: req.command,
291292
positionals: req.positionals ?? [],
@@ -298,6 +299,7 @@ async function startRecording(params: {
298299
data: {
299300
recording: 'started',
300301
outPath: recording.clientOutPath ?? outPath,
302+
sessionStateDir,
301303
showTouches: recording.showTouches,
302304
},
303305
};

0 commit comments

Comments
 (0)