@magpie-community/opencode-kiro-auth 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/index.mjs +34 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -76,6 +76,8 @@ request:
76
76
  - Tool ids Kiro wouldn't take are rewritten. A tool name over 64
77
77
  characters (an MCP plugin's, in Claude Code) is sent shortened with a
78
78
  hash and its calls come back under the full name.
79
+ - A tool's images (a screenshot, Codex's view_image) go with the
80
+ message that answers the call, as Kiro's tool results hold text alone.
79
81
  - Only the latest images are sent.
80
82
  - Tools the history used but the request doesn't offer are declared.
81
83
  - It turns the AWS event stream Kiro answers with back into Messages'
package/index.mjs CHANGED
@@ -614,6 +614,12 @@ function imageFormat(mediaType) {
614
614
  return f
615
615
  }
616
616
 
617
+ // addImage puts an Anthropic image block in images as Kiro takes it: the
618
+ // bytes inline. One named only by a URL has no bytes to send and is left out.
619
+ function addImage(images, p) {
620
+ if (p.source?.type === "base64" && p.source.data) images.push({ format: imageFormat(p.source.media_type), source: { bytes: p.source.data } })
621
+ }
622
+
617
623
  const textOf = (c) =>
618
624
  typeof c === "string" ? c : Array.isArray(c) ? c.filter((p) => p?.type === "text").map((p) => p.text ?? "").join("\n\n") : ""
619
625
  const joinNonEmpty = (a, b) => (a && b ? a + "\n\n" + b : a + b)
@@ -640,6 +646,12 @@ function thinking(req, model) {
640
646
  // buildKiro is the body of a generateAssistantResponse call.
641
647
  function buildKiro(req, model, profile, budget) {
642
648
  const entries = []
649
+ // the messages the caller marked for its prompt cache (cache_control),
650
+ // which go to Kiro as a cachePoint on the message they end up in (#110)
651
+ const marked = new WeakSet()
652
+ const mark = (msg, parts) => {
653
+ if (parts.some((p) => p?.cache_control)) marked.add(msg)
654
+ }
643
655
  const user = () => ({ content: "", modelId: model, origin: "KIRO_CLI" })
644
656
  for (const m of req.messages ?? []) {
645
657
  const parts = typeof m.content === "string" ? [{ type: "text", text: m.content }] : m.content ?? []
@@ -660,6 +672,7 @@ function buildKiro(req, model, profile, budget) {
660
672
  prev.content = joinNonEmpty(prev.content, a.content)
661
673
  prev.toolUses.push(...a.toolUses)
662
674
  } else entries.push({ assistantResponseMessage: a })
675
+ mark(prev ?? a, parts)
663
676
  continue
664
677
  }
665
678
  const u = user()
@@ -669,12 +682,14 @@ function buildKiro(req, model, profile, budget) {
669
682
  if (p?.type === "text") {
670
683
  if (p.text) texts.push(p.text)
671
684
  } else if (p?.type === "document") texts.push(documentText(p))
672
- else if (p?.type === "image") {
673
- if (p.source?.type === "base64" && p.source.data) u.images.push({ format: imageFormat(p.source.media_type), source: { bytes: p.source.data } })
674
- } else if (p?.type === "tool_result") {
685
+ else if (p?.type === "image") addImage(u.images, p)
686
+ else if (p?.type === "tool_result") {
675
687
  let out = textOf(p.content)
676
688
  if (out.length > RESULT_LIMIT) out = out.slice(0, RESULT_LIMIT) + "\n… (cut)"
677
689
  results.push({ toolUseId: toolID(p.tool_use_id), status: p.is_error ? "error" : "success", content: [{ text: out || "(no output)" }] })
690
+ // a tool's images (a screenshot, Codex's view_image) go with the
691
+ // message, as Kiro's tool results carry text alone (magpie #369, #1861)
692
+ if (Array.isArray(p.content)) for (const c of p.content) if (c?.type === "image") addImage(u.images, c)
678
693
  }
679
694
  }
680
695
  u.content = texts.join("\n\n")
@@ -685,6 +700,7 @@ function buildKiro(req, model, profile, budget) {
685
700
  prev.images.push(...u.images)
686
701
  if (results.length) (prev.userInputMessageContext ??= { toolResults: [] }).toolResults.push(...results)
687
702
  } else entries.push({ userInputMessage: u })
703
+ mark(prev ?? u, parts)
688
704
  }
689
705
  // the conversation opens with something the user said: tool results with
690
706
  // no call before them go, and a reply the caller began with
@@ -740,6 +756,7 @@ function buildKiro(req, model, profile, budget) {
740
756
  let system = typeof req.system === "string" ? req.system : textOf(req.system)
741
757
  if (budget) system = `<thinking_mode>enabled</thinking_mode><max_thinking_length>${budget}</max_thinking_length>` + (system ? "\n" + system : "")
742
758
  if (system) entries[0].userInputMessage.content = system + "\n\n" + entries[0].userInputMessage.content
759
+ if (Array.isArray(req.system)) mark(entries[0].userInputMessage, req.system)
743
760
 
744
761
  // the caller's tools, and any the conversation used that it no longer
745
762
  // offers — Kiro rejects a history naming a tool it wasn't given
@@ -752,6 +769,7 @@ function buildKiro(req, model, profile, budget) {
752
769
  const name = toolName(t.name)
753
770
  offered.add(name)
754
771
  tools.push({ toolSpecification: { name, description: t.description || t.name, inputSchema: { json: t.input_schema ?? EMPTY } } })
772
+ if (t.cache_control) tools.push({ cachePoint: { type: "default" } })
755
773
  }
756
774
  for (const e of entries)
757
775
  for (const c of e.assistantResponseMessage?.toolUses ?? [])
@@ -760,6 +778,19 @@ function buildKiro(req, model, profile, budget) {
760
778
  tools.push({ toolSpecification: { name: c.name, description: "Tool", inputSchema: { json: EMPTY } } })
761
779
  }
762
780
  if (tools.length) (current.userInputMessageContext ??= {}).tools = tools
781
+ // Kiro's cachePoint is Bedrock's: it caches the prompt up to the point,
782
+ // and a request may hold 4 at most, as Anthropic's cache_control; past
783
+ // that the last ones are kept, the most of the prompt they cover
784
+ const points = []
785
+ for (const t of tools) if (t.cachePoint) points.push(() => tools.splice(tools.indexOf(t), 1))
786
+ for (const e of entries) {
787
+ const msg = e.userInputMessage ?? e.assistantResponseMessage
788
+ if (marked.has(msg)) {
789
+ msg.cachePoint = { type: "default" }
790
+ points.push(() => delete msg.cachePoint)
791
+ }
792
+ }
793
+ for (const drop of points.slice(0, Math.max(0, points.length - 4))) drop()
763
794
 
764
795
  const state = { chatTriggerType: "MANUAL", agentTaskType: "vibe", conversationId: randomUUID(), currentMessage: { userInputMessage: current } }
765
796
  if (entries.length > 1) state.history = entries.slice(0, -1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-kiro-auth",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Kiro subscriptions (Free, Pro, Pro+, Power) in OpenCode and magpie",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",