Skip to content

Commit e171c5b

Browse files
committed
Add CLI agent settings
1 parent 28ae6fa commit e171c5b

25 files changed

Lines changed: 549 additions & 165 deletions

File tree

cli/resources/settings/cli_generic/test.settings

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,18 @@ StateModelStoreWidgets = false
3030
ResetDataStore = false
3131

3232
AbstractStateAttributes = WidgetControlType,WidgetTitle,WidgetPath
33-
StateObservationMode = INTERACTIVE_SEMANTIC_WIDGETS
33+
34+
CliStateProjectionMode = INTERACTIVE_SEMANTIC_WIDGETS
35+
AgentCLIApiKeyEnvVar = OPENAI_API_KEY
36+
AgentCLIBaseUrl =
37+
AgentCLIModel = gpt-5.4-mini
38+
AgentCLIReasoningEffort = medium
39+
AgentCLISandboxMode = danger-full-access
40+
AgentCLIApprovalPolicy = never
41+
AgentCLINetworkAccessEnabled = false
42+
AgentCLISkipGitRepoCheck = true
43+
AgentCLIPromptTitle = Test Parabank Login
44+
AgentCLIPromptText = As a test agent verify that you can log in with the credentials john/demo. Then the Welcome John Smith message is shown.
3445

3546
WebClickableClasses =
3647
WebTypeableClasses = input

config/src/org/testar/config/StateObservationMode.java renamed to config/src/org/testar/config/CliStateProjectionMode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
package org.testar.config;
88

99
/**
10-
* Consumer-facing state views used by CLI/agents.
10+
* Consumer-facing state projections used by CLI/agents.
1111
* These modes do not change the canonical captured TESTAR state.
1212
*/
13-
public enum StateObservationMode {
13+
public enum CliStateProjectionMode {
1414
FULL_STATE,
1515
LEAF_WIDGETS,
1616
SEMANTIC_WIDGETS,

config/src/org/testar/config/ConfigTags.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,46 @@ private ConfigTags() { }
9999
(Class<List<String>>) (Class<?>) List.class,
100100
"Specify the widget attributes that you wish to use in constructing the widget and state hash strings. Use a comma separated list.");
101101

102-
public static final Tag<StateObservationMode> StateObservationMode = Tag.from("StateObservationMode", StateObservationMode.class,
103-
"Controls how the captured state is projected for CLI/agents. Interactive and actionable modes use the active Java policies.");
102+
/**
103+
* CLI State Projection settings
104+
*/
105+
106+
public static final Tag<CliStateProjectionMode> CliStateProjectionMode = Tag.from("CliStateProjectionMode", org.testar.config.CliStateProjectionMode.class,
107+
"Controls how captured state is projected for CLI/agent consumers. Interactive and actionable modes use the active Java policies.");
108+
109+
/**
110+
* Agent settings that enables CLI control
111+
*/
112+
113+
public static final Tag<String> AgentCLIApiKeyEnvVar = Tag.from("AgentCLIApiKeyEnvVar", String.class,
114+
"Environment variable name that contains the API key for Agent CLI execution.");
115+
116+
public static final Tag<String> AgentCLIBaseUrl = Tag.from("AgentCLIBaseUrl", String.class,
117+
"Optional base URL for the Agent CLI model provider.");
118+
119+
public static final Tag<String> AgentCLIModel = Tag.from("AgentCLIModel", String.class,
120+
"Model used by Agent CLI execution.");
121+
122+
public static final Tag<String> AgentCLIReasoningEffort = Tag.from("AgentCLIReasoningEffort", String.class,
123+
"Reasoning effort used by Agent CLI execution.");
124+
125+
public static final Tag<String> AgentCLISandboxMode = Tag.from("AgentCLISandboxMode", String.class,
126+
"Sandbox mode used by Agent CLI execution.");
127+
128+
public static final Tag<String> AgentCLIApprovalPolicy = Tag.from("AgentCLIApprovalPolicy", String.class,
129+
"Approval policy used by Agent CLI execution.");
130+
131+
public static final Tag<Boolean> AgentCLINetworkAccessEnabled = Tag.from("AgentCLINetworkAccessEnabled", Boolean.class,
132+
"Allow Agent CLI execution to request network access.");
133+
134+
public static final Tag<Boolean> AgentCLISkipGitRepoCheck = Tag.from("AgentCLISkipGitRepoCheck", Boolean.class,
135+
"Skip the Agent CLI git repository check.");
136+
137+
public static final Tag<String> AgentCLIPromptTitle = Tag.from("AgentCLIPromptTitle", String.class,
138+
"Title used for the Agent CLI prompt.");
139+
140+
public static final Tag<String> AgentCLIPromptText = Tag.from("AgentCLIPromptText", String.class,
141+
"User goal sent to Agent CLI execution.");
104142

105143
/**
106144
* Large Language Models settings

config/src/org/testar/config/settings/Settings.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import java.util.Properties;
2020

2121
import org.testar.config.ConfigTags;
22-
import org.testar.config.StateObservationMode;
22+
import org.testar.config.CliStateProjectionMode;
2323
import org.testar.config.TestarMode;
2424
import org.testar.core.Assert;
2525
import org.testar.core.Pair;
@@ -90,11 +90,11 @@ public static <T> T parse(String stringValue, Tag<T> tag) throws ConfigParseExce
9090
} catch (IllegalArgumentException iae) {
9191
throw new ConfigParseException("Unknown Mode!");
9292
}
93-
} else if (tag.type().equals(StateObservationMode.class)) {
93+
} else if (tag.type().equals(CliStateProjectionMode.class)) {
9494
try {
95-
return (T) StateObservationMode.valueOf(stringValue);
95+
return (T) CliStateProjectionMode.valueOf(stringValue);
9696
} catch (IllegalArgumentException iae) {
97-
throw new ConfigParseException("Unknown StateObservationMode!");
97+
throw new ConfigParseException("Unknown CliStateProjectionMode!");
9898
}
9999
} else if (tag.type().equals(Integer.class)) {
100100
try {

config/src/org/testar/config/settings/SettingsDefaults.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,18 @@
9797
import static org.testar.config.ConfigTags.ShowVisualSettingsDialogOnStartup;
9898
import static org.testar.config.ConfigTags.SpyTagAttributes;
9999
import static org.testar.config.ConfigTags.StartupTime;
100-
import static org.testar.config.ConfigTags.StateObservationMode;
101-
import static org.testar.config.StateObservationMode.FULL_STATE;
100+
import static org.testar.config.ConfigTags.AgentCLIApiKeyEnvVar;
101+
import static org.testar.config.ConfigTags.AgentCLIApprovalPolicy;
102+
import static org.testar.config.ConfigTags.AgentCLIBaseUrl;
103+
import static org.testar.config.ConfigTags.AgentCLIModel;
104+
import static org.testar.config.ConfigTags.AgentCLINetworkAccessEnabled;
105+
import static org.testar.config.ConfigTags.AgentCLIPromptText;
106+
import static org.testar.config.ConfigTags.AgentCLIPromptTitle;
107+
import static org.testar.config.ConfigTags.AgentCLIReasoningEffort;
108+
import static org.testar.config.ConfigTags.AgentCLISandboxMode;
109+
import static org.testar.config.ConfigTags.AgentCLISkipGitRepoCheck;
110+
import static org.testar.config.ConfigTags.CliStateProjectionMode;
111+
import static org.testar.config.CliStateProjectionMode.INTERACTIVE_SEMANTIC_WIDGETS;
102112
import static org.testar.config.ConfigTags.StopGenerationOnFault;
103113
import static org.testar.config.ConfigTags.SuspiciousProcessOutput;
104114
import static org.testar.config.ConfigTags.SuspiciousTags;
@@ -210,7 +220,17 @@ private SettingsDefaults() { }
210220
add("WidgetControlType");
211221
}
212222
}));
213-
defaults.add(Pair.from(StateObservationMode, FULL_STATE));
223+
defaults.add(Pair.from(CliStateProjectionMode, INTERACTIVE_SEMANTIC_WIDGETS));
224+
defaults.add(Pair.from(AgentCLIApiKeyEnvVar, "OPENAI_API_KEY"));
225+
defaults.add(Pair.from(AgentCLIBaseUrl, ""));
226+
defaults.add(Pair.from(AgentCLIModel, "gpt-5.4-mini"));
227+
defaults.add(Pair.from(AgentCLIReasoningEffort, "medium"));
228+
defaults.add(Pair.from(AgentCLISandboxMode, "danger-full-access"));
229+
defaults.add(Pair.from(AgentCLIApprovalPolicy, "never"));
230+
defaults.add(Pair.from(AgentCLINetworkAccessEnabled, false));
231+
defaults.add(Pair.from(AgentCLISkipGitRepoCheck, true));
232+
defaults.add(Pair.from(AgentCLIPromptTitle, "Test Parabank Login"));
233+
defaults.add(Pair.from(AgentCLIPromptText, "As a test agent verify that you can log in with the credentials john/demo. Then the Welcome John Smith message is shown."));
214234

215235
defaults.add(Pair.from(WebClickableClasses, new ArrayList<String>() {
216236
{
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.testar.config.settings;
2+
3+
import org.junit.Test;
4+
import org.testar.config.CliStateProjectionMode;
5+
import org.testar.config.ConfigTags;
6+
import org.testar.core.Assert;
7+
8+
import java.util.Properties;
9+
10+
public class CliAgentSettingsDefaultsTest {
11+
12+
@Test
13+
public void defaultCliStateProjectionModeIsInteractiveSemanticWidgets() {
14+
Settings settings = new Settings(SettingsDefaults.getSettingsDefaults(), verifiedProperties());
15+
16+
Assert.isTrue(settings.get(ConfigTags.CliStateProjectionMode)
17+
.equals(CliStateProjectionMode.INTERACTIVE_SEMANTIC_WIDGETS));
18+
}
19+
20+
@Test
21+
public void parsesCliStateProjectionModeFromSettingsValue() {
22+
CliStateProjectionMode parsedMode = Settings.parse(
23+
"ACTIONABLE_WIDGETS",
24+
ConfigTags.CliStateProjectionMode
25+
);
26+
27+
Assert.isTrue(parsedMode.equals(CliStateProjectionMode.ACTIONABLE_WIDGETS));
28+
}
29+
30+
@Test
31+
public void defaultAgentCliSettingsAreAvailable() {
32+
Settings settings = new Settings(SettingsDefaults.getSettingsDefaults(), verifiedProperties());
33+
34+
Assert.isTrue(settings.get(ConfigTags.AgentCLIApiKeyEnvVar).equals("OPENAI_API_KEY"));
35+
Assert.isTrue(settings.get(ConfigTags.AgentCLIModel).equals("gpt-5.4-mini"));
36+
Assert.isTrue(settings.get(ConfigTags.AgentCLIReasoningEffort).equals("medium"));
37+
Assert.isTrue(settings.get(ConfigTags.AgentCLISandboxMode).equals("danger-full-access"));
38+
Assert.isTrue(settings.get(ConfigTags.AgentCLIApprovalPolicy).equals("never"));
39+
Assert.isTrue(!settings.get(ConfigTags.AgentCLINetworkAccessEnabled));
40+
Assert.isTrue(settings.get(ConfigTags.AgentCLISkipGitRepoCheck));
41+
}
42+
43+
private Properties verifiedProperties() {
44+
Properties properties = new Properties();
45+
properties.setProperty(ConfigTags.SUTConnectorValue.name(), "notepad.exe");
46+
return properties;
47+
}
48+
}

docs/architecture_distribution_workspace.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,12 @@ Examples:
158158
- `Sequences` and `SequenceLength`
159159
- scriptless `Generate` settings
160160
- not required by manual CLI sessions
161-
- `StateObservationMode`
161+
- `CliStateProjectionMode`
162162
- currently used by CLI and agent state projection
163163
- agent CLI settings
164164
- used by agent CLI mode
165165
- ignored by scriptless `Generate` and `Spy`
166+
- stored as `AgentCLI...` values in shared `test.settings`
166167

167168
## Policies and Semantics
168169

docs/webstudio/WEBSTUDIO_FUNCTIONAL_SPEC.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ The `Edit test.settings file` view is treated as one logical editing area for un
8181
- `Edit Settings` renders the content of the `test.settings` file into a visual grouped form of settings.
8282
The `Edit Settings` view is treated as one logical editing area for unsaved-change behavior.
8383

84-
### State Observation Mode
84+
### CLI State Projection Mode
8585

86-
`StateObservationMode` controls how a captured TESTAR state is projected for consumers. Currently, it is applied to CLI/agent state output.
86+
`CliStateProjectionMode` controls how a captured TESTAR state is projected for CLI/agent consumers.
8787

8888
The setting must not redefine the canonical captured state used by TESTAR internals.
8989

@@ -103,9 +103,9 @@ The distinction between interactive and actionable is intentional:
103103
- interactive means the widget has an interaction capability
104104
- actionable means the widget is interactive and also passes enabled, non-blocked, visible, widget-filter, and top-level policies
105105

106-
The default value is `FULL_STATE`.
106+
The default value is `INTERACTIVE_SEMANTIC_WIDGETS`.
107107

108-
The `Edit Settings` view must render `StateObservationMode` as a dropdown using the available enum values.
108+
The `Edit Settings` view must render `CliStateProjectionMode` as a dropdown using the available enum values.
109109

110110
## Navigation Rules
111111

@@ -335,12 +335,30 @@ CLI Mode supports:
335335
- agent CLI execution start
336336
- CLI command execution
337337
- CLI session stop
338+
- focused editing of Agent CLI settings stored in the selected workspace `test.settings`
338339

339340
Workspace selection:
340341

341342
- WebStudio uses the shared workspace list for CLI mode with other testar modes
342343
- WebStudio sends the selected workspace to CLI startup
343344
- CLI validates connector values server-side and reports startup errors to the user
345+
- Manual and Agent CLI execution use the selected workspace settings, including `CliStateProjectionMode` and `AgentCLI...` values
346+
347+
### Agent CLI Settings
348+
349+
Agent CLI settings are stored in the selected workspace `test.settings` file with `AgentCLI...` names.
350+
351+
The `Edit Settings` view must expose these settings in an `Agent CLI` group.
352+
353+
The CLI mode view may also expose a focused `Agent CLI Settings` panel for the same settings.
354+
355+
If users edit Agent CLI settings in CLI mode and then leave CLI mode, change workspace, or start Manual/Agent CLI execution, WebStudio must show an unsaved settings dialog.
356+
357+
Dialog actions:
358+
359+
- `Save`: persist Agent CLI settings to the selected workspace `test.settings`, then continue the pending action
360+
- `Discard`: restore the last persisted Agent CLI settings in the CLI panel, then continue the pending action
361+
- `Cancel`: keep the user in CLI mode and abort the pending action
344362

345363
## Test Results
346364

docs/webstudio/WEBSTUDIO_UX_SPEC.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ Expected behavior:
229229
- console output scrolls internally
230230
- session start/stop does not shift panels
231231
- command buttons do not overflow horizontally
232+
- Agent CLI settings edited in CLI mode use the same unsaved-change dialog pattern as configuration settings
232233

233234
## Test Results UX Contract
234235

engine/src/org/testar/engine/state/StateCompositionPlan.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
package org.testar.engine.state;
88

9-
import org.testar.config.StateObservationMode;
9+
import org.testar.config.CliStateProjectionMode;
1010
import org.testar.core.Assert;
1111
import org.testar.core.service.StateService;
1212
import org.testar.core.state.State;
@@ -91,13 +91,13 @@ public State query(State state, SessionPolicyContext context) {
9191
}
9292

9393
public State projectState(State originalState,
94-
StateObservationMode observationMode,
94+
CliStateProjectionMode projectionMode,
9595
SessionPolicyContext policyContext) {
9696
Assert.notNull(originalState);
97-
Assert.notNull(observationMode);
97+
Assert.notNull(projectionMode);
9898
Assert.notNull(policyContext);
9999

100-
switch (observationMode) {
100+
switch (projectionMode) {
101101
case FULL_STATE:
102102
return originalState;
103103
case LEAF_WIDGETS:
@@ -109,33 +109,33 @@ public State projectState(State originalState,
109109
case TEXTUAL_CONTEXT:
110110
return StateProjectionSupport.projectSemanticWidgets(
111111
originalState,
112-
requireSemanticWidgetDescriptor(observationMode)
112+
requireSemanticWidgetDescriptor(projectionMode)
113113
);
114114
case INTERACTIVE_WIDGETS:
115115
return StateProjectionSupport.projectInteractiveWidgets(originalState, policyContext);
116116
case INTERACTIVE_SEMANTIC_WIDGETS:
117117
return StateProjectionSupport.projectInteractiveSemanticWidgets(
118118
originalState,
119-
requireSemanticWidgetDescriptor(observationMode),
119+
requireSemanticWidgetDescriptor(projectionMode),
120120
policyContext
121121
);
122122
case ACTIONABLE_WIDGETS:
123123
return StateProjectionSupport.projectActionableWidgets(originalState, policyContext);
124124
case ACTIONABLE_SEMANTIC_WIDGETS:
125125
return StateProjectionSupport.projectActionableSemanticWidgets(
126126
originalState,
127-
requireSemanticWidgetDescriptor(observationMode),
127+
requireSemanticWidgetDescriptor(projectionMode),
128128
policyContext
129129
);
130130
default:
131-
throw new IllegalArgumentException("Unsupported state observation mode: " + observationMode);
131+
throw new IllegalArgumentException("Unsupported CLI state projection mode: " + projectionMode);
132132
}
133133
}
134134

135-
private SemanticWidgetDescriptor requireSemanticWidgetDescriptor(StateObservationMode observationMode) {
135+
private SemanticWidgetDescriptor requireSemanticWidgetDescriptor(CliStateProjectionMode projectionMode) {
136136
if (semanticWidgetDescriptor == null) {
137137
throw new IllegalStateException(
138-
"State observation mode requires a semantic descriptor: " + observationMode
138+
"CLI state projection mode requires a semantic descriptor: " + projectionMode
139139
);
140140
}
141141
return semanticWidgetDescriptor;

0 commit comments

Comments
 (0)