@tangleai/assistant 0.21.1 → 0.24.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/src/settings.js CHANGED
@@ -1,11 +1,12 @@
1
- //@ts-check
2
1
  import { PROVIDERS } from '@tangleai/models/providers';
3
2
  export const PROVIDER_OPTIONS = Object.entries(PROVIDERS)
4
- .map(([value, preset]) => ({ value, label: preset.label, local: preset.local }));
5
-
3
+ .map(([value, preset]) => ({ value, label: preset.label, local: preset.local }));
6
4
  export function isConfigured(s) {
7
- if (s.model.trim() === '') return false;
8
- if (s.provider === 'custom') return s.baseUrl.trim() !== '';
9
- if (s.provider === 'openrouter') return s.apiKey.trim() !== '';
10
- return true; // local runtimes (Ollama, LM Studio) need no key
5
+ if (s.model.trim() === '')
6
+ return false;
7
+ if (s.provider === 'custom')
8
+ return s.baseUrl.trim() !== '';
9
+ if (s.provider === 'openrouter')
10
+ return s.apiKey.trim() !== '';
11
+ return true; // local runtimes (Ollama, LM Studio) need no key
11
12
  }
package/src/state.d.ts CHANGED
@@ -1,11 +1,17 @@
1
- /** @param {any} [aiSettings] @param {any} [aiChat] */
2
- export function createAssistantState(aiSettings?: any, aiChat?: any): {
1
+ export declare const DEFAULT_AI_SETTINGS: {
2
+ provider: string;
3
+ baseUrl: string;
4
+ model: string;
5
+ apiKey: string;
6
+ };
7
+ /** @param [aiSettings] @param [aiChat] */
8
+ export declare function createAssistantState(aiSettings?: any, aiChat?: any): {
3
9
  open: boolean;
4
10
  settingsOpen: boolean;
5
11
  probe: {
6
12
  status: string;
7
- detail: any;
8
- models: any[];
13
+ detail: null;
14
+ models: never[];
9
15
  };
10
16
  reasoningChars: number;
11
17
  settings: any;
@@ -13,20 +19,14 @@ export function createAssistantState(aiSettings?: any, aiChat?: any): {
13
19
  draft: string;
14
20
  pending: string;
15
21
  status: string;
16
- activity: any;
17
- error: any;
18
- goal: any;
22
+ activity: null;
23
+ error: null;
24
+ goal: null;
19
25
  goalDraft: string;
20
26
  memories: number;
21
- persistence: any;
22
- retention: any;
27
+ persistence: null;
28
+ retention: null;
23
29
  archived: number;
24
30
  remembering: boolean;
25
- remembered: any;
31
+ remembered: null;
26
32
  };
27
- export namespace DEFAULT_AI_SETTINGS {
28
- let provider: string;
29
- let baseUrl: string;
30
- let model: string;
31
- let apiKey: string;
32
- }
package/src/state.js CHANGED
@@ -1,40 +1,39 @@
1
- //@ts-check
2
1
  export const DEFAULT_AI_SETTINGS = {
3
- provider: 'openrouter', // 'openrouter' | 'ollama' | 'lmstudio' | 'custom'
4
- baseUrl: '', // required for 'custom'; overrides the preset otherwise
5
- model: '', // e.g. 'qwen/qwen3-4b' or a local model name
6
- apiKey: '', // bring your own; local runtimes need none
2
+ provider: 'openrouter', // 'openrouter' | 'ollama' | 'lmstudio' | 'custom'
3
+ baseUrl: '', // required for 'custom'; overrides the preset otherwise
4
+ model: '', // e.g. 'qwen/qwen3-4b' or a local model name
5
+ apiKey: '', // bring your own; local runtimes need none
7
6
  };
8
- /** @param {any} [aiSettings] @param {any} [aiChat] */
7
+ /** @param [aiSettings] @param [aiChat] */
9
8
  export function createAssistantState(aiSettings = null, aiChat = null) {
10
- return {
11
- open: false,
12
- settingsOpen: false,
13
- // the settings "Test connection" probe: idle | busy | ok | fail,
14
- // a human detail line, and the model ids a successful probe found
15
- probe: { status: 'idle', detail: null, models: [] },
16
- // reasoning characters streamed this turn (thinking models emit
17
- // reasoning before - or instead of - visible content)
18
- reasoningChars: 0,
19
- settings: { ...DEFAULT_AI_SETTINGS, ...(aiSettings ?? {}) },
20
- // visible transcript: { role, content }; restored from local
21
- // storage so a page reload keeps the conversation
22
- messages: Array.isArray(aiChat?.messages) ? structuredClone(aiChat.messages) : [],
23
- draft: '', // composer text
24
- pending: '', // the assistant reply currently streaming
25
- status: 'idle', // 'idle' | 'streaming' | 'error'
26
- activity: null, // the tool the model is currently calling
27
- error: null,
28
- // the @tangleai/context ledger, as the panel shows it. Read from the
29
- // ledger (not mirrored into it): the objective is durable state and
30
- // this slice is a view of it, so a reload shows what storage holds
31
- // rather than what this tab last typed.
32
- goal: null, // { objective, progress: [ { at, note, evidence } ] }
33
- goalDraft: '', // the objective composer
34
- memories: 0, // evidenced facts carried into every turn
35
- persistence: null, retention: null,
36
- archived: 0, // dropped rounds sitting in slots, recallable
37
- remembering: false, // a refinement is in flight
38
- remembered: null, // what the last refinement did, in one line
9
+ return {
10
+ open: false,
11
+ settingsOpen: false,
12
+ // the settings "Test connection" probe: idle | busy | ok | fail,
13
+ // a human detail line, and the model ids a successful probe found
14
+ probe: { status: 'idle', detail: null, models: [] },
15
+ // reasoning characters streamed this turn (thinking models emit
16
+ // reasoning before - or instead of - visible content)
17
+ reasoningChars: 0,
18
+ settings: { ...DEFAULT_AI_SETTINGS, ...(aiSettings ?? {}) },
19
+ // visible transcript: { role, content }; restored from local
20
+ // storage so a page reload keeps the conversation
21
+ messages: Array.isArray(aiChat?.messages) ? structuredClone(aiChat.messages) : [],
22
+ draft: '', // composer text
23
+ pending: '', // the assistant reply currently streaming
24
+ status: 'idle', // 'idle' | 'streaming' | 'error'
25
+ activity: null, // the tool the model is currently calling
26
+ error: null,
27
+ // the @tangleai/context ledger, as the panel shows it. Read from the
28
+ // ledger (not mirrored into it): the objective is durable state and
29
+ // this slice is a view of it, so a reload shows what storage holds
30
+ // rather than what this tab last typed.
31
+ goal: null, // { objective, progress: [ { at, note, evidence } ] }
32
+ goalDraft: '', // the objective composer
33
+ memories: 0, // evidenced facts carried into every turn
34
+ persistence: null, retention: null,
35
+ archived: 0, // dropped rounds sitting in slots, recallable
36
+ remembering: false, // a refinement is in flight
37
+ remembered: null, // what the last refinement did, in one line
39
38
  };
40
39
  }
@@ -1,5 +1,5 @@
1
- /** @param {any} ai @param {(source: string) => any} renderMarkdown */
2
- export function assistantView(ai: any, renderMarkdown: (source: string) => any): {
1
+ /** @param ai @param renderMarkdown */
2
+ export declare function assistantView(ai: any, renderMarkdown: (source: string) => any): {
3
3
  open: any;
4
4
  status: any;
5
5
  activity: any;
@@ -42,7 +42,7 @@ export function assistantView(ai: any, renderMarkdown: (source: string) => any):
42
42
  evidence: any;
43
43
  }[];
44
44
  more: number;
45
- };
45
+ } | null;
46
46
  goalDraft: any;
47
47
  memories: any;
48
48
  memoryLabel: string;
package/src/viewmodel.js CHANGED
@@ -1,77 +1,75 @@
1
- //@ts-check
2
- import { PROVIDER_OPTIONS, isConfigured } from './settings.js';
3
- /** @param {any} ai @param {(source: string) => any} renderMarkdown */
1
+ import { PROVIDER_OPTIONS, isConfigured } from "./settings.js";
2
+ /** @param ai @param renderMarkdown */
4
3
  export function assistantView(ai, renderMarkdown) {
5
- const s = ai.settings;
6
- const configured = isConfigured(s);
7
- return {
8
- open: ai.open,
9
- status: ai.status,
10
- activity: ai.activity,
11
- error: ai.error,
12
- draft: ai.draft,
13
- configured,
14
- // the settings form shows until the assistant can actually run, or
15
- // whenever the user opens it explicitly
16
- showSettings: ai.settingsOpen || !configured,
17
- settings: {
18
- provider: s.provider,
19
- baseUrl: s.baseUrl,
20
- model: s.model,
21
- apiKey: s.apiKey,
22
- needsKey: s.provider === 'openrouter' || s.provider === 'custom',
23
- probe: {
24
- status: ai.probe.status,
25
- detail: ai.probe.detail,
26
- busy: ai.probe.status === 'busy',
27
- ok: ai.probe.status === 'ok',
28
- fail: ai.probe.status === 'fail',
29
- models: ai.probe.models.map((id) => ({ id })),
30
- },
31
- },
32
- providers: PROVIDER_OPTIONS.map((p) => ({ ...p, selected: p.value === s.provider })),
33
- empty: ai.messages.length === 0,
34
- messages: ai.messages.map((m) => (m.role === 'user'
35
- ? { role: 'user', text: m.content }
36
- : { role: 'assistant', article: renderMarkdown(m.content) })),
37
- // the reply currently streaming in (plain text: it changes per token)
38
- streaming: ai.status === 'streaming',
39
- pending: ai.pending,
40
- // the quiet-phase label: reasoning models think before they speak,
41
- // and watching the thinking grow beats a blind spinner
42
- thinkingLabel: ai.reasoningChars > 0
43
- ? `Thinking… (${ai.reasoningChars} characters of reasoning)`
44
- : 'Thinking…',
45
- // the ledger: a persistent objective, what has been recorded against
46
- // it, and what a compacted session can still reach. Every number here
47
- // is derived from the ledger read, so the panel cannot claim a
48
- // memory the store does not hold.
49
- goal: ai.goal === null ? null : {
50
- objective: ai.goal.objective,
51
- entries: ai.goal.progress.length + (ai.goal.checkpoint?.sources.length ?? 0),
52
- checkpointLabel: ai.goal.checkpoint ? `${ai.goal.checkpoint.sources.length} earlier progress entries retained with their evidence.` : '',
53
- // newest first: the last thing that happened is the thing a reader
54
- // wants, and the whole log would push the conversation off screen
55
- progress: [...ai.goal.progress].reverse().slice(0, PROGRESS_SHOWN)
56
- .map((entry) => ({ note: entry.note, evidence: entry.evidence })),
57
- more: Math.max(0, ai.goal.progress.length - PROGRESS_SHOWN),
58
- },
59
- goalDraft: ai.goalDraft,
60
- memories: ai.memories,
61
- memoryLabel: `${ai.memories} remembered fact${ai.memories === 1 ? '' : 's'}`,
62
- persistenceLabel: ai.persistence?.error ? `Storage failed: ${ai.persistence.error}`
63
- : ai.persistence?.concurrency === 'single-writer' ? 'Use one tab at a time to update remembered state.' : '',
64
- retentionLabel: ai.retention?.evicted?.length
65
- ? `${ai.retention.evicted.length} earlier archive address(es) evicted under the storage budget.` : '',
66
- archived: ai.archived,
67
- // said in full sentences, because "12" beside a chat is not
68
- // information: a compacted session has to LOOK recoverable
69
- archivedLabel: `${ai.archived} earlier round${ai.archived === 1 ? '' : 's'} archived —`
70
- + ' the assistant can fetch any of them back with recall.',
71
- remembering: ai.remembering,
72
- remembered: ai.remembered,
73
- };
4
+ const s = ai.settings;
5
+ const configured = isConfigured(s);
6
+ return {
7
+ open: ai.open,
8
+ status: ai.status,
9
+ activity: ai.activity,
10
+ error: ai.error,
11
+ draft: ai.draft,
12
+ configured,
13
+ // the settings form shows until the assistant can actually run, or
14
+ // whenever the user opens it explicitly
15
+ showSettings: ai.settingsOpen || !configured,
16
+ settings: {
17
+ provider: s.provider,
18
+ baseUrl: s.baseUrl,
19
+ model: s.model,
20
+ apiKey: s.apiKey,
21
+ needsKey: s.provider === 'openrouter' || s.provider === 'custom',
22
+ probe: {
23
+ status: ai.probe.status,
24
+ detail: ai.probe.detail,
25
+ busy: ai.probe.status === 'busy',
26
+ ok: ai.probe.status === 'ok',
27
+ fail: ai.probe.status === 'fail',
28
+ models: ai.probe.models.map((id) => ({ id })),
29
+ },
30
+ },
31
+ providers: PROVIDER_OPTIONS.map((p) => ({ ...p, selected: p.value === s.provider })),
32
+ empty: ai.messages.length === 0,
33
+ messages: ai.messages.map((m) => (m.role === 'user'
34
+ ? { role: 'user', text: m.content }
35
+ : { role: 'assistant', article: renderMarkdown(m.content) })),
36
+ // the reply currently streaming in (plain text: it changes per token)
37
+ streaming: ai.status === 'streaming',
38
+ pending: ai.pending,
39
+ // the quiet-phase label: reasoning models think before they speak,
40
+ // and watching the thinking grow beats a blind spinner
41
+ thinkingLabel: ai.reasoningChars > 0
42
+ ? `Thinking… (${ai.reasoningChars} characters of reasoning)`
43
+ : 'Thinking…',
44
+ // the ledger: a persistent objective, what has been recorded against
45
+ // it, and what a compacted session can still reach. Every number here
46
+ // is derived from the ledger read, so the panel cannot claim a
47
+ // memory the store does not hold.
48
+ goal: ai.goal === null ? null : {
49
+ objective: ai.goal.objective,
50
+ entries: ai.goal.progress.length + (ai.goal.checkpoint?.sources.length ?? 0),
51
+ checkpointLabel: ai.goal.checkpoint ? `${ai.goal.checkpoint.sources.length} earlier progress entries retained with their evidence.` : '',
52
+ // newest first: the last thing that happened is the thing a reader
53
+ // wants, and the whole log would push the conversation off screen
54
+ progress: [...ai.goal.progress].reverse().slice(0, PROGRESS_SHOWN)
55
+ .map((entry) => ({ note: entry.note, evidence: entry.evidence })),
56
+ more: Math.max(0, ai.goal.progress.length - PROGRESS_SHOWN),
57
+ },
58
+ goalDraft: ai.goalDraft,
59
+ memories: ai.memories,
60
+ memoryLabel: `${ai.memories} remembered fact${ai.memories === 1 ? '' : 's'}`,
61
+ persistenceLabel: ai.persistence?.error ? `Storage failed: ${ai.persistence.error}`
62
+ : ai.persistence?.concurrency === 'single-writer' ? 'Use one tab at a time to update remembered state.' : '',
63
+ retentionLabel: ai.retention?.evicted?.length
64
+ ? `${ai.retention.evicted.length} earlier archive address(es) evicted under the storage budget.` : '',
65
+ archived: ai.archived,
66
+ // said in full sentences, because "12" beside a chat is not
67
+ // information: a compacted session has to LOOK recoverable
68
+ archivedLabel: `${ai.archived} earlier round${ai.archived === 1 ? '' : 's'} archived —`
69
+ + ' the assistant can fetch any of them back with recall.',
70
+ remembering: ai.remembering,
71
+ remembered: ai.remembered,
72
+ };
74
73
  }
75
-
76
74
  /** Progress entries the panel shows before it says "and N more". */
77
75
  const PROGRESS_SHOWN = 3;
package/src/views.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * text because it changes per token.
8
8
  */
9
9
  /** One instance owns its datalist identifier and host copy. */
10
- export function createAssistantRules(modelListId: any): ({
10
+ export declare function createAssistantRules(modelListId: any): ({
11
11
  match: string;
12
12
  mode: string;
13
13
  body: (string | {
@@ -149,6 +149,26 @@ export function createAssistantRules(modelListId: any): ({
149
149
  } | {
150
150
  $if: (string | {
151
151
  $if: (string | (string | {
152
+ class: string;
153
+ on: {
154
+ submit: {
155
+ action: string;
156
+ preventDefault: boolean;
157
+ };
158
+ };
159
+ } | (string | {
160
+ type: string;
161
+ class: string;
162
+ spellcheck: string;
163
+ placeholder: string;
164
+ value: string;
165
+ on: {
166
+ input: string;
167
+ };
168
+ })[] | (string | {
169
+ type: string;
170
+ class: string;
171
+ })[])[] | (string | {
152
172
  class: string;
153
173
  $if?: undefined;
154
174
  } | (string | {
@@ -196,26 +216,6 @@ export function createAssistantRules(modelListId: any): ({
196
216
  class: string;
197
217
  })[])[];
198
218
  class?: undefined;
199
- })[])[] | (string | {
200
- class: string;
201
- on: {
202
- submit: {
203
- action: string;
204
- preventDefault: boolean;
205
- };
206
- };
207
- } | (string | {
208
- type: string;
209
- class: string;
210
- spellcheck: string;
211
- placeholder: string;
212
- value: string;
213
- on: {
214
- input: string;
215
- };
216
- })[] | (string | {
217
- type: string;
218
- class: string;
219
219
  })[])[])[];
220
220
  })[];
221
221
  class?: undefined;