osai-agent 4.2.4 → 4.2.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osai-agent",
3
- "version": "4.2.4",
3
+ "version": "4.2.8",
4
4
  "type": "module",
5
5
  "description": "OS AI Agent - YOUR AI AGENT",
6
6
  "main": "src/index.js",
@@ -44,14 +44,15 @@ export default {
44
44
  ? `${this.device.name} (${this.device.ip}:${this.device.port || 22})`
45
45
  : '';
46
46
 
47
- const systemPrompt = buildSystemPrompt(this._userOS, this.mode, {
48
- executionMode: this.executionMode,
49
- mcpTools,
50
- skills: skillsCatalog.map(s => ({ name: s.name, description: s.description, source: s.source })),
51
- isSubagent: this.isSubagent,
52
- deviceInfo,
53
- ...(memoryContext ? { memoryContext } : {}),
54
- });
47
+ const systemPrompt = buildSystemPrompt(this._userOS, this.mode, {
48
+ provider: provider.type,
49
+ executionMode: this.executionMode,
50
+ mcpTools,
51
+ skills: skillsCatalog.map(s => ({ name: s.name, description: s.description, source: s.source })),
52
+ isSubagent: this.isSubagent,
53
+ deviceInfo,
54
+ ...(memoryContext ? { memoryContext } : {}),
55
+ });
55
56
 
56
57
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
57
58
  if (this._shouldCancel()) {
@@ -162,7 +163,7 @@ export default {
162
163
 
163
164
  if (this._shouldCancel()) return null;
164
165
 
165
- if (retrying) { this.onUpdateLastText(''); retrying = false; }
166
+ if (retrying) { this.onRetryStatus(''); retrying = false; }
166
167
 
167
168
  if (!fullResponse) return null;
168
169
 
@@ -185,7 +186,7 @@ export default {
185
186
  }
186
187
  retrying = true;
187
188
  const backoffMs = DEFAULTS.API_RETRY_DELAY * (attempt + 1);
188
- this.onMarkdown(`⏳ Retrying in ${Math.ceil(backoffMs / 1000)}s...`);
189
+ this.onRetryStatus(`Retrying in ${Math.ceil(backoffMs / 1000)}s...`);
189
190
  const slept = await this._cancellableSleep(backoffMs);
190
191
  if (!slept || this._shouldCancel()) return null;
191
192
  }
@@ -39,7 +39,7 @@ export class AgentLoop {
39
39
  onMarkdown, onThought, onToolStart, onToolResult, onObservation,
40
40
  onError, onComplete, onTodos, onBlocked, onThinkingStart, onThinkingEnd,
41
41
  onBadge, onConfirmPrompt, onAskUserPrompt, onPlanPrompt,
42
- onStats, onConnectionChange, onUpdateLastText, readline, noConfirm = false,
42
+ onStats, onConnectionChange, onUpdateLastText, onRetryStatus, readline, noConfirm = false,
43
43
  isSubagent = false, maxIterations = null, local = false,
44
44
  }) {
45
45
  this.device = device;
@@ -115,6 +115,7 @@ export class AgentLoop {
115
115
  this.onPlanPrompt = onPlanPrompt || (() => {});
116
116
  this.onStats = onStats || (() => {});
117
117
  this.onConnectionChange = onConnectionChange || (() => {});
118
+ this.onRetryStatus = onRetryStatus || (() => {});
118
119
  this.onUpdateLastText = onUpdateLastText || (() => {});
119
120
  this.onStats(0);
120
121
  }
@@ -299,7 +300,7 @@ export class AgentLoop {
299
300
  this.iteration++;
300
301
  logger.debug(`Iteration ${this.iteration}/${this._maxIterations}`);
301
302
 
302
- if (iterationRetrying) { this.onUpdateLastText(''); iterationRetrying = false; }
303
+ if (iterationRetrying) { this.onRetryStatus(''); iterationRetrying = false; }
303
304
 
304
305
  try {
305
306
  const shouldContinue = await this._executeIteration();
@@ -332,7 +333,7 @@ export class AgentLoop {
332
333
  if (this.iteration <= 1 && !this._shouldCancel()) {
333
334
  logger.info('Retrying iteration after error...');
334
335
  iterationRetrying = true;
335
- this.onMarkdown(`\n⏳ Retrying after error: ${error.message}\n`);
336
+ this.onRetryStatus(`Retrying after error: ${error.message}`);
336
337
  this.iteration--;
337
338
  const slept = await this._cancellableSleep(DEFAULTS.API_RETRY_DELAY);
338
339
  if (!slept || this._shouldCancel()) break;
@@ -618,7 +619,7 @@ export class AgentLoop {
618
619
 
619
620
  const backoffMs = DEFAULTS.API_RETRY_DELAY * (attempt + 1);
620
621
  retrying = true;
621
- this.onMarkdown(`⏳ Request failed (${response.status}) — retrying in ${Math.ceil(backoffMs / 1000)}s...`);
622
+ this.onRetryStatus(`Request failed (${response.status}) — retrying in ${Math.ceil(backoffMs / 1000)}s...`);
622
623
  const slept = await this._cancellableSleep(backoffMs);
623
624
  if (!slept || this._shouldCancel()) return null;
624
625
  continue;
@@ -629,7 +630,7 @@ export class AgentLoop {
629
630
  if (contentType.includes('application/json')) {
630
631
  const json = await response.json();
631
632
  if (json.content || json.message) {
632
- if (retrying) { this.onUpdateLastText(''); retrying = false; }
633
+ if (retrying) { this.onRetryStatus(''); retrying = false; }
633
634
  const textContent = json.content || json.message || '';
634
635
  const cleanText = textContent
635
636
  .replace(/^\s*\{(?:\\")?tool(?:\\")?\s*:\s*.*$/gim, '')
@@ -647,7 +648,7 @@ export class AgentLoop {
647
648
  }
648
649
  retrying = true;
649
650
  const backoffMs = DEFAULTS.API_RETRY_DELAY * (attempt + 1);
650
- this.onMarkdown(`⏳ Worker error — retrying in ${Math.ceil(backoffMs / 1000)}s...`);
651
+ this.onRetryStatus(`Worker error — retrying in ${Math.ceil(backoffMs / 1000)}s...`);
651
652
  const slept = await this._cancellableSleep(backoffMs);
652
653
  if (!slept || this._shouldCancel()) return null;
653
654
  continue;
@@ -655,7 +656,7 @@ export class AgentLoop {
655
656
  return null;
656
657
  }
657
658
 
658
- if (retrying) { this.onUpdateLastText(''); retrying = false; }
659
+ if (retrying) { this.onRetryStatus(''); retrying = false; }
659
660
  return response;
660
661
  } catch (error) {
661
662
  if (error.name === 'AbortError' || this._shouldCancel()) {
@@ -668,7 +669,7 @@ export class AgentLoop {
668
669
  }
669
670
  retrying = true;
670
671
  const backoffMs = DEFAULTS.API_RETRY_DELAY * (attempt + 1);
671
- this.onMarkdown(`⏳ Retrying in ${Math.ceil(backoffMs / 1000)}s...`);
672
+ this.onRetryStatus(`Retrying in ${Math.ceil(backoffMs / 1000)}s...`);
672
673
  const slept = await this._cancellableSleep(backoffMs);
673
674
  if (!slept || this._shouldCancel()) return null;
674
675
  }
@@ -103,6 +103,9 @@ export async function runSubagent(parent, { prompt, description = '' }) {
103
103
  onUpdateLastText: (text) => {
104
104
  if (STREAM_SUBAGENT_TO_PARENT) parent.onUpdateLastText(text);
105
105
  },
106
+ onRetryStatus: (text) => {
107
+ if (STREAM_SUBAGENT_TO_PARENT) parent.onRetryStatus(text);
108
+ },
106
109
  onThought: (text) => {
107
110
  if (STREAM_SUBAGENT_TO_PARENT) parent.onThought(text);
108
111
  },
@@ -63,7 +63,14 @@ export const buildSystemPrompt = (os = 'linux', mode = 'GENERAL', options = {})
63
63
  prompt += `You can also create skills at runtime with CREATE_SKILL.\n`;
64
64
  }
65
65
 
66
- if (!isSubagent) {
66
+ if (options.provider === 'osai' || options.provider === 'auto') {
67
+ prompt += `\n\n## IDENTITY\n`;
68
+ prompt += `You are OS AI Agent, created by the OS AI team.\n`;
69
+ prompt += `Do not reveal or mention any other company or model name (Anthropic, OpenAI, Google, Claude, GPT, etc.).\n`;
70
+ prompt += `If asked who created you or what your name is, answer: "I am OS AI Agent, created by the OS AI team."\n`;
71
+ }
72
+
73
+ if (!isSubagent) {
67
74
  prompt += `\n\n### Subagent (exploration — max 1 at a time)\n`;
68
75
  prompt += `You can delegate read-only exploration to a subagent. You will receive a [SUBAGENT_RESULT] when it finishes.\n`;
69
76
  prompt += `{"tool":"TASK","prompt":"<detailed task>","description":"<short label>"}\n`;
package/src/ui/App.js CHANGED
@@ -719,6 +719,36 @@ export function App({ createAgentLoop, agentConfig, initialSession = null, onExi
719
719
  updateLastEvent(ev => ({ ...ev, content: text }));
720
720
  }
721
721
  },
722
+ onRetryStatus: (text) => {
723
+ const arr = eventsRef.current;
724
+ const findRetryIdx = () => {
725
+ for (let i = arr.length - 1; i >= 0; i--) {
726
+ if (arr[i].type === 'retry_status') return i;
727
+ }
728
+ return -1;
729
+ };
730
+ if (!text) {
731
+ const idx = findRetryIdx();
732
+ if (idx !== -1) {
733
+ const newArr = [...arr];
734
+ newArr.splice(idx, 1);
735
+ eventsRef.current = newArr;
736
+ flushEventsToState(false);
737
+ }
738
+ return;
739
+ }
740
+ const cleanedText = sanitizeUiText(text);
741
+ if (!cleanedText.trim()) return;
742
+ const idx = findRetryIdx();
743
+ if (idx !== -1) {
744
+ const newArr = [...arr];
745
+ newArr[idx] = { ...newArr[idx], content: cleanedText };
746
+ eventsRef.current = newArr;
747
+ flushEventsToState(false);
748
+ } else {
749
+ addEvent({ type: 'retry_status', content: cleanedText });
750
+ }
751
+ },
722
752
  onToolStart: (toolCall) => {
723
753
  if (toolCall.tool === 'ASK_USER') return;
724
754
  const uid = ++toolUiIdCounterRef.current;
@@ -928,6 +928,8 @@ function renderEvent(ev, i, events, expandedOutputIndexes, thoughtStreaming, exp
928
928
  const expanded = thoughtId != null && expandedThoughtIds && expandedThoughtIds.has(thoughtId);
929
929
  return h(CollapsibleThought, { key: `thought_${thoughtId ?? i}`, id: thoughtId, content: ev.content, expanded, streaming: thoughtStreaming, animate });
930
930
  }
931
+ case 'retry_status':
932
+ return h(Box, { key: i, paddingLeft: 2 }, h(Text, { color: 'red' }, ev.content));
931
933
  case 'text':
932
934
  return h(TextContent, { key: i, content: ev.content, events, animate });
933
935
  case 'tool_start':