@lazyingart/agintiflow 0.20.151 → 0.20.156
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.
- package/docs/student-committee-supervisor.md +10 -2
- package/package.json +1 -1
- package/scripts/smoke-cli-chat.js +8 -4
- package/scripts/smoke-coding-tools.js +26 -0
- package/scripts/smoke-dynamic-step-budget.js +65 -1
- package/scripts/smoke-model-roles.js +6 -1
- package/scripts/smoke-skills.js +8 -0
- package/skills/browser-automation/SKILL.md +56 -0
- package/src/agent-runner.js +248 -54
- package/src/browser-automation-guidance.js +12 -0
- package/src/cli.js +1 -1
- package/src/command-policy.js +13 -1
- package/src/config.js +1 -1
- package/src/engineering-guidance.js +10 -0
- package/src/interactive-cli.js +10 -2
- package/src/model-client.js +3 -1
- package/src/permission-advice.js +45 -0
- package/src/scs-controller.js +218 -14
- package/src/task-profiles.js +1 -1
|
@@ -31,7 +31,7 @@ SCS uses the selected main model for every internal role.
|
|
|
31
31
|
| Role | Right | Boundary |
|
|
32
32
|
| --- | --- | --- |
|
|
33
33
|
| Committee | Draft one next-phase plan with acceptance criteria and stop conditions. | Cannot approve plans or call tools. |
|
|
34
|
-
| Student |
|
|
34
|
+
| Student | Act as the independent validator: approve/veto the phase plan, review failure evidence, and approve/reject finish. | Cannot call tools, approve its own work, or override runtime safety. |
|
|
35
35
|
| Supervisor | Execute the approved phase with the existing browser, shell, file, canvas, and wrapper tools. | Cannot replace the strategic plan without student review. |
|
|
36
36
|
|
|
37
37
|
The runtime remains the real authority for command policy, filesystem guardrails, secret redaction, session persistence, and user interruption.
|
|
@@ -44,9 +44,10 @@ When SCS is active:
|
|
|
44
44
|
- Parallel scouts are disabled by default unless explicitly requested, avoiding duplicate advisory layers.
|
|
45
45
|
- The normal `createPlan()` path is replaced by a committee draft plus student approval.
|
|
46
46
|
- An approved supervisor instruction is injected into the execution loop.
|
|
47
|
-
- Failed or
|
|
47
|
+
- Failed, blocked, suspicious, or mismatched tools trigger a bounded student validator review.
|
|
48
48
|
- Every fourth execution step triggers a bounded progress review for long runs.
|
|
49
49
|
- `finish` and assistant-content completion pass through a final student gate.
|
|
50
|
+
- If the student validator rejects progress or finish, the runtime asks the committee for a new phase plan and sends that plan back through the student gate before the supervisor continues.
|
|
50
51
|
- Decisions are persisted as `scs.*` events and the phase pack is saved as a session artifact.
|
|
51
52
|
|
|
52
53
|
The current implementation is deliberately bounded:
|
|
@@ -93,6 +94,10 @@ Avoid SCS for:
|
|
|
93
94
|
|
|
94
95
|
SCS emits compact event names:
|
|
95
96
|
|
|
97
|
+
- `conversation.continued`
|
|
98
|
+
- `surgical_context.prepared`
|
|
99
|
+
- `surgical_context.failed`
|
|
100
|
+
- `scs.plan.requested`
|
|
96
101
|
- `scs.enabled`
|
|
97
102
|
- `scs.committee.plan_drafted`
|
|
98
103
|
- `scs.student.approve_plan`
|
|
@@ -102,10 +107,13 @@ SCS emits compact event names:
|
|
|
102
107
|
- `scs.student.reject_phase`
|
|
103
108
|
- `scs.student.finish_allowed`
|
|
104
109
|
- `scs.student.finish_rejected`
|
|
110
|
+
- `scs.committee.replan_drafted`
|
|
105
111
|
- `scs.supervisor.phase_started`
|
|
106
112
|
|
|
107
113
|
These are saved in the normal session event log under `~/.agintiflow/sessions/<session-id>/events.jsonl`, with project pointers under `.aginti-sessions/`.
|
|
108
114
|
|
|
115
|
+
Browser and CDP helper commands are reviewed by evidence, not only by exit status. If a click or selector command reports `ok: true` but returns broad whole-page text, repeated navigation/history/sidebar text, or no scoped target evidence, SCS treats the result as suspicious and asks the supervisor to verify state or switch to a precise selector before continuing.
|
|
116
|
+
|
|
109
117
|
## Design Reference
|
|
110
118
|
|
|
111
119
|
The deeper design research lives in [references/student-committee-supervisor-mode.md](../references/student-committee-supervisor-mode.md). The important design choice is that SCS is a typed event gate, not a free-form debate. Internal roles emit structured decisions, and the runtime decides what those decisions are allowed to do.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.156",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -683,9 +683,13 @@ try {
|
|
|
683
683
|
if (!reviewResult.stdout.includes("Review focus: changed files only") || !reviewResult.stdout.includes("Mock run complete")) {
|
|
684
684
|
throw new Error("interactive /review did not launch the bounded review workflow");
|
|
685
685
|
}
|
|
686
|
-
const
|
|
686
|
+
const scsDefaultStatusResult = await runChat("/scs status\n");
|
|
687
|
+
if (!scsDefaultStatusResult.stdout.includes("SCS mode: auto")) {
|
|
688
|
+
throw new Error("interactive /scs status did not show default auto mode");
|
|
689
|
+
}
|
|
690
|
+
const scsOnResult = await runChat("/scs on\n");
|
|
687
691
|
if (!scsOnResult.stdout.includes("scs=on")) {
|
|
688
|
-
throw new Error("interactive /scs did not
|
|
692
|
+
throw new Error("interactive /scs on did not enable SCS");
|
|
689
693
|
}
|
|
690
694
|
const legacyScsAlias = "/enable" + "ss";
|
|
691
695
|
const oldScsAliasResult = await runChat(`${legacyScsAlias}\n/exit\n`);
|
|
@@ -696,9 +700,9 @@ try {
|
|
|
696
700
|
if (!scsOffResult.stdout.includes("scs=off")) {
|
|
697
701
|
throw new Error("interactive /scs did not toggle SCS off");
|
|
698
702
|
}
|
|
699
|
-
const scsStatusResult = await
|
|
703
|
+
const scsStatusResult = await runCli(["chat", "--provider", "mock", "--routing", "manual", "--profile", "code", "--no-scs"], "/scs status\n");
|
|
700
704
|
if (!scsStatusResult.stdout.includes("SCS mode: off")) {
|
|
701
|
-
throw new Error("interactive /scs status did not show
|
|
705
|
+
throw new Error("interactive /scs status did not show explicit off mode");
|
|
702
706
|
}
|
|
703
707
|
const scsAutoResult = await runChat("/scs auto\n");
|
|
704
708
|
if (!scsAutoResult.stdout.includes("scs=auto")) {
|
|
@@ -441,6 +441,18 @@ try {
|
|
|
441
441
|
);
|
|
442
442
|
assert(androidReadonlyProbePolicy.allowed, "Android host read-only probes should not require full-host destructive access");
|
|
443
443
|
assert(androidReadonlyProbePolicy.category === "read-only", "Android host probes should remain read-only");
|
|
444
|
+
const hostLocalhostJsonProbePolicy = evaluateCommandPolicy(
|
|
445
|
+
"curl -s http://127.0.0.1:9222/json/version | python3 -m json.tool",
|
|
446
|
+
hostWorkspacePolicy
|
|
447
|
+
);
|
|
448
|
+
assert(hostLocalhostJsonProbePolicy.allowed, "host localhost JSON probe should not require full-host destructive access");
|
|
449
|
+
assert(hostLocalhostJsonProbePolicy.category === "network-fetch", "host localhost JSON probe should stay classified as network-fetch");
|
|
450
|
+
const absolutePythonHelperPolicy = evaluateCommandPolicy(
|
|
451
|
+
"/home/lachlan/miniconda3/bin/python scripts/xyq_cdp_browser.py list-pages",
|
|
452
|
+
hostWorkspacePolicy
|
|
453
|
+
);
|
|
454
|
+
assert(absolutePythonHelperPolicy.allowed, "host absolute Python helper script should be allowed without full-host destructive access");
|
|
455
|
+
assert(absolutePythonHelperPolicy.category === "toolchain", "host absolute Python helper should remain classified as toolchain");
|
|
444
456
|
const androidGradleBuildPolicy = evaluateCommandPolicy("cd android-app && ./gradlew :app:assembleDebug", hostWorkspacePolicy);
|
|
445
457
|
assert(androidGradleBuildPolicy.allowed, "workspace-local Gradle Android build should be allowed in host workspace mode");
|
|
446
458
|
assert(androidGradleBuildPolicy.category === "toolchain", "workspace-local Gradle Android build should be toolchain");
|
|
@@ -555,6 +567,18 @@ try {
|
|
|
555
567
|
failedNetworkAdvice.instruction.includes("Stop and present this blocker"),
|
|
556
568
|
"network failure advice did not tell the model to stop and ask"
|
|
557
569
|
);
|
|
570
|
+
const failedDockerLocalhostAdvice = buildFailedCommandAdvice({
|
|
571
|
+
args: { command: "curl -fsS http://127.0.0.1:9222/json/version" },
|
|
572
|
+
commandPolicy: evaluateCommandPolicy("curl -fsS http://127.0.0.1:9222/json/version", dockerWorkspacePolicy),
|
|
573
|
+
commandResult: { ok: false, stderr: "curl: (7) Failed to connect to 127.0.0.1 port 9222: Connection refused" },
|
|
574
|
+
config: dockerWorkspacePolicy,
|
|
575
|
+
state: { sessionId: "coding-localhost-cdp-smoke" },
|
|
576
|
+
});
|
|
577
|
+
assert(failedDockerLocalhostAdvice?.failureKind === "host-local-service", "Docker localhost failure advice was not generated");
|
|
578
|
+
assert(
|
|
579
|
+
failedDockerLocalhostAdvice.suggestedCommand.includes("--sandbox-mode host"),
|
|
580
|
+
"Docker localhost advice did not suggest host mode"
|
|
581
|
+
);
|
|
558
582
|
const failedOutsidePathAdvice = buildFailedCommandAdvice({
|
|
559
583
|
args: { command: 'echo "outside permission test" > /home/lachlan/ProjectsLFS/outside.txt' },
|
|
560
584
|
commandPolicy: evaluateCommandPolicy('echo "outside permission test" > /home/lachlan/ProjectsLFS/outside.txt', dockerWorkspacePolicy),
|
|
@@ -1149,6 +1173,8 @@ try {
|
|
|
1149
1173
|
"command_policy_safe_chmod_sequence",
|
|
1150
1174
|
"command_policy_host_workspace_chmod",
|
|
1151
1175
|
"command_policy_android_host_probes",
|
|
1176
|
+
"command_policy_host_localhost_json_probe",
|
|
1177
|
+
"command_policy_absolute_python_helper",
|
|
1152
1178
|
"command_policy_android_gradle_build",
|
|
1153
1179
|
"command_policy_android_gradle_build_with_safe_env",
|
|
1154
1180
|
"command_policy_cd_workspace",
|
|
@@ -5,13 +5,19 @@ import path from "node:path";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { runAgent } from "../src/agent-runner.js";
|
|
7
7
|
import { resolveRuntimeConfig } from "../src/config.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
browserSubmitFinishIssue,
|
|
10
|
+
isSuspiciousBroadBrowserToolResult,
|
|
11
|
+
shouldActivateScs,
|
|
12
|
+
shouldReviewToolResult,
|
|
13
|
+
} from "../src/scs-controller.js";
|
|
9
14
|
import {
|
|
10
15
|
createStepBudgetState,
|
|
11
16
|
decideStepBudgetExtension,
|
|
12
17
|
normalizeDynamicStepsMode,
|
|
13
18
|
} from "../src/step-budget-controller.js";
|
|
14
19
|
import { SessionStore } from "../src/session-store.js";
|
|
20
|
+
import { recommendedMaxStepsForTask } from "../src/engineering-guidance.js";
|
|
15
21
|
|
|
16
22
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
23
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-dynamic-budget-"));
|
|
@@ -44,10 +50,67 @@ try {
|
|
|
44
50
|
}),
|
|
45
51
|
"/scs auto should activate for complex engineering prompts"
|
|
46
52
|
);
|
|
53
|
+
assert(
|
|
54
|
+
shouldActivateScs("auto", {
|
|
55
|
+
goal: "Use Chrome Driver/CDP on 127.0.0.1:9222 to upload five reference images, choose an asset-library video, submit the browser form, and monitor progress.",
|
|
56
|
+
taskProfile: "auto",
|
|
57
|
+
complexityScore: 1,
|
|
58
|
+
}),
|
|
59
|
+
"/scs auto should activate for browser automation and host-local CDP workflows"
|
|
60
|
+
);
|
|
47
61
|
assert(
|
|
48
62
|
!shouldActivateScs("auto", { goal: "say hello", taskProfile: "auto", complexityScore: 0 }),
|
|
49
63
|
"/scs auto should stay off for trivial prompts"
|
|
50
64
|
);
|
|
65
|
+
const broadBrowserClick = {
|
|
66
|
+
toolName: "run_command",
|
|
67
|
+
ok: true,
|
|
68
|
+
args: {
|
|
69
|
+
command: "python scripts/browser_cdp.py click-text PAGE \"Create\"",
|
|
70
|
+
},
|
|
71
|
+
stdout: JSON.stringify({
|
|
72
|
+
ok: true,
|
|
73
|
+
text: Array(30)
|
|
74
|
+
.fill("New chat\nAsset library\nHistory\nAll\nYesterday\nThis month\nSettings\nUpload reference\nSubmit prompt")
|
|
75
|
+
.join("\n"),
|
|
76
|
+
x: 650,
|
|
77
|
+
y: 390,
|
|
78
|
+
}),
|
|
79
|
+
};
|
|
80
|
+
assert(
|
|
81
|
+
isSuspiciousBroadBrowserToolResult(broadBrowserClick),
|
|
82
|
+
"SCS should flag successful browser clicks that return broad whole-page text"
|
|
83
|
+
);
|
|
84
|
+
assert(
|
|
85
|
+
shouldReviewToolResult(broadBrowserClick, { meta: {} }),
|
|
86
|
+
"SCS should review suspicious broad browser click results"
|
|
87
|
+
);
|
|
88
|
+
assert(
|
|
89
|
+
!isSuspiciousBroadBrowserToolResult({
|
|
90
|
+
toolName: "run_command",
|
|
91
|
+
ok: true,
|
|
92
|
+
args: { command: "echo ok" },
|
|
93
|
+
stdout: "ok",
|
|
94
|
+
}),
|
|
95
|
+
"SCS should not flag ordinary successful shell output as a browser click problem"
|
|
96
|
+
);
|
|
97
|
+
assert(
|
|
98
|
+
recommendedMaxStepsForTask({
|
|
99
|
+
goal: "Use Chrome CDP to upload five images, select an asset-library video, choose non-VIP model, and submit the Xiaoyunque browser composer.",
|
|
100
|
+
}) >= 48,
|
|
101
|
+
"browser submit workflows need a larger default step budget"
|
|
102
|
+
);
|
|
103
|
+
assert(
|
|
104
|
+
browserSubmitFinishIssue(
|
|
105
|
+
"小云雀网页上传五张图,从资产库选择参考视频,然后提交生成",
|
|
106
|
+
"资产库旧三人视频 未执行;提交 未执行;步骤不足。"
|
|
107
|
+
),
|
|
108
|
+
"SCS finish gate should reject unfinished browser submit reports"
|
|
109
|
+
);
|
|
110
|
+
assert(
|
|
111
|
+
!browserSubmitFinishIssue("小云雀网页提交生成", "停止:积分不足,需要用户处理 credits not enough。"),
|
|
112
|
+
"SCS finish gate should allow real external browser blockers"
|
|
113
|
+
);
|
|
51
114
|
|
|
52
115
|
const normalBudget = createStepBudgetState(
|
|
53
116
|
{
|
|
@@ -131,6 +194,7 @@ try {
|
|
|
131
194
|
sessionId: "dynamic-step-budget-smoke",
|
|
132
195
|
}
|
|
133
196
|
);
|
|
197
|
+
assert(config.enableScs === "auto", "runtime config should default SCS mode to auto");
|
|
134
198
|
const run = await runAgent(config);
|
|
135
199
|
assert(!run.stopped, "mock run stopped instead of using dynamic extension");
|
|
136
200
|
const written = await fs.readFile(path.join(workspace, "notes/dynamic-budget.md"), "utf8");
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "../src/model-routing.js";
|
|
12
12
|
import { normalizeTextToolCallResponse, parseTextToolCalls, usesTextToolProtocol } from "../src/model-client.js";
|
|
13
13
|
import { modelRoleChoices, selectorVisibleWindow } from "../src/interactive-cli.js";
|
|
14
|
-
import { buildScsEvidencePack, buildSupervisorInstruction } from "../src/scs-controller.js";
|
|
14
|
+
import { buildScsEvidencePack, buildSupervisorInstruction, shouldRequestScsReplan } from "../src/scs-controller.js";
|
|
15
15
|
|
|
16
16
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
17
|
|
|
@@ -219,6 +219,10 @@ assert(!usesTextToolProtocol({ provider: "venice", model: "venice-uncensored-1-2
|
|
|
219
219
|
const scsInstruction = buildSupervisorInstruction({ plan: "Create one file.", acceptanceCriteria: ["File exists."] });
|
|
220
220
|
assert(scsInstruction.includes("Student-Committee-Supervisor"), "SCS supervisor instruction should define the acronym");
|
|
221
221
|
assert(!scsInstruction.includes("Syntax-Checker Sentinel"), "SCS supervisor instruction should not allow alternate acronym expansions");
|
|
222
|
+
assert(scsInstruction.includes("student is the independent validator"), "SCS supervisor instruction should define student as validator");
|
|
223
|
+
assert(shouldRequestScsReplan({ decision: "finish_rejected" }), "finish rejection should trigger committee replan");
|
|
224
|
+
assert(shouldRequestScsReplan({ decision: "rethink_plan" }), "student rethink should trigger committee replan");
|
|
225
|
+
assert(!shouldRequestScsReplan({ decision: "finish_allowed" }), "finish approval should not trigger committee replan");
|
|
222
226
|
const longStdout = [
|
|
223
227
|
"=== Student-Committee-Supervisor present ===",
|
|
224
228
|
"3:SCS stands for **Student-Committee-Supervisor**",
|
|
@@ -270,6 +274,7 @@ console.log(
|
|
|
270
274
|
"requested-tools-parser",
|
|
271
275
|
"malformed-text-tool-retry",
|
|
272
276
|
"scs-supervisor-identity",
|
|
277
|
+
"scs-student-validator-replan",
|
|
273
278
|
"scs-evidence-stdout",
|
|
274
279
|
"cli-models-command",
|
|
275
280
|
"venice-shortcut",
|
package/scripts/smoke-skills.js
CHANGED
|
@@ -22,6 +22,7 @@ assert(skills.length >= 27, "expected built-in skills to load");
|
|
|
22
22
|
for (const required of [
|
|
23
23
|
"aaps",
|
|
24
24
|
"autonomous-artifact-pipeline",
|
|
25
|
+
"browser-automation",
|
|
25
26
|
"code",
|
|
26
27
|
"code-review",
|
|
27
28
|
"data-analysis",
|
|
@@ -56,6 +57,13 @@ for (const required of [
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
assert(selectedIds("write a beautiful React website and preview it").includes("website-app"), "website prompt did not select website-app");
|
|
60
|
+
assert(
|
|
61
|
+
selectedIds("control an existing Chrome CDP browser composer, upload images, choose model duration, and submit").includes(
|
|
62
|
+
"browser-automation"
|
|
63
|
+
),
|
|
64
|
+
"browser automation prompt did not select browser-automation"
|
|
65
|
+
);
|
|
66
|
+
assert(selectedIds("小云雀 网页 上传 五张图 从资产库选择视频 提交").includes("browser-automation"), "Chinese XYQ prompt did not select browser-automation");
|
|
59
67
|
assert(selectedIds("write a LaTeX paper and compile a PDF").includes("latex-manuscript"), "latex prompt did not select latex-manuscript");
|
|
60
68
|
assert(selectedIds("edit a Microsoft Word docx and preserve the original").includes("word-documents"), "docx prompt did not select word-documents");
|
|
61
69
|
assert(selectedIds("generate a logo image with grsai nanobanana").includes("image-generation"), "image prompt did not select image-generation");
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: browser-automation
|
|
3
|
+
label: Browser Automation
|
|
4
|
+
description: Control existing browser sessions, Chrome/CDP pages, web composers, upload dialogs, asset pickers, model selectors, forms, and submit/publish workflows with inspect-set-verify discipline.
|
|
5
|
+
triggers:
|
|
6
|
+
- browser
|
|
7
|
+
- chrome
|
|
8
|
+
- cdp
|
|
9
|
+
- chromedriver
|
|
10
|
+
- playwright
|
|
11
|
+
- selenium
|
|
12
|
+
- web ui
|
|
13
|
+
- website
|
|
14
|
+
- upload
|
|
15
|
+
- attach
|
|
16
|
+
- asset library
|
|
17
|
+
- submit
|
|
18
|
+
- publish
|
|
19
|
+
- model selector
|
|
20
|
+
- prompt composer
|
|
21
|
+
- 小云雀
|
|
22
|
+
- 浏览器
|
|
23
|
+
- 网页
|
|
24
|
+
- 上传
|
|
25
|
+
- 资产库
|
|
26
|
+
- 提交
|
|
27
|
+
tools:
|
|
28
|
+
- open_url
|
|
29
|
+
- click
|
|
30
|
+
- type
|
|
31
|
+
- press
|
|
32
|
+
- wait
|
|
33
|
+
- run_command
|
|
34
|
+
- read_image
|
|
35
|
+
- read_file
|
|
36
|
+
- search_files
|
|
37
|
+
---
|
|
38
|
+
# Browser Automation
|
|
39
|
+
|
|
40
|
+
Treat browser work as state reconciliation, not passive inspection.
|
|
41
|
+
|
|
42
|
+
Workflow:
|
|
43
|
+
|
|
44
|
+
1. Identify the active page and the target final state: page/workspace, mode, model, duration, attachments, prompt text, and submit state.
|
|
45
|
+
2. Inspect the current state from the latest snapshot, project helper scripts, screenshots, or read-only CDP/Playwright queries.
|
|
46
|
+
3. If a required control is absent or unknown, set it with the smallest scoped action, then wait and verify. Do not stop just because the first state dump lacks a field.
|
|
47
|
+
4. Prefer scoped selectors inside the relevant composer, toolbar, modal, asset picker, or form. Broad text clicks over the whole page are unreliable.
|
|
48
|
+
5. If a helper reports `ok: true` but returns whole-page text, history/sidebar/nav text, unrelated examples, or an unscoped match, treat it as a wrong target and retry with scoped JS, coordinates from a screenshot, or a narrower selector.
|
|
49
|
+
6. For uploads or asset-library selection, verify visible chips, thumbnails, filenames, counters, or previews before submitting.
|
|
50
|
+
7. When multiple browser tabs or CDP targets exist, reconcile the target with the user-visible tab. Bring the controlled page to the front when possible, name the exact page id, URL, and workflow, and save screenshot evidence. Do not report that the user can see an upload if only a background page was verified.
|
|
51
|
+
8. For model/duration/mode controls, distinguish selected state from ads, recommendations, and history labels.
|
|
52
|
+
9. For model selectors, honor the exact requested tier. If the user asked for a non-VIP model and the toolbar says VIP, open the selector and choose a non-VIP option; if none exists, stop with that evidence instead of assuming account membership changes the selected model.
|
|
53
|
+
10. Before irreversible or externally visible actions such as submit, publish, purchase, or account changes, verify all requested state. If verification remains impossible after a bounded set attempt, stop with the exact blocker and evidence.
|
|
54
|
+
11. If the user requested submit/publish/generation, do not finish merely because time or steps are running low while assets, reference media, model choice, or the final submit remain skipped. Continue with a narrower action or stop only for an external blocker such as login, credits, captcha, server/internal error, missing account permission, or a user confirmation dialog.
|
|
55
|
+
|
|
56
|
+
For logged-in browser sessions, preserve the user’s state. Do not open new tabs, navigate to home/history, or close pages unless the current composer is unusable and the user allowed it.
|