Skip to content

Commit f146bb1

Browse files
committed
refactor: centralize common input field metadata
1 parent 67b813c commit f146bb1

5 files changed

Lines changed: 288 additions & 199 deletions

File tree

src/ai-sdk/index.ts

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { AppError, type NormalizedError } from '@agent-device/kernel/errors';
44
import { createAgentDeviceClient } from '../agent-device-client.ts';
55
import type { AgentDeviceClient } from '../client/client-types.ts';
66
import type { JsonSchema } from '../commands/command-contract.ts';
7+
import { commonInputKeysHiddenFromAiSdk } from '../commands/command-input.ts';
78
import { resolveCommandFrameworkTier } from '../core/command-descriptor/registry.ts';
89
import { createCommandToolExecutor, listCommandTools } from '../mcp/command-tools.ts';
910
import { formatToolErrorText } from '../mcp/tool-error.ts';
@@ -49,21 +50,11 @@ export type AgentDeviceTools = {
4950
toolApproval?: Partial<Record<string, ToolApprovalStatus>>;
5051
};
5152

52-
// Hidden unconditionally, from both the schema the model sees and the input
53-
// `execute` forwards to the shared executor:
54-
// - `session` is always pinned by this factory — the whole point is that a
55-
// tool call can never target a session other than the one passed in.
56-
// - `stateDir` selects which daemon state directory (and therefore which
57-
// daemon/session namespace) a call resolves against. Left model-visible,
58-
// it would let a call escape the pinned session into another daemon's
59-
// state entirely, defeating that guarantee.
60-
// - `mcpOutputFormat`, `includeCost`, `responseLevel` are MCP tool-config
61-
// knobs, not command arguments; irrelevant here since `execute` below
62-
// returns structuredContent directly and never reads a tool's rendered
63-
// text, and shaping the response is this factory's decision, not the
64-
// model's.
65-
const ALWAYS_HIDDEN_FIELDS = [
66-
'session',
53+
// MCP tool-config knobs, not command arguments; irrelevant here since
54+
// `execute` below returns structuredContent directly and never reads a tool's
55+
// rendered text. Common pinned fields such as `session`/`platform` come from
56+
// the common input-field table.
57+
const AI_SDK_HIDDEN_MCP_FIELDS = [
6758
'stateDir',
6859
'mcpOutputFormat',
6960
'includeCost',
@@ -104,8 +95,10 @@ export async function createAgentDeviceTools(
10495
});
10596
const executor = createCommandToolExecutor();
10697

107-
const hiddenFields = new Set<string>(ALWAYS_HIDDEN_FIELDS);
108-
if (platform) hiddenFields.add('platform');
98+
const hiddenFields = new Set<string>([
99+
...AI_SDK_HIDDEN_MCP_FIELDS,
100+
...commonInputKeysHiddenFromAiSdk({ platformPinned: Boolean(platform) }),
101+
]);
109102

110103
const tools: ToolSet = {};
111104
for (const definition of listCommandTools()) {

src/commands/cli-grammar/common.ts

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import {
1111
SELECTOR_EXPRESSION_REQUIRED_MESSAGE,
1212
splitSelectorFromArgs,
1313
} from '@agent-device/selectors';
14-
import { compactRecord, type SelectorSnapshotInput } from '../command-input.ts';
14+
import {
15+
commonCommandInputFromFlags,
16+
compactRecord,
17+
type SelectorSnapshotInput,
18+
} from '../command-input.ts';
1519
import type {
1620
CommandInput,
1721
DaemonCommandRequest,
@@ -56,33 +60,7 @@ function readDeviceTarget(value: unknown): InternalRequestOptions['target'] | un
5660
}
5761

5862
export function commonInputFromFlags(flags: CliFlags): Record<string, unknown> {
59-
return compactRecord({
60-
// `--no-record` is a COMMON flag (`COMMON_COMMAND_SUPPORTED_FLAG_KEYS`): it
61-
// is accepted on, and meaningful for, every recordable command. It rides
62-
// the common seam every reader already spreads, so a reader cannot forget
63-
// it and a new reader inherits it for free. The three seams it must survive
64-
// are this one, `readCommonInput`, and `commonToClientOptions`
65-
// (`commands/command-input.ts`) — a drop at any one of them silently
66-
// disables the flag (#1304/#1305 fixed only the reader layer, so the flag
67-
// still never reached the daemon).
68-
//
69-
// `--record` deliberately does NOT ride here: it is scoped to the
70-
// observation-only commands the repair-segment exclusion can drop
71-
// (ADR 0012 decision 6 amendment), so it stays on the narrow
72-
// `observationRecordInputFromFlags` seam below.
73-
noRecord: flags.noRecord,
74-
session: flags.session,
75-
platform: flags.platform,
76-
deviceTarget: flags.target,
77-
device: flags.device,
78-
udid: flags.udid,
79-
serial: flags.serial,
80-
iosSimulatorDeviceSet: flags.iosSimulatorDeviceSet,
81-
iosXctestrunFile: flags.iosXctestrunFile,
82-
iosXctestDerivedDataPath: flags.iosXctestDerivedDataPath,
83-
iosXctestEnvDir: flags.iosXctestEnvDir,
84-
androidDeviceAllowlist: flags.androidDeviceAllowlist,
85-
});
63+
return commonCommandInputFromFlags(flags);
8664
}
8765

8866
/**

src/commands/cli-grammar/flag-groups.ts

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { FlagKey } from './flag-types.ts';
2+
import { commonCommandSupportedFlagKeys } from '../command-input.ts';
23

34
function flagKeys<const TKeys extends readonly FlagKey[]>(...keys: TKeys): TKeys {
45
return keys;
@@ -45,22 +46,14 @@ export const REPEATED_TOUCH_FLAGS = flagKeys(
4546
export const SETTLE_FLAGS = flagKeys('settle', 'settleQuietMs', 'timeoutMs');
4647
export const REPLAY_FLAGS = flagKeys('replayUpdate', 'replayEnv');
4748

48-
export const COMMON_COMMAND_SUPPORTED_FLAG_KEYS = flagKeys(
49+
export const COMMON_COMMAND_SUPPORTED_FLAG_KEYS: readonly FlagKey[] = flagKeys(
4950
'remoteConfig',
5051
'stateDir',
51-
'daemonBaseUrl',
52-
'daemonAuthToken',
5352
'daemonTransport',
5453
'daemonServerMode',
55-
'tenant',
5654
'sessionIsolation',
57-
'runId',
58-
'leaseId',
5955
'leaseBackend',
6056
'sessionLock',
61-
'platform',
62-
'target',
63-
'device',
6457
'providerApp',
6558
'providerOsVersion',
6659
'providerProject',
@@ -79,21 +72,7 @@ export const COMMON_COMMAND_SUPPORTED_FLAG_KEYS = flagKeys(
7972
'awsAppArn',
8073
'awsRegion',
8174
'awsInteractionMode',
82-
'udid',
83-
'serial',
84-
'iosSimulatorDeviceSet',
85-
'iosXctestrunFile',
86-
'iosXctestDerivedDataPath',
87-
'iosXctestEnvDir',
88-
'androidDeviceAllowlist',
89-
'session',
90-
// `--no-record` is genuinely common: it applies to every recordable command,
91-
// mutations included. `--record` is NOT — it only means anything for the
92-
// observation-only commands the repair-segment exclusion can drop
93-
// (snapshot/get/is/find), so it is scoped per-command via each schema's
94-
// `allowedFlags` instead of being accepted everywhere and silently ignored
95-
// (#1271 stage 2).
96-
'noRecord',
75+
...commonCommandSupportedFlagKeys(),
9776
);
9877

9978
export const GLOBAL_FLAG_KEYS: ReadonlySet<FlagKey> = new Set([

0 commit comments

Comments
 (0)