@7n/tauri-components 0.10.1 → 0.11.0

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +1 -1
  3. package/src/components/AgentDialog.vue +21 -46
  4. package/src/components/AuditDialog.vue +9 -17
  5. package/src/components/BaseDialog.vue +2 -6
  6. package/src/components/DialogActions.vue +2 -3
  7. package/src/components/RequestView.vue +2 -3
  8. package/src/components/StatePill.vue +1 -1
  9. package/src/components/docs/AgentDialog.md +4 -4
  10. package/src/components/docs/index.md +4 -4
  11. package/src/components/index.js +2 -2
  12. package/src/components/status.js +1 -1
  13. package/src/core/acp-agent-presets.js +2 -2
  14. package/src/core/acp-agent.js +21 -15
  15. package/src/core/acp-kit.js +79 -30
  16. package/src/core/dispatch.js +3 -3
  17. package/src/core/docs/acp-agent.md +4 -4
  18. package/src/core/docs/acp-kit.md +4 -4
  19. package/src/core/manifest.js +10 -8
  20. package/src/docs/index.md +4 -4
  21. package/src/index.js +3 -8
  22. package/src/testing/quasar.js +2 -2
  23. package/src/vue/docs/index.md +4 -4
  24. package/src/vue/docs/journal-store-tauri.md +4 -4
  25. package/src/vue/docs/use-acp-agent.md +4 -4
  26. package/src/vue/index.js +4 -6
  27. package/src/vue/journal-store-tauri.js +2 -2
  28. package/src/vue/use-acp-agent.js +22 -14
  29. package/src/vue/use-updater.js +4 -6
  30. package/types/core/dispatch.d.ts +5 -2
  31. package/types/core/manifest.d.ts +14 -9
  32. package/types/core/scope.d.ts +24 -11
  33. package/types/core/tools.d.ts +1 -1
  34. package/types/index.d.ts +4 -9
  35. package/src/core/agent-handler.js +0 -171
  36. package/src/core/agent-kit.js +0 -43
  37. package/src/core/docs/agent-handler.md +0 -31
  38. package/src/core/docs/agent-kit.md +0 -25
  39. package/src/core/docs/llm.md +0 -29
  40. package/src/core/docs/omlx-models.md +0 -30
  41. package/src/core/docs/resolve-omlx-base-url.md +0 -40
  42. package/src/core/llm.js +0 -90
  43. package/src/core/omlx-models.js +0 -34
  44. package/src/core/resolve-omlx-base-url.js +0 -84
  45. package/src/vue/docs/use-agent.md +0 -29
  46. package/src/vue/docs/use-omlx.md +0 -30
  47. package/src/vue/use-agent.js +0 -60
  48. package/src/vue/use-omlx.js +0 -102
  49. package/types/core/agent-handler.d.ts +0 -43
  50. package/types/core/agent-kit.d.ts +0 -30
  51. package/types/core/llm.d.ts +0 -52
  52. package/types/core/omlx-models.d.ts +0 -17
  53. package/types/core/resolve-omlx-base-url.d.ts +0 -42
@@ -1,171 +0,0 @@
1
- import { runAgent } from './llm.js'
2
-
3
- // Agent-gateway handlers — start / resume / approve the agent loop on behalf of a
4
- // caller (human via UI or agent via MCP). They own the journal lifecycle and
5
- // return a structured result contract.
6
- //
7
- // All collaborators are injected (no module-level singletons): `dispatch`,
8
- // `journal`, the scoped `tools` manifest, the `gate` decision fn, `buildSystem`
9
- // (turns optional grounding context into a system prompt) and `grounding` (an
10
- // optional read tool whose output grounds the prompt). createAgentKit wires
11
- // these from an app catalog; tests pass fakes.
12
- //
13
- // Trust: destructive tool calls from an agent pause as `needs_approval` (the
14
- // loop never executes them); a human approves later via handleApprove.
15
-
16
- const QUESTION_RE = /\?\s*$/
17
-
18
- /**
19
- * @param {string} text candidate text
20
- * @returns {boolean} true when text ends with a question mark
21
- */
22
- function isQuestion(text) {
23
- return typeof text === 'string' && QUESTION_RE.test(text.trim())
24
- }
25
-
26
- /**
27
- * Derive the structured result fields from a runAgent result.
28
- * @param {object} result runAgent result
29
- * @returns {{ status: string, summary: string|null, question: string|null, pendingApproval: object|null }} fields
30
- */
31
- function finalize(result) {
32
- const question = isQuestion(result.content) ? result.content : null
33
- let status = 'done'
34
- if (result.stopped === 'needs_approval') status = 'needs_approval'
35
- else if (result.stopped === 'max_steps') status = 'partial'
36
- else if (question) status = 'needs_clarification'
37
- return {
38
- status,
39
- summary: question ? null : (result.content || null),
40
- question,
41
- pendingApproval: result.pendingApproval ?? null,
42
- }
43
- }
44
-
45
- /**
46
- * Run the loop (fresh or resumed), persist to the journal, return the envelope.
47
- * @param {{ requestId: string, runArgs: object, baseActions: object[], journal: object }} ctx run context
48
- * @returns {Promise<object>} structured result envelope
49
- */
50
- async function runAndJournal({ requestId, runArgs, baseActions, journal }) {
51
- let result
52
- try {
53
- result = await runAgent(runArgs)
54
- }
55
- catch (error) {
56
- await journal.update(requestId, { status: 'failed', error: String(error?.message ?? error) })
57
- return { requestId, status: 'failed', summary: null, actions: baseActions, question: null, pendingApproval: null }
58
- }
59
-
60
- const fields = finalize(result)
61
- const actions = [...baseActions, ...result.trace]
62
- await journal.update(requestId, { ...fields, messages: result.messages, actions })
63
- return { requestId, ...fields, actions }
64
- }
65
-
66
- /**
67
- * Fetch optional grounding context via a read tool; {} when not configured / on failure.
68
- * @param {Function} dispatch tool dispatcher
69
- * @param {{ tool: string, key?: string, fallback?: unknown }} [grounding] grounding config
70
- * @returns {Promise<object>} context object keyed by grounding.key (default "grounding")
71
- */
72
- async function groundingContext(dispatch, grounding) {
73
- if (!grounding?.tool) return {}
74
- const key = grounding.key ?? 'grounding'
75
- const fallback = grounding.fallback ?? []
76
- try {
77
- const res = await dispatch(grounding.tool, {})
78
- return { [key]: res?.ok ? res.output : fallback }
79
- }
80
- catch {
81
- return { [key]: fallback }
82
- }
83
- }
84
-
85
- /**
86
- * Start a new agent request.
87
- * @param {{ intent: string, actor: object, chat: Function, dispatch: Function, journal: object, tools: object[], gate: Function, buildSystem?: Function, grounding?: object }} opts request parameters
88
- * @returns {Promise<object>} structured result envelope
89
- */
90
- export async function handleRequest({ intent, actor, chat, dispatch, journal, tools, gate, buildSystem, grounding }) {
91
- const id = await journal.create({ intent, actor })
92
- await journal.update(id, { status: 'running' })
93
- const ctx = await groundingContext(dispatch, grounding)
94
- const system = buildSystem ? buildSystem(ctx) : undefined
95
- return runAndJournal({
96
- requestId: id,
97
- baseActions: [],
98
- journal,
99
- runArgs: { system, prompt: intent, chat, dispatch, tools, gate },
100
- })
101
- }
102
-
103
- /**
104
- * Resume a conversation with a follow-up / clarification answer.
105
- * @param {{ requestId: string, message: string, actor: object, chat: Function, dispatch: Function, journal: object, tools: object[], gate: Function }} opts resume parameters
106
- * @returns {Promise<object>} updated result envelope
107
- */
108
- export async function handleRespond({ requestId, message, chat, dispatch, journal, tools, gate }) {
109
- let record
110
- try {
111
- record = await journal.load(requestId)
112
- }
113
- catch {
114
- return { requestId, status: 'failed', summary: null, actions: [], question: 'Request not found.', pendingApproval: null }
115
- }
116
-
117
- if (record.status === 'running' || !Array.isArray(record.messages) || record.messages.length === 0) {
118
- return { requestId, status: record.status, summary: record.summary, actions: record.actions, question: null, pendingApproval: record.pendingApproval ?? null }
119
- }
120
-
121
- await journal.update(requestId, { status: 'running' })
122
- return runAndJournal({
123
- requestId,
124
- baseActions: record.actions ?? [],
125
- journal,
126
- runArgs: { messages: [...record.messages, { role: 'user', content: message }], chat, dispatch, tools, gate },
127
- })
128
- }
129
-
130
- /**
131
- * Approve (or reject) a pending destructive action. Executes with the approver's
132
- * (human) authority via the injected dispatch — no gate.
133
- * @param {{ requestId: string, approve: boolean, dispatch: Function, journal: object }} opts approval parameters
134
- * @returns {Promise<object>} updated result envelope
135
- */
136
- export async function handleApprove({ requestId, approve, dispatch, journal }) {
137
- let record
138
- try {
139
- record = await journal.load(requestId)
140
- }
141
- catch {
142
- return { requestId, status: 'failed', summary: null, actions: [], question: 'Request not found.', pendingApproval: null }
143
- }
144
-
145
- if (record.status !== 'needs_approval' || !record.pendingApproval) {
146
- return { requestId, status: record.status, summary: record.summary, actions: record.actions ?? [], question: null, pendingApproval: null }
147
- }
148
-
149
- if (!approve) {
150
- const fields = { status: 'rejected', summary: 'Rejected by human.', pendingApproval: null }
151
- await journal.update(requestId, fields)
152
- return { requestId, ...fields, actions: record.actions ?? [], question: null }
153
- }
154
-
155
- const { tool, input } = record.pendingApproval
156
- await journal.update(requestId, { status: 'running' })
157
- const envelope = await dispatch(tool, input)
158
- const actions = [...(record.actions ?? []), { tool, input, envelope }]
159
-
160
- if (!envelope.ok) {
161
- // Execution failed (e.g. transient) — stay needs_approval so the human can
162
- // retry after fixing; keep pendingApproval, record the error and the attempt.
163
- const error = envelope.error?.message ?? 'execution failed'
164
- await journal.update(requestId, { status: 'needs_approval', actions, error })
165
- return { requestId, status: 'needs_approval', summary: null, actions, error, question: null, pendingApproval: record.pendingApproval }
166
- }
167
-
168
- const summary = `Approved: ${tool} executed.`
169
- await journal.update(requestId, { status: 'done', actions, summary, error: null, pendingApproval: null })
170
- return { requestId, status: 'done', summary, actions, question: null, pendingApproval: null }
171
- }
@@ -1,43 +0,0 @@
1
- import { handleApprove, handleRequest, handleRespond } from './agent-handler.js'
2
- import { createDispatch } from './dispatch.js'
3
- import { toolManifest } from './manifest.js'
4
- import { classify, DEFAULT_ACTOR_TIERS, scopedManifest } from './scope.js'
5
-
6
- // createAgentKit binds the generic agent machinery to ONE app's tool catalog.
7
- // It is the single integration seam: an app supplies its catalog, a domain
8
- // system prompt, a transport (Tauri invoke / CLI spawn / fetch), a journal store
9
- // and optional grounding, and gets back request/respond/approve plus the derived
10
- // manifest + scope helpers — all pre-bound, so callers never re-thread the
11
- // catalog. The `chat` fn stays per-call (its config can change between requests).
12
-
13
- /**
14
- * @param {object} config kit configuration
15
- * @param {object[]} config.catalog tool definitions (required)
16
- * @param {string|((ctx: object) => string)} [config.systemPrompt] domain system prompt (or builder from grounding ctx)
17
- * @param {(tool: object, input: object) => unknown} [config.transport] backend runner; omit to build the kit without dispatch (manifest/scope only)
18
- * @param {object} [config.journal] journal store { create, load, update, list }
19
- * @param {Record<string, number>} [config.actorTiers] max executable tier rank per actor kind
20
- * @param {{ tool: string, key?: string, fallback?: unknown }} [config.grounding] optional read tool to ground the prompt
21
- * @returns {{ dispatch: Function|null, classify: Function, scopedManifest: Function, toolManifest: Function, request: Function, respond: Function, approve: Function }} bound kit
22
- */
23
- export function createAgentKit({ catalog, systemPrompt = '', transport, journal, actorTiers = DEFAULT_ACTOR_TIERS, grounding } = {}) {
24
- if (!Array.isArray(catalog)) throw new Error('createAgentKit: catalog (array) is required')
25
-
26
- const dispatch = transport ? createDispatch(catalog, transport) : null
27
- const buildSystem = typeof systemPrompt === 'function' ? systemPrompt : () => systemPrompt
28
- const toolsFor = actor => scopedManifest(catalog, actorTiers, actor)
29
- const gateFor = actor => name => classify(catalog, actorTiers, actor, name)
30
-
31
- return {
32
- dispatch,
33
- classify: (actor, name) => classify(catalog, actorTiers, actor, name),
34
- scopedManifest: actor => toolsFor(actor),
35
- toolManifest: allow => toolManifest(catalog, allow),
36
- request: ({ intent, actor, chat }) =>
37
- handleRequest({ intent, actor, chat, dispatch, journal, tools: toolsFor(actor), gate: gateFor(actor), buildSystem, grounding }),
38
- respond: ({ requestId, message, actor, chat }) =>
39
- handleRespond({ requestId, message, actor, chat, dispatch, journal, tools: toolsFor(actor), gate: gateFor(actor) }),
40
- approve: ({ requestId, approve }) =>
41
- handleApprove({ requestId, approve, dispatch, journal }),
42
- }
43
- }
@@ -1,31 +0,0 @@
1
- ---
2
- docgen:
3
- source: npm/src/core/agent-handler.js
4
- crc: 8ef1230c
5
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
- score: 100
7
- issues: judge:inaccurate:0.97
8
- judgeModel: openai-codex/gpt-5.4-mini
9
- ---
10
-
11
- # agent-handler.js
12
-
13
- ## Огляд
14
-
15
- Модуль керує життєвим циклом агентського запиту. Він ініціює запит через `handleRequest`, підтримує діалог з користувачем через `handleRespond` та реалізує логіку схвалення через `handleApprove`.
16
-
17
- ## Поведінка
18
-
19
- handleRequest ініціює новий агентський запит, створює запис у журналі та запускає цикл агента.
20
- handleRespond продовжує розмову, використовуючи попередній стан запису та нове повідомлення користувача.
21
- handleApprove виконує або відхиляє заплановану деструктивну дію, якщо запис перебуває у стані очікування схвалення.
22
-
23
- ## Публічний API
24
-
25
- handleRequest — Ініціює новий запит до агента.
26
- handleRespond — Продовжує діалог, надаючи відповідь або уточнення.
27
- handleApprove — Підтверджує (або відхиляє) заплановану руйнівну дію, виконуючи її з дозволу користувача.
28
-
29
- ## Гарантії поведінки
30
-
31
- - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
@@ -1,25 +0,0 @@
1
- ---
2
- docgen:
3
- source: npm/src/core/agent-kit.js
4
- crc: 27461b4b
5
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
- score: 100
7
- judgeModel: openai-codex/gpt-5.4-mini
8
- ---
9
-
10
- # agent-kit.js
11
-
12
- ## Огляд
13
-
14
- Цей модуль відповідає за ініціалізацію та конфігурування набору інструментів для агента. Функція `createAgentKit` створює об'єкт, що містить необхідні методи та механізми для роботи агента, забезпечуючи його доступ до інструментів та системних налаштувань.
15
-
16
- ## Поведінка
17
-
18
- 1. Викликати `createAgentKit` для створення набору інструментів агента.
19
- 2. Надати `createAgentKit` каталог інструментів.
20
- 3. За бажанням надати системний підказку, транспортний механізм, сховище журналу, рівні акторів та інструмент для заземлення.
21
- 4. Отримати об'єкт набору інструментів, що містить функції для диспетчеризації, класифікації, отримання маніфесту інструментів, створення запитів, відповідей та схвалення.
22
-
23
- ## Гарантії поведінки
24
-
25
- - (специфічних машинно-виведених гарантій немає)
@@ -1,29 +0,0 @@
1
- ---
2
- docgen:
3
- source: npm/src/core/llm.js
4
- crc: 2f379651
5
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
- score: 100
7
- issues: judge:inaccurate:0.98
8
- judgeModel: openai-codex/gpt-5.4-mini
9
- ---
10
-
11
- # llm.js
12
-
13
- ## Огляд
14
-
15
- Модуль дозволяє ініціювати сеанс чату з мовною моделлю через мережевий ендпоінт <http://127.0.0.1:10240/v1>. Функціонал реалізується через публічні функції `runAgent` та `createOpenAiChat`. Код виконує виклик API та керує циклом виклику інструментів у контексті агента, доки модель не надасть фінальну відповідь або не буде перевищено ліміт кроків. Робота залежить від конфігурації, описаної у `response.json`.
16
-
17
- ## Поведінка
18
-
19
- runAgent виконує цикл виклику інструментів, поки модель не надасть відповідь без виклику інструменту, або доки не буде досягнуто максимальної кількості кроків.
20
- createOpenAiChat створює функцію, яка викликає мережевий ендпоінт OpenAI-сумісного API (наприклад, <http://127.0.0.1:10240/v1>) для отримання відповіді моделі.
21
-
22
- ## Публічний API
23
-
24
- runAgent — Запускає цикл виклику інструментів до моменту, коли модель надає відповідь без виклику інструменту. Може використовувати новий запит або продовжувати сесію на основі існуючої історії повідомлень.
25
- createOpenAiChat — Створює функцію для взаємодії з кінцевою точкою, сумісною з OpenAI (omlx).
26
-
27
- ## Гарантії поведінки
28
-
29
- - Read-only: не виконує операцій запису (ФС/БД).
@@ -1,30 +0,0 @@
1
- ---
2
- docgen:
3
- source: npm/src/core/omlx-models.js
4
- crc: 81a5dc3a
5
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
- score: 100
7
- issues: judge:inaccurate:0.97
8
- judgeModel: openai-codex/gpt-5.4-mini
9
- ---
10
-
11
- # omlx-models.js
12
-
13
- ## Огляд
14
-
15
- Модуль надає публічні механізми для взаємодії з локально завантаженими моделями. Він дозволяє отримати повний список доступних моделей та вибрати конкретну модель із цього списку. Функціонал працює в режимі, що перехоплює помилки (fail-safe), і не здійснює запису у файлову систему чи бази даних. Робота модуля залежить від конфігурацій, зокрема `response.json`.
16
-
17
- ## Поведінка
18
-
19
- listOmlxModels отримує список ідентифікаторів моделей, завантажених локальним сервером, або повертає порожній масив у разі будь-якої помилки.
20
- resolveModel обирає модель з наданого списку, надаючи пріоритет обраній моделі, або повертає першу доступну модель, якщо пріоритетна відсутня.
21
-
22
- ## Публічний API
23
-
24
- listOmlxModels — отримує список доступних моделей.
25
- resolveModel — вибирає модель: обрану, якщо вона завантажена, інакше першу доступну, або порожній рядок.
26
-
27
- ## Гарантії поведінки
28
-
29
- - Read-only: не виконує операцій запису (ФС/БД).
30
- - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
@@ -1,40 +0,0 @@
1
- ---
2
- docgen:
3
- source: npm/src/core/resolve-omlx-base-url.js
4
- crc: 36c282c7
5
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
- score: 100
7
- issues: judge:inaccurate:0.99
8
- judgeModel: openai-codex/gpt-5.4-mini
9
- ---
10
-
11
- # resolve-omlx-base-url.js
12
-
13
- ## Огляд
14
-
15
- Модуль визначає та надає базові URL для взаємодії з серверами omlx. Він експортує константи: DIRECT_OMLX_BASE_URL="<http://127.0.0.1:8000/v1>" для прямого доступу та PROXY_OMLX_BASE_URL="<http://127.0.0.1:8088/v1>" для проксі-доступу. Модуль звертається до мережі для визначення робочого URL, перевіряючи доступність проксі-сервера. Вибір URL кешується у межах прогону. При помилках мережевого доступу модуль перехоплює їх (fail-safe) і повертає порожнє значення замість кидання винятків. Модуль є лише для читання (не виконує операцій з ФС/БД).
16
-
17
- ## Поведінка
18
-
19
- DIRECT_OMLX_BASE_URL: Надає базовий URL для прямого доступу до сервера omlx, який є <http://127.0.0.1:8000/v1>.
20
- PROXY_OMLX_BASE_URL: Надає базовий URL для доступу через проксі-сервер omlx, який є <http://127.0.0.1:8088/v1>.
21
- isDirectOmlxUrl: Перевіряє, чи вказаний URL є стандартним локальним сервером omlx (<http://127.0.0.1:8000> або localhost:8000).
22
- resolveOmlxBaseUrl: Визначає ефективний базовий URL omlx, перевіряючи доступність проксі-сервера через зондування `/health`; у разі успіху повертає URL проксі, інакше — прямий URL.
23
- resolveOmlxBaseUrlCached: Визначає ефективний базовий URL omlx, використовуючи кешування для уникнення повторних зондувань протягом заданого часу життя (TTL).
24
- __resetOmlxBaseUrlCache: Очищає кеш результатів зондування базового URL omlx.
25
-
26
- ## Публічний API
27
-
28
- DIRECT_OMLX_BASE_URL — Базовий URL для прямого доступу до локального OMLX сервера, що вказує на <http://127.0.0.1:8000/v1>.
29
- PROXY_OMLX_BASE_URL — Базовий URL для проксі-доступу до OMLX сервера, що вказує на <http://127.0.0.1:8088/v1>.
30
- isDirectOmlxUrl — Визначає, чи є URL основним локальним сервером OMLX, який не повинен бути перенаправлений через проксі.
31
- resolveOmlxBaseUrl — Перевіряє доступність проксі-сервера шляхом виконання GET-запиту до <http://127.0.0.1:8088/v1>; якщо відповідь успішна, використовується проксі, інакше — прямий URL.
32
- resolveOmlxBaseUrlCached — Виконує перевірку доступності проксі-сервера з кешуванням, щоб уникнути повторних перевірок при кожному виклику LLM.
33
- __resetOmlxBaseUrlCache — Очищає всі збережені результати перевірки доступності базового URL OMLX.
34
-
35
- ## Гарантії поведінки
36
-
37
- - Read-only: не виконує операцій запису (ФС/БД).
38
- - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
39
- - За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
40
- - Кешує результати в межах одного прогону.
package/src/core/llm.js DELETED
@@ -1,90 +0,0 @@
1
- // The tool-calling loop and an OpenAI-compatible chat adapter. Both are
2
- // stateless and domain-agnostic: the system prompt and tool manifest are passed
3
- // in by the caller (an app builds them from its catalog — see agent-kit.js).
4
-
5
- const DEFAULT_SYSTEM = 'You are a tool-using assistant. Call one tool at a time and wait for its result. If the request is ambiguous, ask a clarifying question with no tool call. When done, reply with a plain-text summary and no tool call.'
6
-
7
- /**
8
- * Run the tool-calling loop until the model answers without a tool call.
9
- * Accepts either a fresh prompt or an existing messages[] for sessional resume.
10
- * @param {object} params loop parameters
11
- * @param {string} [params.prompt] user request (fresh start)
12
- * @param {object[]} [params.messages] existing conversation to resume (takes priority over prompt)
13
- * @param {(name: string, input: object) => Promise<object>} params.dispatch tool dispatcher returning an envelope
14
- * @param {(req: {messages: object[], tools: object[]}) => Promise<object>} params.chat model call returning an assistant message
15
- * @param {number} [params.maxSteps] safety cap on loop iterations
16
- * @param {string} [params.system] system prompt (only used when building fresh from prompt)
17
- * @param {object[]} [params.tools] LLM tool manifest (pass a scoped manifest to restrict)
18
- * @param {(name: string) => 'allow'|'approval'|'deny'} [params.gate] per-call decision (default: allow)
19
- * @returns {Promise<{content: string, steps: number, trace: object[], messages: object[], stopped?: string, pendingApproval?: object}>} loop result
20
- */
21
- export async function runAgent({ prompt, messages: initialMessages, dispatch, chat, maxSteps = 6, system = DEFAULT_SYSTEM, tools = [], gate = () => 'allow' }) {
22
- const messages = initialMessages
23
- ? [...initialMessages]
24
- : [
25
- { role: 'system', content: system },
26
- { role: 'user', content: prompt },
27
- ]
28
- const trace = []
29
-
30
- for (let step = 0; step < maxSteps; step++) {
31
- const reply = await chat({ messages, tools })
32
- messages.push(reply)
33
-
34
- const calls = reply.tool_calls ?? []
35
- if (calls.length === 0) {
36
- return { content: reply.content ?? '', steps: step + 1, trace, messages }
37
- }
38
-
39
- for (const call of calls) {
40
- let input = {}
41
- try {
42
- input = call.function.arguments ? JSON.parse(call.function.arguments) : {}
43
- }
44
- catch {
45
- // leave input empty — dispatch's schema validation reports the problem
46
- }
47
-
48
- const decision = gate(call.function.name)
49
- if (decision === 'approval') {
50
- // Destructive request from a non-human: pause for human approval.
51
- return { content: reply.content ?? '', steps: step + 1, trace, messages, stopped: 'needs_approval', pendingApproval: { tool: call.function.name, input } }
52
- }
53
-
54
- const envelope = decision === 'deny'
55
- ? { ok: false, error: { code: 'forbidden', message: `Tool "${call.function.name}" is not allowed.` } }
56
- : await dispatch(call.function.name, input)
57
- trace.push({ tool: call.function.name, input, envelope })
58
- messages.push({ role: 'tool', tool_call_id: call.id, content: JSON.stringify(envelope) })
59
- }
60
- }
61
-
62
- return { content: '', steps: maxSteps, trace, messages, stopped: 'max_steps' }
63
- }
64
-
65
- /**
66
- * Build a `chat` function that calls an OpenAI-compatible endpoint (omlx).
67
- * @param {object} params config
68
- * @param {string} params.baseUrl base URL incl. /v1 (e.g. http://127.0.0.1:10240/v1)
69
- * @param {string} params.model served model id
70
- * @param {string} [params.apiKey] optional bearer token
71
- * @param {typeof fetch} [params.fetchFn] fetch implementation (injectable for tests / tauri-http)
72
- * @returns {(req: {messages: object[], tools: object[]}) => Promise<object>} chat function
73
- */
74
- export function createOpenAiChat({ baseUrl, model, apiKey, fetchFn = fetch }) {
75
- return async function chat({ messages, tools }) {
76
- const response = await fetchFn(`${baseUrl}/chat/completions`, {
77
- method: 'POST',
78
- headers: {
79
- 'content-type': 'application/json',
80
- ...(apiKey ? { authorization: `Bearer ${apiKey}` } : {}),
81
- },
82
- body: JSON.stringify({ model, messages, tools, tool_choice: 'auto' }),
83
- })
84
- if (!response.ok) {
85
- throw new Error(`omlx ${response.status}: ${await response.text()}`)
86
- }
87
- const data = await response.json()
88
- return data.choices[0].message
89
- }
90
- }
@@ -1,34 +0,0 @@
1
- // List the models a local omlx (OpenAI-compatible) server has loaded, via
2
- // GET {baseUrl}/models. Generic OpenAI shape: { data: [{ id }] }. Returns [] on
3
- // any failure so a model picker degrades gracefully to manual entry.
4
-
5
- /**
6
- * @param {{ baseUrl?: string, apiKey?: string, fetchFn?: typeof fetch, signal?: AbortSignal }} [params] config
7
- * @returns {Promise<string[]>} loaded model ids (empty on error)
8
- */
9
- export async function listOmlxModels({ baseUrl, apiKey, fetchFn = fetch, signal } = {}) {
10
- if (!baseUrl) return []
11
- try {
12
- const response = await fetchFn(`${baseUrl}/models`, {
13
- headers: apiKey ? { authorization: `Bearer ${apiKey}` } : {},
14
- signal,
15
- })
16
- if (!response.ok) return []
17
- const data = await response.json()
18
- return Array.isArray(data?.data) ? data.data.map(m => m?.id).filter(Boolean) : []
19
- }
20
- catch {
21
- return []
22
- }
23
- }
24
-
25
- /**
26
- * Pick a model: the preferred one if loaded, else the first available, else ''.
27
- * @param {string[]} models loaded model ids
28
- * @param {string} [preferred] preferred id
29
- * @returns {string} chosen model id
30
- */
31
- export function resolveModel(models, preferred) {
32
- if (preferred && models.includes(preferred)) return preferred
33
- return models[0] ?? ''
34
- }
@@ -1,84 +0,0 @@
1
- // Resolve the effective omlx base URL: when the myllm reverse proxy
2
- // (a Tauri app that forwards /v1/* to the real omlx server and logs every
3
- // request/response for its history view) is alive on its well-known local
4
- // port, route traffic through it; otherwise talk to omlx directly. The probe
5
- // hits {proxy origin}/health — the proxy forwards it to omlx itself, so a
6
- // 2xx proves BOTH the proxy and the upstream are up (omlx down behind a live
7
- // proxy yields 502 → direct). Framework-free so headless consumers (MCP
8
- // wrappers, node scripts) can reuse it; pass a Tauri fetch as fetchFn from
9
- // webview contexts to avoid CORS on localhost.
10
-
11
- export const DIRECT_OMLX_BASE_URL = 'http://127.0.0.1:8000/v1'
12
- export const PROXY_OMLX_BASE_URL = 'http://127.0.0.1:8088/v1'
13
-
14
- const DEFAULT_TIMEOUT_MS = 400
15
- const DEFAULT_TTL_MS = 12_000
16
-
17
- /**
18
- * Is this URL the default local omlx server — the only target eligible for
19
- * the proxy override? A user who deliberately pointed an app at another
20
- * host/port must never be silently rerouted (the proxy's upstream is
21
- * hardwired to the local :8000).
22
- * @param {string} url candidate base URL
23
- * @returns {boolean} true for 127.0.0.1:8000 / localhost:8000, false otherwise (incl. parse errors)
24
- */
25
- export function isDirectOmlxUrl(url) {
26
- try {
27
- const { host } = new URL(url)
28
- return host === '127.0.0.1:8000' || host === 'localhost:8000'
29
- }
30
- catch {
31
- return false
32
- }
33
- }
34
-
35
- /**
36
- * One-shot probe: GET {proxy origin}/health with a short timeout. 2xx means
37
- * the proxy (and omlx behind it) is alive → use the proxy; anything else
38
- * (timeout, refused, 502) → use the direct URL.
39
- * @param {{ directUrl?: string, proxyUrl?: string, fetchFn?: typeof fetch, timeoutMs?: number }} [params] config
40
- * @returns {Promise<string>} the base URL to use
41
- */
42
- export async function resolveOmlxBaseUrl({
43
- directUrl = DIRECT_OMLX_BASE_URL,
44
- proxyUrl = PROXY_OMLX_BASE_URL,
45
- fetchFn = fetch,
46
- timeoutMs = DEFAULT_TIMEOUT_MS,
47
- } = {}) {
48
- try {
49
- const healthUrl = new URL('/health', proxyUrl).toString()
50
- const signal = typeof AbortSignal?.timeout === 'function' ? AbortSignal.timeout(timeoutMs) : undefined
51
- const response = await fetchFn(healthUrl, { signal })
52
- return response.ok ? proxyUrl : directUrl
53
- }
54
- catch {
55
- return directUrl
56
- }
57
- }
58
-
59
- // proxyUrl → { promise, expiresAt }. Caching the promise (not the value)
60
- // dedupes concurrent probes: several composables firing loadEnv() at once
61
- // trigger a single /health request.
62
- const cache = new Map()
63
-
64
- /**
65
- * Cached {@link resolveOmlxBaseUrl}: at most one probe per proxyUrl per TTL
66
- * window, so callers may resolve before every LLM call without paying the
67
- * probe latency each time, while still noticing the proxy starting/stopping
68
- * within one TTL.
69
- * @param {{ directUrl?: string, proxyUrl?: string, fetchFn?: typeof fetch, timeoutMs?: number, ttlMs?: number, now?: () => number }} [params] config
70
- * @returns {Promise<string>} the base URL to use
71
- */
72
- export function resolveOmlxBaseUrlCached({ ttlMs = DEFAULT_TTL_MS, now = Date.now, ...probeOptions } = {}) {
73
- const proxyUrl = probeOptions.proxyUrl ?? PROXY_OMLX_BASE_URL
74
- const cached = cache.get(proxyUrl)
75
- if (cached && now() < cached.expiresAt) return cached.promise
76
- const promise = resolveOmlxBaseUrl(probeOptions)
77
- cache.set(proxyUrl, { promise, expiresAt: now() + ttlMs })
78
- return promise
79
- }
80
-
81
- /** Test hook: drop all cached probe results. */
82
- export function __resetOmlxBaseUrlCache() {
83
- cache.clear()
84
- }
@@ -1,29 +0,0 @@
1
- ---
2
- docgen:
3
- source: npm/src/vue/use-agent.js
4
- crc: 84427f61
5
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
- score: 100
7
- judgeModel: openai-codex/gpt-5.4-mini
8
- ---
9
-
10
- # use-agent.js
11
-
12
- ## Огляд
13
-
14
- Модуль ініціалізує шлюз агента, надаючи йому інструменти, системний промпт та конфігурацію. Він дозволяє ініціювати запити до агента через `useAgent`, отримувати відповіді, затверджувати дії та переглядати доступні моделі. Звернення до мережі здійснюється через цей модуль.
15
-
16
- ## Поведінка
17
-
18
- 1. Викликати `useAgent` для ініціалізації шлюзу агента.
19
- 2. Надати `useAgent` каталог інструментів, системний промпт, опціональне заземлення, рівні акторів, ідентичність актора, конфігурацію omlx та транспорт для інструментів.
20
- 3. Отримати з `useAgent` посилання на базовий URL, модель, ключ API та функції для роботи з omlx.
21
- 4. Отримати з `useAgent` посилання на журнал.
22
- 5. Викликати `request` з отриманого шлюзу, передаючи намір та ідентичність актора, щоб ініціювати запит агента.
23
- 6. Викликати `respond` з отриманого шлюзу, передаючи ідентифікатор запиту та повідомлення, щоб надати відповідь агенту.
24
- 7. Викликати `approve` з отриманого шлюзу, передаючи ідентифікатор запиту та результат схвалення, щоб затвердити дію агента.
25
- 8. Викликати `listModels` з отриманого шлюзу, щоб отримати список доступних моделей omlx.
26
-
27
- ## Гарантії поведінки
28
-
29
- - (специфічних машинно-виведених гарантій немає)
@@ -1,30 +0,0 @@
1
- ---
2
- docgen:
3
- source: npm/src/vue/use-omlx.js
4
- crc: d1e34b0a
5
- model: omlx/gemma-4-e4b-it-OptiQ-4bit
6
- score: 100
7
- issues: judge:inaccurate:0.98
8
- judgeModel: openai-codex/gpt-5.4-mini
9
- ---
10
-
11
- # use-omlx.js
12
-
13
- ## Огляд
14
-
15
- Модуль відповідає за ініціалізацію та кешування конфігурації для взаємодії з OMLX. Він завантажує налаштування з `settings.json` та середовища виконання, використовуючи <http://127.0.0.1:8000/v1> як резервний URL. Публічна функція useOmlx звертається до мережі, виконує кешування у межах прогону. При виникненні помилок вона перехоплює їх (fail-safe), не кидаючи винятків назовні, а повертаючи порожнє значення замість винятку.
16
-
17
- ## Поведінка
18
-
19
- 1. Ініціалізує конфігурацію OMLX, використовуючи значення з локального сховища. Якщо значення відсутнє, використовує наданий за замовчуванням URL, який може бути `http://127.0.0.1:8000/v1`.
20
- 2. Завантажує глобальні налаштування OMLX з середовища виконання через Rust.
21
- 3. Якщо завантаження успішне, застосовує API ключ до внутрішнього стану.
22
- 4. Якщо локальне сховище відсутнє, застосовує URL та модель з глобальних налаштувань.
23
- 5. Якщо URL відповідає локальному замовленню, виконується перенаправлення через проксі myllm, що є тимчасовим перевизначенням.
24
- 6. Зберігає поточні значення URL та моделі у локальне сховище.
25
-
26
- ## Гарантії поведінки
27
-
28
- - Перехоплює помилки і не пропускає винятків назовні (fail-safe).
29
- - За певних помилок повертає порожнє значення (напр. `null`) замість винятку.
30
- - Кешує результати в межах одного прогону.