@magpie-community/opencode-kiro-auth 0.1.21 → 0.1.23
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/README.md +13 -0
- package/index.mjs +71 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,6 +108,10 @@ request:
|
|
|
108
108
|
profile ARN names.
|
|
109
109
|
- It turns the request into a Kiro conversation:
|
|
110
110
|
- The system prompt goes at the head of the first message.
|
|
111
|
+
- System messages that end the conversation (Claude Code's environment
|
|
112
|
+
context) are sent as a turn before the question, answered by Kiro IDE's
|
|
113
|
+
"Understood.", so the question is the message answered (#538). After
|
|
114
|
+
tool results they stay with them.
|
|
111
115
|
- Tool calls and results are paired: an unanswered call gets an error
|
|
112
116
|
result, and a result longer than 250,000 characters is cut.
|
|
113
117
|
- Tool ids Kiro wouldn't take are rewritten. A tool name over 64
|
|
@@ -136,6 +140,15 @@ request:
|
|
|
136
140
|
the prompt, with a budget: low 10k, medium 20k, high 30k, xhigh and max
|
|
137
141
|
50k (as magpie's built-in Kiro: #120). The
|
|
138
142
|
`<thinking>` block of the reply comes back as thinking.
|
|
143
|
+
A model whose Kiro catalog lists effort levels with adaptive thinking
|
|
144
|
+
(`additionalModelRequestFieldsSchema`) is sent the level instead, as Kiro
|
|
145
|
+
IDE sends it: `additionalModelRequestFields` with `thinking.type:
|
|
146
|
+
"adaptive"` and `output_config.effort` (or `reasoning.effort`), and no
|
|
147
|
+
budget. So xhigh and max stay apart there (#538).
|
|
148
|
+
Thinking is asked for only when the request's `thinking` is `enabled` or
|
|
149
|
+
`adaptive`. An effort with no `thinking`, or with it `disabled` (Claude
|
|
150
|
+
Code's title requests, or a turn with thinking off), is sent with no
|
|
151
|
+
thinking at any level.
|
|
139
152
|
- Anthropic's hosted web search is answered with Kiro's own web search
|
|
140
153
|
(see [Web search](#web-search)).
|
|
141
154
|
- Kiro's failures keep their meaning:
|
package/index.mjs
CHANGED
|
@@ -574,6 +574,24 @@ function account(client) {
|
|
|
574
574
|
// ---- models ----------------------------------------------------------------------
|
|
575
575
|
|
|
576
576
|
const windows = new Map() // a model's context, as Kiro last listed it
|
|
577
|
+
const natives = new Map() // a model's effort levels sent as fields, as Kiro last listed them (nativeOf)
|
|
578
|
+
|
|
579
|
+
// nativeOf is what a model's additionalModelRequestFieldsSchema in Kiro's
|
|
580
|
+
// list offers, read as Kiro IDE reads it: the effort levels in the enum of
|
|
581
|
+
// output_config's (or reasoning's) effort, and whether thinking.type may be
|
|
582
|
+
// "adaptive". A model offering both is sent the caller's effort in
|
|
583
|
+
// additionalModelRequestFields, as the IDE sends it; any other keeps the
|
|
584
|
+
// <max_thinking_length> budget (#538).
|
|
585
|
+
function nativeOf(s) {
|
|
586
|
+
if (!s || typeof s !== "object" || Array.isArray(s)) return undefined
|
|
587
|
+
const types = s.properties?.thinking?.properties?.type?.enum
|
|
588
|
+
if (!Array.isArray(types) || !types.includes("adaptive")) return undefined
|
|
589
|
+
for (const path of ["output_config", "reasoning"]) {
|
|
590
|
+
const levels = s.properties?.[path]?.properties?.effort?.enum
|
|
591
|
+
if (Array.isArray(levels) && levels.length) return { path, levels }
|
|
592
|
+
}
|
|
593
|
+
return undefined
|
|
594
|
+
}
|
|
577
595
|
|
|
578
596
|
const thinks = (id) => /claude/i.test(id) || id === "auto"
|
|
579
597
|
// the levels a thinking model offers, as the built-in thinks them (xhigh as
|
|
@@ -653,6 +671,7 @@ async function listModels(a) {
|
|
|
653
671
|
}
|
|
654
672
|
if (!list.length) throw new Error("Kiro listed no models")
|
|
655
673
|
for (const m of list) windows.set(m.id, m.context)
|
|
674
|
+
for (const m of out?.models ?? []) if (m?.modelId) natives.set(m.modelId, nativeOf(m.additionalModelRequestFieldsSchema))
|
|
656
675
|
return list
|
|
657
676
|
}
|
|
658
677
|
|
|
@@ -823,10 +842,15 @@ function documentText(p) {
|
|
|
823
842
|
}
|
|
824
843
|
|
|
825
844
|
// thinking is whether the model is asked to think aloud, and for how
|
|
826
|
-
// long: when the caller asked for it, of a Claude model (or Auto).
|
|
845
|
+
// long: when the caller asked for it, of a Claude model (or Auto). Only a
|
|
846
|
+
// thinking of type enabled or adaptive asks for it, as Anthropic's API
|
|
847
|
+
// reads a request: an effort with no thinking (Claude Code's title and
|
|
848
|
+
// other side requests, or its main turn with thinking turned off), or with
|
|
849
|
+
// thinking disabled, is sent no thinking at any level (#538), as magpie's
|
|
850
|
+
// gateway reads it too.
|
|
827
851
|
function thinking(req, model) {
|
|
828
852
|
const effort = req.output_config?.effort ?? ""
|
|
829
|
-
const on = req.thinking?.type === "enabled" || req.thinking?.type === "adaptive"
|
|
853
|
+
const on = req.thinking?.type === "enabled" || req.thinking?.type === "adaptive"
|
|
830
854
|
if (!on || !thinks(String(model).toLowerCase())) return 0
|
|
831
855
|
const budget = { low: 10000, high: 30000, xhigh: 50000, max: 50000 }[effort]
|
|
832
856
|
if (budget) return budget
|
|
@@ -834,6 +858,17 @@ function thinking(req, model) {
|
|
|
834
858
|
return 20000
|
|
835
859
|
}
|
|
836
860
|
|
|
861
|
+
// nativeFields is the additionalModelRequestFields a thinking request is
|
|
862
|
+
// sent with when Kiro's list says the model takes its effort as a field:
|
|
863
|
+
// adaptive thinking at the caller's level, so xhigh and max reach the model
|
|
864
|
+
// as two levels rather than one 50000 budget (#538). undefined otherwise.
|
|
865
|
+
function nativeFields(req, model) {
|
|
866
|
+
const n = natives.get(model)
|
|
867
|
+
const effort = req.output_config?.effort
|
|
868
|
+
if (!n || typeof effort !== "string" || !n.levels.includes(effort) || !thinking(req, model)) return undefined
|
|
869
|
+
return { thinking: { type: "adaptive" }, [n.path]: { effort } }
|
|
870
|
+
}
|
|
871
|
+
|
|
837
872
|
// formatText is the structured output a request asks for, said in words:
|
|
838
873
|
// Anthropic's output_config.format (or the AI SDK's output_format) with a
|
|
839
874
|
// JSON schema, "" when it asks for none. A JSON object with no schema
|
|
@@ -872,6 +907,34 @@ async function kiroBody(req, model, profile, budget) {
|
|
|
872
907
|
return JSON.stringify(await fitImages(kiroRequest(req, model, profile, budget)))
|
|
873
908
|
}
|
|
874
909
|
|
|
910
|
+
// UNDERSTOOD is the reply Kiro IDE puts in its history where a turn has
|
|
911
|
+
// none of its own, before the message being answered
|
|
912
|
+
const UNDERSTOOD = "Understood."
|
|
913
|
+
|
|
914
|
+
// contextFirst is a conversation that ends in system messages (Claude
|
|
915
|
+
// Code's environment context, sent after the user's question) with that
|
|
916
|
+
// context said first: a turn of its own, answered by Kiro IDE's
|
|
917
|
+
// placeholder, before the question, which stays the message being
|
|
918
|
+
// answered rather than the head of "question, then context" (#538). The
|
|
919
|
+
// instructions' own field isn't used: Kiro refuses it ("systemPrompt field
|
|
920
|
+
// is not enabled for this user"). A question carrying tool results, or
|
|
921
|
+
// following tool calls, keeps the context in it as before, so no call is
|
|
922
|
+
// parted from its result.
|
|
923
|
+
function contextFirst(messages) {
|
|
924
|
+
let end = messages.length
|
|
925
|
+
while (end > 0 && messages[end - 1]?.role === "system") end--
|
|
926
|
+
if (end === messages.length) return messages
|
|
927
|
+
let start = end
|
|
928
|
+
while (start > 0 && messages[start - 1]?.role === "user") start--
|
|
929
|
+
const parts = (m) => (typeof m?.content === "string" ? [{ type: "text", text: m.content }] : Array.isArray(m?.content) ? m.content : [])
|
|
930
|
+
const context = messages.slice(end)
|
|
931
|
+
if (start === end || !context.some((m) => parts(m).some((p) => p?.type !== "text" || p.text))) return messages
|
|
932
|
+
const turn = messages.slice(start, end)
|
|
933
|
+
if (turn.some((m) => parts(m).some((p) => p?.type === "tool_result"))) return messages
|
|
934
|
+
if (parts(messages[start - 1]).some((p) => p?.type === "tool_use" || p?.type === "server_tool_use" || p?.type === "web_search_tool_result")) return messages
|
|
935
|
+
return [...messages.slice(0, start), ...context.map((m) => ({ ...m, role: "user" })), { role: "assistant", content: UNDERSTOOD }, ...turn]
|
|
936
|
+
}
|
|
937
|
+
|
|
875
938
|
function kiroRequest(req, model, profile, budget) {
|
|
876
939
|
const entries = []
|
|
877
940
|
// the messages the caller marked for its prompt cache (cache_control),
|
|
@@ -882,7 +945,7 @@ function kiroRequest(req, model, profile, budget) {
|
|
|
882
945
|
}
|
|
883
946
|
const user = () => ({ content: "", modelId: model, origin: "KIRO_CLI" })
|
|
884
947
|
const resultsOnly = new WeakSet() // the messages made to carry a reply's web search results
|
|
885
|
-
for (const m of req.messages ?? []) {
|
|
948
|
+
for (const m of contextFirst(req.messages ?? [])) {
|
|
886
949
|
const parts = typeof m.content === "string" ? [{ type: "text", text: m.content }] : m.content ?? []
|
|
887
950
|
const texts = []
|
|
888
951
|
if (m.role === "assistant") {
|
|
@@ -1013,7 +1076,9 @@ function kiroRequest(req, model, profile, budget) {
|
|
|
1013
1076
|
// end of the instructions, as magpie's built-in tells it (inSystem)
|
|
1014
1077
|
const format = formatText(req)
|
|
1015
1078
|
if (format) system = system ? system + "\n\n" + format : format
|
|
1016
|
-
|
|
1079
|
+
// a level Kiro takes as a field is sent as one, not also as a budget
|
|
1080
|
+
const native = budget ? nativeFields(req, model) : undefined
|
|
1081
|
+
if (budget && !native) system = `<thinking_mode>enabled</thinking_mode><max_thinking_length>${budget}</max_thinking_length>` + (system ? "\n" + system : "")
|
|
1017
1082
|
if (system) entries[0].userInputMessage.content = system + "\n\n" + entries[0].userInputMessage.content
|
|
1018
1083
|
if (Array.isArray(req.system)) mark(entries[0].userInputMessage, req.system)
|
|
1019
1084
|
|
|
@@ -1063,6 +1128,7 @@ function kiroRequest(req, model, profile, budget) {
|
|
|
1063
1128
|
if (entries.length > 1) state.history = entries.slice(0, -1)
|
|
1064
1129
|
const out = { conversationState: state, agentMode: "vibe" }
|
|
1065
1130
|
if (profile) out.profileArn = profile
|
|
1131
|
+
if (native) out.additionalModelRequestFields = native
|
|
1066
1132
|
return out
|
|
1067
1133
|
}
|
|
1068
1134
|
|
|
@@ -2309,4 +2375,4 @@ export async function KiroAuthPlugin({ client } = {}) {
|
|
|
2309
2375
|
}
|
|
2310
2376
|
|
|
2311
2377
|
// for tests
|
|
2312
|
-
export const _internal = { generate, refresh, usageOf, buildKiro, kiroBody, IMAGE_LIMIT, events, reply, failure, toolID, toolName, toolNames, thinking, configModel, runtimeModel, readCLI, readIDE, regionOf, planName, frames }
|
|
2378
|
+
export const _internal = { generate, refresh, usageOf, buildKiro, kiroBody, IMAGE_LIMIT, events, reply, failure, toolID, toolName, toolNames, thinking, nativeFields, listModels, contextFirst, configModel, runtimeModel, readCLI, readIDE, regionOf, planName, frames }
|