@javargasm/pi-kiro 0.4.2 → 0.4.4
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/CHANGELOG.md +18 -0
- package/dist/core.js +2272 -2414
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +2327 -2419
- package/dist/kiro-defaults.d.ts +1 -1
- package/dist/models.d.ts +1 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/oauth.d.ts +2 -0
- package/dist/oauth.d.ts.map +1 -1
- package/dist/stream.d.ts +6 -8
- package/dist/stream.d.ts.map +1 -1
- package/dist/transform.d.ts +1 -2
- package/dist/transform.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/core.js
CHANGED
|
@@ -549,2520 +549,2378 @@ var init_kiro_cli_sync = __esm(() => {
|
|
|
549
549
|
// src/oauth.ts
|
|
550
550
|
init_debug();
|
|
551
551
|
|
|
552
|
-
// src/
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
else if (char === "}") {
|
|
580
|
-
braceCount--;
|
|
581
|
-
if (braceCount === 0)
|
|
582
|
-
return i;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
return -1;
|
|
552
|
+
// src/models.ts
|
|
553
|
+
var KIRO_MODEL_IDS = new Set([
|
|
554
|
+
"claude-fable-5",
|
|
555
|
+
"claude-opus-4.8",
|
|
556
|
+
"claude-opus-4.7",
|
|
557
|
+
"claude-opus-4.6",
|
|
558
|
+
"claude-opus-4.6-1m",
|
|
559
|
+
"claude-sonnet-4.6",
|
|
560
|
+
"claude-sonnet-4.6-1m",
|
|
561
|
+
"claude-opus-4.5",
|
|
562
|
+
"claude-sonnet-4.5",
|
|
563
|
+
"claude-sonnet-4.5-1m",
|
|
564
|
+
"claude-sonnet-4",
|
|
565
|
+
"claude-haiku-4.5",
|
|
566
|
+
"deepseek-3.2",
|
|
567
|
+
"kimi-k2.5",
|
|
568
|
+
"minimax-m2.1",
|
|
569
|
+
"minimax-m2.5",
|
|
570
|
+
"glm-4.7",
|
|
571
|
+
"glm-4.7-flash",
|
|
572
|
+
"qwen3-coder-next",
|
|
573
|
+
"agi-nova-beta-1m",
|
|
574
|
+
"qwen3-coder-480b",
|
|
575
|
+
"auto"
|
|
576
|
+
]);
|
|
577
|
+
function dashToDot(modelId) {
|
|
578
|
+
return modelId.replace(/(\d)-(\d)/g, "$1.$2");
|
|
587
579
|
}
|
|
588
|
-
function
|
|
589
|
-
|
|
590
|
-
return { type: "content", data: parsed.content };
|
|
591
|
-
}
|
|
592
|
-
if (parsed.reasoningText !== undefined || parsed.signature !== undefined || parsed.text !== undefined && !parsed.content && !parsed.name && !parsed.message) {
|
|
593
|
-
let text = "";
|
|
594
|
-
let signature;
|
|
595
|
-
if (parsed.reasoningText) {
|
|
596
|
-
const rt = parsed.reasoningText;
|
|
597
|
-
text = (rt.text ?? rt.Text) || "";
|
|
598
|
-
signature = rt.signature ?? rt.Signature;
|
|
599
|
-
} else {
|
|
600
|
-
text = parsed.text || "";
|
|
601
|
-
signature = parsed.signature;
|
|
602
|
-
}
|
|
603
|
-
return {
|
|
604
|
-
type: "reasoning",
|
|
605
|
-
data: { text, signature }
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
if (parsed.name && parsed.toolUseId) {
|
|
609
|
-
const rawInput = parsed.input;
|
|
610
|
-
const input = typeof rawInput === "string" ? rawInput : rawInput && typeof rawInput === "object" && Object.keys(rawInput).length > 0 ? JSON.stringify(rawInput) : "";
|
|
611
|
-
return {
|
|
612
|
-
type: "toolUse",
|
|
613
|
-
data: {
|
|
614
|
-
name: parsed.name,
|
|
615
|
-
toolUseId: parsed.toolUseId,
|
|
616
|
-
input,
|
|
617
|
-
stop: parsed.stop
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
if (parsed.input !== undefined && !parsed.name) {
|
|
622
|
-
return {
|
|
623
|
-
type: "toolUseInput",
|
|
624
|
-
data: {
|
|
625
|
-
input: typeof parsed.input === "string" ? parsed.input : JSON.stringify(parsed.input)
|
|
626
|
-
}
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
if (parsed.stop !== undefined && parsed.contextUsagePercentage === undefined) {
|
|
630
|
-
return { type: "toolUseStop", data: { stop: parsed.stop } };
|
|
631
|
-
}
|
|
632
|
-
if (parsed.contextUsagePercentage !== undefined) {
|
|
633
|
-
return {
|
|
634
|
-
type: "contextUsage",
|
|
635
|
-
data: { contextUsagePercentage: parsed.contextUsagePercentage }
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
if (parsed.followupPrompt !== undefined) {
|
|
639
|
-
return { type: "followupPrompt", data: parsed.followupPrompt };
|
|
640
|
-
}
|
|
641
|
-
if (parsed.error !== undefined || parsed.Error !== undefined) {
|
|
642
|
-
const err = parsed.error || parsed.Error || "unknown";
|
|
643
|
-
const message = parsed.message || parsed.Message || parsed.reason;
|
|
644
|
-
return {
|
|
645
|
-
type: "error",
|
|
646
|
-
data: {
|
|
647
|
-
error: typeof err === "string" ? err : JSON.stringify(err),
|
|
648
|
-
message
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
if (parsed.usage !== undefined) {
|
|
653
|
-
const u = parsed.usage;
|
|
654
|
-
return {
|
|
655
|
-
type: "usage",
|
|
656
|
-
data: {
|
|
657
|
-
inputTokens: u.inputTokens,
|
|
658
|
-
outputTokens: u.outputTokens
|
|
659
|
-
}
|
|
660
|
-
};
|
|
661
|
-
}
|
|
662
|
-
return null;
|
|
580
|
+
function dotToDash(modelId) {
|
|
581
|
+
return modelId.replace(/(\d)\.(\d)/g, "$1-$2");
|
|
663
582
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
'{"text":',
|
|
669
|
-
'{"name":',
|
|
670
|
-
'{"input":',
|
|
671
|
-
'{"stop":',
|
|
672
|
-
'{"contextUsagePercentage":',
|
|
673
|
-
'{"followupPrompt":',
|
|
674
|
-
'{"usage":',
|
|
675
|
-
'{"toolUseId":',
|
|
676
|
-
'{"unit":',
|
|
677
|
-
'{"error":',
|
|
678
|
-
'{"Error":',
|
|
679
|
-
'{"message":'
|
|
680
|
-
];
|
|
681
|
-
function findNextEventStart(buffer, from) {
|
|
682
|
-
let earliest = -1;
|
|
683
|
-
for (const pattern of EVENT_PATTERNS) {
|
|
684
|
-
const idx = buffer.indexOf(pattern, from);
|
|
685
|
-
if (idx >= 0 && (earliest < 0 || idx < earliest))
|
|
686
|
-
earliest = idx;
|
|
583
|
+
function resolveKiroModel(modelId) {
|
|
584
|
+
const kiroId = dashToDot(modelId);
|
|
585
|
+
if (!KIRO_MODEL_IDS.has(kiroId)) {
|
|
586
|
+
throw new Error(`Unknown Kiro model ID: ${modelId}`);
|
|
687
587
|
}
|
|
688
|
-
return
|
|
588
|
+
return kiroId;
|
|
689
589
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
const jsonEnd = findJsonEnd(buffer, jsonStart);
|
|
719
|
-
if (jsonEnd < 0) {
|
|
720
|
-
return { events, remaining: buffer.substring(jsonStart) };
|
|
721
|
-
}
|
|
722
|
-
try {
|
|
723
|
-
const parsed = JSON.parse(buffer.substring(jsonStart, jsonEnd + 1));
|
|
724
|
-
const event = parseKiroEvent(parsed);
|
|
725
|
-
if (event) {
|
|
726
|
-
events.push(event);
|
|
727
|
-
} else if (log.isDebug()) {
|
|
728
|
-
log.debug("event.unknown", { keys: Object.keys(parsed), raw: parsed });
|
|
729
|
-
}
|
|
730
|
-
} catch (err) {
|
|
731
|
-
if (log.isDebug()) {
|
|
732
|
-
log.debug("event.parseFail", {
|
|
733
|
-
err: err instanceof Error ? err.message : String(err),
|
|
734
|
-
snippet: buffer.substring(jsonStart, Math.min(jsonEnd + 1, jsonStart + 200))
|
|
735
|
-
});
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
pos = jsonEnd + 1;
|
|
739
|
-
}
|
|
740
|
-
return { events, remaining: "" };
|
|
590
|
+
var API_REGION_MAP = {
|
|
591
|
+
"us-west-1": "us-east-1",
|
|
592
|
+
"us-west-2": "us-east-1",
|
|
593
|
+
"us-east-2": "us-east-1",
|
|
594
|
+
"eu-west-1": "eu-central-1",
|
|
595
|
+
"eu-west-2": "eu-central-1",
|
|
596
|
+
"eu-west-3": "eu-central-1",
|
|
597
|
+
"eu-north-1": "eu-central-1",
|
|
598
|
+
"eu-south-1": "eu-central-1",
|
|
599
|
+
"eu-south-2": "eu-central-1",
|
|
600
|
+
"eu-central-2": "eu-central-1",
|
|
601
|
+
"ap-northeast-1": "us-east-1",
|
|
602
|
+
"ap-northeast-2": "us-east-1",
|
|
603
|
+
"ap-northeast-3": "us-east-1",
|
|
604
|
+
"ap-southeast-1": "us-east-1",
|
|
605
|
+
"ap-southeast-2": "us-east-1",
|
|
606
|
+
"ap-south-1": "us-east-1",
|
|
607
|
+
"ap-east-1": "us-east-1",
|
|
608
|
+
"ap-south-2": "us-east-1",
|
|
609
|
+
"ap-southeast-3": "us-east-1",
|
|
610
|
+
"ap-southeast-4": "us-east-1"
|
|
611
|
+
};
|
|
612
|
+
function resolveApiRegion(ssoRegion) {
|
|
613
|
+
if (!ssoRegion)
|
|
614
|
+
return "us-east-1";
|
|
615
|
+
return API_REGION_MAP[ssoRegion] ?? ssoRegion;
|
|
741
616
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
617
|
+
var MODELS_BY_REGION = {
|
|
618
|
+
"us-east-1": new Set([
|
|
619
|
+
"claude-fable-5",
|
|
620
|
+
"claude-opus-4-8",
|
|
621
|
+
"claude-opus-4-7",
|
|
622
|
+
"claude-opus-4-6",
|
|
623
|
+
"claude-opus-4-6-1m",
|
|
624
|
+
"claude-sonnet-4-6",
|
|
625
|
+
"claude-sonnet-4-6-1m",
|
|
626
|
+
"claude-opus-4-5",
|
|
627
|
+
"claude-sonnet-4-5",
|
|
628
|
+
"claude-sonnet-4-5-1m",
|
|
629
|
+
"claude-sonnet-4",
|
|
630
|
+
"claude-haiku-4-5",
|
|
631
|
+
"deepseek-3-2",
|
|
632
|
+
"kimi-k2-5",
|
|
633
|
+
"minimax-m2-1",
|
|
634
|
+
"minimax-m2-5",
|
|
635
|
+
"glm-4-7",
|
|
636
|
+
"glm-4-7-flash",
|
|
637
|
+
"qwen3-coder-next",
|
|
638
|
+
"qwen3-coder-480b",
|
|
639
|
+
"agi-nova-beta-1m",
|
|
640
|
+
"auto"
|
|
641
|
+
]),
|
|
642
|
+
"eu-central-1": new Set([
|
|
643
|
+
"claude-fable-5",
|
|
644
|
+
"claude-opus-4-8",
|
|
645
|
+
"claude-opus-4-7",
|
|
646
|
+
"claude-opus-4-6",
|
|
647
|
+
"claude-sonnet-4-6",
|
|
648
|
+
"claude-opus-4-5",
|
|
649
|
+
"claude-sonnet-4-5",
|
|
650
|
+
"claude-sonnet-4",
|
|
651
|
+
"claude-haiku-4-5",
|
|
652
|
+
"minimax-m2-1",
|
|
653
|
+
"minimax-m2-5",
|
|
654
|
+
"qwen3-coder-next",
|
|
655
|
+
"auto"
|
|
656
|
+
])
|
|
657
|
+
};
|
|
658
|
+
function filterModelsByRegion(models, apiRegion) {
|
|
659
|
+
const allowed = MODELS_BY_REGION[apiRegion];
|
|
660
|
+
if (!allowed) {
|
|
661
|
+
console.warn(`[pi-kiro] Unknown API region "${apiRegion}" — no models available. Update MODELS_BY_REGION in models.ts.`);
|
|
662
|
+
return [];
|
|
775
663
|
}
|
|
776
|
-
return
|
|
664
|
+
return models.filter((m) => allowed.has(m.id));
|
|
777
665
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
return
|
|
666
|
+
var RUNTIME_ENDPOINTS = {
|
|
667
|
+
"us-east-1": "https://runtime.us-east-1.kiro.dev",
|
|
668
|
+
"eu-central-1": "https://runtime.eu-central-1.kiro.dev"
|
|
669
|
+
};
|
|
670
|
+
function resolveRuntimeUrl(apiRegion) {
|
|
671
|
+
return RUNTIME_ENDPOINTS[apiRegion] ?? `https://runtime.${apiRegion}.kiro.dev`;
|
|
784
672
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
673
|
+
var BASE_URL = resolveRuntimeUrl("us-east-1");
|
|
674
|
+
var ZERO_COST = Object.freeze({ input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
|
|
675
|
+
var KIRO_DEFAULTS = {
|
|
676
|
+
api: "kiro-api",
|
|
677
|
+
provider: "kiro",
|
|
678
|
+
baseUrl: BASE_URL,
|
|
679
|
+
cost: ZERO_COST
|
|
680
|
+
};
|
|
681
|
+
var MULTIMODAL = ["text", "image"];
|
|
682
|
+
var TEXT_ONLY = ["text"];
|
|
683
|
+
var kiroModels = [
|
|
684
|
+
{
|
|
685
|
+
...KIRO_DEFAULTS,
|
|
686
|
+
id: "claude-fable-5",
|
|
687
|
+
name: "Claude Fable 5",
|
|
688
|
+
reasoning: true,
|
|
689
|
+
input: MULTIMODAL,
|
|
690
|
+
contextWindow: 1e6,
|
|
691
|
+
maxTokens: 128000,
|
|
692
|
+
firstTokenTimeout: 180000,
|
|
693
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
694
|
+
supportsThinkingConfig: true
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
...KIRO_DEFAULTS,
|
|
698
|
+
id: "claude-opus-4-8",
|
|
699
|
+
name: "Claude Opus 4.8",
|
|
700
|
+
reasoning: true,
|
|
701
|
+
input: MULTIMODAL,
|
|
702
|
+
contextWindow: 1e6,
|
|
703
|
+
maxTokens: 128000,
|
|
704
|
+
firstTokenTimeout: 180000,
|
|
705
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
706
|
+
supportsThinkingConfig: true
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
...KIRO_DEFAULTS,
|
|
710
|
+
id: "claude-opus-4-7",
|
|
711
|
+
name: "Claude Opus 4.7",
|
|
712
|
+
reasoning: true,
|
|
713
|
+
input: MULTIMODAL,
|
|
714
|
+
contextWindow: 1e6,
|
|
715
|
+
maxTokens: 128000,
|
|
716
|
+
firstTokenTimeout: 180000,
|
|
717
|
+
supportedEfforts: ["low", "medium", "high", "xhigh", "max"],
|
|
718
|
+
supportsThinkingConfig: true
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
...KIRO_DEFAULTS,
|
|
722
|
+
id: "claude-opus-4-6",
|
|
723
|
+
name: "Claude Opus 4.6",
|
|
724
|
+
reasoning: true,
|
|
725
|
+
input: MULTIMODAL,
|
|
726
|
+
contextWindow: 1e6,
|
|
727
|
+
maxTokens: 64000,
|
|
728
|
+
supportedEfforts: ["low", "medium", "high", "max"],
|
|
729
|
+
supportsThinkingConfig: true
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
...KIRO_DEFAULTS,
|
|
733
|
+
id: "claude-opus-4-6-1m",
|
|
734
|
+
name: "Claude Opus 4.6 (1M)",
|
|
735
|
+
reasoning: true,
|
|
736
|
+
input: MULTIMODAL,
|
|
737
|
+
contextWindow: 1e6,
|
|
738
|
+
maxTokens: 64000,
|
|
739
|
+
supportedEfforts: ["low", "medium", "high", "max"],
|
|
740
|
+
supportsThinkingConfig: true
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
...KIRO_DEFAULTS,
|
|
744
|
+
id: "claude-sonnet-4-6",
|
|
745
|
+
name: "Claude Sonnet 4.6",
|
|
746
|
+
reasoning: true,
|
|
747
|
+
input: MULTIMODAL,
|
|
748
|
+
contextWindow: 1e6,
|
|
749
|
+
maxTokens: 64000,
|
|
750
|
+
supportedEfforts: ["low", "medium", "high", "max"],
|
|
751
|
+
supportsThinkingConfig: true
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
...KIRO_DEFAULTS,
|
|
755
|
+
id: "claude-sonnet-4-6-1m",
|
|
756
|
+
name: "Claude Sonnet 4.6 (1M)",
|
|
757
|
+
reasoning: true,
|
|
758
|
+
input: MULTIMODAL,
|
|
759
|
+
contextWindow: 1e6,
|
|
760
|
+
maxTokens: 64000,
|
|
761
|
+
supportedEfforts: ["low", "medium", "high", "max"],
|
|
762
|
+
supportsThinkingConfig: true
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
...KIRO_DEFAULTS,
|
|
766
|
+
id: "claude-opus-4-5",
|
|
767
|
+
name: "Claude Opus 4.5",
|
|
768
|
+
reasoning: true,
|
|
769
|
+
input: MULTIMODAL,
|
|
770
|
+
contextWindow: 200000,
|
|
771
|
+
maxTokens: 64000
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
...KIRO_DEFAULTS,
|
|
775
|
+
id: "claude-sonnet-4-5",
|
|
776
|
+
name: "Claude Sonnet 4.5",
|
|
777
|
+
reasoning: true,
|
|
778
|
+
input: MULTIMODAL,
|
|
779
|
+
contextWindow: 200000,
|
|
780
|
+
maxTokens: 65536
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
...KIRO_DEFAULTS,
|
|
784
|
+
id: "claude-sonnet-4-5-1m",
|
|
785
|
+
name: "Claude Sonnet 4.5 (1M)",
|
|
786
|
+
reasoning: true,
|
|
787
|
+
input: MULTIMODAL,
|
|
788
|
+
contextWindow: 1e6,
|
|
789
|
+
maxTokens: 65536
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
...KIRO_DEFAULTS,
|
|
793
|
+
id: "claude-sonnet-4",
|
|
794
|
+
name: "Claude Sonnet 4",
|
|
795
|
+
reasoning: true,
|
|
796
|
+
input: MULTIMODAL,
|
|
797
|
+
contextWindow: 200000,
|
|
798
|
+
maxTokens: 65536
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
...KIRO_DEFAULTS,
|
|
802
|
+
id: "claude-haiku-4-5",
|
|
803
|
+
name: "Claude Haiku 4.5",
|
|
804
|
+
reasoning: false,
|
|
805
|
+
input: MULTIMODAL,
|
|
806
|
+
contextWindow: 200000,
|
|
807
|
+
maxTokens: 65536
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
...KIRO_DEFAULTS,
|
|
811
|
+
id: "deepseek-3-2",
|
|
812
|
+
name: "DeepSeek 3.2",
|
|
813
|
+
reasoning: true,
|
|
814
|
+
input: TEXT_ONLY,
|
|
815
|
+
contextWindow: 128000,
|
|
816
|
+
maxTokens: 8192
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
...KIRO_DEFAULTS,
|
|
820
|
+
id: "kimi-k2-5",
|
|
821
|
+
name: "Kimi K2.5",
|
|
822
|
+
reasoning: true,
|
|
823
|
+
input: TEXT_ONLY,
|
|
824
|
+
contextWindow: 200000,
|
|
825
|
+
maxTokens: 8192
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
...KIRO_DEFAULTS,
|
|
829
|
+
id: "minimax-m2-5",
|
|
830
|
+
name: "MiniMax M2.5",
|
|
831
|
+
reasoning: false,
|
|
832
|
+
input: TEXT_ONLY,
|
|
833
|
+
contextWindow: 196000,
|
|
834
|
+
maxTokens: 64000
|
|
835
|
+
},
|
|
836
|
+
{
|
|
837
|
+
...KIRO_DEFAULTS,
|
|
838
|
+
id: "minimax-m2-1",
|
|
839
|
+
name: "MiniMax M2.1",
|
|
840
|
+
reasoning: false,
|
|
841
|
+
input: MULTIMODAL,
|
|
842
|
+
contextWindow: 196000,
|
|
843
|
+
maxTokens: 64000
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
...KIRO_DEFAULTS,
|
|
847
|
+
id: "glm-4-7",
|
|
848
|
+
name: "GLM 4.7",
|
|
849
|
+
reasoning: true,
|
|
850
|
+
input: TEXT_ONLY,
|
|
851
|
+
contextWindow: 128000,
|
|
852
|
+
maxTokens: 8192
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
...KIRO_DEFAULTS,
|
|
856
|
+
id: "glm-4-7-flash",
|
|
857
|
+
name: "GLM 4.7 Flash",
|
|
858
|
+
reasoning: false,
|
|
859
|
+
input: TEXT_ONLY,
|
|
860
|
+
contextWindow: 128000,
|
|
861
|
+
maxTokens: 8192
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
...KIRO_DEFAULTS,
|
|
865
|
+
id: "qwen3-coder-next",
|
|
866
|
+
name: "Qwen3 Coder Next",
|
|
867
|
+
reasoning: true,
|
|
868
|
+
input: MULTIMODAL,
|
|
869
|
+
contextWindow: 256000,
|
|
870
|
+
maxTokens: 64000
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
...KIRO_DEFAULTS,
|
|
874
|
+
id: "qwen3-coder-480b",
|
|
875
|
+
name: "Qwen3 Coder 480B",
|
|
876
|
+
reasoning: true,
|
|
877
|
+
input: TEXT_ONLY,
|
|
878
|
+
contextWindow: 128000,
|
|
879
|
+
maxTokens: 8192
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
...KIRO_DEFAULTS,
|
|
883
|
+
id: "agi-nova-beta-1m",
|
|
884
|
+
name: "AGI Nova Beta (1M)",
|
|
885
|
+
reasoning: true,
|
|
886
|
+
input: MULTIMODAL,
|
|
887
|
+
contextWindow: 1e6,
|
|
888
|
+
maxTokens: 65536
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
...KIRO_DEFAULTS,
|
|
892
|
+
id: "auto",
|
|
893
|
+
name: "Auto",
|
|
894
|
+
reasoning: true,
|
|
895
|
+
input: MULTIMODAL,
|
|
896
|
+
contextWindow: 200000,
|
|
897
|
+
maxTokens: 65536
|
|
898
|
+
}
|
|
899
|
+
];
|
|
900
|
+
async function fetchAvailableModels(accessToken, apiRegion, profileArn) {
|
|
901
|
+
const url = `https://management.${apiRegion}.kiro.dev/?origin=KIRO_CLI&profileArn=${encodeURIComponent(profileArn)}`;
|
|
902
|
+
const resp = await fetch(url, {
|
|
903
|
+
method: "POST",
|
|
904
|
+
headers: {
|
|
905
|
+
Authorization: `Bearer ${accessToken}`,
|
|
906
|
+
"Content-Type": "application/x-amz-json-1.0",
|
|
907
|
+
"X-Amz-Target": "AmazonCodeWhispererService.ListAvailableModels",
|
|
908
|
+
"user-agent": "aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererruntime/0.1.16551 os/macos lang/rust/1.92.0 md/appVersion-2.7.1 app/AmazonQ-For-CLI",
|
|
909
|
+
"x-amz-user-agent": "aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererruntime/0.1.16551 os/macos lang/rust/1.92.0 m/F app/AmazonQ-For-CLI",
|
|
910
|
+
"x-amzn-codewhisperer-optout": "true",
|
|
911
|
+
"amz-sdk-request": "attempt=1; max=3",
|
|
912
|
+
"amz-sdk-invocation-id": crypto.randomUUID(),
|
|
913
|
+
accept: "*/*",
|
|
914
|
+
Pragma: "no-cache",
|
|
915
|
+
"Cache-Control": "no-cache"
|
|
916
|
+
},
|
|
917
|
+
body: JSON.stringify({ origin: "KIRO_CLI", profileArn })
|
|
918
|
+
});
|
|
919
|
+
if (!resp.ok) {
|
|
920
|
+
const body = await resp.text().catch(() => "");
|
|
921
|
+
if (resp.status === 401 || resp.status === 400 && body.includes("Invalid token")) {
|
|
922
|
+
throw new Error(`Authentication failed: 401 ListAvailableModels failed - ${body}`);
|
|
972
923
|
}
|
|
973
|
-
|
|
974
|
-
if (!block)
|
|
975
|
-
return;
|
|
976
|
-
block.thinking += thinking;
|
|
977
|
-
this.stream.push({
|
|
978
|
-
type: "thinking_delta",
|
|
979
|
-
contentIndex: this.thinkingBlockIndex,
|
|
980
|
-
delta: thinking,
|
|
981
|
-
partial: this.output
|
|
982
|
-
});
|
|
924
|
+
throw new Error(`ListAvailableModels failed: HTTP ${resp.status} - ${body}`);
|
|
983
925
|
}
|
|
926
|
+
const data = await resp.json();
|
|
927
|
+
return (data.models ?? []).filter((m) => m.modelId !== "auto");
|
|
984
928
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
` + `The current model is {{modelId}}.
|
|
1002
|
-
`;
|
|
1003
|
-
var SYSTEM_SEED_ACK = "I will fully incorporate this information when generating my responses, " + "and explicitly acknowledge relevant parts of the summary when answering questions.";
|
|
1004
|
-
function resolveOS() {
|
|
1005
|
-
switch (process.platform) {
|
|
1006
|
-
case "darwin":
|
|
1007
|
-
return "macos";
|
|
1008
|
-
case "win32":
|
|
1009
|
-
return "windows";
|
|
1010
|
-
default:
|
|
1011
|
-
return process.platform;
|
|
929
|
+
var REASONING_FAMILIES = new Set([
|
|
930
|
+
"claude-fable",
|
|
931
|
+
"claude-sonnet",
|
|
932
|
+
"claude-opus",
|
|
933
|
+
"deepseek",
|
|
934
|
+
"kimi",
|
|
935
|
+
"glm",
|
|
936
|
+
"qwen",
|
|
937
|
+
"agi-nova",
|
|
938
|
+
"minimax"
|
|
939
|
+
]);
|
|
940
|
+
function isReasoningModel(dotId) {
|
|
941
|
+
for (const family of REASONING_FAMILIES) {
|
|
942
|
+
if (dotId.startsWith(family))
|
|
943
|
+
return true;
|
|
1012
944
|
}
|
|
945
|
+
return false;
|
|
1013
946
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
return messages.filter((msg) => msg.role !== "assistant" || msg.stopReason !== "error" && msg.stopReason !== "aborted");
|
|
947
|
+
function firstTokenTimeout(dotId) {
|
|
948
|
+
if (dotId.startsWith("claude-fable") || dotId.startsWith("claude-opus"))
|
|
949
|
+
return 180000;
|
|
950
|
+
return 90000;
|
|
1019
951
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
952
|
+
function buildModelsFromApi(apiModels) {
|
|
953
|
+
return apiModels.map((m) => {
|
|
954
|
+
KIRO_MODEL_IDS.add(m.modelId);
|
|
955
|
+
const dashId = dotToDash(m.modelId);
|
|
956
|
+
const supportedTypes = m.supportedInputTypes ?? ["TEXT"];
|
|
957
|
+
const input = supportedTypes.includes("IMAGE") ? ["text", "image"] : ["text"];
|
|
958
|
+
const effortEnum = m.additionalModelRequestFieldsSchema?.properties?.output_config?.properties?.effort?.enum;
|
|
959
|
+
const supportedEfforts = Array.isArray(effortEnum) && effortEnum.length > 0 ? effortEnum : undefined;
|
|
960
|
+
const supportsThinkingConfig = !!m.additionalModelRequestFieldsSchema?.properties?.thinking;
|
|
961
|
+
return {
|
|
962
|
+
id: dashId,
|
|
963
|
+
name: m.modelName,
|
|
964
|
+
reasoning: isReasoningModel(m.modelId),
|
|
965
|
+
input,
|
|
966
|
+
contextWindow: m.tokenLimits?.maxInputTokens ?? 200000,
|
|
967
|
+
maxTokens: m.tokenLimits?.maxOutputTokens ?? 8192,
|
|
968
|
+
firstTokenTimeout: firstTokenTimeout(m.modelId),
|
|
969
|
+
...supportedEfforts ? { supportedEfforts } : {},
|
|
970
|
+
...supportsThinkingConfig ? { supportsThinkingConfig } : {}
|
|
971
|
+
};
|
|
972
|
+
});
|
|
1030
973
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
if (!Array.isArray(msg.content))
|
|
1035
|
-
return [];
|
|
1036
|
-
return msg.content.filter((c) => c.type === "image");
|
|
974
|
+
var cachedDynamicModels = null;
|
|
975
|
+
function getCachedDynamicModels() {
|
|
976
|
+
return cachedDynamicModels;
|
|
1037
977
|
}
|
|
1038
|
-
function
|
|
1039
|
-
|
|
1040
|
-
return msg.content.map((c) => c.type === "text" ? c.text : "").join("");
|
|
1041
|
-
}
|
|
1042
|
-
if (typeof msg.content === "string")
|
|
1043
|
-
return msg.content;
|
|
1044
|
-
if (!Array.isArray(msg.content))
|
|
1045
|
-
return "";
|
|
1046
|
-
return msg.content.map((c) => {
|
|
1047
|
-
if (c.type === "text")
|
|
1048
|
-
return c.text;
|
|
1049
|
-
if (c.type === "thinking")
|
|
1050
|
-
return c.thinking;
|
|
1051
|
-
return "";
|
|
1052
|
-
}).join("");
|
|
978
|
+
function setCachedDynamicModels(models) {
|
|
979
|
+
cachedDynamicModels = models;
|
|
1053
980
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
981
|
+
|
|
982
|
+
// src/oauth.ts
|
|
983
|
+
var BUILDER_ID_START_URL = "https://view.awsapps.com/start";
|
|
984
|
+
var BUILDER_ID_REGION = "us-east-1";
|
|
985
|
+
var SSO_SCOPES = [
|
|
986
|
+
"codewhisperer:completions",
|
|
987
|
+
"codewhisperer:analysis",
|
|
988
|
+
"codewhisperer:conversations",
|
|
989
|
+
"codewhisperer:transformations",
|
|
990
|
+
"codewhisperer:taskassist"
|
|
991
|
+
];
|
|
992
|
+
var IDC_PROBE_REGIONS = [
|
|
993
|
+
"us-east-1",
|
|
994
|
+
"eu-west-1",
|
|
995
|
+
"eu-central-1",
|
|
996
|
+
"us-east-2",
|
|
997
|
+
"eu-west-2",
|
|
998
|
+
"eu-west-3",
|
|
999
|
+
"eu-north-1",
|
|
1000
|
+
"ap-southeast-1",
|
|
1001
|
+
"ap-northeast-1",
|
|
1002
|
+
"us-west-2"
|
|
1003
|
+
];
|
|
1004
|
+
var EXPIRES_BUFFER_MS = 5 * 60 * 1000;
|
|
1005
|
+
function abortableDelay(ms, signal) {
|
|
1006
|
+
if (signal?.aborted)
|
|
1007
|
+
return Promise.reject(signal.reason ?? new Error("Login cancelled"));
|
|
1008
|
+
return new Promise((resolve2, reject) => {
|
|
1009
|
+
const timer = setTimeout(resolve2, ms);
|
|
1010
|
+
signal?.addEventListener("abort", () => {
|
|
1011
|
+
clearTimeout(timer);
|
|
1012
|
+
reject(signal.reason ?? new Error("Login cancelled"));
|
|
1013
|
+
}, { once: true });
|
|
1014
|
+
});
|
|
1064
1015
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1016
|
+
async function tryRegisterAndAuthorize(startUrl, region) {
|
|
1017
|
+
const oidcEndpoint = `https://oidc.${region}.amazonaws.com`;
|
|
1018
|
+
const regResp = await fetch(`${oidcEndpoint}/client/register`, {
|
|
1019
|
+
method: "POST",
|
|
1020
|
+
headers: { "Content-Type": "application/json", "User-Agent": "pi-kiro" },
|
|
1021
|
+
body: JSON.stringify({
|
|
1022
|
+
clientName: "pi-kiro",
|
|
1023
|
+
clientType: "public",
|
|
1024
|
+
scopes: SSO_SCOPES,
|
|
1025
|
+
grantTypes: ["urn:ietf:params:oauth:grant-type:device_code", "refresh_token"]
|
|
1026
|
+
})
|
|
1027
|
+
});
|
|
1028
|
+
if (!regResp.ok)
|
|
1029
|
+
return null;
|
|
1030
|
+
const { clientId, clientSecret } = await regResp.json();
|
|
1031
|
+
const devResp = await fetch(`${oidcEndpoint}/device_authorization`, {
|
|
1032
|
+
method: "POST",
|
|
1033
|
+
headers: { "Content-Type": "application/json", "User-Agent": "pi-kiro" },
|
|
1034
|
+
body: JSON.stringify({ clientId, clientSecret, startUrl })
|
|
1035
|
+
});
|
|
1036
|
+
if (!devResp.ok)
|
|
1037
|
+
return null;
|
|
1038
|
+
return {
|
|
1039
|
+
clientId,
|
|
1040
|
+
clientSecret,
|
|
1041
|
+
oidcEndpoint,
|
|
1042
|
+
devAuth: await devResp.json()
|
|
1043
|
+
};
|
|
1071
1044
|
}
|
|
1072
|
-
function
|
|
1073
|
-
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
if (
|
|
1078
|
-
|
|
1045
|
+
async function pollForToken(oidcEndpoint, clientId, clientSecret, devAuth, signal) {
|
|
1046
|
+
const deadline = Date.now() + (devAuth.expiresIn || 600) * 1000;
|
|
1047
|
+
const baseInterval = (devAuth.interval || 5) * 1000;
|
|
1048
|
+
let interval = baseInterval;
|
|
1049
|
+
while (Date.now() < deadline) {
|
|
1050
|
+
if (signal?.aborted)
|
|
1051
|
+
throw new Error("Login cancelled");
|
|
1052
|
+
await abortableDelay(interval, signal);
|
|
1053
|
+
let resp;
|
|
1054
|
+
try {
|
|
1055
|
+
resp = await fetch(`${oidcEndpoint}/token`, {
|
|
1056
|
+
method: "POST",
|
|
1057
|
+
headers: { "Content-Type": "application/json", "User-Agent": "pi-kiro" },
|
|
1058
|
+
body: JSON.stringify({
|
|
1059
|
+
clientId,
|
|
1060
|
+
clientSecret,
|
|
1061
|
+
deviceCode: devAuth.deviceCode,
|
|
1062
|
+
grantType: "urn:ietf:params:oauth:grant-type:device_code"
|
|
1063
|
+
})
|
|
1064
|
+
});
|
|
1065
|
+
} catch {
|
|
1079
1066
|
continue;
|
|
1080
1067
|
}
|
|
1081
|
-
if (
|
|
1082
|
-
|
|
1068
|
+
if (resp.status >= 500)
|
|
1069
|
+
continue;
|
|
1070
|
+
let data;
|
|
1071
|
+
try {
|
|
1072
|
+
data = await resp.json();
|
|
1073
|
+
} catch {
|
|
1074
|
+
if (!resp.ok) {
|
|
1075
|
+
throw new Error(`Authorization failed: HTTP ${resp.status}`);
|
|
1076
|
+
}
|
|
1083
1077
|
continue;
|
|
1084
1078
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1079
|
+
if (!data.error && data.accessToken && data.refreshToken)
|
|
1080
|
+
return data;
|
|
1081
|
+
if (data.error === "authorization_pending")
|
|
1082
|
+
continue;
|
|
1083
|
+
if (data.error === "slow_down") {
|
|
1084
|
+
interval += baseInterval;
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
if (data.error)
|
|
1088
|
+
throw new Error(`Authorization failed: ${data.error}`);
|
|
1089
1089
|
}
|
|
1090
|
-
|
|
1090
|
+
throw new Error("Authorization timed out");
|
|
1091
1091
|
}
|
|
1092
|
-
function
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1092
|
+
async function loginKiro(callbacks) {
|
|
1093
|
+
const method = await callbacks.onSelect({
|
|
1094
|
+
message: "Select login method:",
|
|
1095
|
+
options: [
|
|
1096
|
+
{ id: "builder-id", label: "AWS Builder ID (personal account)" },
|
|
1097
|
+
{ id: "idc", label: "IAM Identity Center (enterprise SSO)" },
|
|
1098
|
+
{ id: "sync", label: "Import from Kiro CLI/IDE (auto-sync local DB)" },
|
|
1099
|
+
{ id: "desktop", label: "Desktop refresh token (manual)" }
|
|
1100
|
+
]
|
|
1101
|
+
});
|
|
1102
|
+
if (!method)
|
|
1103
|
+
throw new Error("Login cancelled");
|
|
1104
|
+
if (method === "sync") {
|
|
1105
|
+
return loginCliSync(callbacks);
|
|
1098
1106
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
const hasToolCall = Array.isArray(anchor.content) && anchor.content.some((b) => b.type === "toolCall");
|
|
1102
|
-
if (!hasToolCall)
|
|
1103
|
-
currentMsgStartIdx++;
|
|
1107
|
+
if (method === "desktop") {
|
|
1108
|
+
return loginDesktopManual(callbacks);
|
|
1104
1109
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
const msg = historyMessages[i];
|
|
1108
|
-
if (!msg)
|
|
1109
|
-
continue;
|
|
1110
|
-
if (msg.role === "user") {
|
|
1111
|
-
let content = typeof msg.content === "string" ? msg.content : getContentText(msg);
|
|
1112
|
-
if (systemPrompt && !systemPrepended) {
|
|
1113
|
-
content = `${systemPrompt}
|
|
1114
|
-
|
|
1115
|
-
${content}`;
|
|
1116
|
-
systemPrepended = true;
|
|
1117
|
-
}
|
|
1118
|
-
const images = extractImages(msg);
|
|
1119
|
-
const uim = {
|
|
1120
|
-
content,
|
|
1121
|
-
origin: "KIRO_CLI",
|
|
1122
|
-
...images.length > 0 ? { images: convertImagesToKiro(images).images } : {}
|
|
1123
|
-
};
|
|
1124
|
-
const prev2 = history[history.length - 1];
|
|
1125
|
-
if (prev2?.userInputMessage) {
|
|
1126
|
-
prev2.userInputMessage.content += `
|
|
1127
|
-
|
|
1128
|
-
${uim.content}`;
|
|
1129
|
-
if (uim.images) {
|
|
1130
|
-
prev2.userInputMessage.images = [...prev2.userInputMessage.images ?? [], ...uim.images];
|
|
1131
|
-
}
|
|
1132
|
-
} else {
|
|
1133
|
-
history.push({ userInputMessage: uim });
|
|
1134
|
-
}
|
|
1135
|
-
continue;
|
|
1136
|
-
}
|
|
1137
|
-
if (msg.role === "assistant") {
|
|
1138
|
-
let armContent = "";
|
|
1139
|
-
const armToolUses = [];
|
|
1140
|
-
if (Array.isArray(msg.content)) {
|
|
1141
|
-
for (const block of msg.content) {
|
|
1142
|
-
if (block.type === "text") {
|
|
1143
|
-
armContent += block.text;
|
|
1144
|
-
} else if (block.type === "thinking") {
|
|
1145
|
-
armContent = `<thinking>${block.thinking}</thinking>
|
|
1146
|
-
|
|
1147
|
-
${armContent}`;
|
|
1148
|
-
} else if (block.type === "toolCall") {
|
|
1149
|
-
const tc = block;
|
|
1150
|
-
armToolUses.push({
|
|
1151
|
-
name: tc.name,
|
|
1152
|
-
toolUseId: toKiroToolUseId(tc.id),
|
|
1153
|
-
input: parseToolArgs(tc.arguments)
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
if (!armContent && armToolUses.length === 0)
|
|
1159
|
-
continue;
|
|
1160
|
-
history.push({
|
|
1161
|
-
assistantResponseMessage: {
|
|
1162
|
-
content: armContent,
|
|
1163
|
-
...armToolUses.length > 0 ? { toolUses: armToolUses } : {}
|
|
1164
|
-
}
|
|
1165
|
-
});
|
|
1166
|
-
continue;
|
|
1167
|
-
}
|
|
1168
|
-
const trMsg = msg;
|
|
1169
|
-
const toolResults = [
|
|
1170
|
-
{
|
|
1171
|
-
content: [{ text: truncate(getContentText(msg), TOOL_RESULT_LIMIT) }],
|
|
1172
|
-
status: trMsg.isError ? "error" : "success",
|
|
1173
|
-
toolUseId: toKiroToolUseId(trMsg.toolCallId)
|
|
1174
|
-
}
|
|
1175
|
-
];
|
|
1176
|
-
const trImages = [];
|
|
1177
|
-
if (Array.isArray(trMsg.content)) {
|
|
1178
|
-
for (const c of trMsg.content)
|
|
1179
|
-
if (c.type === "image")
|
|
1180
|
-
trImages.push(c);
|
|
1181
|
-
}
|
|
1182
|
-
let j = i + 1;
|
|
1183
|
-
while (j < historyMessages.length && historyMessages[j]?.role === "toolResult") {
|
|
1184
|
-
const next = historyMessages[j];
|
|
1185
|
-
toolResults.push({
|
|
1186
|
-
content: [{ text: truncate(getContentText(next), TOOL_RESULT_LIMIT) }],
|
|
1187
|
-
status: next.isError ? "error" : "success",
|
|
1188
|
-
toolUseId: toKiroToolUseId(next.toolCallId)
|
|
1189
|
-
});
|
|
1190
|
-
if (Array.isArray(next.content)) {
|
|
1191
|
-
for (const c of next.content)
|
|
1192
|
-
if (c.type === "image")
|
|
1193
|
-
trImages.push(c);
|
|
1194
|
-
}
|
|
1195
|
-
j++;
|
|
1196
|
-
}
|
|
1197
|
-
i = j - 1;
|
|
1198
|
-
const prev = history[history.length - 1];
|
|
1199
|
-
if (prev?.userInputMessage) {
|
|
1200
|
-
prev.userInputMessage.content += `
|
|
1201
|
-
|
|
1202
|
-
Tool results provided.`;
|
|
1203
|
-
if (trImages.length > 0) {
|
|
1204
|
-
prev.userInputMessage.images = [
|
|
1205
|
-
...prev.userInputMessage.images ?? [],
|
|
1206
|
-
...convertImagesToKiro(trImages).images
|
|
1207
|
-
];
|
|
1208
|
-
}
|
|
1209
|
-
if (!prev.userInputMessage.userInputMessageContext) {
|
|
1210
|
-
prev.userInputMessage.userInputMessageContext = {};
|
|
1211
|
-
}
|
|
1212
|
-
prev.userInputMessage.userInputMessageContext.toolResults = [
|
|
1213
|
-
...prev.userInputMessage.userInputMessageContext.toolResults ?? [],
|
|
1214
|
-
...toolResults
|
|
1215
|
-
];
|
|
1216
|
-
} else {
|
|
1217
|
-
history.push({
|
|
1218
|
-
userInputMessage: {
|
|
1219
|
-
content: "Tool results provided.",
|
|
1220
|
-
origin: "KIRO_CLI",
|
|
1221
|
-
...trImages.length > 0 ? { images: convertImagesToKiro(trImages).images } : {},
|
|
1222
|
-
userInputMessageContext: { toolResults }
|
|
1223
|
-
}
|
|
1224
|
-
});
|
|
1225
|
-
}
|
|
1110
|
+
if (method === "builder-id") {
|
|
1111
|
+
return runDeviceCodeFlow(callbacks, BUILDER_ID_START_URL, [BUILDER_ID_REGION], "builder-id");
|
|
1226
1112
|
}
|
|
1227
|
-
|
|
1113
|
+
const startUrl = (await callbacks.onPrompt({
|
|
1114
|
+
message: "Paste your IAM Identity Center start URL:",
|
|
1115
|
+
placeholder: "https://mycompany.awsapps.com/start",
|
|
1116
|
+
allowEmpty: false
|
|
1117
|
+
}))?.trim();
|
|
1118
|
+
if (!startUrl || !startUrl.startsWith("http")) {
|
|
1119
|
+
throw new Error(`Invalid start URL "${startUrl ?? ""}" — expected https://…`);
|
|
1120
|
+
}
|
|
1121
|
+
const regionRaw = await callbacks.onPrompt({
|
|
1122
|
+
message: `Identity Center region, or blank to auto-detect (${IDC_PROBE_REGIONS.join(", ")})`,
|
|
1123
|
+
placeholder: "us-east-1",
|
|
1124
|
+
allowEmpty: true
|
|
1125
|
+
});
|
|
1126
|
+
const region = (regionRaw ?? "").trim();
|
|
1127
|
+
const regions = region ? [region] : IDC_PROBE_REGIONS;
|
|
1128
|
+
callbacks.onProgress?.(region ? `Connecting to ${region}…` : "Detecting your Identity Center region…");
|
|
1129
|
+
return runDeviceCodeFlow(callbacks, startUrl, regions, "idc");
|
|
1228
1130
|
}
|
|
1229
|
-
function
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
if (pairCount > 1) {
|
|
1249
|
-
for (let k = i;k < j; k += 2) {
|
|
1250
|
-
const asst = history[k];
|
|
1251
|
-
const user = history[k + 1];
|
|
1252
|
-
if (k === i) {
|
|
1253
|
-
result.push(asst);
|
|
1254
|
-
} else {
|
|
1255
|
-
result.push({
|
|
1256
|
-
assistantResponseMessage: {
|
|
1257
|
-
content: "[tool calling continues]",
|
|
1258
|
-
toolUses: asst.assistantResponseMessage.toolUses
|
|
1259
|
-
}
|
|
1260
|
-
});
|
|
1261
|
-
}
|
|
1262
|
-
result.push(user);
|
|
1263
|
-
}
|
|
1264
|
-
} else {
|
|
1265
|
-
result.push(history[i], history[i + 1]);
|
|
1266
|
-
}
|
|
1267
|
-
i = j;
|
|
1268
|
-
} else {
|
|
1269
|
-
result.push(entry);
|
|
1270
|
-
i++;
|
|
1131
|
+
async function loginCliSync(callbacks) {
|
|
1132
|
+
callbacks.onProgress?.("Scanning for Kiro IDE credentials (~/.kiro/db)…");
|
|
1133
|
+
const { importFromKiroCli: importFromKiroCli2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
1134
|
+
const imported = await importFromKiroCli2();
|
|
1135
|
+
if (!imported || !imported.accessToken && !imported.refreshToken) {
|
|
1136
|
+
throw new Error(`No Kiro IDE credentials found.
|
|
1137
|
+
Make sure Kiro IDE is installed and you're logged in, then try again.
|
|
1138
|
+
Alternatively, use 'desktop' to paste a refresh token manually.`);
|
|
1139
|
+
}
|
|
1140
|
+
log.info("Successfully imported credentials from Kiro IDE");
|
|
1141
|
+
callbacks.onProgress?.(`Imported from Kiro IDE (${imported.authMethod}, ${imported.region}${imported.email ? `, ${imported.email}` : ""})`);
|
|
1142
|
+
if (imported.profileArn) {
|
|
1143
|
+
try {
|
|
1144
|
+
const apiRegion = resolveApiRegion(imported.region);
|
|
1145
|
+
const apiModels = await fetchAvailableModels(imported.accessToken, apiRegion, imported.profileArn);
|
|
1146
|
+
setCachedDynamicModels(buildModelsFromApi(apiModels));
|
|
1147
|
+
log.info(`Fetched and cached ${apiModels.length} models after CLI sync`);
|
|
1148
|
+
} catch (err) {
|
|
1149
|
+
log.warn(`Failed to fetch models after CLI sync: ${err}`);
|
|
1271
1150
|
}
|
|
1272
1151
|
}
|
|
1273
|
-
return
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
// src/stream.ts
|
|
1277
|
-
var FIRST_TOKEN_TIMEOUT_DEFAULT_MS = 90000;
|
|
1278
|
-
var IDLE_TIMEOUT_MS = 60000;
|
|
1279
|
-
var MAX_RETRIES = 3;
|
|
1280
|
-
var MAX_RETRY_DELAY_MS = 1e4;
|
|
1281
|
-
var CAPACITY_MAX_RETRIES = 3;
|
|
1282
|
-
var CAPACITY_BASE_DELAY_MS = 5000;
|
|
1283
|
-
var CAPACITY_MAX_DELAY_MS = 30000;
|
|
1284
|
-
var TRANSIENT_MAX_RETRIES = 3;
|
|
1285
|
-
var TRANSIENT_BASE_DELAY_MS = 2000;
|
|
1286
|
-
var TRANSIENT_MAX_DELAY_MS = 15000;
|
|
1287
|
-
var CONTEXT_TRUNCATION_MAX_RETRIES = 3;
|
|
1288
|
-
var CONTEXT_TRUNCATION_DROP_RATIO = 0.3;
|
|
1289
|
-
var TOO_BIG_PATTERNS = ["CONTENT_LENGTH_EXCEEDS_THRESHOLD", "Input is too long", "Improperly formed"];
|
|
1290
|
-
var NON_RETRYABLE_BODY_PATTERNS = ["MONTHLY_REQUEST_COUNT"];
|
|
1291
|
-
var CAPACITY_PATTERN = "INSUFFICIENT_MODEL_CAPACITY";
|
|
1292
|
-
function exponentialBackoff(attempt, baseMs, maxMs) {
|
|
1293
|
-
return Math.min(baseMs * 2 ** attempt, maxMs);
|
|
1294
|
-
}
|
|
1295
|
-
function isTooBigError(status, body) {
|
|
1296
|
-
return status === 413 || status === 400 && TOO_BIG_PATTERNS.some((p) => body.includes(p));
|
|
1297
|
-
}
|
|
1298
|
-
function isNonRetryableBodyError(body) {
|
|
1299
|
-
return NON_RETRYABLE_BODY_PATTERNS.some((p) => body.includes(p));
|
|
1300
|
-
}
|
|
1301
|
-
function isCapacityError(body) {
|
|
1302
|
-
return body.includes(CAPACITY_PATTERN);
|
|
1303
|
-
}
|
|
1304
|
-
function isTransientError(status) {
|
|
1305
|
-
return status === 429 || status >= 500;
|
|
1306
|
-
}
|
|
1307
|
-
function firstTokenTimeoutForModel(modelId) {
|
|
1308
|
-
const m = kiroModels.find((x) => x.id === modelId);
|
|
1309
|
-
return m?.firstTokenTimeout ?? FIRST_TOKEN_TIMEOUT_DEFAULT_MS;
|
|
1152
|
+
return kiroCredsFromCliImport(imported);
|
|
1310
1153
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
thinking: HIDDEN_REASONING_PLACEHOLDER,
|
|
1318
|
-
redacted: true
|
|
1319
|
-
};
|
|
1320
|
-
output.content.push(block);
|
|
1321
|
-
stream.push({ type: "thinking_start", contentIndex, partial: output });
|
|
1322
|
-
stream.push({
|
|
1323
|
-
type: "thinking_delta",
|
|
1324
|
-
contentIndex,
|
|
1325
|
-
delta: HIDDEN_REASONING_PLACEHOLDER,
|
|
1326
|
-
partial: output
|
|
1154
|
+
async function loginDesktopManual(callbacks) {
|
|
1155
|
+
const refreshRaw = await callbacks.onPrompt({
|
|
1156
|
+
message: `Paste your Kiro desktop refresh token
|
|
1157
|
+
` + "(find it in ~/.kiro/db/kiro.db → auth_kv table):",
|
|
1158
|
+
placeholder: "refresh-token",
|
|
1159
|
+
allowEmpty: true
|
|
1327
1160
|
});
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
if (signal?.aborted)
|
|
1337
|
-
return Promise.reject(signal.reason);
|
|
1338
|
-
return new Promise((resolve2, reject) => {
|
|
1339
|
-
const timer = setTimeout(resolve2, ms);
|
|
1340
|
-
signal?.addEventListener("abort", () => {
|
|
1341
|
-
clearTimeout(timer);
|
|
1342
|
-
reject(signal.reason);
|
|
1343
|
-
}, { once: true });
|
|
1161
|
+
const refreshToken = (refreshRaw ?? "").trim();
|
|
1162
|
+
if (!refreshToken) {
|
|
1163
|
+
throw new Error("Login cancelled — no refresh token provided");
|
|
1164
|
+
}
|
|
1165
|
+
const regionRaw = await callbacks.onPrompt({
|
|
1166
|
+
message: "Kiro region:",
|
|
1167
|
+
placeholder: "us-east-1",
|
|
1168
|
+
allowEmpty: true
|
|
1344
1169
|
});
|
|
1170
|
+
const region = (regionRaw ?? "").trim() || "us-east-1";
|
|
1171
|
+
const refreshCreds = {
|
|
1172
|
+
refresh: `${refreshToken}|||desktop`,
|
|
1173
|
+
access: "",
|
|
1174
|
+
expires: 0,
|
|
1175
|
+
clientId: "",
|
|
1176
|
+
clientSecret: "",
|
|
1177
|
+
region,
|
|
1178
|
+
authMethod: "desktop"
|
|
1179
|
+
};
|
|
1180
|
+
callbacks.onProgress?.("Exchanging refresh token…");
|
|
1181
|
+
return refreshKiroToken(refreshCreds);
|
|
1345
1182
|
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
ep.hostname = ep.hostname.replace("runtime.", "management.");
|
|
1357
|
-
ep.pathname = "/";
|
|
1358
|
-
ep.search = "";
|
|
1359
|
-
ep.hash = "";
|
|
1360
|
-
const resp = await fetch(ep.toString(), {
|
|
1361
|
-
method: "POST",
|
|
1362
|
-
headers: {
|
|
1363
|
-
"Content-Type": "application/x-amz-json-1.0",
|
|
1364
|
-
Authorization: `Bearer ${accessToken}`,
|
|
1365
|
-
"X-Amz-Target": "AmazonCodeWhispererService.ListAvailableProfiles"
|
|
1366
|
-
},
|
|
1367
|
-
body: "{}"
|
|
1368
|
-
});
|
|
1369
|
-
if (!resp.ok) {
|
|
1370
|
-
log.warn(`profileArn resolution failed: ${resp.status} ${resp.statusText}`);
|
|
1371
|
-
return;
|
|
1372
|
-
}
|
|
1373
|
-
const j = await resp.json();
|
|
1374
|
-
const arn = j.profiles?.find((p) => p.arn)?.arn;
|
|
1375
|
-
if (!arn) {
|
|
1376
|
-
log.warn("profileArn resolution returned no profile ARN");
|
|
1377
|
-
return;
|
|
1183
|
+
async function runDeviceCodeFlow(callbacks, startUrl, regions, authMethod) {
|
|
1184
|
+
let result = null;
|
|
1185
|
+
let detectedRegion = "";
|
|
1186
|
+
for (const region of regions) {
|
|
1187
|
+
result = await tryRegisterAndAuthorize(startUrl, region);
|
|
1188
|
+
if (result) {
|
|
1189
|
+
detectedRegion = region;
|
|
1190
|
+
if (regions.length > 1)
|
|
1191
|
+
callbacks.onProgress?.(`Region: ${region}`);
|
|
1192
|
+
break;
|
|
1378
1193
|
}
|
|
1379
|
-
profileArnCache.set(endpoint, arn);
|
|
1380
|
-
return arn;
|
|
1381
|
-
} catch (error) {
|
|
1382
|
-
log.warn(`profileArn resolution threw: ${error instanceof Error ? error.message : String(error)}`);
|
|
1383
|
-
return;
|
|
1384
1194
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1195
|
+
if (!result || !detectedRegion) {
|
|
1196
|
+
throw new Error(`Could not authorize ${startUrl} in ${regions.join(", ")}. Check your start URL${regions.length === 1 ? " and region" : ""} and try again.`);
|
|
1197
|
+
}
|
|
1198
|
+
callbacks.onAuth({
|
|
1199
|
+
url: result.devAuth.verificationUriComplete,
|
|
1200
|
+
instructions: `Code: ${result.devAuth.userCode}
|
|
1201
|
+
Complete authorization within 10 minutes.`
|
|
1202
|
+
});
|
|
1203
|
+
callbacks.onProgress?.("Waiting for browser authorization (up to 10 minutes)…");
|
|
1204
|
+
const tok = await pollForToken(result.oidcEndpoint, result.clientId, result.clientSecret, result.devAuth, callbacks.signal);
|
|
1205
|
+
if (!tok.accessToken || !tok.refreshToken) {
|
|
1206
|
+
throw new Error("Authorization completed but no tokens returned");
|
|
1397
1207
|
}
|
|
1398
|
-
return [...dupes].sort();
|
|
1399
|
-
}
|
|
1400
|
-
function objectKeys(value) {
|
|
1401
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1402
|
-
return [];
|
|
1403
|
-
return Object.keys(value).sort();
|
|
1404
|
-
}
|
|
1405
|
-
function summarizeToolUse(toolUse) {
|
|
1406
|
-
const input = toolUse.input;
|
|
1407
1208
|
return {
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1209
|
+
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|${authMethod}`,
|
|
1210
|
+
access: tok.accessToken,
|
|
1211
|
+
expires: Date.now() + (tok.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
1212
|
+
clientId: result.clientId,
|
|
1213
|
+
clientSecret: result.clientSecret,
|
|
1214
|
+
region: detectedRegion,
|
|
1215
|
+
authMethod
|
|
1412
1216
|
};
|
|
1413
1217
|
}
|
|
1414
|
-
function
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1218
|
+
async function syncBackToKiroCli(result) {
|
|
1219
|
+
if (result.kiroSyncSource !== "kiro-cli-db" || !result.kiroSyncTokenKey) {
|
|
1220
|
+
log.debug("Credential sync-back skipped: credential did not originate from kiro-cli DB");
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
try {
|
|
1224
|
+
const { saveKiroCliCredentials: saveKiroCliCredentials2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
1225
|
+
const synced = await saveKiroCliCredentials2({
|
|
1226
|
+
accessToken: result.access,
|
|
1227
|
+
refreshToken: result.refresh.split("|")[0] ?? "",
|
|
1228
|
+
region: result.region,
|
|
1229
|
+
authMethod: result.authMethod === "builder-id" ? "idc" : result.authMethod,
|
|
1230
|
+
source: result.kiroSyncSource,
|
|
1231
|
+
tokenKey: result.kiroSyncTokenKey
|
|
1232
|
+
});
|
|
1233
|
+
if (synced)
|
|
1234
|
+
log.info("Synced refreshed credentials back to Kiro CLI DB");
|
|
1235
|
+
} catch (err) {
|
|
1236
|
+
log.debug(`Credential sync-back skipped: ${err}`);
|
|
1237
|
+
}
|
|
1421
1238
|
}
|
|
1422
|
-
function
|
|
1423
|
-
const
|
|
1424
|
-
const
|
|
1425
|
-
const
|
|
1426
|
-
const required = schema.required;
|
|
1239
|
+
function kiroCredsFromCliImport(imported) {
|
|
1240
|
+
const hasOidcCreds = !!imported.clientId && !!imported.clientSecret;
|
|
1241
|
+
const authMethod = hasOidcCreds && imported.authMethod === "idc" ? "idc" : "desktop";
|
|
1242
|
+
const refreshPacked = hasOidcCreds ? `${imported.refreshToken}|${imported.clientId}|${imported.clientSecret ?? ""}|${authMethod}` : `${imported.refreshToken}|||desktop`;
|
|
1427
1243
|
return {
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1244
|
+
refresh: refreshPacked,
|
|
1245
|
+
access: imported.accessToken,
|
|
1246
|
+
expires: Date.now() + 3600000 - EXPIRES_BUFFER_MS,
|
|
1247
|
+
clientId: imported.clientId ?? "",
|
|
1248
|
+
clientSecret: imported.clientSecret ?? "",
|
|
1249
|
+
region: imported.region,
|
|
1250
|
+
authMethod,
|
|
1251
|
+
profileArn: imported.profileArn,
|
|
1252
|
+
kiroSyncSource: imported.source,
|
|
1253
|
+
kiroSyncTokenKey: imported.tokenKey
|
|
1433
1254
|
};
|
|
1434
1255
|
}
|
|
1435
|
-
function
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1256
|
+
async function refreshTokenInner(credentials) {
|
|
1257
|
+
const parts = credentials.refresh.split("|");
|
|
1258
|
+
const refreshToken = parts[0] ?? "";
|
|
1259
|
+
const clientId = parts[1] ?? credentials.clientId ?? "";
|
|
1260
|
+
const clientSecret = parts[2] ?? credentials.clientSecret ?? "";
|
|
1261
|
+
const region = credentials.region;
|
|
1262
|
+
const authMethod = credentials.authMethod;
|
|
1263
|
+
if (!refreshToken || !region) {
|
|
1264
|
+
throw new Error("Refresh token is missing region — re-login required");
|
|
1265
|
+
}
|
|
1266
|
+
if (authMethod !== "desktop" && (!clientId || !clientSecret)) {
|
|
1267
|
+
throw new Error("Refresh token is missing clientId/clientSecret — re-login required");
|
|
1445
1268
|
}
|
|
1446
|
-
if (
|
|
1447
|
-
const
|
|
1269
|
+
if (authMethod === "desktop") {
|
|
1270
|
+
const desktopEndpoint = `https://prod.${region}.auth.desktop.kiro.dev/refreshToken`;
|
|
1271
|
+
const resp2 = await fetch(desktopEndpoint, {
|
|
1272
|
+
method: "POST",
|
|
1273
|
+
headers: { "Content-Type": "application/json", "User-Agent": "pi-kiro" },
|
|
1274
|
+
body: JSON.stringify({ refreshToken })
|
|
1275
|
+
});
|
|
1276
|
+
if (!resp2.ok) {
|
|
1277
|
+
const body = await resp2.text().catch(() => "");
|
|
1278
|
+
throw new Error(`Desktop token refresh failed: ${resp2.status} ${body}`);
|
|
1279
|
+
}
|
|
1280
|
+
const data2 = await resp2.json();
|
|
1281
|
+
if (credentials.profileArn) {
|
|
1282
|
+
try {
|
|
1283
|
+
const apiRegion = resolveApiRegion(region);
|
|
1284
|
+
const apiModels = await fetchAvailableModels(data2.accessToken, apiRegion, credentials.profileArn);
|
|
1285
|
+
setCachedDynamicModels(buildModelsFromApi(apiModels));
|
|
1286
|
+
log.info(`Fetched and cached ${apiModels.length} models after desktop token refresh`);
|
|
1287
|
+
} catch (err) {
|
|
1288
|
+
log.warn(`Failed to fetch models after desktop token refresh: ${err}`);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1448
1291
|
return {
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1292
|
+
refresh: `${data2.refreshToken}|||desktop`,
|
|
1293
|
+
access: data2.accessToken,
|
|
1294
|
+
expires: Date.now() + (data2.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
1295
|
+
clientId: "",
|
|
1296
|
+
clientSecret: "",
|
|
1297
|
+
region,
|
|
1298
|
+
authMethod: "desktop",
|
|
1299
|
+
profileArn: credentials.profileArn,
|
|
1300
|
+
kiroSyncSource: credentials.kiroSyncSource,
|
|
1301
|
+
kiroSyncTokenKey: credentials.kiroSyncTokenKey
|
|
1453
1302
|
};
|
|
1454
1303
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1304
|
+
const endpoint = `https://oidc.${region}.amazonaws.com/token`;
|
|
1305
|
+
const resp = await fetch(endpoint, {
|
|
1306
|
+
method: "POST",
|
|
1307
|
+
headers: { "Content-Type": "application/json", "User-Agent": "pi-kiro" },
|
|
1308
|
+
body: JSON.stringify({ clientId, clientSecret, refreshToken, grantType: "refresh_token" })
|
|
1309
|
+
});
|
|
1310
|
+
if (!resp.ok) {
|
|
1311
|
+
const body = await resp.text().catch(() => "");
|
|
1312
|
+
throw new Error(`Token refresh failed: ${resp.status} ${body}`);
|
|
1313
|
+
}
|
|
1314
|
+
const data = await resp.json();
|
|
1315
|
+
if (credentials.profileArn) {
|
|
1316
|
+
try {
|
|
1317
|
+
const apiRegion = resolveApiRegion(region);
|
|
1318
|
+
const apiModels = await fetchAvailableModels(data.accessToken, apiRegion, credentials.profileArn);
|
|
1319
|
+
setCachedDynamicModels(buildModelsFromApi(apiModels));
|
|
1320
|
+
log.info(`Fetched and cached ${apiModels.length} models after token refresh`);
|
|
1321
|
+
} catch (err) {
|
|
1322
|
+
log.warn(`Failed to fetch models after token refresh, falling back: ${err}`);
|
|
1466
1323
|
}
|
|
1467
1324
|
}
|
|
1468
|
-
const useSet = new Set(toolUseIds);
|
|
1469
|
-
const resultSet = new Set(toolResultIds);
|
|
1470
1325
|
return {
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1326
|
+
refresh: `${data.refreshToken}|${clientId}|${clientSecret}|${authMethod}`,
|
|
1327
|
+
access: data.accessToken,
|
|
1328
|
+
expires: Date.now() + (data.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
1329
|
+
clientId,
|
|
1330
|
+
clientSecret,
|
|
1331
|
+
region,
|
|
1332
|
+
authMethod,
|
|
1333
|
+
profileArn: credentials.profileArn,
|
|
1334
|
+
kiroSyncSource: credentials.kiroSyncSource,
|
|
1335
|
+
kiroSyncTokenKey: credentials.kiroSyncTokenKey
|
|
1477
1336
|
};
|
|
1478
1337
|
}
|
|
1479
|
-
function
|
|
1480
|
-
const
|
|
1481
|
-
const
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
roleSequence: historySummary.map((entry) => entry.role),
|
|
1492
|
-
history: historySummary,
|
|
1493
|
-
current: {
|
|
1494
|
-
contentChars: current.content.length,
|
|
1495
|
-
imageCount: current.images?.length ?? 0,
|
|
1496
|
-
toolResultCount: currentToolResults.length,
|
|
1497
|
-
toolSpecCount: currentTools.length,
|
|
1498
|
-
toolSpecNames: currentTools.map((toolSpec) => toolSpec.toolSpecification.name).sort(),
|
|
1499
|
-
toolResults: currentToolResults.map(summarizeToolResult),
|
|
1500
|
-
toolSpecs: currentTools.map(summarizeToolSpec)
|
|
1501
|
-
},
|
|
1502
|
-
toolIntegrity: collectToolIntegrity(history, currentToolResults),
|
|
1503
|
-
additionalModelRequestFieldKeys: objectKeys(request.additionalModelRequestFields),
|
|
1504
|
-
hasProfileArn: !!request.profileArn,
|
|
1505
|
-
agentMode: request.agentMode
|
|
1338
|
+
async function refreshKiroToken(credentials) {
|
|
1339
|
+
const inputMethod = credentials.authMethod;
|
|
1340
|
+
const authMethod = inputMethod === "builder-id" || inputMethod === "idc" || inputMethod === "desktop" ? inputMethod : "idc";
|
|
1341
|
+
if (inputMethod !== undefined && inputMethod !== "builder-id" && inputMethod !== "idc" && inputMethod !== "desktop") {
|
|
1342
|
+
log.warn(`refreshKiroToken: unrecognized authMethod "${String(inputMethod)}" — defaulting to "idc"`);
|
|
1343
|
+
}
|
|
1344
|
+
const baseCreds = {
|
|
1345
|
+
...credentials,
|
|
1346
|
+
clientId: credentials.clientId ?? credentials.refresh.split("|")[1] ?? "",
|
|
1347
|
+
clientSecret: credentials.clientSecret ?? credentials.refresh.split("|")[2] ?? "",
|
|
1348
|
+
region: credentials.region,
|
|
1349
|
+
authMethod
|
|
1506
1350
|
};
|
|
1507
|
-
|
|
1508
|
-
function emitToolCall(state, output, stream) {
|
|
1509
|
-
if (!state.input.trim())
|
|
1510
|
-
state.input = "{}";
|
|
1511
|
-
let args;
|
|
1351
|
+
const errors = [];
|
|
1512
1352
|
try {
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1353
|
+
log.debug("refresh.cascade: layer 1 — normal refresh");
|
|
1354
|
+
const result = await refreshTokenInner(baseCreds);
|
|
1355
|
+
syncBackToKiroCli(result);
|
|
1356
|
+
return result;
|
|
1357
|
+
} catch (err) {
|
|
1358
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1359
|
+
errors.push(`L1(normal): ${msg}`);
|
|
1360
|
+
log.warn(`refresh.cascade: layer 1 failed — ${msg}`);
|
|
1361
|
+
}
|
|
1362
|
+
let freshImport = null;
|
|
1363
|
+
try {
|
|
1364
|
+
log.debug("refresh.cascade: layer 2 — fresh kiro-cli import");
|
|
1365
|
+
const { importFromKiroCli: importFromKiroCli2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
1366
|
+
freshImport = await importFromKiroCli2();
|
|
1367
|
+
if (freshImport?.accessToken) {
|
|
1368
|
+
const result = kiroCredsFromCliImport(freshImport);
|
|
1369
|
+
log.info("refresh.cascade: layer 2 succeeded — using fresh kiro-cli credentials");
|
|
1370
|
+
return result;
|
|
1516
1371
|
}
|
|
1517
|
-
|
|
1518
|
-
log.
|
|
1519
|
-
|
|
1372
|
+
errors.push("L2(fresh-import): no valid credentials found");
|
|
1373
|
+
log.debug("refresh.cascade: layer 2 — no fresh credentials");
|
|
1374
|
+
} catch (err) {
|
|
1375
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1376
|
+
errors.push(`L2(fresh-import): ${msg}`);
|
|
1377
|
+
log.warn(`refresh.cascade: layer 2 failed — ${msg}`);
|
|
1520
1378
|
}
|
|
1521
|
-
|
|
1522
|
-
const toolCall = { type: "toolCall", id: state.toolUseId, name: state.name, arguments: args };
|
|
1523
|
-
output.content.push(toolCall);
|
|
1524
|
-
stream.push({ type: "toolcall_start", contentIndex, partial: output });
|
|
1525
|
-
stream.push({ type: "toolcall_delta", contentIndex, delta: state.input, partial: output });
|
|
1526
|
-
stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: output });
|
|
1527
|
-
return true;
|
|
1528
|
-
}
|
|
1529
|
-
function streamKiro(model, context, options) {
|
|
1530
|
-
const stream = createAssistantMessageEventStream();
|
|
1531
|
-
(async () => {
|
|
1532
|
-
const output = {
|
|
1533
|
-
role: "assistant",
|
|
1534
|
-
content: [],
|
|
1535
|
-
api: model.api,
|
|
1536
|
-
provider: model.provider,
|
|
1537
|
-
model: model.id,
|
|
1538
|
-
usage: {
|
|
1539
|
-
input: 0,
|
|
1540
|
-
output: 0,
|
|
1541
|
-
cacheRead: 0,
|
|
1542
|
-
cacheWrite: 0,
|
|
1543
|
-
totalTokens: 0,
|
|
1544
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
|
|
1545
|
-
},
|
|
1546
|
-
stopReason: "stop",
|
|
1547
|
-
timestamp: Date.now()
|
|
1548
|
-
};
|
|
1549
|
-
let hiddenShimTimer = null;
|
|
1379
|
+
if (freshImport?.refreshToken) {
|
|
1550
1380
|
try {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1381
|
+
log.debug("refresh.cascade: layer 3 — refresh fresh kiro-cli creds");
|
|
1382
|
+
const freshCreds = kiroCredsFromCliImport(freshImport);
|
|
1383
|
+
const result = await refreshTokenInner(freshCreds);
|
|
1384
|
+
syncBackToKiroCli(result);
|
|
1385
|
+
log.info("refresh.cascade: layer 3 succeeded — refreshed fresh kiro-cli credentials");
|
|
1386
|
+
return result;
|
|
1387
|
+
} catch (err) {
|
|
1388
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1389
|
+
errors.push(`L3(refresh-fresh): ${msg}`);
|
|
1390
|
+
log.warn(`refresh.cascade: layer 3 failed — ${msg}`);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
let expiredImport = null;
|
|
1394
|
+
try {
|
|
1395
|
+
log.debug("refresh.cascade: layer 4 — expired kiro-cli import");
|
|
1396
|
+
const { getKiroCliCredentialsAllowExpired: getKiroCliCredentialsAllowExpired2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
1397
|
+
expiredImport = await getKiroCliCredentialsAllowExpired2(freshImport);
|
|
1398
|
+
if (expiredImport?.accessToken) {
|
|
1399
|
+
const result = kiroCredsFromCliImport(expiredImport);
|
|
1400
|
+
log.info("refresh.cascade: layer 4 succeeded — using expired kiro-cli credentials");
|
|
1401
|
+
return result;
|
|
1402
|
+
} else {
|
|
1403
|
+
errors.push("L4(expired-import): no different expired credentials");
|
|
1404
|
+
log.debug("refresh.cascade: layer 4 — no additional expired credentials");
|
|
1405
|
+
}
|
|
1406
|
+
} catch (err) {
|
|
1407
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1408
|
+
errors.push(`L4(expired-import): ${msg}`);
|
|
1409
|
+
log.warn(`refresh.cascade: layer 4 failed — ${msg}`);
|
|
1410
|
+
}
|
|
1411
|
+
if (expiredImport?.refreshToken) {
|
|
1412
|
+
try {
|
|
1413
|
+
log.debug("refresh.cascade: layer 5 — refresh expired kiro-cli creds");
|
|
1414
|
+
const expiredCreds = kiroCredsFromCliImport(expiredImport);
|
|
1415
|
+
const result = await refreshTokenInner(expiredCreds);
|
|
1416
|
+
syncBackToKiroCli(result);
|
|
1417
|
+
log.info("refresh.cascade: layer 5 succeeded — refreshed expired kiro-cli credentials");
|
|
1418
|
+
return result;
|
|
1419
|
+
} catch (err) {
|
|
1420
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1421
|
+
errors.push(`L5(refresh-expired): ${msg}`);
|
|
1422
|
+
log.warn(`refresh.cascade: layer 5 failed — ${msg}`);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
throw new Error(`Kiro token refresh failed — all 5 cascade layers exhausted. ` + `Re-login required.
|
|
1426
|
+
${errors.join(`
|
|
1427
|
+
`)}`);
|
|
1428
|
+
}
|
|
1429
|
+
// src/stream.ts
|
|
1430
|
+
init_debug();
|
|
1431
|
+
import { calculateCost, createAssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
1432
|
+
|
|
1433
|
+
// src/event-parser.ts
|
|
1434
|
+
init_debug();
|
|
1435
|
+
function findJsonEnd(text, start) {
|
|
1436
|
+
let braceCount = 0;
|
|
1437
|
+
let inString = false;
|
|
1438
|
+
let escapeNext = false;
|
|
1439
|
+
for (let i = start;i < text.length; i++) {
|
|
1440
|
+
const char = text[i];
|
|
1441
|
+
if (escapeNext) {
|
|
1442
|
+
escapeNext = false;
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
if (char === "\\") {
|
|
1446
|
+
escapeNext = true;
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
if (char === '"') {
|
|
1450
|
+
inString = !inString;
|
|
1451
|
+
continue;
|
|
1452
|
+
}
|
|
1453
|
+
if (!inString) {
|
|
1454
|
+
if (char === "{")
|
|
1455
|
+
braceCount++;
|
|
1456
|
+
else if (char === "}") {
|
|
1457
|
+
braceCount--;
|
|
1458
|
+
if (braceCount === 0)
|
|
1459
|
+
return i;
|
|
1554
1460
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
return -1;
|
|
1464
|
+
}
|
|
1465
|
+
function parseKiroEvent(parsed) {
|
|
1466
|
+
if (parsed.content !== undefined) {
|
|
1467
|
+
return { type: "content", data: parsed.content };
|
|
1468
|
+
}
|
|
1469
|
+
if (parsed.reasoningText !== undefined || parsed.signature !== undefined || parsed.text !== undefined && !parsed.content && !parsed.name && !parsed.message) {
|
|
1470
|
+
let text = "";
|
|
1471
|
+
let signature;
|
|
1472
|
+
if (parsed.reasoningText) {
|
|
1473
|
+
const rt = parsed.reasoningText;
|
|
1474
|
+
text = (rt.text ?? rt.Text) || "";
|
|
1475
|
+
signature = rt.signature ?? rt.Signature;
|
|
1476
|
+
} else {
|
|
1477
|
+
text = parsed.text || "";
|
|
1478
|
+
signature = parsed.signature;
|
|
1479
|
+
}
|
|
1480
|
+
return {
|
|
1481
|
+
type: "reasoning",
|
|
1482
|
+
data: { text, signature }
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
if (parsed.name && parsed.toolUseId) {
|
|
1486
|
+
const rawInput = parsed.input;
|
|
1487
|
+
const input = typeof rawInput === "string" ? rawInput : rawInput && typeof rawInput === "object" && Object.keys(rawInput).length > 0 ? JSON.stringify(rawInput) : "";
|
|
1488
|
+
return {
|
|
1489
|
+
type: "toolUse",
|
|
1490
|
+
data: {
|
|
1491
|
+
name: parsed.name,
|
|
1492
|
+
toolUseId: parsed.toolUseId,
|
|
1493
|
+
input,
|
|
1494
|
+
stop: parsed.stop
|
|
1579
1495
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
if (toolResultImages.length > 0) {
|
|
1668
|
-
const { images: converted, omitted } = convertImagesToKiro(toolResultImages);
|
|
1669
|
-
if (omitted > 0)
|
|
1670
|
-
log.warn(`${omitted} tool-result image(s) omitted (size/count limit)`);
|
|
1671
|
-
currentImages = currentImages ? [...currentImages, ...converted] : converted;
|
|
1672
|
-
}
|
|
1673
|
-
currentContent = currentToolResults.length > 0 ? "Tool results provided." : "Please proceed with the task.";
|
|
1674
|
-
} else if (firstMsg?.role === "toolResult") {
|
|
1675
|
-
const toolResultImages = [];
|
|
1676
|
-
for (const m of currentMessages) {
|
|
1677
|
-
if (m?.role === "toolResult") {
|
|
1678
|
-
const trm = m;
|
|
1679
|
-
currentToolResults.push({
|
|
1680
|
-
content: [{ text: truncate(getContentText(m), TOOL_RESULT_LIMIT) }],
|
|
1681
|
-
status: trm.isError ? "error" : "success",
|
|
1682
|
-
toolUseId: toKiroToolUseId(trm.toolCallId)
|
|
1683
|
-
});
|
|
1684
|
-
if (Array.isArray(trm.content)) {
|
|
1685
|
-
for (const c of trm.content) {
|
|
1686
|
-
if (c.type === "image")
|
|
1687
|
-
toolResultImages.push(c);
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
if (toolResultImages.length > 0) {
|
|
1693
|
-
const { images: converted, omitted } = convertImagesToKiro(toolResultImages);
|
|
1694
|
-
if (omitted > 0)
|
|
1695
|
-
log.warn(`${omitted} tool-result image(s) omitted (size/count limit)`);
|
|
1696
|
-
currentImages = currentImages ? [...currentImages, ...converted] : converted;
|
|
1697
|
-
}
|
|
1698
|
-
currentContent = "Tool results provided.";
|
|
1699
|
-
} else if (firstMsg?.role === "user") {
|
|
1700
|
-
currentContent = typeof firstMsg.content === "string" ? firstMsg.content : getContentText(firstMsg);
|
|
1701
|
-
if (systemPrompt && !systemPrepended) {
|
|
1702
|
-
currentContent = `${systemPrompt}
|
|
1703
|
-
|
|
1704
|
-
${currentContent}`;
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
const now = new Date;
|
|
1708
|
-
const tzOffset = -now.getTimezoneOffset();
|
|
1709
|
-
const tzSign = tzOffset >= 0 ? "+" : "-";
|
|
1710
|
-
const tzH = String(Math.floor(Math.abs(tzOffset) / 60)).padStart(2, "0");
|
|
1711
|
-
const tzM = String(Math.abs(tzOffset) % 60).padStart(2, "0");
|
|
1712
|
-
const isoLocal = now.getFullYear() + "-" + String(now.getMonth() + 1).padStart(2, "0") + "-" + String(now.getDate()).padStart(2, "0") + "T" + String(now.getHours()).padStart(2, "0") + ":" + String(now.getMinutes()).padStart(2, "0") + ":" + String(now.getSeconds()).padStart(2, "0") + "." + String(now.getMilliseconds()).padStart(3, "0") + tzSign + tzH + ":" + tzM;
|
|
1713
|
-
const weekday = now.toLocaleDateString("en-US", { weekday: "long" });
|
|
1714
|
-
currentContent = `--- CONTEXT ENTRY BEGIN ---
|
|
1715
|
-
` + `Current time: ${weekday}, ${isoLocal}
|
|
1716
|
-
` + `--- CONTEXT ENTRY END ---
|
|
1717
|
-
|
|
1718
|
-
` + `--- USER MESSAGE BEGIN ---
|
|
1719
|
-
` + `${currentContent}
|
|
1720
|
-
` + `--- USER MESSAGE END ---`;
|
|
1721
|
-
let uimc = {
|
|
1722
|
-
envState
|
|
1723
|
-
};
|
|
1724
|
-
if (currentToolResults.length > 0)
|
|
1725
|
-
uimc.toolResults = currentToolResults;
|
|
1726
|
-
if (context.tools?.length) {
|
|
1727
|
-
const deepCleanSchema = (obj) => {
|
|
1728
|
-
if (Array.isArray(obj)) {
|
|
1729
|
-
return obj.map(deepCleanSchema);
|
|
1730
|
-
} else if (obj !== null && typeof obj === "object") {
|
|
1731
|
-
const cleaned = {};
|
|
1732
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
1733
|
-
if (k === "$schema")
|
|
1734
|
-
continue;
|
|
1735
|
-
if (k === "required" && Array.isArray(v) && v.length === 0)
|
|
1736
|
-
continue;
|
|
1737
|
-
cleaned[k] = deepCleanSchema(v);
|
|
1738
|
-
}
|
|
1739
|
-
return cleaned;
|
|
1740
|
-
}
|
|
1741
|
-
return obj;
|
|
1742
|
-
};
|
|
1743
|
-
uimc.tools = context.tools.map((t) => {
|
|
1744
|
-
const params = deepCleanSchema(t.parameters);
|
|
1745
|
-
if (params.properties) {
|
|
1746
|
-
params.properties.__tool_use_purpose = {
|
|
1747
|
-
type: "string",
|
|
1748
|
-
description: "A brief explanation why you are making this tool use."
|
|
1749
|
-
};
|
|
1750
|
-
}
|
|
1751
|
-
return {
|
|
1752
|
-
toolSpecification: {
|
|
1753
|
-
name: t.name,
|
|
1754
|
-
description: t.description || `Use ${t.name}`,
|
|
1755
|
-
inputSchema: { json: params }
|
|
1756
|
-
}
|
|
1757
|
-
};
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
if (parsed.input !== undefined && !parsed.name) {
|
|
1499
|
+
return {
|
|
1500
|
+
type: "toolUseInput",
|
|
1501
|
+
data: {
|
|
1502
|
+
input: typeof parsed.input === "string" ? parsed.input : JSON.stringify(parsed.input)
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
if (parsed.stop !== undefined && parsed.contextUsagePercentage === undefined) {
|
|
1507
|
+
return { type: "toolUseStop", data: { stop: parsed.stop } };
|
|
1508
|
+
}
|
|
1509
|
+
if (parsed.contextUsagePercentage !== undefined) {
|
|
1510
|
+
return {
|
|
1511
|
+
type: "contextUsage",
|
|
1512
|
+
data: { contextUsagePercentage: parsed.contextUsagePercentage }
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
if (parsed.followupPrompt !== undefined) {
|
|
1516
|
+
return { type: "followupPrompt", data: parsed.followupPrompt };
|
|
1517
|
+
}
|
|
1518
|
+
if (parsed.error !== undefined || parsed.Error !== undefined) {
|
|
1519
|
+
const err = parsed.error || parsed.Error || "unknown";
|
|
1520
|
+
const message = parsed.message || parsed.Message || parsed.reason;
|
|
1521
|
+
return {
|
|
1522
|
+
type: "error",
|
|
1523
|
+
data: {
|
|
1524
|
+
error: typeof err === "string" ? err : JSON.stringify(err),
|
|
1525
|
+
message
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
if (parsed.usage !== undefined) {
|
|
1530
|
+
const u = parsed.usage;
|
|
1531
|
+
return {
|
|
1532
|
+
type: "usage",
|
|
1533
|
+
data: {
|
|
1534
|
+
inputTokens: u.inputTokens,
|
|
1535
|
+
outputTokens: u.outputTokens
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
return null;
|
|
1540
|
+
}
|
|
1541
|
+
var EVENT_PATTERNS = [
|
|
1542
|
+
'{"content":',
|
|
1543
|
+
'{"reasoningText":',
|
|
1544
|
+
'{"signature":',
|
|
1545
|
+
'{"text":',
|
|
1546
|
+
'{"name":',
|
|
1547
|
+
'{"input":',
|
|
1548
|
+
'{"stop":',
|
|
1549
|
+
'{"contextUsagePercentage":',
|
|
1550
|
+
'{"followupPrompt":',
|
|
1551
|
+
'{"usage":',
|
|
1552
|
+
'{"toolUseId":',
|
|
1553
|
+
'{"unit":',
|
|
1554
|
+
'{"error":',
|
|
1555
|
+
'{"Error":',
|
|
1556
|
+
'{"message":'
|
|
1557
|
+
];
|
|
1558
|
+
function findNextEventStart(buffer, from) {
|
|
1559
|
+
let earliest = -1;
|
|
1560
|
+
for (const pattern of EVENT_PATTERNS) {
|
|
1561
|
+
const idx = buffer.indexOf(pattern, from);
|
|
1562
|
+
if (idx >= 0 && (earliest < 0 || idx < earliest))
|
|
1563
|
+
earliest = idx;
|
|
1564
|
+
}
|
|
1565
|
+
return earliest;
|
|
1566
|
+
}
|
|
1567
|
+
function parseKiroEvents(buffer) {
|
|
1568
|
+
const events = [];
|
|
1569
|
+
let pos = 0;
|
|
1570
|
+
while (pos < buffer.length) {
|
|
1571
|
+
const jsonStart = findNextEventStart(buffer, pos);
|
|
1572
|
+
if (jsonStart < 0) {
|
|
1573
|
+
if (log.isDebug()) {
|
|
1574
|
+
const gap = buffer.substring(pos);
|
|
1575
|
+
const braceIdx = gap.indexOf('{"');
|
|
1576
|
+
if (braceIdx >= 0) {
|
|
1577
|
+
log.debug("event.unmatchedBrace", {
|
|
1578
|
+
from: pos + braceIdx,
|
|
1579
|
+
preview: gap.substring(braceIdx, Math.min(braceIdx + 200, gap.length))
|
|
1758
1580
|
});
|
|
1759
1581
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1582
|
+
}
|
|
1583
|
+
break;
|
|
1584
|
+
}
|
|
1585
|
+
if (log.isDebug() && jsonStart > pos) {
|
|
1586
|
+
const skipped = buffer.substring(pos, jsonStart);
|
|
1587
|
+
const braceIdx = skipped.indexOf('{"');
|
|
1588
|
+
if (braceIdx >= 0) {
|
|
1589
|
+
log.debug("event.skippedBrace", {
|
|
1590
|
+
from: pos + braceIdx,
|
|
1591
|
+
preview: skipped.substring(braceIdx, Math.min(braceIdx + 200, skipped.length))
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
const jsonEnd = findJsonEnd(buffer, jsonStart);
|
|
1596
|
+
if (jsonEnd < 0) {
|
|
1597
|
+
return { events, remaining: buffer.substring(jsonStart) };
|
|
1598
|
+
}
|
|
1599
|
+
try {
|
|
1600
|
+
const parsed = JSON.parse(buffer.substring(jsonStart, jsonEnd + 1));
|
|
1601
|
+
const event = parseKiroEvent(parsed);
|
|
1602
|
+
if (event) {
|
|
1603
|
+
events.push(event);
|
|
1604
|
+
} else if (log.isDebug()) {
|
|
1605
|
+
log.debug("event.unknown", { keys: Object.keys(parsed), raw: parsed });
|
|
1606
|
+
}
|
|
1607
|
+
} catch (err) {
|
|
1608
|
+
if (log.isDebug()) {
|
|
1609
|
+
log.debug("event.parseFail", {
|
|
1610
|
+
err: err instanceof Error ? err.message : String(err),
|
|
1611
|
+
snippet: buffer.substring(jsonStart, Math.min(jsonEnd + 1, jsonStart + 200))
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
pos = jsonEnd + 1;
|
|
1616
|
+
}
|
|
1617
|
+
return { events, remaining: "" };
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
// src/health.ts
|
|
1621
|
+
var PERMANENT_PATTERNS = [
|
|
1622
|
+
"Invalid refresh token",
|
|
1623
|
+
"Invalid grant provided",
|
|
1624
|
+
"InvalidGrantException",
|
|
1625
|
+
"UnauthorizedClientException",
|
|
1626
|
+
"AuthorizationPendingException",
|
|
1627
|
+
"ExpiredTokenException",
|
|
1628
|
+
"client is not registered",
|
|
1629
|
+
"The security token is expired",
|
|
1630
|
+
"Access denied"
|
|
1631
|
+
];
|
|
1632
|
+
function isPermanentError(reason) {
|
|
1633
|
+
if (!reason)
|
|
1634
|
+
return false;
|
|
1635
|
+
return PERMANENT_PATTERNS.some((p) => reason.includes(p));
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// src/thinking-parser.ts
|
|
1639
|
+
init_debug();
|
|
1640
|
+
var THINKING_END_TAG = "</thinking>";
|
|
1641
|
+
var THINKING_TAG_VARIANTS = [
|
|
1642
|
+
{ open: "<thinking>", close: "</thinking>" },
|
|
1643
|
+
{ open: "<think>", close: "</think>" },
|
|
1644
|
+
{ open: "<reasoning>", close: "</reasoning>" },
|
|
1645
|
+
{ open: "<thought>", close: "</thought>" }
|
|
1646
|
+
];
|
|
1647
|
+
function trailingPrefixLength(text, tag) {
|
|
1648
|
+
const max = Math.min(text.length, tag.length - 1);
|
|
1649
|
+
for (let len = max;len > 0; len--) {
|
|
1650
|
+
if (text.endsWith(tag.slice(0, len)))
|
|
1651
|
+
return len;
|
|
1652
|
+
}
|
|
1653
|
+
return 0;
|
|
1654
|
+
}
|
|
1655
|
+
function maxTrailingPrefixLength(text, tags) {
|
|
1656
|
+
let max = 0;
|
|
1657
|
+
for (const tag of tags) {
|
|
1658
|
+
max = Math.max(max, trailingPrefixLength(text, tag));
|
|
1659
|
+
}
|
|
1660
|
+
return max;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
class ThinkingTagParser {
|
|
1664
|
+
output;
|
|
1665
|
+
stream;
|
|
1666
|
+
textBuffer = "";
|
|
1667
|
+
inThinking = false;
|
|
1668
|
+
thinkingExtracted = false;
|
|
1669
|
+
thinkingBlockIndex = null;
|
|
1670
|
+
textBlockIndex = null;
|
|
1671
|
+
lastTextBlockIndex = null;
|
|
1672
|
+
activeEndTag = THINKING_END_TAG;
|
|
1673
|
+
constructor(output, stream) {
|
|
1674
|
+
this.output = output;
|
|
1675
|
+
this.stream = stream;
|
|
1676
|
+
}
|
|
1677
|
+
processChunk(chunk) {
|
|
1678
|
+
this.textBuffer += chunk;
|
|
1679
|
+
if (log.isDebug()) {
|
|
1680
|
+
log.debug("thinking.chunk", {
|
|
1681
|
+
chunkLen: chunk.length,
|
|
1682
|
+
bufferLen: this.textBuffer.length,
|
|
1683
|
+
inThinking: this.inThinking,
|
|
1684
|
+
thinkingExtracted: this.thinkingExtracted
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
while (this.textBuffer.length > 0) {
|
|
1688
|
+
const prev = this.textBuffer.length;
|
|
1689
|
+
if (!this.inThinking && !this.thinkingExtracted) {
|
|
1690
|
+
this.processBeforeThinking();
|
|
1691
|
+
if (this.textBuffer.length === 0)
|
|
1692
|
+
break;
|
|
1693
|
+
}
|
|
1694
|
+
if (this.inThinking) {
|
|
1695
|
+
this.processInsideThinking();
|
|
1696
|
+
if (this.textBuffer.length === 0)
|
|
1697
|
+
break;
|
|
1698
|
+
}
|
|
1699
|
+
if (this.thinkingExtracted) {
|
|
1700
|
+
this.processAfterThinking();
|
|
1701
|
+
break;
|
|
1702
|
+
}
|
|
1703
|
+
if (this.textBuffer.length >= prev)
|
|
1704
|
+
break;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
finalize() {
|
|
1708
|
+
if (log.isDebug()) {
|
|
1709
|
+
log.debug("thinking.finalize", {
|
|
1710
|
+
bufferLen: this.textBuffer.length,
|
|
1711
|
+
inThinking: this.inThinking,
|
|
1712
|
+
thinkingExtracted: this.thinkingExtracted,
|
|
1713
|
+
textBlockIndex: this.textBlockIndex,
|
|
1714
|
+
thinkingBlockIndex: this.thinkingBlockIndex
|
|
1715
|
+
});
|
|
1716
|
+
}
|
|
1717
|
+
if (this.textBuffer.length === 0)
|
|
1718
|
+
return;
|
|
1719
|
+
if (this.inThinking && this.thinkingBlockIndex !== null) {
|
|
1720
|
+
const block = this.output.content[this.thinkingBlockIndex];
|
|
1721
|
+
if (block) {
|
|
1722
|
+
block.thinking += this.textBuffer;
|
|
1723
|
+
this.stream.push({
|
|
1724
|
+
type: "thinking_delta",
|
|
1725
|
+
contentIndex: this.thinkingBlockIndex,
|
|
1726
|
+
delta: this.textBuffer,
|
|
1727
|
+
partial: this.output
|
|
1728
|
+
});
|
|
1729
|
+
this.stream.push({
|
|
1730
|
+
type: "thinking_end",
|
|
1731
|
+
contentIndex: this.thinkingBlockIndex,
|
|
1732
|
+
content: block.thinking,
|
|
1733
|
+
partial: this.output
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
} else {
|
|
1737
|
+
this.emitText(this.textBuffer);
|
|
1738
|
+
}
|
|
1739
|
+
this.textBuffer = "";
|
|
1740
|
+
}
|
|
1741
|
+
getTextBlockIndex() {
|
|
1742
|
+
return this.textBlockIndex ?? this.lastTextBlockIndex;
|
|
1743
|
+
}
|
|
1744
|
+
processBeforeThinking() {
|
|
1745
|
+
let bestPos = -1;
|
|
1746
|
+
let bestVariant = null;
|
|
1747
|
+
for (const variant of THINKING_TAG_VARIANTS) {
|
|
1748
|
+
const pos = this.textBuffer.indexOf(variant.open);
|
|
1749
|
+
if (pos !== -1 && (bestPos === -1 || pos < bestPos)) {
|
|
1750
|
+
bestPos = pos;
|
|
1751
|
+
bestVariant = variant;
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
if (bestPos !== -1 && bestVariant) {
|
|
1755
|
+
if (log.isDebug()) {
|
|
1756
|
+
log.debug("thinking.open", { tag: bestVariant.open, at: bestPos });
|
|
1757
|
+
}
|
|
1758
|
+
if (bestPos > 0)
|
|
1759
|
+
this.emitText(this.textBuffer.slice(0, bestPos));
|
|
1760
|
+
this.textBuffer = this.textBuffer.slice(bestPos + bestVariant.open.length);
|
|
1761
|
+
this.activeEndTag = bestVariant.close;
|
|
1762
|
+
this.inThinking = true;
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
const trailing = maxTrailingPrefixLength(this.textBuffer, THINKING_TAG_VARIANTS.map((v) => v.open));
|
|
1766
|
+
const safeLen = this.textBuffer.length - trailing;
|
|
1767
|
+
if (safeLen > 0) {
|
|
1768
|
+
this.emitText(this.textBuffer.slice(0, safeLen));
|
|
1769
|
+
this.textBuffer = this.textBuffer.slice(safeLen);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
processInsideThinking() {
|
|
1773
|
+
const endPos = this.textBuffer.indexOf(this.activeEndTag);
|
|
1774
|
+
if (endPos !== -1) {
|
|
1775
|
+
if (log.isDebug()) {
|
|
1776
|
+
log.debug("thinking.close", { tag: this.activeEndTag, at: endPos });
|
|
1777
|
+
}
|
|
1778
|
+
if (endPos > 0)
|
|
1779
|
+
this.emitThinking(this.textBuffer.slice(0, endPos));
|
|
1780
|
+
if (this.thinkingBlockIndex !== null) {
|
|
1781
|
+
const block = this.output.content[this.thinkingBlockIndex];
|
|
1782
|
+
if (block) {
|
|
1783
|
+
this.stream.push({
|
|
1784
|
+
type: "thinking_end",
|
|
1785
|
+
contentIndex: this.thinkingBlockIndex,
|
|
1786
|
+
content: block.thinking,
|
|
1787
|
+
partial: this.output
|
|
1874
1788
|
});
|
|
1875
|
-
if (isCapacityError(errText) && capacityRetryCount < CAPACITY_MAX_RETRIES) {
|
|
1876
|
-
capacityRetryCount++;
|
|
1877
|
-
const delayMs = exponentialBackoff(capacityRetryCount - 1, CAPACITY_BASE_DELAY_MS, CAPACITY_MAX_DELAY_MS);
|
|
1878
|
-
log.warn(`INSUFFICIENT_MODEL_CAPACITY — retrying in ${delayMs}ms (${capacityRetryCount}/${CAPACITY_MAX_RETRIES})`);
|
|
1879
|
-
await abortableDelay(delayMs, options?.signal);
|
|
1880
|
-
continue;
|
|
1881
|
-
}
|
|
1882
|
-
if (isNonRetryableBodyError(errText) || isCapacityError(errText)) {
|
|
1883
|
-
throw new Error(`Kiro API error: ${errText || response.statusText}`);
|
|
1884
|
-
}
|
|
1885
|
-
if (isTooBigError(response.status, errText)) {
|
|
1886
|
-
if (contextTruncationAttempt < CONTEXT_TRUNCATION_MAX_RETRIES && history.length > 0) {
|
|
1887
|
-
contextTruncationAttempt++;
|
|
1888
|
-
const dropCount = Math.max(1, Math.floor(history.length * CONTEXT_TRUNCATION_DROP_RATIO));
|
|
1889
|
-
const before = history.length;
|
|
1890
|
-
history.splice(0, dropCount);
|
|
1891
|
-
log.warn(`context too large — truncated history from ${before} to ${history.length} entries ` + `(attempt ${contextTruncationAttempt}/${CONTEXT_TRUNCATION_MAX_RETRIES})`);
|
|
1892
|
-
request.conversationState.history = history.length > 0 ? history : undefined;
|
|
1893
|
-
continue;
|
|
1894
|
-
}
|
|
1895
|
-
throw new Error(`Kiro API error: context_length_exceeded (${response.status} ${errText})`);
|
|
1896
|
-
}
|
|
1897
|
-
if (isTransientError(response.status) && transientRetryCount < TRANSIENT_MAX_RETRIES) {
|
|
1898
|
-
transientRetryCount++;
|
|
1899
|
-
const jitter = Math.floor(Math.random() * 1000);
|
|
1900
|
-
const delayMs = exponentialBackoff(transientRetryCount - 1, TRANSIENT_BASE_DELAY_MS, TRANSIENT_MAX_DELAY_MS) + jitter;
|
|
1901
|
-
log.warn(`transient error ${response.status} — retrying in ${delayMs}ms ` + `(${transientRetryCount}/${TRANSIENT_MAX_RETRIES})`);
|
|
1902
|
-
await abortableDelay(delayMs, options?.signal);
|
|
1903
|
-
continue;
|
|
1904
|
-
}
|
|
1905
|
-
if (response.status === 401) {
|
|
1906
|
-
const permanent = isPermanentError(errText);
|
|
1907
|
-
if (permanent) {
|
|
1908
|
-
profileArnCache.delete(endpoint);
|
|
1909
|
-
throw new Error(`Kiro API error: credentials permanently invalid — run /login kiro to re-authenticate. ${errText}`);
|
|
1910
|
-
}
|
|
1911
|
-
}
|
|
1912
|
-
if (response.status === 403) {
|
|
1913
|
-
profileArnCache.delete(endpoint);
|
|
1914
|
-
throw new Error(`Kiro API error: access token rejected (403) — run /login kiro to re-authenticate. ${errText}`);
|
|
1915
|
-
}
|
|
1916
|
-
throw new Error(`Kiro API error: ${response.status} ${response.statusText} ${errText}`);
|
|
1917
|
-
}
|
|
1918
|
-
if (capacityRetryCount > 0) {
|
|
1919
|
-
log.info(`recovered from capacity pressure after ${capacityRetryCount} retries`);
|
|
1920
|
-
}
|
|
1921
|
-
if (transientRetryCount > 0) {
|
|
1922
|
-
log.info(`recovered from transient error after ${transientRetryCount} retries`);
|
|
1923
1789
|
}
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1790
|
+
}
|
|
1791
|
+
this.textBuffer = this.textBuffer.slice(endPos + this.activeEndTag.length);
|
|
1792
|
+
this.inThinking = false;
|
|
1793
|
+
this.thinkingExtracted = true;
|
|
1794
|
+
this.lastTextBlockIndex = this.textBlockIndex;
|
|
1795
|
+
this.textBlockIndex = null;
|
|
1796
|
+
if (this.textBuffer.startsWith(`
|
|
1797
|
+
|
|
1798
|
+
`))
|
|
1799
|
+
this.textBuffer = this.textBuffer.slice(2);
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
const trailing = trailingPrefixLength(this.textBuffer, this.activeEndTag);
|
|
1803
|
+
const safeLen = this.textBuffer.length - trailing;
|
|
1804
|
+
if (safeLen > 0) {
|
|
1805
|
+
this.emitThinking(this.textBuffer.slice(0, safeLen));
|
|
1806
|
+
this.textBuffer = this.textBuffer.slice(safeLen);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
processAfterThinking() {
|
|
1810
|
+
this.emitText(this.textBuffer);
|
|
1811
|
+
this.textBuffer = "";
|
|
1812
|
+
}
|
|
1813
|
+
emitText(text) {
|
|
1814
|
+
if (!text)
|
|
1815
|
+
return;
|
|
1816
|
+
if (this.textBlockIndex === null) {
|
|
1817
|
+
this.textBlockIndex = this.output.content.length;
|
|
1818
|
+
this.output.content.push({ type: "text", text: "" });
|
|
1819
|
+
this.stream.push({ type: "text_start", contentIndex: this.textBlockIndex, partial: this.output });
|
|
1820
|
+
}
|
|
1821
|
+
const block = this.output.content[this.textBlockIndex];
|
|
1822
|
+
if (!block)
|
|
1823
|
+
return;
|
|
1824
|
+
block.text += text;
|
|
1825
|
+
this.stream.push({
|
|
1826
|
+
type: "text_delta",
|
|
1827
|
+
contentIndex: this.textBlockIndex,
|
|
1828
|
+
delta: text,
|
|
1829
|
+
partial: this.output
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
emitThinking(thinking) {
|
|
1833
|
+
if (!thinking)
|
|
1834
|
+
return;
|
|
1835
|
+
if (this.thinkingBlockIndex === null) {
|
|
1836
|
+
if (this.textBlockIndex !== null) {
|
|
1837
|
+
this.thinkingBlockIndex = this.textBlockIndex;
|
|
1838
|
+
this.output.content.splice(this.thinkingBlockIndex, 0, { type: "thinking", thinking: "" });
|
|
1839
|
+
this.textBlockIndex = this.textBlockIndex + 1;
|
|
1840
|
+
} else {
|
|
1841
|
+
this.thinkingBlockIndex = this.output.content.length;
|
|
1842
|
+
this.output.content.push({ type: "thinking", thinking: "" });
|
|
1843
|
+
}
|
|
1844
|
+
this.stream.push({
|
|
1845
|
+
type: "thinking_start",
|
|
1846
|
+
contentIndex: this.thinkingBlockIndex,
|
|
1847
|
+
partial: this.output
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
const block = this.output.content[this.thinkingBlockIndex];
|
|
1851
|
+
if (!block)
|
|
1852
|
+
return;
|
|
1853
|
+
block.thinking += thinking;
|
|
1854
|
+
this.stream.push({
|
|
1855
|
+
type: "thinking_delta",
|
|
1856
|
+
contentIndex: this.thinkingBlockIndex,
|
|
1857
|
+
delta: thinking,
|
|
1858
|
+
partial: this.output
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// src/tokenizer.ts
|
|
1864
|
+
function countTokens(text) {
|
|
1865
|
+
if (!text)
|
|
1866
|
+
return 0;
|
|
1867
|
+
return Math.ceil(text.length / 4);
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
// src/transform.ts
|
|
1871
|
+
import { createHash } from "node:crypto";
|
|
1872
|
+
function normalizeMessages(messages) {
|
|
1873
|
+
return messages.filter((msg) => msg.role !== "assistant" || msg.stopReason !== "error" && msg.stopReason !== "aborted");
|
|
1874
|
+
}
|
|
1875
|
+
var TOOL_RESULT_LIMIT = 250000;
|
|
1876
|
+
var MAX_KIRO_IMAGES = 4;
|
|
1877
|
+
var MAX_KIRO_IMAGE_BYTES = 3750000;
|
|
1878
|
+
function truncate(text, limit) {
|
|
1879
|
+
if (text.length <= limit)
|
|
1880
|
+
return text;
|
|
1881
|
+
const half = Math.floor(limit / 2);
|
|
1882
|
+
return `${text.substring(0, half)}
|
|
1883
|
+
... [TRUNCATED] ...
|
|
1884
|
+
${text.substring(text.length - half)}`;
|
|
1885
|
+
}
|
|
1886
|
+
function extractImages(msg) {
|
|
1887
|
+
if (msg.role === "toolResult" || typeof msg.content === "string")
|
|
1888
|
+
return [];
|
|
1889
|
+
if (!Array.isArray(msg.content))
|
|
1890
|
+
return [];
|
|
1891
|
+
return msg.content.filter((c) => c.type === "image");
|
|
1892
|
+
}
|
|
1893
|
+
function getContentText(msg) {
|
|
1894
|
+
if (msg.role === "toolResult") {
|
|
1895
|
+
return msg.content.map((c) => c.type === "text" ? c.text : "").join("");
|
|
1896
|
+
}
|
|
1897
|
+
if (typeof msg.content === "string")
|
|
1898
|
+
return msg.content;
|
|
1899
|
+
if (!Array.isArray(msg.content))
|
|
1900
|
+
return "";
|
|
1901
|
+
return msg.content.map((c) => {
|
|
1902
|
+
if (c.type === "text")
|
|
1903
|
+
return c.text;
|
|
1904
|
+
if (c.type === "thinking")
|
|
1905
|
+
return c.thinking;
|
|
1906
|
+
return "";
|
|
1907
|
+
}).join("");
|
|
1908
|
+
}
|
|
1909
|
+
function parseToolArgs(input) {
|
|
1910
|
+
if (input && typeof input === "object")
|
|
1911
|
+
return input;
|
|
1912
|
+
if (typeof input !== "string")
|
|
1913
|
+
return {};
|
|
1914
|
+
try {
|
|
1915
|
+
return JSON.parse(input);
|
|
1916
|
+
} catch {
|
|
1917
|
+
return {};
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
var KIRO_TOOL_USE_ID_RE = /^tooluse_[A-Za-z0-9]+$/;
|
|
1921
|
+
function toKiroToolUseId(id) {
|
|
1922
|
+
if (KIRO_TOOL_USE_ID_RE.test(id))
|
|
1923
|
+
return id;
|
|
1924
|
+
const digest = createHash("sha256").update(id).digest("hex").slice(0, 22);
|
|
1925
|
+
return `tooluse_${digest}`;
|
|
1926
|
+
}
|
|
1927
|
+
function convertImagesToKiro(images) {
|
|
1928
|
+
let omitted = 0;
|
|
1929
|
+
const valid = [];
|
|
1930
|
+
for (const img of images) {
|
|
1931
|
+
const estimatedBytes = Math.ceil(img.data.length * 3 / 4);
|
|
1932
|
+
if (estimatedBytes > MAX_KIRO_IMAGE_BYTES) {
|
|
1933
|
+
omitted++;
|
|
1934
|
+
continue;
|
|
1935
|
+
}
|
|
1936
|
+
if (valid.length >= MAX_KIRO_IMAGES) {
|
|
1937
|
+
omitted++;
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
valid.push({
|
|
1941
|
+
format: img.mimeType.split("/")[1] || "png",
|
|
1942
|
+
source: { bytes: img.data }
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
return { images: valid, omitted };
|
|
1946
|
+
}
|
|
1947
|
+
function buildHistory(messages, _modelId, systemPrompt) {
|
|
1948
|
+
const history = [];
|
|
1949
|
+
let systemPrepended = false;
|
|
1950
|
+
let currentMsgStartIdx = messages.length - 1;
|
|
1951
|
+
while (currentMsgStartIdx > 0 && messages[currentMsgStartIdx]?.role === "toolResult") {
|
|
1952
|
+
currentMsgStartIdx--;
|
|
1953
|
+
}
|
|
1954
|
+
const anchor = messages[currentMsgStartIdx];
|
|
1955
|
+
if (anchor?.role === "assistant") {
|
|
1956
|
+
const hasToolCall = Array.isArray(anchor.content) && anchor.content.some((b) => b.type === "toolCall");
|
|
1957
|
+
if (!hasToolCall)
|
|
1958
|
+
currentMsgStartIdx++;
|
|
1959
|
+
}
|
|
1960
|
+
const historyMessages = messages.slice(0, currentMsgStartIdx);
|
|
1961
|
+
for (let i = 0;i < historyMessages.length; i++) {
|
|
1962
|
+
const msg = historyMessages[i];
|
|
1963
|
+
if (!msg)
|
|
1964
|
+
continue;
|
|
1965
|
+
if (msg.role === "user") {
|
|
1966
|
+
let content = typeof msg.content === "string" ? msg.content : getContentText(msg);
|
|
1967
|
+
if (systemPrompt && !systemPrepended) {
|
|
1968
|
+
content = `${systemPrompt}
|
|
1969
|
+
|
|
1970
|
+
${content}`;
|
|
1971
|
+
systemPrepended = true;
|
|
1972
|
+
}
|
|
1973
|
+
const images = extractImages(msg);
|
|
1974
|
+
const uim = {
|
|
1975
|
+
content,
|
|
1976
|
+
origin: "KIRO_CLI",
|
|
1977
|
+
...images.length > 0 ? { images: convertImagesToKiro(images).images } : {}
|
|
1978
|
+
};
|
|
1979
|
+
const prev2 = history[history.length - 1];
|
|
1980
|
+
if (prev2?.userInputMessage) {
|
|
1981
|
+
prev2.userInputMessage.content += `
|
|
1982
|
+
|
|
1983
|
+
${uim.content}`;
|
|
1984
|
+
if (uim.images) {
|
|
1985
|
+
prev2.userInputMessage.images = [...prev2.userInputMessage.images ?? [], ...uim.images];
|
|
1986
|
+
}
|
|
1987
|
+
} else {
|
|
1988
|
+
history.push({ userInputMessage: uim });
|
|
1989
|
+
}
|
|
1990
|
+
continue;
|
|
1991
|
+
}
|
|
1992
|
+
if (msg.role === "assistant") {
|
|
1993
|
+
let armContent = "";
|
|
1994
|
+
const armToolUses = [];
|
|
1995
|
+
if (Array.isArray(msg.content)) {
|
|
1996
|
+
for (const block of msg.content) {
|
|
1997
|
+
if (block.type === "text") {
|
|
1998
|
+
armContent += block.text;
|
|
1999
|
+
} else if (block.type === "thinking") {
|
|
2000
|
+
armContent = `<thinking>${block.thinking}</thinking>
|
|
2001
|
+
|
|
2002
|
+
${armContent}`;
|
|
2003
|
+
} else if (block.type === "toolCall") {
|
|
2004
|
+
const tc = block;
|
|
2005
|
+
armToolUses.push({
|
|
2006
|
+
name: tc.name,
|
|
2007
|
+
toolUseId: toKiroToolUseId(tc.id),
|
|
2008
|
+
input: parseToolArgs(tc.arguments)
|
|
2009
|
+
});
|
|
1954
2010
|
}
|
|
1955
|
-
};
|
|
1956
|
-
let idleTimer = null;
|
|
1957
|
-
let idleCancelled = false;
|
|
1958
|
-
const resetIdle = () => {
|
|
1959
|
-
if (idleTimer)
|
|
1960
|
-
clearTimeout(idleTimer);
|
|
1961
|
-
idleTimer = setTimeout(() => {
|
|
1962
|
-
idleCancelled = true;
|
|
1963
|
-
reader.cancel().catch(() => {});
|
|
1964
|
-
}, IDLE_TIMEOUT_MS);
|
|
1965
|
-
};
|
|
1966
|
-
let gotFirstToken = false;
|
|
1967
|
-
let firstTokenTimedOut = false;
|
|
1968
|
-
let streamError = null;
|
|
1969
|
-
const FIRST_TOKEN_SENTINEL = Symbol("firstTokenTimeout");
|
|
1970
|
-
while (true) {
|
|
1971
|
-
let readResult;
|
|
1972
|
-
if (!gotFirstToken) {
|
|
1973
|
-
const readPromise = reader.read();
|
|
1974
|
-
let firstTokenTimer = null;
|
|
1975
|
-
const result = await Promise.race([
|
|
1976
|
-
readPromise,
|
|
1977
|
-
new Promise((resolve2) => {
|
|
1978
|
-
firstTokenTimer = setTimeout(() => resolve2(FIRST_TOKEN_SENTINEL), firstTokenTimeoutForModel(model.id));
|
|
1979
|
-
})
|
|
1980
|
-
]);
|
|
1981
|
-
if (firstTokenTimer)
|
|
1982
|
-
clearTimeout(firstTokenTimer);
|
|
1983
|
-
if (result === FIRST_TOKEN_SENTINEL) {
|
|
1984
|
-
readPromise.catch(() => {});
|
|
1985
|
-
reader.cancel().catch(() => {});
|
|
1986
|
-
firstTokenTimedOut = true;
|
|
1987
|
-
break;
|
|
1988
|
-
}
|
|
1989
|
-
readResult = result;
|
|
1990
|
-
gotFirstToken = true;
|
|
1991
|
-
resetIdle();
|
|
1992
|
-
} else {
|
|
1993
|
-
readResult = await reader.read();
|
|
1994
|
-
}
|
|
1995
|
-
const { done, value } = readResult;
|
|
1996
|
-
if (done)
|
|
1997
|
-
break;
|
|
1998
|
-
const decoded = decoder.decode(value, { stream: true });
|
|
1999
|
-
buffer += decoded;
|
|
2000
|
-
if (log.isDebug()) {
|
|
2001
|
-
log.debug("stream.chunk", {
|
|
2002
|
-
seq: chunkSeq++,
|
|
2003
|
-
bytes: value?.byteLength ?? 0,
|
|
2004
|
-
decodedLen: decoded.length,
|
|
2005
|
-
preview: previewChunk(decoded)
|
|
2006
|
-
});
|
|
2007
|
-
}
|
|
2008
|
-
const { events, remaining } = parseKiroEvents(buffer);
|
|
2009
|
-
buffer = remaining;
|
|
2010
|
-
if (events.length > 0)
|
|
2011
|
-
resetIdle();
|
|
2012
|
-
if (log.isDebug() && events.length > 0) {
|
|
2013
|
-
for (const ev of events) {
|
|
2014
|
-
log.debug("stream.event", { seq: eventSeq++, event: ev });
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
for (const event of events) {
|
|
2018
|
-
switch (event.type) {
|
|
2019
|
-
case "contextUsage": {
|
|
2020
|
-
const pct = event.data.contextUsagePercentage;
|
|
2021
|
-
output.usage.input = pct >= COMPACTION_THRESHOLD_PCT ? model.contextWindow + 1 : Math.round(pct / 100 * model.contextWindow);
|
|
2022
|
-
receivedContextUsage = true;
|
|
2023
|
-
log.debug("contextUsage", { pct, threshold: COMPACTION_THRESHOLD_PCT, willCompact: pct >= COMPACTION_THRESHOLD_PCT });
|
|
2024
|
-
break;
|
|
2025
|
-
}
|
|
2026
|
-
case "reasoning": {
|
|
2027
|
-
cancelHiddenShim();
|
|
2028
|
-
if (output.content.length === 0 || output.content[output.content.length - 1]?.type !== "thinking") {
|
|
2029
|
-
output.content.push({ type: "thinking", thinking: "" });
|
|
2030
|
-
stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
|
|
2031
|
-
}
|
|
2032
|
-
const contentIndex = output.content.length - 1;
|
|
2033
|
-
const tc = output.content[contentIndex];
|
|
2034
|
-
if (event.data.text) {
|
|
2035
|
-
tc.thinking += event.data.text;
|
|
2036
|
-
stream.push({ type: "thinking_delta", contentIndex, delta: event.data.text, partial: output });
|
|
2037
|
-
}
|
|
2038
|
-
if (event.data.signature) {
|
|
2039
|
-
tc.thinkingSignature = event.data.signature;
|
|
2040
|
-
}
|
|
2041
|
-
break;
|
|
2042
|
-
}
|
|
2043
|
-
case "content": {
|
|
2044
|
-
if (event.data === lastContentData)
|
|
2045
|
-
continue;
|
|
2046
|
-
lastContentData = event.data;
|
|
2047
|
-
totalContent += event.data;
|
|
2048
|
-
cancelHiddenShim();
|
|
2049
|
-
if (thinkingParser) {
|
|
2050
|
-
thinkingParser.processChunk(event.data);
|
|
2051
|
-
} else {
|
|
2052
|
-
if (textBlockIndex === null) {
|
|
2053
|
-
textBlockIndex = output.content.length;
|
|
2054
|
-
output.content.push({ type: "text", text: "" });
|
|
2055
|
-
stream.push({ type: "text_start", contentIndex: textBlockIndex, partial: output });
|
|
2056
|
-
}
|
|
2057
|
-
const block = output.content[textBlockIndex];
|
|
2058
|
-
if (block) {
|
|
2059
|
-
block.text += event.data;
|
|
2060
|
-
stream.push({
|
|
2061
|
-
type: "text_delta",
|
|
2062
|
-
contentIndex: textBlockIndex,
|
|
2063
|
-
delta: event.data,
|
|
2064
|
-
partial: output
|
|
2065
|
-
});
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
break;
|
|
2069
|
-
}
|
|
2070
|
-
case "toolUse": {
|
|
2071
|
-
const tc = event.data;
|
|
2072
|
-
cancelHiddenShim();
|
|
2073
|
-
sawAnyToolCalls = true;
|
|
2074
|
-
if (!currentToolCall || currentToolCall.toolUseId !== tc.toolUseId) {
|
|
2075
|
-
flushToolCall();
|
|
2076
|
-
currentToolCall = { toolUseId: tc.toolUseId, name: tc.name, input: "" };
|
|
2077
|
-
}
|
|
2078
|
-
currentToolCall.input += tc.input || "";
|
|
2079
|
-
if (tc.input)
|
|
2080
|
-
totalContent += tc.input;
|
|
2081
|
-
if (tc.stop)
|
|
2082
|
-
flushToolCall();
|
|
2083
|
-
break;
|
|
2084
|
-
}
|
|
2085
|
-
case "toolUseInput": {
|
|
2086
|
-
if (currentToolCall)
|
|
2087
|
-
currentToolCall.input += event.data.input || "";
|
|
2088
|
-
if (event.data.input)
|
|
2089
|
-
totalContent += event.data.input;
|
|
2090
|
-
break;
|
|
2091
|
-
}
|
|
2092
|
-
case "toolUseStop": {
|
|
2093
|
-
if (event.data.stop)
|
|
2094
|
-
flushToolCall();
|
|
2095
|
-
break;
|
|
2096
|
-
}
|
|
2097
|
-
case "usage": {
|
|
2098
|
-
usageEvent = event.data;
|
|
2099
|
-
break;
|
|
2100
|
-
}
|
|
2101
|
-
case "error": {
|
|
2102
|
-
streamError = event.data.message ? `${event.data.error}: ${event.data.message}` : event.data.error;
|
|
2103
|
-
reader.cancel().catch(() => {});
|
|
2104
|
-
break;
|
|
2105
|
-
}
|
|
2106
|
-
}
|
|
2107
|
-
if (streamError)
|
|
2108
|
-
break;
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
if (idleTimer)
|
|
2112
|
-
clearTimeout(idleTimer);
|
|
2113
|
-
if (firstTokenTimedOut || idleCancelled || streamError) {
|
|
2114
|
-
if (retryCount < MAX_RETRIES) {
|
|
2115
|
-
retryCount++;
|
|
2116
|
-
const delayMs = exponentialBackoff(retryCount - 1, 1000, MAX_RETRY_DELAY_MS);
|
|
2117
|
-
log.warn(`stream ${firstTokenTimedOut ? "first-token timed out" : idleCancelled ? "idle timed out" : `error: ${streamError}`} — retrying (${retryCount}/${MAX_RETRIES})`);
|
|
2118
|
-
cancelHiddenShim();
|
|
2119
|
-
await abortableDelay(delayMs, options?.signal);
|
|
2120
|
-
output.content = [];
|
|
2121
|
-
textBlockIndex = null;
|
|
2122
|
-
continue;
|
|
2123
|
-
}
|
|
2124
|
-
if (streamError)
|
|
2125
|
-
throw new Error(`Kiro API stream error after max retries: ${streamError}`);
|
|
2126
|
-
throw new Error(`Kiro API error: ${firstTokenTimedOut ? "first token" : "idle"} timeout after max retries`);
|
|
2127
|
-
}
|
|
2128
|
-
cancelHiddenShim();
|
|
2129
|
-
if (currentToolCall && emitToolCall(currentToolCall, output, stream))
|
|
2130
|
-
emittedToolCalls++;
|
|
2131
|
-
if (thinkingParser) {
|
|
2132
|
-
thinkingParser.finalize();
|
|
2133
|
-
textBlockIndex = thinkingParser.getTextBlockIndex();
|
|
2134
|
-
}
|
|
2135
|
-
if (textBlockIndex !== null) {
|
|
2136
|
-
const block = output.content[textBlockIndex];
|
|
2137
|
-
if (block) {
|
|
2138
|
-
stream.push({
|
|
2139
|
-
type: "text_end",
|
|
2140
|
-
contentIndex: textBlockIndex,
|
|
2141
|
-
content: block.text,
|
|
2142
|
-
partial: output
|
|
2143
|
-
});
|
|
2144
|
-
}
|
|
2145
|
-
}
|
|
2146
|
-
if (usageEvent?.inputTokens !== undefined)
|
|
2147
|
-
output.usage.input = usageEvent.inputTokens;
|
|
2148
|
-
output.usage.output = usageEvent?.outputTokens ?? countTokens(totalContent);
|
|
2149
|
-
output.usage.totalTokens = output.usage.input + output.usage.output;
|
|
2150
|
-
try {
|
|
2151
|
-
calculateCost(model, output.usage);
|
|
2152
|
-
} catch {
|
|
2153
|
-
output.usage.cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
2154
|
-
}
|
|
2155
|
-
const textBlock = textBlockIndex !== null ? output.content[textBlockIndex] : undefined;
|
|
2156
|
-
const hasText = !!textBlock && textBlock.text.length > 0;
|
|
2157
|
-
if (!hasText && !sawAnyToolCalls) {
|
|
2158
|
-
if (retryCount < MAX_RETRIES) {
|
|
2159
|
-
retryCount++;
|
|
2160
|
-
const delayMs = exponentialBackoff(retryCount - 1, 1000, MAX_RETRY_DELAY_MS);
|
|
2161
|
-
log.warn(`empty response — retrying (${retryCount}/${MAX_RETRIES})`);
|
|
2162
|
-
cancelHiddenShim();
|
|
2163
|
-
output.content = [];
|
|
2164
|
-
textBlockIndex = null;
|
|
2165
|
-
await abortableDelay(delayMs, options?.signal);
|
|
2166
|
-
continue;
|
|
2167
|
-
}
|
|
2168
|
-
log.warn(`empty response persisted after ${MAX_RETRIES} retries`);
|
|
2169
|
-
cancelHiddenShim();
|
|
2170
|
-
}
|
|
2171
|
-
if (!receivedContextUsage && emittedToolCalls === 0) {
|
|
2172
|
-
output.stopReason = "length";
|
|
2173
|
-
} else {
|
|
2174
|
-
output.stopReason = emittedToolCalls > 0 ? "toolUse" : "stop";
|
|
2175
2011
|
}
|
|
2176
|
-
stream.push({
|
|
2177
|
-
type: "done",
|
|
2178
|
-
reason: output.stopReason,
|
|
2179
|
-
message: output
|
|
2180
|
-
});
|
|
2181
|
-
log.debug("response.done", {
|
|
2182
|
-
stopReason: output.stopReason,
|
|
2183
|
-
emittedToolCalls,
|
|
2184
|
-
sawAnyToolCalls,
|
|
2185
|
-
usage: output.usage
|
|
2186
|
-
});
|
|
2187
|
-
stream.end();
|
|
2188
|
-
return;
|
|
2189
2012
|
}
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2013
|
+
if (!armContent && armToolUses.length === 0)
|
|
2014
|
+
continue;
|
|
2015
|
+
history.push({
|
|
2016
|
+
assistantResponseMessage: {
|
|
2017
|
+
content: armContent,
|
|
2018
|
+
...armToolUses.length > 0 ? { toolUses: armToolUses } : {}
|
|
2019
|
+
}
|
|
2020
|
+
});
|
|
2021
|
+
continue;
|
|
2022
|
+
}
|
|
2023
|
+
const trMsg = msg;
|
|
2024
|
+
const toolResults = [
|
|
2025
|
+
{
|
|
2026
|
+
content: [{ text: truncate(getContentText(msg), TOOL_RESULT_LIMIT) }],
|
|
2027
|
+
status: trMsg.isError ? "error" : "success",
|
|
2028
|
+
toolUseId: toKiroToolUseId(trMsg.toolCallId)
|
|
2197
2029
|
}
|
|
2198
|
-
|
|
2199
|
-
|
|
2030
|
+
];
|
|
2031
|
+
const trImages = [];
|
|
2032
|
+
if (Array.isArray(trMsg.content)) {
|
|
2033
|
+
for (const c of trMsg.content)
|
|
2034
|
+
if (c.type === "image")
|
|
2035
|
+
trImages.push(c);
|
|
2200
2036
|
}
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2037
|
+
let j = i + 1;
|
|
2038
|
+
while (j < historyMessages.length && historyMessages[j]?.role === "toolResult") {
|
|
2039
|
+
const next = historyMessages[j];
|
|
2040
|
+
toolResults.push({
|
|
2041
|
+
content: [{ text: truncate(getContentText(next), TOOL_RESULT_LIMIT) }],
|
|
2042
|
+
status: next.isError ? "error" : "success",
|
|
2043
|
+
toolUseId: toKiroToolUseId(next.toolCallId)
|
|
2044
|
+
});
|
|
2045
|
+
if (Array.isArray(next.content)) {
|
|
2046
|
+
for (const c of next.content)
|
|
2047
|
+
if (c.type === "image")
|
|
2048
|
+
trImages.push(c);
|
|
2049
|
+
}
|
|
2050
|
+
j++;
|
|
2051
|
+
}
|
|
2052
|
+
i = j - 1;
|
|
2053
|
+
const prev = history[history.length - 1];
|
|
2054
|
+
if (prev?.userInputMessage) {
|
|
2055
|
+
prev.userInputMessage.content += `
|
|
2208
2056
|
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
]);
|
|
2234
|
-
function dotToDash(modelId) {
|
|
2235
|
-
return modelId.replace(/(\d)\.(\d)/g, "$1-$2");
|
|
2236
|
-
}
|
|
2237
|
-
function resolveKiroModel(modelId) {
|
|
2238
|
-
const kiroId = modelId.replace(/(\d)-(\d)/g, "$1.$2");
|
|
2239
|
-
if (!KIRO_MODEL_IDS.has(kiroId)) {
|
|
2240
|
-
throw new Error(`Unknown Kiro model ID: ${modelId}`);
|
|
2057
|
+
Tool results provided.`;
|
|
2058
|
+
if (trImages.length > 0) {
|
|
2059
|
+
prev.userInputMessage.images = [
|
|
2060
|
+
...prev.userInputMessage.images ?? [],
|
|
2061
|
+
...convertImagesToKiro(trImages).images
|
|
2062
|
+
];
|
|
2063
|
+
}
|
|
2064
|
+
if (!prev.userInputMessage.userInputMessageContext) {
|
|
2065
|
+
prev.userInputMessage.userInputMessageContext = {};
|
|
2066
|
+
}
|
|
2067
|
+
prev.userInputMessage.userInputMessageContext.toolResults = [
|
|
2068
|
+
...prev.userInputMessage.userInputMessageContext.toolResults ?? [],
|
|
2069
|
+
...toolResults
|
|
2070
|
+
];
|
|
2071
|
+
} else {
|
|
2072
|
+
history.push({
|
|
2073
|
+
userInputMessage: {
|
|
2074
|
+
content: "Tool results provided.",
|
|
2075
|
+
origin: "KIRO_CLI",
|
|
2076
|
+
...trImages.length > 0 ? { images: convertImagesToKiro(trImages).images } : {},
|
|
2077
|
+
userInputMessageContext: { toolResults }
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2241
2081
|
}
|
|
2242
|
-
return
|
|
2082
|
+
return { history: collapseAgenticLoops(history), systemPrepended, currentMsgStartIdx };
|
|
2243
2083
|
}
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2084
|
+
function collapseAgenticLoops(history) {
|
|
2085
|
+
if (history.length < 4)
|
|
2086
|
+
return history;
|
|
2087
|
+
const result = [];
|
|
2088
|
+
let i = 0;
|
|
2089
|
+
while (i < history.length) {
|
|
2090
|
+
const entry = history[i];
|
|
2091
|
+
if (entry?.assistantResponseMessage?.toolUses && i + 1 < history.length && history[i + 1]?.userInputMessage?.userInputMessageContext?.toolResults) {
|
|
2092
|
+
let j = i;
|
|
2093
|
+
while (j < history.length) {
|
|
2094
|
+
const asst = history[j];
|
|
2095
|
+
if (!asst?.assistantResponseMessage?.toolUses)
|
|
2096
|
+
break;
|
|
2097
|
+
const nextUser = j + 1 < history.length ? history[j + 1] : null;
|
|
2098
|
+
if (!nextUser?.userInputMessage?.userInputMessageContext?.toolResults)
|
|
2099
|
+
break;
|
|
2100
|
+
j += 2;
|
|
2101
|
+
}
|
|
2102
|
+
const pairCount = (j - i) / 2;
|
|
2103
|
+
if (pairCount > 1) {
|
|
2104
|
+
for (let k = i;k < j; k += 2) {
|
|
2105
|
+
const asst = history[k];
|
|
2106
|
+
const user = history[k + 1];
|
|
2107
|
+
if (k === i) {
|
|
2108
|
+
result.push(asst);
|
|
2109
|
+
} else {
|
|
2110
|
+
result.push({
|
|
2111
|
+
assistantResponseMessage: {
|
|
2112
|
+
content: "[tool calling continues]",
|
|
2113
|
+
toolUses: asst.assistantResponseMessage.toolUses
|
|
2114
|
+
}
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
result.push(user);
|
|
2118
|
+
}
|
|
2119
|
+
} else {
|
|
2120
|
+
result.push(history[i], history[i + 1]);
|
|
2121
|
+
}
|
|
2122
|
+
i = j;
|
|
2123
|
+
} else {
|
|
2124
|
+
result.push(entry);
|
|
2125
|
+
i++;
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return result;
|
|
2270
2129
|
}
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
"
|
|
2283
|
-
|
|
2284
|
-
"
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
"minimax-m2-5",
|
|
2289
|
-
"glm-4-7",
|
|
2290
|
-
"glm-4-7-flash",
|
|
2291
|
-
"qwen3-coder-next",
|
|
2292
|
-
"qwen3-coder-480b",
|
|
2293
|
-
"agi-nova-beta-1m",
|
|
2294
|
-
"auto"
|
|
2295
|
-
]),
|
|
2296
|
-
"eu-central-1": new Set([
|
|
2297
|
-
"claude-fable-5",
|
|
2298
|
-
"claude-opus-4-8",
|
|
2299
|
-
"claude-opus-4-7",
|
|
2300
|
-
"claude-opus-4-6",
|
|
2301
|
-
"claude-sonnet-4-6",
|
|
2302
|
-
"claude-opus-4-5",
|
|
2303
|
-
"claude-sonnet-4-5",
|
|
2304
|
-
"claude-sonnet-4",
|
|
2305
|
-
"claude-haiku-4-5",
|
|
2306
|
-
"minimax-m2-1",
|
|
2307
|
-
"minimax-m2-5",
|
|
2308
|
-
"qwen3-coder-next",
|
|
2309
|
-
"auto"
|
|
2310
|
-
])
|
|
2311
|
-
};
|
|
2312
|
-
function filterModelsByRegion(models, apiRegion) {
|
|
2313
|
-
const allowed = MODELS_BY_REGION[apiRegion];
|
|
2314
|
-
if (!allowed) {
|
|
2315
|
-
console.warn(`[pi-kiro] Unknown API region "${apiRegion}" — no models available. Update MODELS_BY_REGION in models.ts.`);
|
|
2316
|
-
return [];
|
|
2130
|
+
|
|
2131
|
+
// src/kiro-defaults.ts
|
|
2132
|
+
var SYSTEM_SEED_INSTRUCTION = `Follow this instruction: # Kiro CLI Default Agent
|
|
2133
|
+
|
|
2134
|
+
` + "You are the default Kiro CLI agent, bringing the power of AI-assisted development " + "directly to the user's terminal. You help with coding tasks, system operations, " + `AWS management, and development workflows.
|
|
2135
|
+
|
|
2136
|
+
` + `The current model is {{modelId}}.
|
|
2137
|
+
`;
|
|
2138
|
+
var SYSTEM_SEED_ACK = "I will fully incorporate this information when generating my responses, " + "and explicitly acknowledge relevant parts of the summary when answering questions.";
|
|
2139
|
+
function resolveOS() {
|
|
2140
|
+
switch (process.platform) {
|
|
2141
|
+
case "darwin":
|
|
2142
|
+
return "macos";
|
|
2143
|
+
case "win32":
|
|
2144
|
+
return "windows";
|
|
2145
|
+
default:
|
|
2146
|
+
return process.platform;
|
|
2317
2147
|
}
|
|
2318
|
-
return models.filter((m) => allowed.has(m.id));
|
|
2319
2148
|
}
|
|
2320
|
-
var
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2149
|
+
var COMPACTION_THRESHOLD_PCT = 95;
|
|
2150
|
+
|
|
2151
|
+
// src/stream.ts
|
|
2152
|
+
var FIRST_TOKEN_TIMEOUT_DEFAULT_MS = 90000;
|
|
2153
|
+
var IDLE_TIMEOUT_MS = 60000;
|
|
2154
|
+
var MAX_RETRIES = 3;
|
|
2155
|
+
var MAX_RETRY_DELAY_MS = 1e4;
|
|
2156
|
+
var CAPACITY_MAX_RETRIES = 3;
|
|
2157
|
+
var CAPACITY_BASE_DELAY_MS = 5000;
|
|
2158
|
+
var CAPACITY_MAX_DELAY_MS = 30000;
|
|
2159
|
+
var TRANSIENT_MAX_RETRIES = 3;
|
|
2160
|
+
var TRANSIENT_BASE_DELAY_MS = 2000;
|
|
2161
|
+
var TRANSIENT_MAX_DELAY_MS = 15000;
|
|
2162
|
+
var CONTEXT_TRUNCATION_MAX_RETRIES = 3;
|
|
2163
|
+
var CONTEXT_TRUNCATION_DROP_RATIO = 0.3;
|
|
2164
|
+
var TOO_BIG_PATTERNS = ["CONTENT_LENGTH_EXCEEDS_THRESHOLD", "Input is too long", "Improperly formed"];
|
|
2165
|
+
var NON_RETRYABLE_BODY_PATTERNS = ["MONTHLY_REQUEST_COUNT"];
|
|
2166
|
+
var CAPACITY_PATTERN = "INSUFFICIENT_MODEL_CAPACITY";
|
|
2167
|
+
function exponentialBackoff(attempt, baseMs, maxMs) {
|
|
2168
|
+
return Math.min(baseMs * 2 ** attempt, maxMs);
|
|
2326
2169
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
var KIRO_DEFAULTS = {
|
|
2330
|
-
api: "kiro-api",
|
|
2331
|
-
provider: "kiro",
|
|
2332
|
-
baseUrl: BASE_URL,
|
|
2333
|
-
cost: ZERO_COST
|
|
2334
|
-
};
|
|
2335
|
-
var MULTIMODAL = ["text", "image"];
|
|
2336
|
-
var TEXT_ONLY = ["text"];
|
|
2337
|
-
var kiroModels = [
|
|
2338
|
-
{
|
|
2339
|
-
...KIRO_DEFAULTS,
|
|
2340
|
-
id: "claude-fable-5",
|
|
2341
|
-
name: "Claude Fable 5",
|
|
2342
|
-
reasoning: true,
|
|
2343
|
-
input: MULTIMODAL,
|
|
2344
|
-
contextWindow: 1e6,
|
|
2345
|
-
maxTokens: 128000,
|
|
2346
|
-
firstTokenTimeout: 180000,
|
|
2347
|
-
supportedEfforts: ["low", "medium", "high", "xhigh", "max"]
|
|
2348
|
-
},
|
|
2349
|
-
{
|
|
2350
|
-
...KIRO_DEFAULTS,
|
|
2351
|
-
id: "claude-opus-4-8",
|
|
2352
|
-
name: "Claude Opus 4.8",
|
|
2353
|
-
reasoning: true,
|
|
2354
|
-
input: MULTIMODAL,
|
|
2355
|
-
contextWindow: 1e6,
|
|
2356
|
-
maxTokens: 128000,
|
|
2357
|
-
firstTokenTimeout: 180000,
|
|
2358
|
-
supportedEfforts: ["low", "medium", "high", "xhigh", "max"]
|
|
2359
|
-
},
|
|
2360
|
-
{
|
|
2361
|
-
...KIRO_DEFAULTS,
|
|
2362
|
-
id: "claude-opus-4-7",
|
|
2363
|
-
name: "Claude Opus 4.7",
|
|
2364
|
-
reasoning: true,
|
|
2365
|
-
input: MULTIMODAL,
|
|
2366
|
-
contextWindow: 1e6,
|
|
2367
|
-
maxTokens: 128000,
|
|
2368
|
-
firstTokenTimeout: 180000,
|
|
2369
|
-
supportedEfforts: ["low", "medium", "high", "xhigh", "max"]
|
|
2370
|
-
},
|
|
2371
|
-
{
|
|
2372
|
-
...KIRO_DEFAULTS,
|
|
2373
|
-
id: "claude-opus-4-6",
|
|
2374
|
-
name: "Claude Opus 4.6",
|
|
2375
|
-
reasoning: true,
|
|
2376
|
-
input: MULTIMODAL,
|
|
2377
|
-
contextWindow: 1e6,
|
|
2378
|
-
maxTokens: 64000,
|
|
2379
|
-
supportedEfforts: ["low", "medium", "high", "max"]
|
|
2380
|
-
},
|
|
2381
|
-
{
|
|
2382
|
-
...KIRO_DEFAULTS,
|
|
2383
|
-
id: "claude-opus-4-6-1m",
|
|
2384
|
-
name: "Claude Opus 4.6 (1M)",
|
|
2385
|
-
reasoning: true,
|
|
2386
|
-
input: MULTIMODAL,
|
|
2387
|
-
contextWindow: 1e6,
|
|
2388
|
-
maxTokens: 64000,
|
|
2389
|
-
supportedEfforts: ["low", "medium", "high", "max"]
|
|
2390
|
-
},
|
|
2391
|
-
{
|
|
2392
|
-
...KIRO_DEFAULTS,
|
|
2393
|
-
id: "claude-sonnet-4-6",
|
|
2394
|
-
name: "Claude Sonnet 4.6",
|
|
2395
|
-
reasoning: true,
|
|
2396
|
-
input: MULTIMODAL,
|
|
2397
|
-
contextWindow: 1e6,
|
|
2398
|
-
maxTokens: 64000,
|
|
2399
|
-
supportedEfforts: ["low", "medium", "high", "max"]
|
|
2400
|
-
},
|
|
2401
|
-
{
|
|
2402
|
-
...KIRO_DEFAULTS,
|
|
2403
|
-
id: "claude-sonnet-4-6-1m",
|
|
2404
|
-
name: "Claude Sonnet 4.6 (1M)",
|
|
2405
|
-
reasoning: true,
|
|
2406
|
-
input: MULTIMODAL,
|
|
2407
|
-
contextWindow: 1e6,
|
|
2408
|
-
maxTokens: 64000,
|
|
2409
|
-
supportedEfforts: ["low", "medium", "high", "max"]
|
|
2410
|
-
},
|
|
2411
|
-
{
|
|
2412
|
-
...KIRO_DEFAULTS,
|
|
2413
|
-
id: "claude-opus-4-5",
|
|
2414
|
-
name: "Claude Opus 4.5",
|
|
2415
|
-
reasoning: true,
|
|
2416
|
-
input: MULTIMODAL,
|
|
2417
|
-
contextWindow: 200000,
|
|
2418
|
-
maxTokens: 64000
|
|
2419
|
-
},
|
|
2420
|
-
{
|
|
2421
|
-
...KIRO_DEFAULTS,
|
|
2422
|
-
id: "claude-sonnet-4-5",
|
|
2423
|
-
name: "Claude Sonnet 4.5",
|
|
2424
|
-
reasoning: true,
|
|
2425
|
-
input: MULTIMODAL,
|
|
2426
|
-
contextWindow: 200000,
|
|
2427
|
-
maxTokens: 65536
|
|
2428
|
-
},
|
|
2429
|
-
{
|
|
2430
|
-
...KIRO_DEFAULTS,
|
|
2431
|
-
id: "claude-sonnet-4-5-1m",
|
|
2432
|
-
name: "Claude Sonnet 4.5 (1M)",
|
|
2433
|
-
reasoning: true,
|
|
2434
|
-
input: MULTIMODAL,
|
|
2435
|
-
contextWindow: 1e6,
|
|
2436
|
-
maxTokens: 65536
|
|
2437
|
-
},
|
|
2438
|
-
{
|
|
2439
|
-
...KIRO_DEFAULTS,
|
|
2440
|
-
id: "claude-sonnet-4",
|
|
2441
|
-
name: "Claude Sonnet 4",
|
|
2442
|
-
reasoning: true,
|
|
2443
|
-
input: MULTIMODAL,
|
|
2444
|
-
contextWindow: 200000,
|
|
2445
|
-
maxTokens: 65536
|
|
2446
|
-
},
|
|
2447
|
-
{
|
|
2448
|
-
...KIRO_DEFAULTS,
|
|
2449
|
-
id: "claude-haiku-4-5",
|
|
2450
|
-
name: "Claude Haiku 4.5",
|
|
2451
|
-
reasoning: false,
|
|
2452
|
-
input: MULTIMODAL,
|
|
2453
|
-
contextWindow: 200000,
|
|
2454
|
-
maxTokens: 65536
|
|
2455
|
-
},
|
|
2456
|
-
{
|
|
2457
|
-
...KIRO_DEFAULTS,
|
|
2458
|
-
id: "deepseek-3-2",
|
|
2459
|
-
name: "DeepSeek 3.2",
|
|
2460
|
-
reasoning: true,
|
|
2461
|
-
input: TEXT_ONLY,
|
|
2462
|
-
contextWindow: 128000,
|
|
2463
|
-
maxTokens: 8192
|
|
2464
|
-
},
|
|
2465
|
-
{
|
|
2466
|
-
...KIRO_DEFAULTS,
|
|
2467
|
-
id: "kimi-k2-5",
|
|
2468
|
-
name: "Kimi K2.5",
|
|
2469
|
-
reasoning: true,
|
|
2470
|
-
input: TEXT_ONLY,
|
|
2471
|
-
contextWindow: 200000,
|
|
2472
|
-
maxTokens: 8192
|
|
2473
|
-
},
|
|
2474
|
-
{
|
|
2475
|
-
...KIRO_DEFAULTS,
|
|
2476
|
-
id: "minimax-m2-5",
|
|
2477
|
-
name: "MiniMax M2.5",
|
|
2478
|
-
reasoning: false,
|
|
2479
|
-
input: TEXT_ONLY,
|
|
2480
|
-
contextWindow: 196000,
|
|
2481
|
-
maxTokens: 64000
|
|
2482
|
-
},
|
|
2483
|
-
{
|
|
2484
|
-
...KIRO_DEFAULTS,
|
|
2485
|
-
id: "minimax-m2-1",
|
|
2486
|
-
name: "MiniMax M2.1",
|
|
2487
|
-
reasoning: false,
|
|
2488
|
-
input: MULTIMODAL,
|
|
2489
|
-
contextWindow: 196000,
|
|
2490
|
-
maxTokens: 64000
|
|
2491
|
-
},
|
|
2492
|
-
{
|
|
2493
|
-
...KIRO_DEFAULTS,
|
|
2494
|
-
id: "glm-4-7",
|
|
2495
|
-
name: "GLM 4.7",
|
|
2496
|
-
reasoning: true,
|
|
2497
|
-
input: TEXT_ONLY,
|
|
2498
|
-
contextWindow: 128000,
|
|
2499
|
-
maxTokens: 8192
|
|
2500
|
-
},
|
|
2501
|
-
{
|
|
2502
|
-
...KIRO_DEFAULTS,
|
|
2503
|
-
id: "glm-4-7-flash",
|
|
2504
|
-
name: "GLM 4.7 Flash",
|
|
2505
|
-
reasoning: false,
|
|
2506
|
-
input: TEXT_ONLY,
|
|
2507
|
-
contextWindow: 128000,
|
|
2508
|
-
maxTokens: 8192
|
|
2509
|
-
},
|
|
2510
|
-
{
|
|
2511
|
-
...KIRO_DEFAULTS,
|
|
2512
|
-
id: "qwen3-coder-next",
|
|
2513
|
-
name: "Qwen3 Coder Next",
|
|
2514
|
-
reasoning: true,
|
|
2515
|
-
input: MULTIMODAL,
|
|
2516
|
-
contextWindow: 256000,
|
|
2517
|
-
maxTokens: 64000
|
|
2518
|
-
},
|
|
2519
|
-
{
|
|
2520
|
-
...KIRO_DEFAULTS,
|
|
2521
|
-
id: "qwen3-coder-480b",
|
|
2522
|
-
name: "Qwen3 Coder 480B",
|
|
2523
|
-
reasoning: true,
|
|
2524
|
-
input: TEXT_ONLY,
|
|
2525
|
-
contextWindow: 128000,
|
|
2526
|
-
maxTokens: 8192
|
|
2527
|
-
},
|
|
2528
|
-
{
|
|
2529
|
-
...KIRO_DEFAULTS,
|
|
2530
|
-
id: "agi-nova-beta-1m",
|
|
2531
|
-
name: "AGI Nova Beta (1M)",
|
|
2532
|
-
reasoning: true,
|
|
2533
|
-
input: MULTIMODAL,
|
|
2534
|
-
contextWindow: 1e6,
|
|
2535
|
-
maxTokens: 65536
|
|
2536
|
-
},
|
|
2537
|
-
{
|
|
2538
|
-
...KIRO_DEFAULTS,
|
|
2539
|
-
id: "auto",
|
|
2540
|
-
name: "Auto",
|
|
2541
|
-
reasoning: true,
|
|
2542
|
-
input: MULTIMODAL,
|
|
2543
|
-
contextWindow: 200000,
|
|
2544
|
-
maxTokens: 65536
|
|
2545
|
-
}
|
|
2546
|
-
];
|
|
2547
|
-
async function fetchAvailableModels(accessToken, apiRegion) {
|
|
2548
|
-
const runtimeUrl = `https://runtime.${apiRegion}.kiro.dev/`;
|
|
2549
|
-
const profileArn = await resolveProfileArn(accessToken, runtimeUrl);
|
|
2550
|
-
if (!profileArn) {
|
|
2551
|
-
throw new Error("Missing profileArn: cannot fetch available models.");
|
|
2552
|
-
}
|
|
2553
|
-
const url = `https://management.${apiRegion}.kiro.dev/ListAvailableModels?origin=KIRO_CLI&profileArn=${encodeURIComponent(profileArn)}`;
|
|
2554
|
-
const resp = await fetch(url, {
|
|
2555
|
-
method: "GET",
|
|
2556
|
-
headers: {
|
|
2557
|
-
Authorization: `Bearer ${accessToken}`,
|
|
2558
|
-
Accept: "application/json",
|
|
2559
|
-
"User-Agent": "pi-kiro"
|
|
2560
|
-
}
|
|
2561
|
-
});
|
|
2562
|
-
if (!resp.ok) {
|
|
2563
|
-
throw new Error(`ListAvailableModels failed: HTTP ${resp.status}`);
|
|
2564
|
-
}
|
|
2565
|
-
const data = await resp.json();
|
|
2566
|
-
return (data.models ?? []).filter((m) => m.modelId !== "auto");
|
|
2567
|
-
}
|
|
2568
|
-
var REASONING_FAMILIES = new Set([
|
|
2569
|
-
"claude-fable",
|
|
2570
|
-
"claude-sonnet",
|
|
2571
|
-
"claude-opus",
|
|
2572
|
-
"deepseek",
|
|
2573
|
-
"kimi",
|
|
2574
|
-
"glm",
|
|
2575
|
-
"qwen",
|
|
2576
|
-
"agi-nova",
|
|
2577
|
-
"minimax"
|
|
2578
|
-
]);
|
|
2579
|
-
function isReasoningModel(dotId) {
|
|
2580
|
-
for (const family of REASONING_FAMILIES) {
|
|
2581
|
-
if (dotId.startsWith(family))
|
|
2582
|
-
return true;
|
|
2583
|
-
}
|
|
2584
|
-
return false;
|
|
2170
|
+
function isTooBigError(status, body) {
|
|
2171
|
+
return status === 413 || status === 400 && TOO_BIG_PATTERNS.some((p) => body.includes(p));
|
|
2585
2172
|
}
|
|
2586
|
-
function
|
|
2587
|
-
|
|
2588
|
-
return 180000;
|
|
2589
|
-
return 90000;
|
|
2173
|
+
function isNonRetryableBodyError(body) {
|
|
2174
|
+
return NON_RETRYABLE_BODY_PATTERNS.some((p) => body.includes(p));
|
|
2590
2175
|
}
|
|
2591
|
-
function
|
|
2592
|
-
return
|
|
2593
|
-
KIRO_MODEL_IDS.add(m.modelId);
|
|
2594
|
-
const dashId = dotToDash(m.modelId);
|
|
2595
|
-
const supportedTypes = m.supportedInputTypes ?? ["TEXT"];
|
|
2596
|
-
const input = supportedTypes.includes("IMAGE") ? ["text", "image"] : ["text"];
|
|
2597
|
-
const effortEnum = m.additionalModelRequestFieldsSchema?.properties?.output_config?.properties?.effort?.enum;
|
|
2598
|
-
const supportedEfforts = Array.isArray(effortEnum) && effortEnum.length > 0 ? effortEnum : undefined;
|
|
2599
|
-
const supportsThinkingConfig = !!m.additionalModelRequestFieldsSchema?.properties?.thinking;
|
|
2600
|
-
return {
|
|
2601
|
-
id: dashId,
|
|
2602
|
-
name: m.modelName,
|
|
2603
|
-
reasoning: isReasoningModel(m.modelId),
|
|
2604
|
-
input,
|
|
2605
|
-
contextWindow: m.tokenLimits?.maxInputTokens ?? 200000,
|
|
2606
|
-
maxTokens: m.tokenLimits?.maxOutputTokens ?? 8192,
|
|
2607
|
-
firstTokenTimeout: firstTokenTimeout(m.modelId),
|
|
2608
|
-
...supportedEfforts ? { supportedEfforts } : {},
|
|
2609
|
-
...supportsThinkingConfig ? { supportsThinkingConfig } : {}
|
|
2610
|
-
};
|
|
2611
|
-
});
|
|
2176
|
+
function isCapacityError(body) {
|
|
2177
|
+
return body.includes(CAPACITY_PATTERN);
|
|
2612
2178
|
}
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
return cachedDynamicModels;
|
|
2179
|
+
function isTransientError(status) {
|
|
2180
|
+
return status === 429 || status >= 500;
|
|
2616
2181
|
}
|
|
2617
|
-
function
|
|
2618
|
-
|
|
2182
|
+
function firstTokenTimeoutForModel(modelId) {
|
|
2183
|
+
const m = kiroModels.find((x) => x.id === modelId);
|
|
2184
|
+
return m?.firstTokenTimeout ?? FIRST_TOKEN_TIMEOUT_DEFAULT_MS;
|
|
2185
|
+
}
|
|
2186
|
+
var HIDDEN_REASONING_PLACEHOLDER = "Reasoning hidden by provider";
|
|
2187
|
+
var HIDDEN_REASONING_COUNTDOWN_MS = 2000;
|
|
2188
|
+
function emitHiddenReasoningLate(output, stream) {
|
|
2189
|
+
const contentIndex = output.content.length;
|
|
2190
|
+
const block = {
|
|
2191
|
+
type: "thinking",
|
|
2192
|
+
thinking: HIDDEN_REASONING_PLACEHOLDER,
|
|
2193
|
+
redacted: true
|
|
2194
|
+
};
|
|
2195
|
+
output.content.push(block);
|
|
2196
|
+
stream.push({ type: "thinking_start", contentIndex, partial: output });
|
|
2197
|
+
stream.push({
|
|
2198
|
+
type: "thinking_delta",
|
|
2199
|
+
contentIndex,
|
|
2200
|
+
delta: HIDDEN_REASONING_PLACEHOLDER,
|
|
2201
|
+
partial: output
|
|
2202
|
+
});
|
|
2203
|
+
stream.push({
|
|
2204
|
+
type: "thinking_end",
|
|
2205
|
+
contentIndex,
|
|
2206
|
+
content: "",
|
|
2207
|
+
partial: output
|
|
2208
|
+
});
|
|
2619
2209
|
}
|
|
2620
|
-
|
|
2621
|
-
// src/oauth.ts
|
|
2622
|
-
var BUILDER_ID_START_URL = "https://view.awsapps.com/start";
|
|
2623
|
-
var BUILDER_ID_REGION = "us-east-1";
|
|
2624
|
-
var SSO_SCOPES = [
|
|
2625
|
-
"codewhisperer:completions",
|
|
2626
|
-
"codewhisperer:analysis",
|
|
2627
|
-
"codewhisperer:conversations",
|
|
2628
|
-
"codewhisperer:transformations",
|
|
2629
|
-
"codewhisperer:taskassist"
|
|
2630
|
-
];
|
|
2631
|
-
var IDC_PROBE_REGIONS = [
|
|
2632
|
-
"us-east-1",
|
|
2633
|
-
"eu-west-1",
|
|
2634
|
-
"eu-central-1",
|
|
2635
|
-
"us-east-2",
|
|
2636
|
-
"eu-west-2",
|
|
2637
|
-
"eu-west-3",
|
|
2638
|
-
"eu-north-1",
|
|
2639
|
-
"ap-southeast-1",
|
|
2640
|
-
"ap-northeast-1",
|
|
2641
|
-
"us-west-2"
|
|
2642
|
-
];
|
|
2643
|
-
var EXPIRES_BUFFER_MS = 5 * 60 * 1000;
|
|
2644
2210
|
function abortableDelay2(ms, signal) {
|
|
2645
2211
|
if (signal?.aborted)
|
|
2646
|
-
return Promise.reject(signal.reason
|
|
2212
|
+
return Promise.reject(signal.reason);
|
|
2647
2213
|
return new Promise((resolve2, reject) => {
|
|
2648
2214
|
const timer = setTimeout(resolve2, ms);
|
|
2649
2215
|
signal?.addEventListener("abort", () => {
|
|
2650
2216
|
clearTimeout(timer);
|
|
2651
|
-
reject(signal.reason
|
|
2217
|
+
reject(signal.reason);
|
|
2652
2218
|
}, { once: true });
|
|
2653
2219
|
});
|
|
2654
2220
|
}
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
method: "POST",
|
|
2659
|
-
headers: { "Content-Type": "application/json", "User-Agent": "pi-kiro" },
|
|
2660
|
-
body: JSON.stringify({
|
|
2661
|
-
clientName: "pi-kiro",
|
|
2662
|
-
clientType: "public",
|
|
2663
|
-
scopes: SSO_SCOPES,
|
|
2664
|
-
grantTypes: ["urn:ietf:params:oauth:grant-type:device_code", "refresh_token"]
|
|
2665
|
-
})
|
|
2666
|
-
});
|
|
2667
|
-
if (!regResp.ok)
|
|
2668
|
-
return null;
|
|
2669
|
-
const { clientId, clientSecret } = await regResp.json();
|
|
2670
|
-
const devResp = await fetch(`${oidcEndpoint}/device_authorization`, {
|
|
2671
|
-
method: "POST",
|
|
2672
|
-
headers: { "Content-Type": "application/json", "User-Agent": "pi-kiro" },
|
|
2673
|
-
body: JSON.stringify({ clientId, clientSecret, startUrl })
|
|
2674
|
-
});
|
|
2675
|
-
if (!devResp.ok)
|
|
2676
|
-
return null;
|
|
2677
|
-
return {
|
|
2678
|
-
clientId,
|
|
2679
|
-
clientSecret,
|
|
2680
|
-
oidcEndpoint,
|
|
2681
|
-
devAuth: await devResp.json()
|
|
2682
|
-
};
|
|
2683
|
-
}
|
|
2684
|
-
async function pollForToken(oidcEndpoint, clientId, clientSecret, devAuth, signal) {
|
|
2685
|
-
const deadline = Date.now() + (devAuth.expiresIn || 600) * 1000;
|
|
2686
|
-
const baseInterval = (devAuth.interval || 5) * 1000;
|
|
2687
|
-
let interval = baseInterval;
|
|
2688
|
-
while (Date.now() < deadline) {
|
|
2689
|
-
if (signal?.aborted)
|
|
2690
|
-
throw new Error("Login cancelled");
|
|
2691
|
-
await abortableDelay2(interval, signal);
|
|
2692
|
-
let resp;
|
|
2693
|
-
try {
|
|
2694
|
-
resp = await fetch(`${oidcEndpoint}/token`, {
|
|
2695
|
-
method: "POST",
|
|
2696
|
-
headers: { "Content-Type": "application/json", "User-Agent": "pi-kiro" },
|
|
2697
|
-
body: JSON.stringify({
|
|
2698
|
-
clientId,
|
|
2699
|
-
clientSecret,
|
|
2700
|
-
deviceCode: devAuth.deviceCode,
|
|
2701
|
-
grantType: "urn:ietf:params:oauth:grant-type:device_code"
|
|
2702
|
-
})
|
|
2703
|
-
});
|
|
2704
|
-
} catch {
|
|
2705
|
-
continue;
|
|
2706
|
-
}
|
|
2707
|
-
if (resp.status >= 500)
|
|
2708
|
-
continue;
|
|
2709
|
-
let data;
|
|
2710
|
-
try {
|
|
2711
|
-
data = await resp.json();
|
|
2712
|
-
} catch {
|
|
2713
|
-
if (!resp.ok) {
|
|
2714
|
-
throw new Error(`Authorization failed: HTTP ${resp.status}`);
|
|
2715
|
-
}
|
|
2716
|
-
continue;
|
|
2717
|
-
}
|
|
2718
|
-
if (!data.error && data.accessToken && data.refreshToken)
|
|
2719
|
-
return data;
|
|
2720
|
-
if (data.error === "authorization_pending")
|
|
2721
|
-
continue;
|
|
2722
|
-
if (data.error === "slow_down") {
|
|
2723
|
-
interval += baseInterval;
|
|
2724
|
-
continue;
|
|
2725
|
-
}
|
|
2726
|
-
if (data.error)
|
|
2727
|
-
throw new Error(`Authorization failed: ${data.error}`);
|
|
2728
|
-
}
|
|
2729
|
-
throw new Error("Authorization timed out");
|
|
2221
|
+
var profileArnStore = undefined;
|
|
2222
|
+
function seedProfileArn(arn) {
|
|
2223
|
+
profileArnStore = arn;
|
|
2730
2224
|
}
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
message: "Select login method:",
|
|
2734
|
-
options: [
|
|
2735
|
-
{ id: "builder-id", label: "AWS Builder ID (personal account)" },
|
|
2736
|
-
{ id: "idc", label: "IAM Identity Center (enterprise SSO)" },
|
|
2737
|
-
{ id: "sync", label: "Import from Kiro CLI/IDE (auto-sync local DB)" },
|
|
2738
|
-
{ id: "desktop", label: "Desktop refresh token (manual)" }
|
|
2739
|
-
]
|
|
2740
|
-
});
|
|
2741
|
-
if (!method)
|
|
2742
|
-
throw new Error("Login cancelled");
|
|
2743
|
-
if (method === "sync") {
|
|
2744
|
-
return loginCliSync(callbacks);
|
|
2745
|
-
}
|
|
2746
|
-
if (method === "desktop") {
|
|
2747
|
-
return loginDesktopManual(callbacks);
|
|
2748
|
-
}
|
|
2749
|
-
if (method === "builder-id") {
|
|
2750
|
-
return runDeviceCodeFlow(callbacks, BUILDER_ID_START_URL, [BUILDER_ID_REGION], "builder-id");
|
|
2751
|
-
}
|
|
2752
|
-
const startUrl = (await callbacks.onPrompt({
|
|
2753
|
-
message: "Paste your IAM Identity Center start URL:",
|
|
2754
|
-
placeholder: "https://mycompany.awsapps.com/start",
|
|
2755
|
-
allowEmpty: false
|
|
2756
|
-
}))?.trim();
|
|
2757
|
-
if (!startUrl || !startUrl.startsWith("http")) {
|
|
2758
|
-
throw new Error(`Invalid start URL "${startUrl ?? ""}" — expected https://…`);
|
|
2759
|
-
}
|
|
2760
|
-
const regionRaw = await callbacks.onPrompt({
|
|
2761
|
-
message: `Identity Center region, or blank to auto-detect (${IDC_PROBE_REGIONS.join(", ")})`,
|
|
2762
|
-
placeholder: "us-east-1",
|
|
2763
|
-
allowEmpty: true
|
|
2764
|
-
});
|
|
2765
|
-
const region = (regionRaw ?? "").trim();
|
|
2766
|
-
const regions = region ? [region] : IDC_PROBE_REGIONS;
|
|
2767
|
-
callbacks.onProgress?.(region ? `Connecting to ${region}…` : "Detecting your Identity Center region…");
|
|
2768
|
-
return runDeviceCodeFlow(callbacks, startUrl, regions, "idc");
|
|
2225
|
+
function getProfileArn() {
|
|
2226
|
+
return profileArnStore;
|
|
2769
2227
|
}
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
if (!imported || !imported.accessToken && !imported.refreshToken) {
|
|
2775
|
-
throw new Error(`No Kiro IDE credentials found.
|
|
2776
|
-
Make sure Kiro IDE is installed and you're logged in, then try again.
|
|
2777
|
-
Alternatively, use 'desktop' to paste a refresh token manually.`);
|
|
2778
|
-
}
|
|
2779
|
-
log.info("Successfully imported credentials from Kiro IDE");
|
|
2780
|
-
callbacks.onProgress?.(`Imported from Kiro IDE (${imported.authMethod}, ${imported.region}${imported.email ? `, ${imported.email}` : ""})`);
|
|
2228
|
+
function emitToolCall(state, output, stream) {
|
|
2229
|
+
if (!state.input.trim())
|
|
2230
|
+
state.input = "{}";
|
|
2231
|
+
let args;
|
|
2781
2232
|
try {
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
log.info(`Fetched and cached ${apiModels.length} models after CLI sync`);
|
|
2786
|
-
} catch (err) {
|
|
2787
|
-
log.warn(`Failed to fetch models after CLI sync: ${err}`);
|
|
2788
|
-
}
|
|
2789
|
-
return kiroCredsFromCliImport(imported);
|
|
2790
|
-
}
|
|
2791
|
-
async function loginDesktopManual(callbacks) {
|
|
2792
|
-
const refreshRaw = await callbacks.onPrompt({
|
|
2793
|
-
message: `Paste your Kiro desktop refresh token
|
|
2794
|
-
` + "(find it in ~/.kiro/db/kiro.db → auth_kv table):",
|
|
2795
|
-
placeholder: "refresh-token",
|
|
2796
|
-
allowEmpty: true
|
|
2797
|
-
});
|
|
2798
|
-
const refreshToken = (refreshRaw ?? "").trim();
|
|
2799
|
-
if (!refreshToken) {
|
|
2800
|
-
throw new Error("Login cancelled — no refresh token provided");
|
|
2801
|
-
}
|
|
2802
|
-
const regionRaw = await callbacks.onPrompt({
|
|
2803
|
-
message: "Kiro region:",
|
|
2804
|
-
placeholder: "us-east-1",
|
|
2805
|
-
allowEmpty: true
|
|
2806
|
-
});
|
|
2807
|
-
const region = (regionRaw ?? "").trim() || "us-east-1";
|
|
2808
|
-
const refreshCreds = {
|
|
2809
|
-
refresh: `${refreshToken}|||desktop`,
|
|
2810
|
-
access: "",
|
|
2811
|
-
expires: 0,
|
|
2812
|
-
clientId: "",
|
|
2813
|
-
clientSecret: "",
|
|
2814
|
-
region,
|
|
2815
|
-
authMethod: "desktop"
|
|
2816
|
-
};
|
|
2817
|
-
callbacks.onProgress?.("Exchanging refresh token…");
|
|
2818
|
-
return refreshKiroToken(refreshCreds);
|
|
2819
|
-
}
|
|
2820
|
-
async function runDeviceCodeFlow(callbacks, startUrl, regions, authMethod) {
|
|
2821
|
-
let result = null;
|
|
2822
|
-
let detectedRegion = "";
|
|
2823
|
-
for (const region of regions) {
|
|
2824
|
-
result = await tryRegisterAndAuthorize(startUrl, region);
|
|
2825
|
-
if (result) {
|
|
2826
|
-
detectedRegion = region;
|
|
2827
|
-
if (regions.length > 1)
|
|
2828
|
-
callbacks.onProgress?.(`Region: ${region}`);
|
|
2829
|
-
break;
|
|
2233
|
+
args = JSON.parse(state.input);
|
|
2234
|
+
if (args && typeof args === "object" && "__tool_use_purpose" in args) {
|
|
2235
|
+
delete args.__tool_use_purpose;
|
|
2830
2236
|
}
|
|
2237
|
+
} catch (e) {
|
|
2238
|
+
log.warn(`failed to parse tool input for "${state.name}" (${state.toolUseId}): ${e instanceof Error ? e.message : String(e)}`);
|
|
2239
|
+
return false;
|
|
2831
2240
|
}
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
});
|
|
2840
|
-
callbacks.onProgress?.("Waiting for browser authorization (up to 10 minutes)…");
|
|
2841
|
-
const tok = await pollForToken(result.oidcEndpoint, result.clientId, result.clientSecret, result.devAuth, callbacks.signal);
|
|
2842
|
-
if (!tok.accessToken || !tok.refreshToken) {
|
|
2843
|
-
throw new Error("Authorization completed but no tokens returned");
|
|
2844
|
-
}
|
|
2845
|
-
try {
|
|
2846
|
-
const apiRegion = resolveApiRegion(detectedRegion);
|
|
2847
|
-
const apiModels = await fetchAvailableModels(tok.accessToken, apiRegion);
|
|
2848
|
-
setCachedDynamicModels(buildModelsFromApi(apiModels));
|
|
2849
|
-
log.info(`Fetched and cached ${apiModels.length} models after login`);
|
|
2850
|
-
} catch (err) {
|
|
2851
|
-
log.warn(`Failed to fetch models after login, falling back: ${err}`);
|
|
2852
|
-
}
|
|
2853
|
-
return {
|
|
2854
|
-
refresh: `${tok.refreshToken}|${result.clientId}|${result.clientSecret}|${authMethod}`,
|
|
2855
|
-
access: tok.accessToken,
|
|
2856
|
-
expires: Date.now() + (tok.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
2857
|
-
clientId: result.clientId,
|
|
2858
|
-
clientSecret: result.clientSecret,
|
|
2859
|
-
region: detectedRegion,
|
|
2860
|
-
authMethod
|
|
2861
|
-
};
|
|
2862
|
-
}
|
|
2863
|
-
async function syncBackToKiroCli(result) {
|
|
2864
|
-
if (result.kiroSyncSource !== "kiro-cli-db" || !result.kiroSyncTokenKey) {
|
|
2865
|
-
log.debug("Credential sync-back skipped: credential did not originate from kiro-cli DB");
|
|
2866
|
-
return;
|
|
2867
|
-
}
|
|
2868
|
-
try {
|
|
2869
|
-
const { saveKiroCliCredentials: saveKiroCliCredentials2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
2870
|
-
const synced = await saveKiroCliCredentials2({
|
|
2871
|
-
accessToken: result.access,
|
|
2872
|
-
refreshToken: result.refresh.split("|")[0] ?? "",
|
|
2873
|
-
region: result.region,
|
|
2874
|
-
authMethod: result.authMethod === "builder-id" ? "idc" : result.authMethod,
|
|
2875
|
-
source: result.kiroSyncSource,
|
|
2876
|
-
tokenKey: result.kiroSyncTokenKey
|
|
2877
|
-
});
|
|
2878
|
-
if (synced)
|
|
2879
|
-
log.info("Synced refreshed credentials back to Kiro CLI DB");
|
|
2880
|
-
} catch (err) {
|
|
2881
|
-
log.debug(`Credential sync-back skipped: ${err}`);
|
|
2882
|
-
}
|
|
2883
|
-
}
|
|
2884
|
-
function kiroCredsFromCliImport(imported) {
|
|
2885
|
-
const hasOidcCreds = !!imported.clientId && !!imported.clientSecret;
|
|
2886
|
-
const authMethod = hasOidcCreds && imported.authMethod === "idc" ? "idc" : "desktop";
|
|
2887
|
-
const refreshPacked = hasOidcCreds ? `${imported.refreshToken}|${imported.clientId}|${imported.clientSecret ?? ""}|${authMethod}` : `${imported.refreshToken}|||desktop`;
|
|
2888
|
-
return {
|
|
2889
|
-
refresh: refreshPacked,
|
|
2890
|
-
access: imported.accessToken,
|
|
2891
|
-
expires: Date.now() + 3600000 - EXPIRES_BUFFER_MS,
|
|
2892
|
-
clientId: imported.clientId ?? "",
|
|
2893
|
-
clientSecret: imported.clientSecret ?? "",
|
|
2894
|
-
region: imported.region,
|
|
2895
|
-
authMethod,
|
|
2896
|
-
kiroSyncSource: imported.source,
|
|
2897
|
-
kiroSyncTokenKey: imported.tokenKey
|
|
2898
|
-
};
|
|
2241
|
+
const contentIndex = output.content.length;
|
|
2242
|
+
const toolCall = { type: "toolCall", id: state.toolUseId, name: state.name, arguments: args };
|
|
2243
|
+
output.content.push(toolCall);
|
|
2244
|
+
stream.push({ type: "toolcall_start", contentIndex, partial: output });
|
|
2245
|
+
stream.push({ type: "toolcall_delta", contentIndex, delta: state.input, partial: output });
|
|
2246
|
+
stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: output });
|
|
2247
|
+
return true;
|
|
2899
2248
|
}
|
|
2900
|
-
|
|
2901
|
-
const
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
}
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2249
|
+
function streamKiro(model, context, options) {
|
|
2250
|
+
const stream = createAssistantMessageEventStream();
|
|
2251
|
+
(async () => {
|
|
2252
|
+
const output = {
|
|
2253
|
+
role: "assistant",
|
|
2254
|
+
content: [],
|
|
2255
|
+
api: model.api,
|
|
2256
|
+
provider: model.provider,
|
|
2257
|
+
model: model.id,
|
|
2258
|
+
usage: {
|
|
2259
|
+
input: 0,
|
|
2260
|
+
output: 0,
|
|
2261
|
+
cacheRead: 0,
|
|
2262
|
+
cacheWrite: 0,
|
|
2263
|
+
totalTokens: 0,
|
|
2264
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
|
|
2265
|
+
},
|
|
2266
|
+
stopReason: "stop",
|
|
2267
|
+
timestamp: Date.now()
|
|
2268
|
+
};
|
|
2269
|
+
let hiddenShimTimer = null;
|
|
2270
|
+
try {
|
|
2271
|
+
const accessToken = options?.apiKey;
|
|
2272
|
+
if (!accessToken) {
|
|
2273
|
+
throw new Error("Kiro credentials not set. Run /login kiro.");
|
|
2274
|
+
}
|
|
2275
|
+
const endpoint = model.baseUrl || "https://runtime.us-east-1.kiro.dev";
|
|
2276
|
+
const profileArn = getProfileArn();
|
|
2277
|
+
if (!profileArn) {
|
|
2278
|
+
throw new Error("profileArn not resolved. Re-run /login kiro to refresh credentials.");
|
|
2279
|
+
}
|
|
2280
|
+
const kiroModelId = resolveKiroModel(model.id);
|
|
2281
|
+
const thinkingEnabled = !!options?.reasoning || model.reasoning;
|
|
2282
|
+
const reasoningHidden = !!model.reasoningHidden;
|
|
2283
|
+
log.debug("request.init", {
|
|
2284
|
+
endpoint,
|
|
2285
|
+
model: model.id,
|
|
2286
|
+
kiroModelId,
|
|
2287
|
+
contextWindow: model.contextWindow,
|
|
2288
|
+
thinkingEnabled,
|
|
2289
|
+
reasoningHidden,
|
|
2290
|
+
reasoning: options?.reasoning,
|
|
2291
|
+
messageCount: context.messages.length,
|
|
2292
|
+
toolCount: context.tools?.length ?? 0,
|
|
2293
|
+
hasSystemPrompt: !!context.systemPrompt,
|
|
2294
|
+
profileArn,
|
|
2295
|
+
sessionId: options?.sessionId
|
|
2296
|
+
});
|
|
2297
|
+
let systemPrompt = context.systemPrompt ?? "";
|
|
2298
|
+
if (thinkingEnabled && !reasoningHidden) {
|
|
2299
|
+
const budget = options?.reasoning === "xhigh" ? 50000 : options?.reasoning === "high" ? 30000 : options?.reasoning === "medium" ? 20000 : 1e4;
|
|
2300
|
+
systemPrompt = `<thinking_mode>enabled</thinking_mode><max_thinking_length>${budget}</max_thinking_length>${systemPrompt ? `
|
|
2301
|
+
${systemPrompt}` : ""}`;
|
|
2302
|
+
}
|
|
2303
|
+
const envState = {
|
|
2304
|
+
operatingSystem: resolveOS(),
|
|
2305
|
+
currentWorkingDirectory: process.cwd()
|
|
2306
|
+
};
|
|
2307
|
+
const conversationId = options?.sessionId ?? crypto.randomUUID();
|
|
2308
|
+
let retryCount = 0;
|
|
2309
|
+
while (retryCount <= MAX_RETRIES) {
|
|
2310
|
+
if (options?.signal?.aborted)
|
|
2311
|
+
throw options.signal.reason;
|
|
2312
|
+
const normalized = normalizeMessages(context.messages);
|
|
2313
|
+
const {
|
|
2314
|
+
history,
|
|
2315
|
+
systemPrepended,
|
|
2316
|
+
currentMsgStartIdx
|
|
2317
|
+
} = buildHistory(normalized, kiroModelId, systemPrompt);
|
|
2318
|
+
const seedInstruction = SYSTEM_SEED_INSTRUCTION.replace("{{modelId}}", kiroModelId);
|
|
2319
|
+
const seedPair = [
|
|
2320
|
+
{ userInputMessage: { content: seedInstruction, origin: "KIRO_CLI" } },
|
|
2321
|
+
{ assistantResponseMessage: { content: SYSTEM_SEED_ACK } }
|
|
2322
|
+
];
|
|
2323
|
+
history.unshift(...seedPair);
|
|
2324
|
+
const currentMessages = normalized.slice(currentMsgStartIdx);
|
|
2325
|
+
const firstMsg = currentMessages[0];
|
|
2326
|
+
let currentContent = "";
|
|
2327
|
+
const currentToolResults = [];
|
|
2328
|
+
let currentImages;
|
|
2329
|
+
if (firstMsg?.role === "assistant") {
|
|
2330
|
+
const am = firstMsg;
|
|
2331
|
+
let armContent = "";
|
|
2332
|
+
const armToolUses = [];
|
|
2333
|
+
if (Array.isArray(am.content)) {
|
|
2334
|
+
for (const b of am.content) {
|
|
2335
|
+
if (b.type === "text") {
|
|
2336
|
+
armContent += b.text;
|
|
2337
|
+
} else if (b.type === "thinking") {
|
|
2338
|
+
armContent = `<thinking>${b.thinking}</thinking>
|
|
2339
|
+
|
|
2340
|
+
${armContent}`;
|
|
2341
|
+
} else if (b.type === "toolCall") {
|
|
2342
|
+
const tc = b;
|
|
2343
|
+
armToolUses.push({
|
|
2344
|
+
name: tc.name,
|
|
2345
|
+
toolUseId: toKiroToolUseId(tc.id),
|
|
2346
|
+
input: parseToolArgs(tc.arguments)
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
if (armContent || armToolUses.length > 0) {
|
|
2352
|
+
const last = history[history.length - 1];
|
|
2353
|
+
if (last && !last.userInputMessage && last.assistantResponseMessage) {
|
|
2354
|
+
last.assistantResponseMessage.content += `
|
|
2355
|
+
|
|
2356
|
+
${armContent}`;
|
|
2357
|
+
if (armToolUses.length > 0) {
|
|
2358
|
+
last.assistantResponseMessage.toolUses = [
|
|
2359
|
+
...last.assistantResponseMessage.toolUses ?? [],
|
|
2360
|
+
...armToolUses
|
|
2361
|
+
];
|
|
2362
|
+
}
|
|
2363
|
+
} else {
|
|
2364
|
+
history.push({
|
|
2365
|
+
assistantResponseMessage: {
|
|
2366
|
+
content: armContent,
|
|
2367
|
+
...armToolUses.length > 0 ? { toolUses: armToolUses } : {}
|
|
2368
|
+
}
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
const toolResultImages = [];
|
|
2373
|
+
for (let i = 1;i < currentMessages.length; i++) {
|
|
2374
|
+
const m = currentMessages[i];
|
|
2375
|
+
if (m?.role === "toolResult") {
|
|
2376
|
+
const trm = m;
|
|
2377
|
+
currentToolResults.push({
|
|
2378
|
+
content: [{ text: truncate(getContentText(m), TOOL_RESULT_LIMIT) }],
|
|
2379
|
+
status: trm.isError ? "error" : "success",
|
|
2380
|
+
toolUseId: toKiroToolUseId(trm.toolCallId)
|
|
2381
|
+
});
|
|
2382
|
+
if (Array.isArray(trm.content)) {
|
|
2383
|
+
for (const c of trm.content) {
|
|
2384
|
+
if (c.type === "image")
|
|
2385
|
+
toolResultImages.push(c);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
if (toolResultImages.length > 0) {
|
|
2391
|
+
const { images: converted, omitted } = convertImagesToKiro(toolResultImages);
|
|
2392
|
+
if (omitted > 0)
|
|
2393
|
+
log.warn(`${omitted} tool-result image(s) omitted (size/count limit)`);
|
|
2394
|
+
currentImages = currentImages ? [...currentImages, ...converted] : converted;
|
|
2395
|
+
}
|
|
2396
|
+
currentContent = currentToolResults.length > 0 ? "Tool results provided." : "Please proceed with the task.";
|
|
2397
|
+
} else if (firstMsg?.role === "toolResult") {
|
|
2398
|
+
const toolResultImages = [];
|
|
2399
|
+
for (const m of currentMessages) {
|
|
2400
|
+
if (m?.role === "toolResult") {
|
|
2401
|
+
const trm = m;
|
|
2402
|
+
currentToolResults.push({
|
|
2403
|
+
content: [{ text: truncate(getContentText(m), TOOL_RESULT_LIMIT) }],
|
|
2404
|
+
status: trm.isError ? "error" : "success",
|
|
2405
|
+
toolUseId: toKiroToolUseId(trm.toolCallId)
|
|
2406
|
+
});
|
|
2407
|
+
if (Array.isArray(trm.content)) {
|
|
2408
|
+
for (const c of trm.content) {
|
|
2409
|
+
if (c.type === "image")
|
|
2410
|
+
toolResultImages.push(c);
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
if (toolResultImages.length > 0) {
|
|
2416
|
+
const { images: converted, omitted } = convertImagesToKiro(toolResultImages);
|
|
2417
|
+
if (omitted > 0)
|
|
2418
|
+
log.warn(`${omitted} tool-result image(s) omitted (size/count limit)`);
|
|
2419
|
+
currentImages = currentImages ? [...currentImages, ...converted] : converted;
|
|
2420
|
+
}
|
|
2421
|
+
currentContent = "Tool results provided.";
|
|
2422
|
+
} else if (firstMsg?.role === "user") {
|
|
2423
|
+
currentContent = typeof firstMsg.content === "string" ? firstMsg.content : getContentText(firstMsg);
|
|
2424
|
+
if (systemPrompt && !systemPrepended) {
|
|
2425
|
+
currentContent = `${systemPrompt}
|
|
2426
|
+
|
|
2427
|
+
${currentContent}`;
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
const now = new Date;
|
|
2431
|
+
const tzOffset = -now.getTimezoneOffset();
|
|
2432
|
+
const tzSign = tzOffset >= 0 ? "+" : "-";
|
|
2433
|
+
const tzH = String(Math.floor(Math.abs(tzOffset) / 60)).padStart(2, "0");
|
|
2434
|
+
const tzM = String(Math.abs(tzOffset) % 60).padStart(2, "0");
|
|
2435
|
+
const isoLocal = now.getFullYear() + "-" + String(now.getMonth() + 1).padStart(2, "0") + "-" + String(now.getDate()).padStart(2, "0") + "T" + String(now.getHours()).padStart(2, "0") + ":" + String(now.getMinutes()).padStart(2, "0") + ":" + String(now.getSeconds()).padStart(2, "0") + "." + String(now.getMilliseconds()).padStart(3, "0") + tzSign + tzH + ":" + tzM;
|
|
2436
|
+
const weekday = now.toLocaleDateString("en-US", { weekday: "long" });
|
|
2437
|
+
currentContent = `--- CONTEXT ENTRY BEGIN ---
|
|
2438
|
+
` + `Current time: ${weekday}, ${isoLocal}
|
|
2439
|
+
` + `--- CONTEXT ENTRY END ---
|
|
2440
|
+
|
|
2441
|
+
` + `--- USER MESSAGE BEGIN ---
|
|
2442
|
+
` + `${currentContent}
|
|
2443
|
+
` + `--- USER MESSAGE END ---`;
|
|
2444
|
+
let uimc = {
|
|
2445
|
+
envState
|
|
2446
|
+
};
|
|
2447
|
+
if (currentToolResults.length > 0)
|
|
2448
|
+
uimc.toolResults = currentToolResults;
|
|
2449
|
+
if (context.tools?.length) {
|
|
2450
|
+
const deepCleanSchema = (obj) => {
|
|
2451
|
+
if (Array.isArray(obj)) {
|
|
2452
|
+
return obj.map(deepCleanSchema);
|
|
2453
|
+
} else if (obj !== null && typeof obj === "object") {
|
|
2454
|
+
const cleaned = {};
|
|
2455
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
2456
|
+
if (k === "$schema")
|
|
2457
|
+
continue;
|
|
2458
|
+
if (k === "required" && Array.isArray(v) && v.length === 0)
|
|
2459
|
+
continue;
|
|
2460
|
+
cleaned[k] = deepCleanSchema(v);
|
|
2461
|
+
}
|
|
2462
|
+
return cleaned;
|
|
2463
|
+
}
|
|
2464
|
+
return obj;
|
|
2465
|
+
};
|
|
2466
|
+
uimc.tools = context.tools.map((t) => {
|
|
2467
|
+
const params = deepCleanSchema(t.parameters);
|
|
2468
|
+
if (params.properties) {
|
|
2469
|
+
params.properties.__tool_use_purpose = {
|
|
2470
|
+
type: "string",
|
|
2471
|
+
description: "A brief explanation why you are making this tool use."
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
return {
|
|
2475
|
+
toolSpecification: {
|
|
2476
|
+
name: t.name,
|
|
2477
|
+
description: t.description || `Use ${t.name}`,
|
|
2478
|
+
inputSchema: { json: params }
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
});
|
|
2482
|
+
}
|
|
2483
|
+
if (firstMsg?.role === "user") {
|
|
2484
|
+
const imgs = extractImages(firstMsg);
|
|
2485
|
+
if (imgs.length > 0) {
|
|
2486
|
+
const { images: converted, omitted } = convertImagesToKiro(imgs);
|
|
2487
|
+
if (omitted > 0)
|
|
2488
|
+
log.warn(`${omitted} user image(s) omitted (size/count limit)`);
|
|
2489
|
+
currentImages = converted;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
const request = {
|
|
2493
|
+
conversationState: {
|
|
2494
|
+
chatTriggerType: "MANUAL",
|
|
2495
|
+
agentTaskType: "vibe",
|
|
2496
|
+
conversationId,
|
|
2497
|
+
currentMessage: {
|
|
2498
|
+
userInputMessage: {
|
|
2499
|
+
content: currentContent,
|
|
2500
|
+
modelId: kiroModelId,
|
|
2501
|
+
origin: "KIRO_CLI",
|
|
2502
|
+
...currentImages ? { images: currentImages } : {},
|
|
2503
|
+
userInputMessageContext: uimc
|
|
2504
|
+
}
|
|
2505
|
+
},
|
|
2506
|
+
...history.length > 0 ? { history } : {}
|
|
2507
|
+
},
|
|
2508
|
+
profileArn,
|
|
2509
|
+
agentMode: "vibe"
|
|
2510
|
+
};
|
|
2511
|
+
const EFFORT_MAP = {
|
|
2512
|
+
minimal: "low",
|
|
2513
|
+
low: "medium",
|
|
2514
|
+
medium: "high",
|
|
2515
|
+
high: "xhigh",
|
|
2516
|
+
xhigh: "max"
|
|
2517
|
+
};
|
|
2518
|
+
const staticModel = kiroModels.find((m) => m.id === model.id);
|
|
2519
|
+
const dynamicModel = getCachedDynamicModels()?.find((m) => m.id === model.id);
|
|
2520
|
+
const supportedEfforts = staticModel?.supportedEfforts ?? dynamicModel?.supportedEfforts;
|
|
2521
|
+
const supportsThinkingConfig = staticModel?.supportsThinkingConfig ?? dynamicModel?.supportsThinkingConfig;
|
|
2522
|
+
if (supportedEfforts && supportedEfforts.length > 0 && options?.reasoning && typeof options.reasoning === "string") {
|
|
2523
|
+
const kiroEffort = EFFORT_MAP[options.reasoning];
|
|
2524
|
+
if (kiroEffort && supportedEfforts.includes(kiroEffort)) {
|
|
2525
|
+
request.additionalModelRequestFields = request.additionalModelRequestFields || {};
|
|
2526
|
+
request.additionalModelRequestFields.output_config = { effort: kiroEffort };
|
|
2527
|
+
log.debug("effort.set", { piReasoning: options.reasoning, kiroEffort, model: model.id });
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
if (supportsThinkingConfig && thinkingEnabled) {
|
|
2531
|
+
request.additionalModelRequestFields = request.additionalModelRequestFields || {};
|
|
2532
|
+
request.additionalModelRequestFields.thinking = {
|
|
2533
|
+
type: "adaptive",
|
|
2534
|
+
display: "summarized"
|
|
2535
|
+
};
|
|
2536
|
+
log.debug("thinking.set", { type: "adaptive", display: "summarized", model: model.id });
|
|
2537
|
+
}
|
|
2538
|
+
stream.push({ type: "start", partial: output });
|
|
2539
|
+
if (reasoningHidden && thinkingEnabled && hiddenShimTimer === null) {
|
|
2540
|
+
hiddenShimTimer = setTimeout(() => {
|
|
2541
|
+
hiddenShimTimer = null;
|
|
2542
|
+
emitHiddenReasoningLate(output, stream);
|
|
2543
|
+
}, HIDDEN_REASONING_COUNTDOWN_MS);
|
|
2544
|
+
}
|
|
2545
|
+
let response;
|
|
2546
|
+
let capacityRetryCount = 0;
|
|
2547
|
+
let transientRetryCount = 0;
|
|
2548
|
+
let contextTruncationAttempt = 0;
|
|
2549
|
+
while (true) {
|
|
2550
|
+
const osName = resolveOS();
|
|
2551
|
+
const ua = `aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererstreaming/0.1.16551 os/${osName} lang/rust/1.92.0 md/appVersion-2.7.1 app/AmazonQ-For-CLI`;
|
|
2552
|
+
const xAmzUa = `aws-sdk-rust/1.3.15 ua/2.1 api/codewhispererstreaming/0.1.16551 os/${osName} lang/rust/1.92.0 m/F app/AmazonQ-For-CLI`;
|
|
2553
|
+
const requestBody = JSON.stringify(request);
|
|
2554
|
+
log.debug("request.send", {
|
|
2555
|
+
attempt: retryCount,
|
|
2556
|
+
capacityAttempt: capacityRetryCount,
|
|
2557
|
+
historyLen: history.length,
|
|
2558
|
+
currentContentLen: currentContent.length,
|
|
2559
|
+
hasImages: !!currentImages,
|
|
2560
|
+
toolResultCount: currentToolResults.length,
|
|
2561
|
+
requestJsonChars: requestBody.length
|
|
2562
|
+
});
|
|
2563
|
+
response = await fetch(endpoint, {
|
|
2564
|
+
method: "POST",
|
|
2565
|
+
headers: {
|
|
2566
|
+
"Content-Type": "application/x-amz-json-1.0",
|
|
2567
|
+
Accept: "*/*",
|
|
2568
|
+
"Accept-Encoding": "gzip",
|
|
2569
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2570
|
+
"X-Amz-Target": "AmazonCodeWhispererStreamingService.GenerateAssistantResponse",
|
|
2571
|
+
"x-amzn-codewhisperer-optout": "true",
|
|
2572
|
+
"amz-sdk-invocation-id": crypto.randomUUID(),
|
|
2573
|
+
"amz-sdk-request": "attempt=1; max=3",
|
|
2574
|
+
"user-agent": ua,
|
|
2575
|
+
"x-amz-user-agent": xAmzUa,
|
|
2576
|
+
Pragma: "no-cache",
|
|
2577
|
+
"Cache-Control": "no-cache"
|
|
2578
|
+
},
|
|
2579
|
+
body: requestBody,
|
|
2580
|
+
signal: options?.signal
|
|
2581
|
+
});
|
|
2582
|
+
if (response.ok)
|
|
2583
|
+
break;
|
|
2584
|
+
let errText = "";
|
|
2585
|
+
try {
|
|
2586
|
+
errText = await response.text();
|
|
2587
|
+
} catch {
|
|
2588
|
+
errText = "";
|
|
2589
|
+
}
|
|
2590
|
+
log.debug("response.error", {
|
|
2591
|
+
status: response.status,
|
|
2592
|
+
body: errText
|
|
2593
|
+
});
|
|
2594
|
+
if (isCapacityError(errText) && capacityRetryCount < CAPACITY_MAX_RETRIES) {
|
|
2595
|
+
capacityRetryCount++;
|
|
2596
|
+
const delayMs = exponentialBackoff(capacityRetryCount - 1, CAPACITY_BASE_DELAY_MS, CAPACITY_MAX_DELAY_MS);
|
|
2597
|
+
log.warn(`INSUFFICIENT_MODEL_CAPACITY — retrying in ${delayMs}ms (${capacityRetryCount}/${CAPACITY_MAX_RETRIES})`);
|
|
2598
|
+
await abortableDelay2(delayMs, options?.signal);
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
if (isNonRetryableBodyError(errText) || isCapacityError(errText)) {
|
|
2602
|
+
throw new Error(`Kiro API error: ${errText || response.statusText}`);
|
|
2603
|
+
}
|
|
2604
|
+
if (isTooBigError(response.status, errText)) {
|
|
2605
|
+
if (contextTruncationAttempt < CONTEXT_TRUNCATION_MAX_RETRIES && history.length > 0) {
|
|
2606
|
+
contextTruncationAttempt++;
|
|
2607
|
+
const dropCount = Math.max(1, Math.floor(history.length * CONTEXT_TRUNCATION_DROP_RATIO));
|
|
2608
|
+
const before = history.length;
|
|
2609
|
+
history.splice(0, dropCount);
|
|
2610
|
+
log.warn(`context too large — truncated history from ${before} to ${history.length} entries ` + `(attempt ${contextTruncationAttempt}/${CONTEXT_TRUNCATION_MAX_RETRIES})`);
|
|
2611
|
+
request.conversationState.history = history.length > 0 ? history : undefined;
|
|
2612
|
+
continue;
|
|
2613
|
+
}
|
|
2614
|
+
throw new Error(`Kiro API error: context_length_exceeded (${response.status} ${errText})`);
|
|
2615
|
+
}
|
|
2616
|
+
if (isTransientError(response.status) && transientRetryCount < TRANSIENT_MAX_RETRIES) {
|
|
2617
|
+
transientRetryCount++;
|
|
2618
|
+
const jitter = Math.floor(Math.random() * 1000);
|
|
2619
|
+
const delayMs = exponentialBackoff(transientRetryCount - 1, TRANSIENT_BASE_DELAY_MS, TRANSIENT_MAX_DELAY_MS) + jitter;
|
|
2620
|
+
log.warn(`transient error ${response.status} — retrying in ${delayMs}ms ` + `(${transientRetryCount}/${TRANSIENT_MAX_RETRIES})`);
|
|
2621
|
+
await abortableDelay2(delayMs, options?.signal);
|
|
2622
|
+
continue;
|
|
2623
|
+
}
|
|
2624
|
+
if (response.status === 401) {
|
|
2625
|
+
const permanent = isPermanentError(errText);
|
|
2626
|
+
if (permanent) {
|
|
2627
|
+
throw new Error(`Kiro API error: credentials permanently invalid — run /login kiro to re-authenticate. ${errText}`);
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
if (response.status === 403) {
|
|
2631
|
+
throw new Error(`Kiro API error: access token rejected (403) — run /login kiro to re-authenticate. ${errText}`);
|
|
2632
|
+
}
|
|
2633
|
+
throw new Error(`Kiro API error: ${response.status} ${response.statusText} ${errText}`);
|
|
2634
|
+
}
|
|
2635
|
+
if (capacityRetryCount > 0) {
|
|
2636
|
+
log.info(`recovered from capacity pressure after ${capacityRetryCount} retries`);
|
|
2637
|
+
}
|
|
2638
|
+
if (transientRetryCount > 0) {
|
|
2639
|
+
log.info(`recovered from transient error after ${transientRetryCount} retries`);
|
|
2640
|
+
}
|
|
2641
|
+
if (contextTruncationAttempt > 0) {
|
|
2642
|
+
log.info(`recovered after ${contextTruncationAttempt} context truncation(s)`);
|
|
2643
|
+
}
|
|
2644
|
+
const reader = response.body?.getReader();
|
|
2645
|
+
if (!reader)
|
|
2646
|
+
throw new Error("No response body");
|
|
2647
|
+
const decoder = new TextDecoder;
|
|
2648
|
+
let buffer = "";
|
|
2649
|
+
let totalContent = "";
|
|
2650
|
+
let lastContentData = "";
|
|
2651
|
+
let usageEvent = null;
|
|
2652
|
+
let receivedContextUsage = false;
|
|
2653
|
+
let chunkSeq = 0;
|
|
2654
|
+
let eventSeq = 0;
|
|
2655
|
+
const thinkingParser = thinkingEnabled ? new ThinkingTagParser(output, stream) : null;
|
|
2656
|
+
let textBlockIndex = null;
|
|
2657
|
+
let emittedToolCalls = 0;
|
|
2658
|
+
let sawAnyToolCalls = false;
|
|
2659
|
+
let currentToolCall = null;
|
|
2660
|
+
const flushToolCall = () => {
|
|
2661
|
+
if (!currentToolCall)
|
|
2662
|
+
return;
|
|
2663
|
+
if (emitToolCall(currentToolCall, output, stream))
|
|
2664
|
+
emittedToolCalls++;
|
|
2665
|
+
currentToolCall = null;
|
|
2666
|
+
};
|
|
2667
|
+
const cancelHiddenShim = () => {
|
|
2668
|
+
if (hiddenShimTimer) {
|
|
2669
|
+
clearTimeout(hiddenShimTimer);
|
|
2670
|
+
hiddenShimTimer = null;
|
|
2671
|
+
}
|
|
2672
|
+
};
|
|
2673
|
+
let idleTimer = null;
|
|
2674
|
+
let idleCancelled = false;
|
|
2675
|
+
const resetIdle = () => {
|
|
2676
|
+
if (idleTimer)
|
|
2677
|
+
clearTimeout(idleTimer);
|
|
2678
|
+
idleTimer = setTimeout(() => {
|
|
2679
|
+
idleCancelled = true;
|
|
2680
|
+
reader.cancel().catch(() => {});
|
|
2681
|
+
}, IDLE_TIMEOUT_MS);
|
|
2682
|
+
};
|
|
2683
|
+
let gotFirstToken = false;
|
|
2684
|
+
let firstTokenTimedOut = false;
|
|
2685
|
+
let streamError = null;
|
|
2686
|
+
const FIRST_TOKEN_SENTINEL = Symbol("firstTokenTimeout");
|
|
2687
|
+
while (true) {
|
|
2688
|
+
let readResult;
|
|
2689
|
+
if (!gotFirstToken) {
|
|
2690
|
+
const readPromise = reader.read();
|
|
2691
|
+
let firstTokenTimer = null;
|
|
2692
|
+
const result = await Promise.race([
|
|
2693
|
+
readPromise,
|
|
2694
|
+
new Promise((resolve2) => {
|
|
2695
|
+
firstTokenTimer = setTimeout(() => resolve2(FIRST_TOKEN_SENTINEL), firstTokenTimeoutForModel(model.id));
|
|
2696
|
+
})
|
|
2697
|
+
]);
|
|
2698
|
+
if (firstTokenTimer)
|
|
2699
|
+
clearTimeout(firstTokenTimer);
|
|
2700
|
+
if (result === FIRST_TOKEN_SENTINEL) {
|
|
2701
|
+
readPromise.catch(() => {});
|
|
2702
|
+
reader.cancel().catch(() => {});
|
|
2703
|
+
firstTokenTimedOut = true;
|
|
2704
|
+
break;
|
|
2705
|
+
}
|
|
2706
|
+
readResult = result;
|
|
2707
|
+
gotFirstToken = true;
|
|
2708
|
+
resetIdle();
|
|
2709
|
+
} else {
|
|
2710
|
+
readResult = await reader.read();
|
|
2711
|
+
}
|
|
2712
|
+
const { done, value } = readResult;
|
|
2713
|
+
if (done)
|
|
2714
|
+
break;
|
|
2715
|
+
const decoded = decoder.decode(value, { stream: true });
|
|
2716
|
+
buffer += decoded;
|
|
2717
|
+
if (log.isDebug()) {
|
|
2718
|
+
log.debug("stream.chunk", {
|
|
2719
|
+
seq: chunkSeq++,
|
|
2720
|
+
bytes: value?.byteLength ?? 0,
|
|
2721
|
+
decodedLen: decoded.length,
|
|
2722
|
+
preview: previewChunk(decoded)
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
const { events, remaining } = parseKiroEvents(buffer);
|
|
2726
|
+
buffer = remaining;
|
|
2727
|
+
if (events.length > 0)
|
|
2728
|
+
resetIdle();
|
|
2729
|
+
if (log.isDebug() && events.length > 0) {
|
|
2730
|
+
for (const ev of events) {
|
|
2731
|
+
log.debug("stream.event", { seq: eventSeq++, event: ev });
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
for (const event of events) {
|
|
2735
|
+
switch (event.type) {
|
|
2736
|
+
case "contextUsage": {
|
|
2737
|
+
const pct = event.data.contextUsagePercentage;
|
|
2738
|
+
output.usage.input = pct >= COMPACTION_THRESHOLD_PCT ? model.contextWindow + 1 : Math.round(pct / 100 * model.contextWindow);
|
|
2739
|
+
receivedContextUsage = true;
|
|
2740
|
+
log.debug("contextUsage", { pct, threshold: COMPACTION_THRESHOLD_PCT, willCompact: pct >= COMPACTION_THRESHOLD_PCT });
|
|
2741
|
+
break;
|
|
2742
|
+
}
|
|
2743
|
+
case "reasoning": {
|
|
2744
|
+
cancelHiddenShim();
|
|
2745
|
+
if (output.content.length === 0 || output.content[output.content.length - 1]?.type !== "thinking") {
|
|
2746
|
+
output.content.push({ type: "thinking", thinking: "" });
|
|
2747
|
+
stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
|
|
2748
|
+
}
|
|
2749
|
+
const contentIndex = output.content.length - 1;
|
|
2750
|
+
const tc = output.content[contentIndex];
|
|
2751
|
+
if (event.data.text) {
|
|
2752
|
+
tc.thinking += event.data.text;
|
|
2753
|
+
stream.push({ type: "thinking_delta", contentIndex, delta: event.data.text, partial: output });
|
|
2754
|
+
}
|
|
2755
|
+
if (event.data.signature) {
|
|
2756
|
+
tc.thinkingSignature = event.data.signature;
|
|
2757
|
+
}
|
|
2758
|
+
break;
|
|
2759
|
+
}
|
|
2760
|
+
case "content": {
|
|
2761
|
+
if (event.data === lastContentData)
|
|
2762
|
+
continue;
|
|
2763
|
+
lastContentData = event.data;
|
|
2764
|
+
totalContent += event.data;
|
|
2765
|
+
cancelHiddenShim();
|
|
2766
|
+
if (thinkingParser) {
|
|
2767
|
+
thinkingParser.processChunk(event.data);
|
|
2768
|
+
} else {
|
|
2769
|
+
if (textBlockIndex === null) {
|
|
2770
|
+
textBlockIndex = output.content.length;
|
|
2771
|
+
output.content.push({ type: "text", text: "" });
|
|
2772
|
+
stream.push({ type: "text_start", contentIndex: textBlockIndex, partial: output });
|
|
2773
|
+
}
|
|
2774
|
+
const block = output.content[textBlockIndex];
|
|
2775
|
+
if (block) {
|
|
2776
|
+
block.text += event.data;
|
|
2777
|
+
stream.push({
|
|
2778
|
+
type: "text_delta",
|
|
2779
|
+
contentIndex: textBlockIndex,
|
|
2780
|
+
delta: event.data,
|
|
2781
|
+
partial: output
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
break;
|
|
2786
|
+
}
|
|
2787
|
+
case "toolUse": {
|
|
2788
|
+
const tc = event.data;
|
|
2789
|
+
cancelHiddenShim();
|
|
2790
|
+
sawAnyToolCalls = true;
|
|
2791
|
+
if (!currentToolCall || currentToolCall.toolUseId !== tc.toolUseId) {
|
|
2792
|
+
flushToolCall();
|
|
2793
|
+
currentToolCall = { toolUseId: tc.toolUseId, name: tc.name, input: "" };
|
|
2794
|
+
}
|
|
2795
|
+
currentToolCall.input += tc.input || "";
|
|
2796
|
+
if (tc.input)
|
|
2797
|
+
totalContent += tc.input;
|
|
2798
|
+
if (tc.stop)
|
|
2799
|
+
flushToolCall();
|
|
2800
|
+
break;
|
|
2801
|
+
}
|
|
2802
|
+
case "toolUseInput": {
|
|
2803
|
+
if (currentToolCall)
|
|
2804
|
+
currentToolCall.input += event.data.input || "";
|
|
2805
|
+
if (event.data.input)
|
|
2806
|
+
totalContent += event.data.input;
|
|
2807
|
+
break;
|
|
2808
|
+
}
|
|
2809
|
+
case "toolUseStop": {
|
|
2810
|
+
if (event.data.stop)
|
|
2811
|
+
flushToolCall();
|
|
2812
|
+
break;
|
|
2813
|
+
}
|
|
2814
|
+
case "usage": {
|
|
2815
|
+
usageEvent = event.data;
|
|
2816
|
+
break;
|
|
2817
|
+
}
|
|
2818
|
+
case "error": {
|
|
2819
|
+
streamError = event.data.message ? `${event.data.error}: ${event.data.message}` : event.data.error;
|
|
2820
|
+
reader.cancel().catch(() => {});
|
|
2821
|
+
break;
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
if (streamError)
|
|
2825
|
+
break;
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
if (idleTimer)
|
|
2829
|
+
clearTimeout(idleTimer);
|
|
2830
|
+
if (firstTokenTimedOut || idleCancelled || streamError) {
|
|
2831
|
+
if (retryCount < MAX_RETRIES) {
|
|
2832
|
+
retryCount++;
|
|
2833
|
+
const delayMs = exponentialBackoff(retryCount - 1, 1000, MAX_RETRY_DELAY_MS);
|
|
2834
|
+
log.warn(`stream ${firstTokenTimedOut ? "first-token timed out" : idleCancelled ? "idle timed out" : `error: ${streamError}`} — retrying (${retryCount}/${MAX_RETRIES})`);
|
|
2835
|
+
cancelHiddenShim();
|
|
2836
|
+
await abortableDelay2(delayMs, options?.signal);
|
|
2837
|
+
output.content = [];
|
|
2838
|
+
textBlockIndex = null;
|
|
2839
|
+
continue;
|
|
2840
|
+
}
|
|
2841
|
+
if (streamError)
|
|
2842
|
+
throw new Error(`Kiro API stream error after max retries: ${streamError}`);
|
|
2843
|
+
throw new Error(`Kiro API error: ${firstTokenTimedOut ? "first token" : "idle"} timeout after max retries`);
|
|
2844
|
+
}
|
|
2845
|
+
cancelHiddenShim();
|
|
2846
|
+
if (currentToolCall && emitToolCall(currentToolCall, output, stream))
|
|
2847
|
+
emittedToolCalls++;
|
|
2848
|
+
if (thinkingParser) {
|
|
2849
|
+
thinkingParser.finalize();
|
|
2850
|
+
textBlockIndex = thinkingParser.getTextBlockIndex();
|
|
2851
|
+
}
|
|
2852
|
+
if (textBlockIndex !== null) {
|
|
2853
|
+
const block = output.content[textBlockIndex];
|
|
2854
|
+
if (block) {
|
|
2855
|
+
stream.push({
|
|
2856
|
+
type: "text_end",
|
|
2857
|
+
contentIndex: textBlockIndex,
|
|
2858
|
+
content: block.text,
|
|
2859
|
+
partial: output
|
|
2860
|
+
});
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
if (usageEvent?.inputTokens !== undefined)
|
|
2864
|
+
output.usage.input = usageEvent.inputTokens;
|
|
2865
|
+
output.usage.output = usageEvent?.outputTokens ?? countTokens(totalContent);
|
|
2866
|
+
output.usage.totalTokens = output.usage.input + output.usage.output;
|
|
2867
|
+
try {
|
|
2868
|
+
calculateCost(model, output.usage);
|
|
2869
|
+
} catch {
|
|
2870
|
+
output.usage.cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 };
|
|
2871
|
+
}
|
|
2872
|
+
const textBlock = textBlockIndex !== null ? output.content[textBlockIndex] : undefined;
|
|
2873
|
+
const hasText = !!textBlock && textBlock.text.length > 0;
|
|
2874
|
+
if (!hasText && !sawAnyToolCalls) {
|
|
2875
|
+
if (retryCount < MAX_RETRIES) {
|
|
2876
|
+
retryCount++;
|
|
2877
|
+
const delayMs = exponentialBackoff(retryCount - 1, 1000, MAX_RETRY_DELAY_MS);
|
|
2878
|
+
log.warn(`empty response — retrying (${retryCount}/${MAX_RETRIES})`);
|
|
2879
|
+
cancelHiddenShim();
|
|
2880
|
+
output.content = [];
|
|
2881
|
+
textBlockIndex = null;
|
|
2882
|
+
await abortableDelay2(delayMs, options?.signal);
|
|
2883
|
+
continue;
|
|
2884
|
+
}
|
|
2885
|
+
log.warn(`empty response persisted after ${MAX_RETRIES} retries`);
|
|
2886
|
+
cancelHiddenShim();
|
|
2887
|
+
}
|
|
2888
|
+
if (!receivedContextUsage && emittedToolCalls === 0) {
|
|
2889
|
+
output.stopReason = "length";
|
|
2890
|
+
} else {
|
|
2891
|
+
output.stopReason = emittedToolCalls > 0 ? "toolUse" : "stop";
|
|
2892
|
+
}
|
|
2893
|
+
stream.push({
|
|
2894
|
+
type: "done",
|
|
2895
|
+
reason: output.stopReason,
|
|
2896
|
+
message: output
|
|
2897
|
+
});
|
|
2898
|
+
log.debug("response.done", {
|
|
2899
|
+
stopReason: output.stopReason,
|
|
2900
|
+
emittedToolCalls,
|
|
2901
|
+
sawAnyToolCalls,
|
|
2902
|
+
usage: output.usage
|
|
2903
|
+
});
|
|
2904
|
+
stream.end();
|
|
2905
|
+
return;
|
|
2906
|
+
}
|
|
2907
|
+
} catch (error) {
|
|
2908
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
2909
|
+
output.errorMessage = error instanceof Error ? error.message : String(error);
|
|
2910
|
+
log.debug("response.caught", { stopReason: output.stopReason, error: output.errorMessage });
|
|
2911
|
+
if (hiddenShimTimer) {
|
|
2912
|
+
clearTimeout(hiddenShimTimer);
|
|
2913
|
+
hiddenShimTimer = null;
|
|
2914
|
+
}
|
|
2915
|
+
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
2916
|
+
stream.end();
|
|
2923
2917
|
}
|
|
2924
|
-
|
|
2918
|
+
})().catch(() => {
|
|
2925
2919
|
try {
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
setCachedDynamicModels(buildModelsFromApi(apiModels));
|
|
2929
|
-
log.info(`Fetched and cached ${apiModels.length} models after desktop token refresh`);
|
|
2930
|
-
} catch (err) {
|
|
2931
|
-
log.warn(`Failed to fetch models after desktop token refresh: ${err}`);
|
|
2932
|
-
}
|
|
2933
|
-
return {
|
|
2934
|
-
refresh: `${data2.refreshToken}|||desktop`,
|
|
2935
|
-
access: data2.accessToken,
|
|
2936
|
-
expires: Date.now() + (data2.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
2937
|
-
clientId: "",
|
|
2938
|
-
clientSecret: "",
|
|
2939
|
-
region,
|
|
2940
|
-
authMethod: "desktop",
|
|
2941
|
-
kiroSyncSource: credentials.kiroSyncSource,
|
|
2942
|
-
kiroSyncTokenKey: credentials.kiroSyncTokenKey
|
|
2943
|
-
};
|
|
2944
|
-
}
|
|
2945
|
-
const endpoint = `https://oidc.${region}.amazonaws.com/token`;
|
|
2946
|
-
const resp = await fetch(endpoint, {
|
|
2947
|
-
method: "POST",
|
|
2948
|
-
headers: { "Content-Type": "application/json", "User-Agent": "pi-kiro" },
|
|
2949
|
-
body: JSON.stringify({ clientId, clientSecret, refreshToken, grantType: "refresh_token" })
|
|
2920
|
+
stream.end();
|
|
2921
|
+
} catch {}
|
|
2950
2922
|
});
|
|
2951
|
-
|
|
2952
|
-
const body = await resp.text().catch(() => "");
|
|
2953
|
-
throw new Error(`Token refresh failed: ${resp.status} ${body}`);
|
|
2954
|
-
}
|
|
2955
|
-
const data = await resp.json();
|
|
2956
|
-
try {
|
|
2957
|
-
const apiRegion = resolveApiRegion(region);
|
|
2958
|
-
const apiModels = await fetchAvailableModels(data.accessToken, apiRegion);
|
|
2959
|
-
setCachedDynamicModels(buildModelsFromApi(apiModels));
|
|
2960
|
-
log.info(`Fetched and cached ${apiModels.length} models after token refresh`);
|
|
2961
|
-
} catch (err) {
|
|
2962
|
-
log.warn(`Failed to fetch models after token refresh, falling back: ${err}`);
|
|
2963
|
-
}
|
|
2964
|
-
return {
|
|
2965
|
-
refresh: `${data.refreshToken}|${clientId}|${clientSecret}|${authMethod}`,
|
|
2966
|
-
access: data.accessToken,
|
|
2967
|
-
expires: Date.now() + (data.expiresIn ?? 3600) * 1000 - EXPIRES_BUFFER_MS,
|
|
2968
|
-
clientId,
|
|
2969
|
-
clientSecret,
|
|
2970
|
-
region,
|
|
2971
|
-
authMethod,
|
|
2972
|
-
kiroSyncSource: credentials.kiroSyncSource,
|
|
2973
|
-
kiroSyncTokenKey: credentials.kiroSyncTokenKey
|
|
2974
|
-
};
|
|
2975
|
-
}
|
|
2976
|
-
async function refreshKiroToken(credentials) {
|
|
2977
|
-
const inputMethod = credentials.authMethod;
|
|
2978
|
-
const authMethod = inputMethod === "builder-id" || inputMethod === "idc" || inputMethod === "desktop" ? inputMethod : "idc";
|
|
2979
|
-
if (inputMethod !== undefined && inputMethod !== "builder-id" && inputMethod !== "idc" && inputMethod !== "desktop") {
|
|
2980
|
-
log.warn(`refreshKiroToken: unrecognized authMethod "${String(inputMethod)}" — defaulting to "idc"`);
|
|
2981
|
-
}
|
|
2982
|
-
const baseCreds = {
|
|
2983
|
-
...credentials,
|
|
2984
|
-
clientId: credentials.clientId ?? credentials.refresh.split("|")[1] ?? "",
|
|
2985
|
-
clientSecret: credentials.clientSecret ?? credentials.refresh.split("|")[2] ?? "",
|
|
2986
|
-
region: credentials.region,
|
|
2987
|
-
authMethod
|
|
2988
|
-
};
|
|
2989
|
-
const errors = [];
|
|
2990
|
-
try {
|
|
2991
|
-
log.debug("refresh.cascade: layer 1 — normal refresh");
|
|
2992
|
-
const result = await refreshTokenInner(baseCreds);
|
|
2993
|
-
syncBackToKiroCli(result);
|
|
2994
|
-
return result;
|
|
2995
|
-
} catch (err) {
|
|
2996
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2997
|
-
errors.push(`L1(normal): ${msg}`);
|
|
2998
|
-
log.warn(`refresh.cascade: layer 1 failed — ${msg}`);
|
|
2999
|
-
}
|
|
3000
|
-
let freshImport = null;
|
|
3001
|
-
try {
|
|
3002
|
-
log.debug("refresh.cascade: layer 2 — fresh kiro-cli import");
|
|
3003
|
-
const { importFromKiroCli: importFromKiroCli2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
3004
|
-
freshImport = await importFromKiroCli2();
|
|
3005
|
-
if (freshImport?.accessToken) {
|
|
3006
|
-
const result = kiroCredsFromCliImport(freshImport);
|
|
3007
|
-
log.info("refresh.cascade: layer 2 succeeded — using fresh kiro-cli credentials");
|
|
3008
|
-
return result;
|
|
3009
|
-
}
|
|
3010
|
-
errors.push("L2(fresh-import): no valid credentials found");
|
|
3011
|
-
log.debug("refresh.cascade: layer 2 — no fresh credentials");
|
|
3012
|
-
} catch (err) {
|
|
3013
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3014
|
-
errors.push(`L2(fresh-import): ${msg}`);
|
|
3015
|
-
log.warn(`refresh.cascade: layer 2 failed — ${msg}`);
|
|
3016
|
-
}
|
|
3017
|
-
if (freshImport?.refreshToken) {
|
|
3018
|
-
try {
|
|
3019
|
-
log.debug("refresh.cascade: layer 3 — refresh fresh kiro-cli creds");
|
|
3020
|
-
const freshCreds = kiroCredsFromCliImport(freshImport);
|
|
3021
|
-
const result = await refreshTokenInner(freshCreds);
|
|
3022
|
-
syncBackToKiroCli(result);
|
|
3023
|
-
log.info("refresh.cascade: layer 3 succeeded — refreshed fresh kiro-cli credentials");
|
|
3024
|
-
return result;
|
|
3025
|
-
} catch (err) {
|
|
3026
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3027
|
-
errors.push(`L3(refresh-fresh): ${msg}`);
|
|
3028
|
-
log.warn(`refresh.cascade: layer 3 failed — ${msg}`);
|
|
3029
|
-
}
|
|
3030
|
-
}
|
|
3031
|
-
let expiredImport = null;
|
|
3032
|
-
try {
|
|
3033
|
-
log.debug("refresh.cascade: layer 4 — expired kiro-cli import");
|
|
3034
|
-
const { getKiroCliCredentialsAllowExpired: getKiroCliCredentialsAllowExpired2 } = await Promise.resolve().then(() => (init_kiro_cli_sync(), exports_kiro_cli_sync));
|
|
3035
|
-
expiredImport = await getKiroCliCredentialsAllowExpired2(freshImport);
|
|
3036
|
-
if (expiredImport?.accessToken) {
|
|
3037
|
-
const result = kiroCredsFromCliImport(expiredImport);
|
|
3038
|
-
log.info("refresh.cascade: layer 4 succeeded — using expired kiro-cli credentials");
|
|
3039
|
-
return result;
|
|
3040
|
-
} else {
|
|
3041
|
-
errors.push("L4(expired-import): no different expired credentials");
|
|
3042
|
-
log.debug("refresh.cascade: layer 4 — no additional expired credentials");
|
|
3043
|
-
}
|
|
3044
|
-
} catch (err) {
|
|
3045
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3046
|
-
errors.push(`L4(expired-import): ${msg}`);
|
|
3047
|
-
log.warn(`refresh.cascade: layer 4 failed — ${msg}`);
|
|
3048
|
-
}
|
|
3049
|
-
if (expiredImport?.refreshToken) {
|
|
3050
|
-
try {
|
|
3051
|
-
log.debug("refresh.cascade: layer 5 — refresh expired kiro-cli creds");
|
|
3052
|
-
const expiredCreds = kiroCredsFromCliImport(expiredImport);
|
|
3053
|
-
const result = await refreshTokenInner(expiredCreds);
|
|
3054
|
-
syncBackToKiroCli(result);
|
|
3055
|
-
log.info("refresh.cascade: layer 5 succeeded — refreshed expired kiro-cli credentials");
|
|
3056
|
-
return result;
|
|
3057
|
-
} catch (err) {
|
|
3058
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3059
|
-
errors.push(`L5(refresh-expired): ${msg}`);
|
|
3060
|
-
log.warn(`refresh.cascade: layer 5 failed — ${msg}`);
|
|
3061
|
-
}
|
|
3062
|
-
}
|
|
3063
|
-
throw new Error(`Kiro token refresh failed — all 5 cascade layers exhausted. ` + `Re-login required.
|
|
3064
|
-
${errors.join(`
|
|
3065
|
-
`)}`);
|
|
2923
|
+
return stream;
|
|
3066
2924
|
}
|
|
3067
2925
|
|
|
3068
2926
|
// src/core.ts
|