kiosapi 0.1.11 → 0.1.13

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 (2) hide show
  1. package/dist/agent/run.js +27 -8
  2. package/package.json +1 -1
package/dist/agent/run.js CHANGED
@@ -20,6 +20,8 @@ const MAX_CONTEXT_MESSAGES = 40;
20
20
  *
21
21
  * The kept slice always:
22
22
  * - starts with the original system prompt
23
+ * - removes any assistant messages with null content and no tool_calls (invalid for providers;
24
+ * they appear when a stream ends prematurely or the model produces reasoning-only output)
23
25
  * - contains at most MAX_CONTEXT_MESSAGES non-system messages
24
26
  * - begins on a `user` turn boundary (never mid tool-call sequence, which providers reject)
25
27
  * - prepends a system note so the model knows early context was dropped
@@ -31,19 +33,29 @@ function trimContext(messages) {
31
33
  if (messages.length === 0)
32
34
  return messages;
33
35
  const [system, ...rest] = messages;
34
- if (rest.length <= MAX_CONTEXT_MESSAGES)
35
- return messages;
36
+ // Sanitize: assistant messages with no content AND no tool_calls are invalid for providers
37
+ // (they arise from truncated streams or reasoning-only model steps). Remove them so they
38
+ // cannot corrupt subsequent API calls.
39
+ const clean = rest.filter((m) => {
40
+ if (m.role !== 'assistant')
41
+ return true;
42
+ return m.content !== null || (m.tool_calls?.length ?? 0) > 0;
43
+ });
44
+ if (clean.length <= MAX_CONTEXT_MESSAGES) {
45
+ // Return original if nothing changed (avoids allocation on the common path)
46
+ return clean.length === rest.length ? messages : [system, ...clean];
47
+ }
36
48
  // Take the tail we want to keep, then advance past any leading tool/assistant messages so
37
49
  // the slice always starts on a complete user turn. Orphaned tool results (whose paired
38
50
  // assistant message was trimmed) cause provider errors on the next API call.
39
- let tail = rest.slice(-MAX_CONTEXT_MESSAGES);
51
+ let tail = clean.slice(-MAX_CONTEXT_MESSAGES);
40
52
  let skip = 0;
41
53
  while (skip < tail.length && tail[skip]?.role !== 'user')
42
54
  skip++;
43
55
  tail = tail.slice(skip);
44
- const dropped = rest.length - tail.length;
56
+ const dropped = clean.length - tail.length;
45
57
  if (dropped <= 0)
46
- return messages; // nothing actually trimmed
58
+ return [system, ...clean];
47
59
  const note = {
48
60
  role: 'system',
49
61
  content: `[Kiosapi: ${dropped} pesan awal dihapus dari konteks untuk menghemat token. Lanjutkan dari konteks terkini di bawah.]`,
@@ -401,13 +413,20 @@ export async function runTurn(s, userText) {
401
413
  totalIn += reply.usage.promptTokens;
402
414
  totalOut += reply.usage.completionTokens;
403
415
  }
404
- s.messages.push({ role: 'assistant', content: reply.content, tool_calls: reply.tool_calls });
416
+ const calls = reply.tool_calls ?? [];
417
+ // Only push the assistant message when it carries meaningful content. An empty response
418
+ // (null content + no tool_calls) can appear from a truncated stream or a reasoning-only
419
+ // step; pushing it corrupts the history and causes providers to reject subsequent calls.
420
+ if (reply.content !== null || calls.length > 0) {
421
+ s.messages.push({ role: 'assistant', content: reply.content, tool_calls: reply.tool_calls });
422
+ }
405
423
  if (reply.content)
406
424
  lastText = reply.content;
407
- const calls = reply.tool_calls ?? [];
408
425
  if (calls.length === 0) {
409
426
  if (step === 0) {
410
- console.log(dim('(Model tidak memakai tool — pilih model ber-🔧 untuk agen, mis. /model deepseek/deepseek-v4-flash.)'));
427
+ console.log(dim(reply.content
428
+ ? '(Model tidak memakai tool — pilih model ber-🔧 untuk agen, mis. /model deepseek/deepseek-v4-flash.)'
429
+ : '(Model tidak merespons — coba ulangi atau ganti model.)'));
411
430
  }
412
431
  showUsage();
413
432
  s.totalTokens += totalIn + totalOut;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiosapi",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "type": "module",
5
5
  "description": "CLI Kiosapi.id berbahasa Indonesia — bangun aplikasimu pakai API key Kiosapi (agen + multimodal).",
6
6
  "keywords": [