@meistrari/chat-nuxt 4.4.0 → 4.4.1-rc.1

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/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "4.4.0-rc.10",
4
+ "version": "4.4.1-rc.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -8,6 +8,8 @@ type __VLS_Props = {
8
8
  conversationId?: string | null;
9
9
  conversationEvents?: readonly ConversationEvent[] | null;
10
10
  composerDisabled?: boolean;
11
+ /** Keeps typing available while sending waits for the agent protocol to be confirmed. */
12
+ sendBlocked?: boolean;
11
13
  initialConversationId?: string | null;
12
14
  hideSidebar?: boolean;
13
15
  sidebar?: ChatSidebarConfig;
@@ -47,6 +47,7 @@ const props = defineProps({
47
47
  conversationId: { type: [String, null], required: false },
48
48
  conversationEvents: { type: [Array, null], required: false },
49
49
  composerDisabled: { type: Boolean, required: false },
50
+ sendBlocked: { type: Boolean, required: false },
50
51
  initialConversationId: { type: [String, null], required: false, default: null },
51
52
  hideSidebar: { type: Boolean, required: false, default: false },
52
53
  sidebar: { type: Object, required: false },
@@ -567,6 +568,8 @@ async function handleSend(content, files, agentInputs) {
567
568
  restoreQueuedSend({ content: prepared?.retryContent?.() ?? content, files, agentInputs: resolvedAgentInputs });
568
569
  }
569
570
  async function dispatchMessage(outbound, files, resolvedAgentInputs) {
571
+ if (props.sendBlocked)
572
+ return false;
570
573
  let claimedWakeSessionId;
571
574
  const sent = await sendMessage(outbound, files.length > 0 ? files : void 0, resolvedAgentInputs, async (signal) => {
572
575
  await nativeModels?.ensureReady();
@@ -1111,6 +1114,7 @@ async function handleDelete() {
1111
1114
  v-model:selected-reasoning-effort="selectedReasoningEffort"
1112
1115
  :loading="composerLocked"
1113
1116
  :disabled="composerDisabled"
1117
+ :send-blocked="sendBlocked"
1114
1118
  :creator-name="currentUser?.name ?? void 0"
1115
1119
  :creator-email="
1116
1120
  currentConversation?.createdBy ?? currentUser?.email ?? void 0
@@ -1159,6 +1163,7 @@ async function handleDelete() {
1159
1163
  class="!p-0"
1160
1164
  :loading="composerLocked"
1161
1165
  :disabled="composerDisabled"
1166
+ :send-blocked="sendBlocked"
1162
1167
  :creator-name="currentUser?.name ?? void 0"
1163
1168
  :creator-email="
1164
1169
  currentConversation?.createdBy ?? currentUser?.email ?? void 0
@@ -1338,6 +1343,7 @@ async function handleDelete() {
1338
1343
  v-model:selected-reasoning-effort="selectedReasoningEffort"
1339
1344
  :loading="composerLocked"
1340
1345
  :disabled="composerDisabled"
1346
+ :send-blocked="sendBlocked"
1341
1347
  :creator-name="currentUser?.name ?? void 0"
1342
1348
  :creator-email="
1343
1349
  currentConversation?.createdBy ?? currentUser?.email ?? void 0
@@ -1389,6 +1395,7 @@ async function handleDelete() {
1389
1395
  class="!p-0"
1390
1396
  :loading="composerLocked"
1391
1397
  :disabled="composerDisabled"
1398
+ :send-blocked="sendBlocked"
1392
1399
  :creator-name="currentUser?.name ?? void 0"
1393
1400
  :creator-email="
1394
1401
  currentConversation?.createdBy ?? currentUser?.email ?? void 0
@@ -8,6 +8,8 @@ type __VLS_Props = {
8
8
  conversationId?: string | null;
9
9
  conversationEvents?: readonly ConversationEvent[] | null;
10
10
  composerDisabled?: boolean;
11
+ /** Keeps typing available while sending waits for the agent protocol to be confirmed. */
12
+ sendBlocked?: boolean;
11
13
  initialConversationId?: string | null;
12
14
  hideSidebar?: boolean;
13
15
  sidebar?: ChatSidebarConfig;
@@ -61,7 +61,7 @@ function selectConversation(value) {
61
61
  selectedConversationId.value = value;
62
62
  emit("update:conversationId", value);
63
63
  }
64
- const { harness, loading: harnessLoading, error: harnessError, retry: retryHarness } = useAgentHarness({
64
+ const { harness, loading: harnessLoading, confirmed: harnessConfirmed, error: harnessError, retry: retryHarness } = useAgentHarness({
65
65
  workspaceId,
66
66
  agentId: normalizedTelaAgentId,
67
67
  branch: telaAgentBranch,
@@ -157,7 +157,8 @@ provideChatMessageTerminal((payload) => {
157
157
  :sync-browser-state="syncBrowserState"
158
158
  :conversation-id="selectedConversationId"
159
159
  :conversation-events="conversationEvents"
160
- :composer-disabled="composerDisabled || harnessLoading || !!harnessError"
160
+ :composer-disabled="composerDisabled || !!harnessError"
161
+ :send-blocked="!harnessConfirmed"
161
162
  :initial-conversation-id="selectedConversationId"
162
163
  :hide-sidebar="hideSidebar"
163
164
  :sidebar="sidebar"
@@ -1,5 +1,14 @@
1
1
  import { type Ref } from 'vue';
2
- /** Resolve the executable agent before mounting either protocol-specific UI. */
2
+ /**
3
+ * Resolve the executable agent without holding the page on the network round trip.
4
+ *
5
+ * The UI mounts right away with the harness last confirmed for this agent in this
6
+ * browser, or Claude when there is none, so the Claude wake starts with the page
7
+ * and the user can type. `confirmed` only turns true once the server answered for
8
+ * the current agent and conversation; sending must wait for it, because sending
9
+ * picks the stream protocol and a remembered harness can be stale. A failed
10
+ * lookup never keeps a guess: it clears the selection and reports the error.
11
+ */
3
12
  export declare function useAgentHarness(input: {
4
13
  workspaceId: Ref<string>;
5
14
  agentId: Ref<string | null>;
@@ -10,6 +19,7 @@ export declare function useAgentHarness(input: {
10
19
  }): {
11
20
  harness: any;
12
21
  loading: Ref<boolean, boolean>;
22
+ confirmed: Ref<boolean, boolean>;
13
23
  error: Ref<string | null, string | null>;
14
24
  retry: () => void;
15
25
  };
@@ -1,6 +1,26 @@
1
1
  import { ref, watch } from "vue";
2
+ const REMEMBERED_HARNESS_PREFIX = "chat-nuxt:agent-harness:v1:";
3
+ function rememberedHarnessKey(workspaceId, agentId, branch) {
4
+ return `${REMEMBERED_HARNESS_PREFIX}${JSON.stringify([workspaceId, agentId ?? "", branch ?? ""])}`;
5
+ }
6
+ function readRememberedHarness(key) {
7
+ try {
8
+ const value = globalThis.localStorage?.getItem(key);
9
+ return value === "claude" || value === "lightweight" ? value : null;
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+ function rememberHarness(key, harness) {
15
+ try {
16
+ globalThis.localStorage?.setItem(key, harness);
17
+ } catch {
18
+ }
19
+ }
2
20
  export function useAgentHarness(input) {
3
21
  const harness = ref(null);
22
+ const trusted = ref(false);
23
+ const confirmed = ref(false);
4
24
  const loading = ref(false);
5
25
  const error = ref(null);
6
26
  const retryCount = ref(0);
@@ -11,8 +31,12 @@ export function useAgentHarness(input) {
11
31
  }
12
32
  }, { flush: "sync" });
13
33
  watch([input.workspaceId, input.agentId, input.branch, input.conversationId ?? ref(null), input.conversationScope ?? ref(null), retryCount], async ([workspaceId, agentId, branch, conversationId, conversationScope], previous, onCleanup) => {
34
+ const rememberKey = rememberedHarnessKey(workspaceId, agentId, branch);
14
35
  if (workspaceId !== previous?.[0] || agentId !== previous?.[1] || branch !== previous?.[2] || conversationScope !== previous?.[4]) {
15
- harness.value = null;
36
+ const remembered = workspaceId ? readRememberedHarness(rememberKey) : null;
37
+ harness.value = workspaceId ? remembered ?? "claude" : null;
38
+ trusted.value = remembered !== null;
39
+ confirmed.value = false;
16
40
  }
17
41
  error.value = null;
18
42
  loading.value = false;
@@ -40,16 +64,22 @@ export function useAgentHarness(input) {
40
64
  throw new Error("Unsupported or missing agent harness");
41
65
  }
42
66
  harness.value = result.harness;
67
+ trusted.value = true;
68
+ confirmed.value = true;
69
+ if (!conversationId)
70
+ rememberHarness(rememberKey, result.harness);
43
71
  } catch {
44
72
  if (!controller.signal.aborted) {
45
73
  error.value = "N\xE3o foi poss\xEDvel carregar a configura\xE7\xE3o do agente.";
74
+ if (!trusted.value)
75
+ harness.value = null;
46
76
  }
47
77
  } finally {
48
78
  if (!controller.signal.aborted)
49
79
  loading.value = false;
50
80
  }
51
81
  }, { immediate: true, flush: "sync" });
52
- return { harness, loading, error, retry: () => {
82
+ return { harness, loading, confirmed, error, retry: () => {
53
83
  retryCount.value++;
54
84
  } };
55
85
  }
@@ -5,7 +5,7 @@ export declare const DEFAULT_CHAT_LOADING_MESSAGES: readonly ["Analisando sua pe
5
5
  * than as a loading placeholder. The user message is already delivered at this
6
6
  * point, so the wait belongs to the answer.
7
7
  */
8
- export declare const CHAT_PREPARING_STEPS: readonly ["Preparando a resposta", "Analisando a mensagem"];
8
+ export declare const CHAT_PREPARING_STEPS: readonly ["Preparando o assistente", "Analisando sua pergunta"];
9
9
  /**
10
10
  * The first delay is measured from the send, every later one from the step
11
11
  * before it — so the sequence paces itself instead of racing a shared clock.
@@ -13,8 +13,8 @@ export const DEFAULT_CHAT_LOADING_MESSAGES = [
13
13
  "Estruturando resposta"
14
14
  ];
15
15
  export const CHAT_PREPARING_STEPS = [
16
- "Preparando a resposta",
17
- "Analisando a mensagem"
16
+ "Preparando o assistente",
17
+ "Analisando sua pergunta"
18
18
  ];
19
19
  export const CHAT_PREPARING_STEP_DELAYS_MS = [2500, 4e3];
20
20
  export const CHAT_PREPARING_STEP_JITTER_MS = 500;
@@ -2,7 +2,7 @@ const URL_CHAR_CLASS = "[^\\s\"'<>`\\[\\]()*_~]+";
2
2
  export const VAULT_REGEX = /(?<!\]\()vault:\/\/[^\s"'<>`\[\]()*_~]+/gi;
3
3
  export const URL_REGEX = /(?<!\]\(|<)https?:\/\/[^\s"'<>`\[\]()*_~]+/gi;
4
4
  export const VAULT_PATTERN = new RegExp(`vault://${URL_CHAR_CLASS}`, "gi");
5
- export const VAULT_PERMALINK_PATTERN = new RegExp(`https://(?:[a-z0-9-]+\\.)*(?:vault|api)\\.tela(?:staging)?\\.com/(?:[a-z0-9_-]+/)*permalinks/${URL_CHAR_CLASS}`, "gi");
5
+ export const VAULT_PERMALINK_PATTERN = new RegExp(`https://(?:[a-z0-9-]+\\.)*(?:vault|vault-api|api)\\.tela(?:staging)?\\.com/(?:[a-z0-9_-]+/)*permalinks/${URL_CHAR_CLASS}`, "gi");
6
6
  export function stripTrailingPunctuation(url) {
7
7
  return url.replace(/[.,:;!?]+$/, "");
8
8
  }
@@ -22,10 +22,10 @@ export function isVaultPermalink(url) {
22
22
  try {
23
23
  const parsed = new URL(url);
24
24
  const host = parsed.host;
25
- const isTelaHost = host === "vault.tela.com" || host.endsWith(".vault.tela.com") || host === "api.tela.com" || host.endsWith(".api.tela.com") || host === "api.telastaging.com" || host.endsWith(".api.telastaging.com");
25
+ const isTelaHost = host === "vault.tela.com" || host.endsWith(".vault.tela.com") || host === "vault-api.tela.com" || host === "vault-api.telastaging.com" || host === "api.tela.com" || host.endsWith(".api.tela.com") || host === "api.telastaging.com" || host.endsWith(".api.telastaging.com");
26
26
  return isTelaHost && /(?:^|\/)permalinks\//.test(parsed.pathname);
27
27
  } catch {
28
- return /^https:\/\/(?:[a-z0-9-]+\.)*(?:vault|api)\.tela(?:staging)?\.com\/(?:[a-z0-9_-]+\/)*permalinks\//.test(url);
28
+ return /^https:\/\/(?:[a-z0-9-]+\.)*(?:vault|vault-api|api)\.tela(?:staging)?\.com\/(?:[a-z0-9_-]+\/)*permalinks\//.test(url);
29
29
  }
30
30
  }
31
31
  export function parseVaultHashParams(url) {
@@ -20,6 +20,7 @@ type __VLS_Props = {
20
20
  agentSkills?: readonly TelaAgentSkill[] | null;
21
21
  agentContextFiles?: readonly TelaAgentContextFile[] | null;
22
22
  showCancelButton?: boolean;
23
+ sendBlocked?: boolean;
23
24
  };
24
25
  declare function addFiles(files: File[], inputName?: string): void;
25
26
  declare function restoreDraft(draft: {
@@ -24,7 +24,8 @@ const props = defineProps({
24
24
  agentInputSchemaLoading: { type: Boolean, required: false },
25
25
  agentSkills: { type: [Array, null], required: false },
26
26
  agentContextFiles: { type: [Array, null], required: false },
27
- showCancelButton: { type: Boolean, required: false }
27
+ showCancelButton: { type: Boolean, required: false },
28
+ sendBlocked: { type: Boolean, required: false }
28
29
  });
29
30
  const emit = defineEmits(["send", "cancel"]);
30
31
  const selectedModel = defineModel("selectedModel", { type: [String, null], ...{ default: null } });
@@ -125,7 +126,7 @@ const mentionAutocomplete = useMentionAutocomplete({
125
126
  onInsert: handleInput
126
127
  });
127
128
  const canSend = computed(
128
- () => content.value.trim().length > 0 && !isLocked.value && !uploading.value && !props.agentInputSchemaLoading && agentInputReadiness.value.ready
129
+ () => content.value.trim().length > 0 && !isLocked.value && !props.sendBlocked && !uploading.value && !props.agentInputSchemaLoading && agentInputReadiness.value.ready
129
130
  );
130
131
  const submitStatus = computed(() => {
131
132
  if (uploading.value)
@@ -20,6 +20,7 @@ type __VLS_Props = {
20
20
  agentSkills?: readonly TelaAgentSkill[] | null;
21
21
  agentContextFiles?: readonly TelaAgentContextFile[] | null;
22
22
  showCancelButton?: boolean;
23
+ sendBlocked?: boolean;
23
24
  };
24
25
  declare function addFiles(files: File[], inputName?: string): void;
25
26
  declare function restoreDraft(draft: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "4.4.0",
3
+ "version": "4.4.1-rc.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {