@quanta-intellect/vessel-browser 0.1.138 → 0.1.141

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.
@@ -2195,6 +2195,7 @@ let pageDiffActivityThrottleTimer = null;
2195
2195
  let lastPageDiffSignature = "";
2196
2196
  const PAGE_DIFF_ACTIVITY_THROTTLE_MS = 350;
2197
2197
  const PAGE_DIFF_MUTATION_DEBOUNCE_MS = 1200;
2198
+ const CUSTOM_TEXT_FIELD_SELECTOR = '[contenteditable="true"], [role="textbox"], [role="searchbox"], [role="combobox"]';
2198
2199
  function normalizeSignatureText(value) {
2199
2200
  return (value || "").replace(/\s+/g, " ").trim();
2200
2201
  }
@@ -2938,6 +2939,37 @@ function getInputLabelWithSource(el) {
2938
2939
  if (placeholder) return { label: placeholder, source: "placeholder" };
2939
2940
  return {};
2940
2941
  }
2942
+ function getCustomTextFieldLabelWithSource(el) {
2943
+ const ariaLabel = getTrimmedText(el.getAttribute("aria-label"));
2944
+ if (ariaLabel) return { label: ariaLabel, source: "aria-label" };
2945
+ const labelledBy = getNodeTextByIds(el.getAttribute("aria-labelledby"));
2946
+ if (labelledBy) return { label: labelledBy, source: "label" };
2947
+ const placeholder = getTrimmedText(el.getAttribute("placeholder"));
2948
+ if (placeholder) return { label: placeholder, source: "placeholder" };
2949
+ const text = getTrimmedText(el.textContent);
2950
+ if (text) return { label: text, source: "text" };
2951
+ return {};
2952
+ }
2953
+ function isNativeFormField(el) {
2954
+ return el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;
2955
+ }
2956
+ function shouldExposeCustomTextField(el) {
2957
+ if (!(el instanceof HTMLElement) || isNativeFormField(el)) return false;
2958
+ if (!isElementVisible(el) || isElementDisabled(el)) return false;
2959
+ const role = getElementRole(el);
2960
+ return el.isContentEditable || el.getAttribute("contenteditable") === "true" || role === "textbox" || role === "searchbox" || role === "combobox";
2961
+ }
2962
+ function getCustomTextFieldInputType(el) {
2963
+ const role = getElementRole(el);
2964
+ if (role === "searchbox") return "search";
2965
+ if (role === "combobox") return "combobox";
2966
+ if (role === "textbox") return "text";
2967
+ return el.getAttribute("contenteditable") === "true" ? "text" : void 0;
2968
+ }
2969
+ function getCustomTextFieldHasValue(el) {
2970
+ const value = getTrimmedText(el.textContent) || getTrimmedText(el.getAttribute("aria-valuetext")) || getTrimmedText(el.getAttribute("value"));
2971
+ return value ? true : void 0;
2972
+ }
2941
2973
  function getButtonTextWithSource(el) {
2942
2974
  const textContent = getTrimmedText(el.textContent);
2943
2975
  if (textContent) return { text: textContent, source: "text" };
@@ -2986,6 +3018,18 @@ function getElementValue(el) {
2986
3018
  }
2987
3019
  return void 0;
2988
3020
  }
3021
+ function getElementHasValue(el) {
3022
+ if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
3023
+ if (el.type === "password" || el.type === "checkbox" || el.type === "radio") {
3024
+ return void 0;
3025
+ }
3026
+ return getTrimmedText(el.value) ? true : void 0;
3027
+ }
3028
+ if (el instanceof HTMLSelectElement) {
3029
+ return getTrimmedText(el.value) ? true : void 0;
3030
+ }
3031
+ return void 0;
3032
+ }
2989
3033
  function getSelectOptions(el) {
2990
3034
  const options = Array.from(el.options).map((option) => ({
2991
3035
  label: option.textContent?.trim() || option.value.trim(),
@@ -2999,6 +3043,13 @@ function getAriaBoolean(el, attr) {
2999
3043
  if (val === "false") return false;
3000
3044
  return void 0;
3001
3045
  }
3046
+ function getDeepActiveElement() {
3047
+ let active = document.activeElement;
3048
+ while (active instanceof HTMLElement && active.shadowRoot?.activeElement) {
3049
+ active = active.shadowRoot.activeElement;
3050
+ }
3051
+ return active;
3052
+ }
3002
3053
  function buildBaseMetadata(el) {
3003
3054
  return {
3004
3055
  context: getElementContext(el),
@@ -3009,6 +3060,7 @@ function buildBaseMetadata(el) {
3009
3060
  description: getElementDescription(el),
3010
3061
  ...getVisibilityState(el),
3011
3062
  disabled: isElementDisabled(el),
3063
+ focused: getDeepActiveElement() === el || void 0,
3012
3064
  ariaExpanded: getAriaBoolean(el, "aria-expanded"),
3013
3065
  ariaPressed: getAriaBoolean(el, "aria-pressed"),
3014
3066
  ariaSelected: getAriaBoolean(el, "aria-selected")
@@ -3120,6 +3172,7 @@ function extractInteractiveElements() {
3120
3172
  placeholder: element.getAttribute("placeholder") || void 0,
3121
3173
  required: element.hasAttribute("required") || void 0,
3122
3174
  value: getElementValue(element),
3175
+ hasValue: getElementHasValue(element),
3123
3176
  options: element instanceof HTMLSelectElement ? getSelectOptions(element) : void 0,
3124
3177
  ...buildBaseMetadata(input),
3125
3178
  role,
@@ -3128,6 +3181,20 @@ function extractInteractiveElements() {
3128
3181
  ...getFieldMetadata(element)
3129
3182
  });
3130
3183
  });
3184
+ deepQuerySelectorAll(CUSTOM_TEXT_FIELD_SELECTOR).forEach((field) => {
3185
+ if (!shouldExposeCustomTextField(field)) return;
3186
+ const label = getCustomTextFieldLabelWithSource(field);
3187
+ const role = getElementRole(field);
3188
+ elements.push({
3189
+ type: "input",
3190
+ label: label.label?.slice(0, MAX_LABEL_LENGTH),
3191
+ labelSource: label.source,
3192
+ inputType: getCustomTextFieldInputType(field),
3193
+ hasValue: getCustomTextFieldHasValue(field),
3194
+ ...buildBaseMetadata(field),
3195
+ role
3196
+ });
3197
+ });
3131
3198
  return elements;
3132
3199
  }
3133
3200
  function extractForms() {
@@ -3143,23 +3210,28 @@ function extractForms() {
3143
3210
  const form = formEl;
3144
3211
  const fields = [];
3145
3212
  form.querySelectorAll(
3146
- "input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='image']), select, textarea"
3213
+ `input:not([type='hidden']):not([type='submit']):not([type='button']):not([type='image']), select, textarea, ${CUSTOM_TEXT_FIELD_SELECTOR}`
3147
3214
  ).forEach((input) => {
3215
+ if (!isNativeFormField(input) && !shouldExposeCustomTextField(input)) {
3216
+ return;
3217
+ }
3148
3218
  const element = input;
3149
3219
  const tag = input.tagName.toLowerCase();
3150
3220
  const label = getInputLabelWithSource(element);
3221
+ const customLabel = isNativeFormField(input) ? {} : getCustomTextFieldLabelWithSource(input);
3151
3222
  const role = getElementRole(input);
3152
3223
  const radioText = role === "radio" || element instanceof HTMLInputElement && element.type === "radio" ? getTrimmedText(
3153
3224
  element.getAttribute("value") || element.getAttribute("aria-label") || label.label
3154
3225
  ) : void 0;
3155
3226
  fields.push({
3156
3227
  type: tag === "select" ? "select" : tag === "textarea" ? "textarea" : "input",
3157
- label: label.label?.slice(0, MAX_LABEL_LENGTH),
3158
- labelSource: label.source,
3159
- inputType: element.getAttribute("type") || void 0,
3228
+ label: (label.label || customLabel.label)?.slice(0, MAX_LABEL_LENGTH),
3229
+ labelSource: label.source || customLabel.source,
3230
+ inputType: element.getAttribute("type") || getCustomTextFieldInputType(input) || void 0,
3160
3231
  placeholder: element.getAttribute("placeholder") || void 0,
3161
3232
  required: element.hasAttribute("required") || void 0,
3162
3233
  value: getElementValue(element),
3234
+ hasValue: isNativeFormField(input) ? getElementHasValue(element) : getCustomTextFieldHasValue(input),
3163
3235
  options: element instanceof HTMLSelectElement ? getSelectOptions(element) : void 0,
3164
3236
  ...buildBaseMetadata(input),
3165
3237
  role,
@@ -3763,6 +3763,17 @@
3763
3763
  background: var(--surface-active);
3764
3764
  }
3765
3765
 
3766
+ .tool-chip-failed {
3767
+ border-left-color: color-mix(in srgb, var(--error) 60%, transparent);
3768
+ background: color-mix(in srgb, var(--error) 8%, var(--surface-hover));
3769
+ }
3770
+
3771
+ .tool-chip-failed .tool-chip-icon,
3772
+ .tool-chip-failed .tool-chip-args {
3773
+ color: color-mix(in srgb, var(--error) 80%, var(--text-muted));
3774
+ opacity: 1;
3775
+ }
3776
+
3766
3777
  .tool-chip-icon {
3767
3778
  flex-shrink: 0;
3768
3779
  width: 16px;
@@ -6158,6 +6158,14 @@ function getSkillSlashSuggestions(value, kits, limit = 6) {
6158
6158
  return left.kit.name.localeCompare(right.kit.name);
6159
6159
  }).slice(0, limit).map(({ kit }) => kit);
6160
6160
  }
6161
+ function canRunWithoutSlashTask(kit, inputKey) {
6162
+ if (inputKey !== "task") return false;
6163
+ const taskPlaceholder = `{{${inputKey}}}`;
6164
+ const placeholderIndex = kit.promptTemplate.indexOf(taskPlaceholder);
6165
+ if (placeholderIndex <= 0) return false;
6166
+ const leadingInstructions = kit.promptTemplate.slice(0, placeholderIndex).replace(/\{\{\w+\}\}/g, "").replace(/\btask\s*:?\s*$/i, "").trim();
6167
+ return /[a-z0-9]/i.test(leadingInstructions);
6168
+ }
6161
6169
  function buildSlashSkillValues(kit, task) {
6162
6170
  const values = {};
6163
6171
  for (const input of kit.inputs) {
@@ -6167,12 +6175,15 @@ function buildSlashSkillValues(kit, task) {
6167
6175
  if (targetInput) {
6168
6176
  values[targetInput.key] = task;
6169
6177
  }
6170
- const missingLabels = kit.inputs.filter((input) => input.required).filter((input) => !values[input.key]?.trim()).map((input) => input.label);
6178
+ const missingLabels = kit.inputs.filter((input) => input.required).filter((input) => {
6179
+ if (values[input.key]?.trim()) return false;
6180
+ return !canRunWithoutSlashTask(kit, input.key);
6181
+ }).map((input) => input.label);
6171
6182
  return { values, missingLabels };
6172
6183
  }
6173
6184
  function renderKitPrompt(kit, values) {
6174
6185
  for (const input of kit.inputs) {
6175
- if (input.required && !values[input.key]?.trim()) {
6186
+ if (input.required && !values[input.key]?.trim() && !canRunWithoutSlashTask(kit, input.key)) {
6176
6187
  logger$1.warn(
6177
6188
  `Required field "${input.key}" is empty for kit "${kit.id}".`
6178
6189
  );
@@ -7594,8 +7605,9 @@ const TOOL_ICONS = {
7594
7605
  function renderToolChip(name, args) {
7595
7606
  const icon = TOOL_ICONS[name] || "⚙";
7596
7607
  const displayName = name.replace(/_/g, " ");
7608
+ const failed = args.trim().startsWith("⚠");
7597
7609
  const argsHtml = args ? `<span class="tool-chip-args">${escapeHtml(args.length > MAX_PREVIEW_TEXT ? args.slice(0, TRUNCATE_KEEP) + "..." : args)}</span>` : "";
7598
- return `<div class="tool-chip"><span class="tool-chip-icon">${icon}</span><span class="tool-chip-name">${escapeHtml(displayName)}</span>${argsHtml}</div>`;
7610
+ return `<div class="tool-chip${failed ? " tool-chip-failed" : ""}"><span class="tool-chip-icon">${icon}</span><span class="tool-chip-name">${escapeHtml(displayName)}</span>${argsHtml}</div>`;
7599
7611
  }
7600
7612
  function renderMarkdown(source) {
7601
7613
  const codeBlocks = [];
@@ -10089,11 +10101,6 @@ ${contextBlock}` : contextBlock);
10089
10101
  const kits = [...BUNDLED_KITS, ...installedKits];
10090
10102
  const invocation = parseSkillSlashInvocation(prompt, kits);
10091
10103
  if (invocation) {
10092
- if (!invocation.task) {
10093
- const [command] = prompt.split(/\s+/);
10094
- setChatCommandError(`Add instructions after ${command}.`);
10095
- return;
10096
- }
10097
10104
  const {
10098
10105
  values,
10099
10106
  missingLabels
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; base-uri 'none'; object-src 'none'; frame-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self' data:; form-action 'self';" />
7
7
  <title>Vessel</title>
8
- <script type="module" crossorigin src="./assets/index-Cjl9fej2.js"></script>
9
- <link rel="stylesheet" crossorigin href="./assets/index-DMcjRzqj.css">
8
+ <script type="module" crossorigin src="./assets/index-l3uzsLbO.js"></script>
9
+ <link rel="stylesheet" crossorigin href="./assets/index-T8vlGvDJ.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@quanta-intellect/vessel-browser",
3
3
  "mcpName": "io.github.unmodeled-tyler/vessel-browser",
4
- "version": "0.1.138",
4
+ "version": "0.1.141",
5
5
  "description": "AI-native web browser runtime for autonomous agents with human supervision",
6
6
  "main": "./out/main/index.js",
7
7
  "bin": {
@@ -70,7 +70,7 @@
70
70
  },
71
71
  "repository": {
72
72
  "type": "git",
73
- "url": "https://github.com/unmodeled-tyler/quanta-vessel-browser.git"
73
+ "url": "https://github.com/unmodeled-tyler/vessel-browser.git"
74
74
  },
75
75
  "engines": {
76
76
  "node": ">=22.12.0",