diff --git a/__tests__/manifests/manifest-validation.test.ts b/__tests__/manifests/manifest-validation.test.ts index 61ba6b9f5d..89da1649bc 100644 --- a/__tests__/manifests/manifest-validation.test.ts +++ b/__tests__/manifests/manifest-validation.test.ts @@ -33,10 +33,6 @@ describe("validateSetupEntry", () => { "a placeholder namespace the host does not expose", { setup: createSetup({ prompt: "Use {{secrets.githubToken}}." }) }, ], - [ - "an assisted message on an entry that sends a request", - { setup: createSetup({ message: "Finish setup with the agent." }) }, - ], [ // The host reads one trigger kind to build the request, so a second one // would be silently dropped rather than refused. diff --git a/package-lock.json b/package-lock.json index 4d1609cd8e..58f96bf590 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@heroui/react": "2.8.10", "@microlink/react-json-view": "1.31.20", "@monaco-editor/react": "4.7.0", - "@openhands/extensions": "0.12.0", + "@openhands/extensions": "0.13.0", "@openhands/typescript-client": "1.36.1", "@react-router/node": "7.17.0", "@react-router/serve": "7.17.0", @@ -4167,9 +4167,9 @@ "license": "MIT" }, "node_modules/@openhands/extensions": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@openhands/extensions/-/extensions-0.12.0.tgz", - "integrity": "sha512-S7s4hWcvrOfxUT4PMv/Sz5zRNh1tZEF5wQoWo/4TQKJFIgzy9trQJ0xz9UOZFgf4nh0juJEM2jwG9HMo8pmYfA==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@openhands/extensions/-/extensions-0.13.0.tgz", + "integrity": "sha512-+pHEyd2rOdFuLR35yg+M33nQbaDAcIZRWe+yErSkHmTn3wM1hpkgQmG1E0jBgkOTUXMScxDMJAHChrna2GFgHg==", "license": "MIT", "engines": { "node": ">=18.20.0" diff --git a/package.json b/package.json index 3e1d797f11..bd122550ec 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@heroui/react": "2.8.10", "@microlink/react-json-view": "1.31.20", "@monaco-editor/react": "4.7.0", - "@openhands/extensions": "0.12.0", + "@openhands/extensions": "0.13.0", "@openhands/typescript-client": "1.36.1", "@react-router/node": "7.17.0", "@react-router/serve": "7.17.0", diff --git a/src/manifests/manifest-validation.ts b/src/manifests/manifest-validation.ts index f0f6a4190b..11df8f7e47 100644 --- a/src/manifests/manifest-validation.ts +++ b/src/manifests/manifest-validation.ts @@ -41,7 +41,7 @@ const TRIGGER_KINDS = ["cron", "event"] as const; const CONSTRAINT_FORMATS = ["safeExpressionLiteral"] as const; /** Setup context only, so this can never become a channel for runtime instructions. */ -const MAX_ASSISTED_MESSAGE_LENGTH = 2000; +const MAX_MESSAGE_LENGTH = 2000; export interface SetupValidationResult { valid: boolean; @@ -310,6 +310,17 @@ function hasRepoPicker(form: unknown): boolean { ); } +function checkMessage(check: SetupChecker, message: unknown): void { + if (check.templateCopy(message, "setup.message")) { + if ((message as string).length > MAX_MESSAGE_LENGTH) { + check.fail( + "setup.message", + `must be at most ${MAX_MESSAGE_LENGTH} characters`, + ); + } + } +} + function checkMode(check: SetupChecker, setup: Rec, kinds: string[]): void { if (!isOneOf(setup.mode, SETUP_MODES)) { check.fail("setup.mode", "is not a supported mode"); @@ -318,12 +329,9 @@ function checkMode(check: SetupChecker, setup: Rec, kinds: string[]): void { if (setup.mode === "direct") { check.templateValue(setup.prompt, "setup.prompt"); - check.absent( - setup, - "message", - "setup", - "is only allowed for assisted setup", - ); + // Optional here: it seeds the fallback conversation offered when the + // deployment cannot run the direct path. + if (setup.message !== undefined) checkMessage(check, setup.message); // The derivation reads a single trigger kind, and an event trigger takes // its source from the repository field's provider. @@ -349,14 +357,7 @@ function checkMode(check: SetupChecker, setup: Rec, kinds: string[]): void { return; } - if (check.templateCopy(setup.message, "setup.message")) { - if ((setup.message as string).length > MAX_ASSISTED_MESSAGE_LENGTH) { - check.fail( - "setup.message", - `must be at most ${MAX_ASSISTED_MESSAGE_LENGTH} characters`, - ); - } - } + checkMessage(check, setup.message); check.absent(setup, "prompt", "setup", "is only allowed for direct setup"); check.absent(setup, "filter", "setup", "is only allowed for direct setup"); } diff --git a/src/manifests/types.ts b/src/manifests/types.ts index e5f670364d..4580b9fc4c 100644 --- a/src/manifests/types.ts +++ b/src/manifests/types.ts @@ -75,7 +75,11 @@ export interface SetupBlock { prompt?: string; /** direct only, event trigger only. Which delivered events belong to it. */ filter?: string; - /** assisted only. Setup context for the conversation that finishes setup. */ + /** + * Setup context for the conversation that finishes setup. Required for + * assisted mode. Optional for direct mode, where it seeds the fallback + * conversation offered when the deployment cannot run the direct path. + */ message?: string; }