ns-omp-provider-kiro 0.3.7 → 0.3.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.
Files changed (2) hide show
  1. package/dist/index.js +216 -11
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3566,12 +3566,177 @@ function countTokens(text) {
3566
3566
  return getEncoder().encode(text).length;
3567
3567
  }
3568
3568
 
3569
+ // ../kiro-core/dist/tool-name-aliases.js
3570
+ var TOOL_NAME_ALIASES = {
3571
+ // read
3572
+ read_file: "read",
3573
+ readfile: "read",
3574
+ fs_read: "read",
3575
+ view_file: "read",
3576
+ // write
3577
+ write_file: "write",
3578
+ writefile: "write",
3579
+ fs_write: "write",
3580
+ create_file: "write",
3581
+ // edit
3582
+ edit_file: "edit",
3583
+ editfile: "edit",
3584
+ str_replace: "edit",
3585
+ str_replace_editor: "edit",
3586
+ str_replace_based_edit_tool: "edit",
3587
+ apply_patch: "edit",
3588
+ // ls
3589
+ list_directory: "ls",
3590
+ list_dir: "ls",
3591
+ list_files: "ls",
3592
+ // find
3593
+ find_files: "find",
3594
+ file_search: "find"
3595
+ };
3596
+ function normalizeKiroToolName(name, declared) {
3597
+ if (!declared || declared.size === 0 || declared.has(name))
3598
+ return name;
3599
+ const alias = TOOL_NAME_ALIASES[name.toLowerCase()];
3600
+ return alias !== void 0 && declared.has(alias) ? alias : name;
3601
+ }
3602
+
3603
+ // ../kiro-core/dist/tool-use-parser.js
3604
+ var TAG_NAMES = ["tool_use", "tool_call", "function_call", "tool"];
3605
+ var FENCE2 = "```";
3606
+ function nextTagBlock(text, from) {
3607
+ let best = null;
3608
+ for (const name of TAG_NAMES) {
3609
+ const open = `<${name}>`;
3610
+ const close = `</${name}>`;
3611
+ const openStart = text.indexOf(open, from);
3612
+ if (openStart < 0)
3613
+ continue;
3614
+ if (best && openStart >= best.openStart)
3615
+ continue;
3616
+ const bodyStart = openStart + open.length;
3617
+ const closeIdx = text.indexOf(close, bodyStart);
3618
+ if (closeIdx < 0)
3619
+ continue;
3620
+ best = { openStart, bodyStart, closeIdx, closeEnd: closeIdx + close.length };
3621
+ }
3622
+ return best;
3623
+ }
3624
+ function fencedRanges2(text) {
3625
+ const ranges = [];
3626
+ let searchFrom = 0;
3627
+ while (true) {
3628
+ const open = text.indexOf(FENCE2, searchFrom);
3629
+ if (open < 0)
3630
+ break;
3631
+ const close = text.indexOf(FENCE2, open + FENCE2.length);
3632
+ if (close < 0) {
3633
+ ranges.push({ start: open, end: text.length });
3634
+ break;
3635
+ }
3636
+ ranges.push({ start: open, end: close + FENCE2.length });
3637
+ searchFrom = close + FENCE2.length;
3638
+ }
3639
+ return ranges;
3640
+ }
3641
+ function normalizeOne(value) {
3642
+ if (typeof value !== "object" || value === null || Array.isArray(value))
3643
+ return null;
3644
+ const record = value;
3645
+ const rawName = record.tool_name ?? record.name;
3646
+ if (typeof rawName !== "string" || rawName.length === 0)
3647
+ return null;
3648
+ const rawArgs = record.tool_input ?? record.input ?? record.arguments;
3649
+ if (rawArgs === void 0) {
3650
+ return { name: rawName, arguments: {} };
3651
+ }
3652
+ if (typeof rawArgs !== "object" || rawArgs === null || Array.isArray(rawArgs))
3653
+ return null;
3654
+ return { name: rawName, arguments: rawArgs };
3655
+ }
3656
+ function extractDescriptors(value) {
3657
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3658
+ const record = value;
3659
+ const wrapped = record.tool_calls ?? record.calls;
3660
+ if (Array.isArray(wrapped)) {
3661
+ const out = [];
3662
+ for (const entry of wrapped) {
3663
+ const one = normalizeOne(entry);
3664
+ if (one === null)
3665
+ return [];
3666
+ out.push(one);
3667
+ }
3668
+ return out;
3669
+ }
3670
+ }
3671
+ const single = normalizeOne(value);
3672
+ return single ? [single] : [];
3673
+ }
3674
+ function parseToolUseCalls(text) {
3675
+ const toolCalls = [];
3676
+ const removals = [];
3677
+ const fences = fencedRanges2(text);
3678
+ let cursor = 0;
3679
+ while (cursor < text.length) {
3680
+ const hit = nextTagBlock(text, cursor);
3681
+ if (hit === null)
3682
+ break;
3683
+ const { openStart, bodyStart, closeIdx, closeEnd } = hit;
3684
+ const containingFence = fences.find((range) => openStart >= range.start && openStart < range.end);
3685
+ if (containingFence) {
3686
+ cursor = containingFence.end;
3687
+ continue;
3688
+ }
3689
+ const braceStart = text.indexOf("{", bodyStart);
3690
+ if (braceStart < 0 || braceStart > closeIdx) {
3691
+ cursor = closeEnd;
3692
+ continue;
3693
+ }
3694
+ const braceEnd = findJsonEnd(text, braceStart);
3695
+ if (braceEnd < 0 || braceEnd > closeIdx) {
3696
+ cursor = closeEnd;
3697
+ continue;
3698
+ }
3699
+ const beforeJson = text.substring(bodyStart, braceStart).trim();
3700
+ const afterJson = text.substring(braceEnd + 1, closeIdx).trim();
3701
+ if (beforeJson.length > 0 || afterJson.length > 0) {
3702
+ cursor = closeEnd;
3703
+ continue;
3704
+ }
3705
+ let descriptors = [];
3706
+ try {
3707
+ descriptors = extractDescriptors(JSON.parse(text.substring(braceStart, braceEnd + 1)));
3708
+ } catch {
3709
+ descriptors = [];
3710
+ }
3711
+ if (descriptors.length === 0) {
3712
+ cursor = closeEnd;
3713
+ continue;
3714
+ }
3715
+ for (const descriptor of descriptors) {
3716
+ toolCalls.push({
3717
+ toolUseId: crypto.randomUUID(),
3718
+ name: descriptor.name,
3719
+ arguments: descriptor.arguments
3720
+ });
3721
+ }
3722
+ removals.push({ start: openStart, end: closeEnd });
3723
+ cursor = closeEnd;
3724
+ }
3725
+ let cleanedText = text;
3726
+ for (let i = removals.length - 1; i >= 0; i--) {
3727
+ const { start, end } = removals[i];
3728
+ cleanedText = cleanedText.substring(0, start) + cleanedText.substring(end);
3729
+ }
3730
+ return { toolCalls, cleanedText };
3731
+ }
3732
+
3569
3733
  // ../kiro-core/dist/response-assembler.js
3570
3734
  var ECHO_NOISE_PATTERN = /^\s*(continue|\.+)\s*$/i;
3571
3735
  var KiroResponseAssembler = class {
3572
3736
  model;
3573
3737
  thinkingEnabled;
3574
3738
  toolNameAliases;
3739
+ declaredToolNames;
3575
3740
  pending = [];
3576
3741
  blocks = new KiroBlockBuffer((event) => this.pending.push(event));
3577
3742
  usage = {
@@ -3581,7 +3746,6 @@ var KiroResponseAssembler = class {
3581
3746
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
3582
3747
  };
3583
3748
  totalContent = "";
3584
- lastContentData = "";
3585
3749
  usageEvent = null;
3586
3750
  meteringEvent = null;
3587
3751
  receivedContextUsage = false;
@@ -3594,18 +3758,19 @@ var KiroResponseAssembler = class {
3594
3758
  droppedToolCalls = [];
3595
3759
  currentToolCall = null;
3596
3760
  stopReason = "stop";
3597
- constructor(model, thinkingEnabled, toolNameAliases) {
3761
+ constructor(model, thinkingEnabled, toolNameAliases, declaredToolNames) {
3598
3762
  this.model = model;
3599
3763
  this.thinkingEnabled = thinkingEnabled;
3600
3764
  this.toolNameAliases = toolNameAliases;
3765
+ this.declaredToolNames = declaredToolNames;
3601
3766
  }
3767
+ /** Wire name -> the name the host registered, then alias repair. */
3602
3768
  originalToolName(name) {
3603
- return this.toolNameAliases?.get(name) ?? name;
3769
+ return normalizeKiroToolName(this.toolNameAliases?.get(name) ?? name, this.declaredToolNames);
3604
3770
  }
3605
3771
  /** Clear per-attempt state. Only block indexes are kept. */
3606
3772
  beginAttempt() {
3607
3773
  this.totalContent = "";
3608
- this.lastContentData = "";
3609
3774
  this.usageEvent = null;
3610
3775
  this.meteringEvent = null;
3611
3776
  this.receivedContextUsage = false;
@@ -3661,10 +3826,9 @@ var KiroResponseAssembler = class {
3661
3826
  break;
3662
3827
  }
3663
3828
  case "content": {
3664
- this.endNativeThinking();
3665
- if (event.data === this.lastContentData)
3829
+ if (event.data === "")
3666
3830
  break;
3667
- this.lastContentData = event.data;
3831
+ this.endNativeThinking();
3668
3832
  this.totalContent += event.data;
3669
3833
  if (this.thinkingParser) {
3670
3834
  this.thinkingParser.processChunk(event.data);
@@ -3703,6 +3867,14 @@ var KiroResponseAssembler = class {
3703
3867
  }
3704
3868
  case "usage": {
3705
3869
  this.usageEvent = { ...this.usageEvent ?? {}, ...event.data };
3870
+ const pct = event.data.contextUsagePercentage;
3871
+ if (pct !== void 0) {
3872
+ if (this.usageEvent.inputTokens === void 0) {
3873
+ this.usage.input = Math.round(pct / 100 * this.model.contextWindow);
3874
+ }
3875
+ this.usage.contextPercent = pct;
3876
+ this.receivedContextUsage = true;
3877
+ }
3706
3878
  if (debugEnabled())
3707
3879
  debugLog("response.usageRaw", payload);
3708
3880
  break;
@@ -3736,7 +3908,18 @@ var KiroResponseAssembler = class {
3736
3908
  this.stripEchoNoise();
3737
3909
  const responseText = this.textBlockIndex === null ? "" : this.blocks.getText(this.textBlockIndex);
3738
3910
  const hasText2 = responseText.length > 0;
3739
- this.stopReason = !this.receivedContextUsage && this.emittedToolCalls === 0 ? "length" : this.emittedToolCalls > 0 ? "toolUse" : "stop";
3911
+ const wireStopReason = this.usageEvent?.rawStopReason;
3912
+ switch (wireStopReason) {
3913
+ case "MAX_TOKENS":
3914
+ this.stopReason = "length";
3915
+ break;
3916
+ case "END_TURN":
3917
+ case "TOOL_USE":
3918
+ this.stopReason = this.emittedToolCalls > 0 ? "toolUse" : "stop";
3919
+ break;
3920
+ default:
3921
+ this.stopReason = !this.receivedContextUsage && this.emittedToolCalls === 0 ? "length" : this.emittedToolCalls > 0 ? "toolUse" : "stop";
3922
+ }
3740
3923
  return {
3741
3924
  responseText,
3742
3925
  hasText: hasText2,
@@ -3745,7 +3928,9 @@ var KiroResponseAssembler = class {
3745
3928
  isEchoLoop: hasText2 && !this.sawAnyToolCalls && ECHO_NOISE_PATTERN.test(responseText),
3746
3929
  isEmpty: !hasText2 && !this.sawAnyToolCalls,
3747
3930
  stopReason: this.stopReason,
3748
- droppedToolCalls: this.droppedToolCalls
3931
+ droppedToolCalls: this.droppedToolCalls,
3932
+ ...wireStopReason !== void 0 ? { wireStopReason } : {},
3933
+ ...this.usageEvent?.stopDetails !== void 0 ? { wireStopDetails: this.usageEvent.stopDetails } : {}
3749
3934
  };
3750
3935
  }
3751
3936
  /** Drop an echo the caller decided not to retry, so it is not read as a continuation signal. */
@@ -3829,10 +4014,14 @@ var KiroResponseAssembler = class {
3829
4014
  }
3830
4015
  /**
3831
4016
  * Extract text-dialect tool calls from content when no native tool calls
3832
- * arrived. Two dialects are recovered at this seam:
4017
+ * arrived. Three dialects are recovered at this seam:
3833
4018
  * 1. Kiro's own `[Called name with args: {...}]` bracket form.
3834
4019
  * 2. Anthropic's `<invoke name="..."><parameter .../></invoke>` XML form,
3835
4020
  * which opus-class models emit as plain text at high context.
4021
+ * 3. A JSON descriptor wrapped in `<tool_use>`, `<tool_call>`,
4022
+ * `<function_call>` or `<tool>` tags (upstream #163), spelled
4023
+ * `tool_name`/`tool_input`, `name`/`input` or `name`/`arguments`,
4024
+ * or wrapped in a `{"tool_calls": [...]}` array.
3836
4025
  * Without this, the turn ends `stop` with zero tool calls — the agent loop
3837
4026
  * sees a finished answer and an unattended session stalls with no error
3838
4027
  * recorded anywhere.
@@ -3859,6 +4048,11 @@ var KiroResponseAssembler = class {
3859
4048
  text = invokeResult.cleanedText;
3860
4049
  recovered.push(...invokeResult.toolCalls);
3861
4050
  }
4051
+ const toolUseResult = parseToolUseCalls(text);
4052
+ if (toolUseResult.toolCalls.length > 0) {
4053
+ text = toolUseResult.cleanedText;
4054
+ recovered.push(...toolUseResult.toolCalls);
4055
+ }
3862
4056
  if (recovered.length === 0)
3863
4057
  return;
3864
4058
  this.blocks.setText(this.textBlockIndex, text);
@@ -4909,6 +5103,16 @@ function describeReturnedContent(kinds) {
4909
5103
  }
4910
5104
  var skipProfileResolutionForTests = false;
4911
5105
  var TEST_PROFILE_ARN = "arn:aws:codewhisperer:us-east-1:000000000000:profile/test";
5106
+ function throwOnTerminalWireStop(wireStopReason, stopDetails) {
5107
+ switch (wireStopReason) {
5108
+ case "MODEL_CONTEXT_WINDOW_EXCEEDED":
5109
+ throw new Error("Kiro API error: context_length_exceeded (MODEL_CONTEXT_WINDOW_EXCEEDED)");
5110
+ case "CONTENT_FILTERED":
5111
+ throw new Error(`Kiro content filtered: ${clampForDiagnostic(redactSensitiveText(JSON.stringify(stopDetails ?? {})))}`);
5112
+ case "PAUSE_TURN":
5113
+ throw new Error("Kiro paused the turn (PAUSE_TURN); automatic continuation is not supported");
5114
+ }
5115
+ }
4912
5116
  async function* streamKiro(request) {
4913
5117
  const { model, signal } = request;
4914
5118
  const initialAccessToken = request.accessToken;
@@ -4967,7 +5171,7 @@ async function* streamKiro(request) {
4967
5171
  systemPrompt = `<thinking_mode>enabled</thinking_mode><max_thinking_length>${budget}</max_thinking_length>${systemPrompt ? `
4968
5172
  ${systemPrompt}` : ""}`;
4969
5173
  }
4970
- const assembler = new KiroResponseAssembler(model, thinkingEnabled, kiroToolNameAliases(request.tools));
5174
+ const assembler = new KiroResponseAssembler(model, thinkingEnabled, kiroToolNameAliases(request.tools), new Set((request.tools ?? []).map((tool) => String(tool.name))));
4971
5175
  const usageTracking = request.usageTracking ?? KIRO_USAGE_TRACKING_DISABLED;
4972
5176
  let retryCount = 0;
4973
5177
  const maxRetries = 3;
@@ -5151,6 +5355,7 @@ ${systemPrompt}` : ""}`;
5151
5355
  throw new Error(`Kiro API error: ${outcome.firstTokenTimedOut ? "first token" : "idle"} timeout after max retries`);
5152
5356
  }
5153
5357
  const summary = assembler.endTurn();
5358
+ throwOnTerminalWireStop(summary.wireStopReason, summary.wireStopDetails);
5154
5359
  yield* assembler.takeEvents();
5155
5360
  const degenerate = summary.isEmpty || summary.isEchoLoop;
5156
5361
  if (summary.isEchoLoop)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ns-omp-provider-kiro",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "OMP (oh-my-pi) provider plugin: adds Kiro (AWS CodeWhisperer/Q) models via your kiro-cli login",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,7 +37,7 @@
37
37
  "README.md"
38
38
  ],
39
39
  "dependencies": {
40
- "ns-kiro-core": "0.3.7"
40
+ "ns-kiro-core": "0.3.8"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@oh-my-pi/pi-ai": ">=18.2.0",