ns-omp-provider-kiro 0.3.8 → 0.3.9

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 CHANGED
@@ -17,8 +17,8 @@ To build from a clone instead — for developing this package or
17
17
  `ns-kiro-core` before a release — install from the path:
18
18
 
19
19
  ```bash
20
- git clone git@github.com:ngosangns/ns-kiro-provider.git
21
- cd ns-kiro-provider && pnpm install && pnpm -r build
20
+ git clone git@github.com:ngosangns/ns-bridge.git
21
+ cd ns-bridge && pnpm install && pnpm -r build
22
22
  omp plugin install ./packages/omp-provider-kiro
23
23
  ```
24
24
 
@@ -59,4 +59,7 @@ upstream shows up without a release here.
59
59
  - `KIRO_DEBUG=1` writes a full request/response trace to
60
60
  `~/.ns-kiro-provider/logs/kiro-debug.log`, with credentials redacted.
61
61
 
62
- Part of [ns-kiro-provider](https://github.com/ngosangns/ns-kiro-provider).
62
+ This is the recommended Kiro path in OMP. `ns-omp-provider` can also register a
63
+ `kiro` provider, but leaves it off by default so the two never fight over the id.
64
+
65
+ Part of [ns-bridge](https://github.com/ngosangns/ns-bridge).
package/dist/auth.d.ts CHANGED
@@ -1,31 +1,8 @@
1
1
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai";
2
- /**
3
- * v0.1 establishes no browser flow of its own. Every interactive method Kiro
4
- * supports already lands a session in the kiro-cli store or the Kiro IDE token
5
- * file, so this reads one of those and refreshes it. The one credential with no
6
- * browser step — an API key — is accepted directly.
7
- */
2
+ import { type ResolvedKiroRequestCredentials, resolveKiroRequestCredentials } from "ns-kiro-core";
3
+ export type { ResolvedKiroRequestCredentials };
8
4
  export declare function loginKiro(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
9
5
  export declare function refreshKiroCredentials(credentials: OAuthCredentials): Promise<OAuthCredentials>;
10
6
  export declare function getKiroApiKey(credentials: OAuthCredentials): string;
11
- /** The token, region, and profile one request runs on. */
12
- export interface ResolvedKiroRequestCredentials {
13
- accessToken: string;
14
- region: string;
15
- profileArn?: string;
16
- }
17
- /**
18
- * Decide which credential a request or catalog fetch runs on.
19
- *
20
- * The key OMP resolves for the provider is the session this package returned
21
- * from `/login kiro` — but it is also `$KIRO_API_KEY` verbatim when that
22
- * variable is unset, and Kiro answers a bogus bearer with a 403 the core then
23
- * has to recover from. So a host key is trusted only when it is recognizably a
24
- * Kiro credential; anything else defers to the store, which is where the
25
- * session actually lives.
26
- *
27
- * The stored record also carries the region and the profile ARN, and passing
28
- * the ARN skips the ListAvailableProfiles round trip that a social login cannot
29
- * answer anyway.
30
- */
31
- export declare function resolveRequestCredentials(hostKey: string | undefined): Promise<ResolvedKiroRequestCredentials>;
7
+ /** See {@link resolveKiroRequestCredentials}: which credential one request or catalog fetch runs on. */
8
+ export declare const resolveRequestCredentials: typeof resolveKiroRequestCredentials;
package/dist/index.js CHANGED
@@ -2862,6 +2862,72 @@ function syntheticToolResultEntry(toolUseIds, modelId) {
2862
2862
  };
2863
2863
  }
2864
2864
 
2865
+ // ../kiro-core/dist/host-auth.js
2866
+ init_endpoints();
2867
+ init_oauth();
2868
+ var KIRO_NO_SESSION_MESSAGE = "No Kiro session found. Sign in with `kiro-cli login` (Builder ID, IAM Identity Center, Google, or GitHub), then run /login kiro again \u2014 or paste a Kiro API key below.";
2869
+ async function loginKiroFromSession(callbacks) {
2870
+ callbacks.onProgress?.("Looking for an existing Kiro session\u2026");
2871
+ const existing = await resolveKiroCredentials();
2872
+ if (existing) {
2873
+ callbacks.onProgress?.(`Using the ${existing.authMethod} session from kiro-cli.`);
2874
+ return existing;
2875
+ }
2876
+ if (!callbacks.onPrompt)
2877
+ throw new Error(KIRO_NO_SESSION_MESSAGE);
2878
+ const apiKey = (await callbacks.onPrompt({
2879
+ message: KIRO_NO_SESSION_MESSAGE,
2880
+ placeholder: "ksk_\u2026",
2881
+ allowEmpty: true,
2882
+ secret: true
2883
+ })).trim();
2884
+ if (!apiKey)
2885
+ throw new Error(KIRO_NO_SESSION_MESSAGE);
2886
+ return loginKiroWithApiKey(apiKey, callbacks.onProgress);
2887
+ }
2888
+ async function resolveKiroRequestCredentials(hostKey) {
2889
+ const stored = await resolveKiroCredentials();
2890
+ const region = resolveApiRegion(stored?.region);
2891
+ if (hostKey && (isApiKey(hostKey) || hostKey === stored?.access)) {
2892
+ return { accessToken: hostKey, region, ...stored?.profileArn ? { profileArn: stored.profileArn } : {} };
2893
+ }
2894
+ if (stored) {
2895
+ return {
2896
+ accessToken: stored.access,
2897
+ region,
2898
+ ...stored.profileArn ? { profileArn: stored.profileArn } : {}
2899
+ };
2900
+ }
2901
+ if (hostKey)
2902
+ return { accessToken: hostKey, region };
2903
+ throw new Error("Kiro credentials not set. Run `kiro-cli login`, or set KIRO_API_KEY to a `ksk_` API key.");
2904
+ }
2905
+
2906
+ // ../kiro-core/dist/host-model.js
2907
+ init_models();
2908
+ function toKiroModelForHost(model, region) {
2909
+ const known = getCachedModels(region).find((candidate) => candidate.id === model.id);
2910
+ return {
2911
+ ...known ?? {
2912
+ id: model.id,
2913
+ // No fallback to `model.id`: that dash-spelled local id is not a valid
2914
+ // Kiro wire id. `""` is falsy, so `resolveKiroModel` runs its own
2915
+ // cache/bootstrap/dot-normalize resolution instead of being
2916
+ // short-circuited by a value that is really just `model.id` again.
2917
+ kiroModelId: model.kiroModelId ?? "",
2918
+ name: model.name,
2919
+ reasoning: model.reasoning,
2920
+ input: [...model.input],
2921
+ cost: { ...model.cost },
2922
+ contextWindow: model.contextWindow ?? 2e5,
2923
+ maxTokens: model.maxTokens ?? 8192
2924
+ },
2925
+ region,
2926
+ ...model.kiroProfileArn ? { profileArn: model.kiroProfileArn } : {},
2927
+ ...model.additionalModelRequestFieldsSchema ? { additionalModelRequestFieldsSchema: model.additionalModelRequestFieldsSchema } : {}
2928
+ };
2929
+ }
2930
+
2865
2931
  // ../kiro-core/dist/invoke-tool-parser.js
2866
2932
  var INVOKE_OPEN_ANCHORED = /^<invoke name="([\w-]+)">/;
2867
2933
  var INVOKE_OPEN_SEARCH = "<invoke name=";
@@ -5493,23 +5559,8 @@ async function fetchKiroUsage(credentials) {
5493
5559
  }
5494
5560
 
5495
5561
  // src/auth.ts
5496
- var NO_SESSION_MESSAGE = "No Kiro session found. Sign in with `kiro-cli login` (Builder ID, IAM Identity Center, Google, or GitHub), then run /login kiro again \u2014 or paste a Kiro API key below.";
5497
5562
  async function loginKiro(callbacks) {
5498
- callbacks.onProgress?.("Looking for an existing Kiro session\u2026");
5499
- const existing = await resolveKiroCredentials();
5500
- if (existing) {
5501
- callbacks.onProgress?.(`Using the ${existing.authMethod} session from kiro-cli.`);
5502
- return existing;
5503
- }
5504
- if (!callbacks.onPrompt) throw new Error(NO_SESSION_MESSAGE);
5505
- const apiKey = (await callbacks.onPrompt({
5506
- message: NO_SESSION_MESSAGE,
5507
- placeholder: "ksk_\u2026",
5508
- allowEmpty: true,
5509
- secret: true
5510
- })).trim();
5511
- if (!apiKey) throw new Error(NO_SESSION_MESSAGE);
5512
- return await loginKiroWithApiKey(apiKey, callbacks.onProgress);
5563
+ return await loginKiroFromSession(callbacks);
5513
5564
  }
5514
5565
  async function refreshKiroCredentials(credentials) {
5515
5566
  return await refreshKiroToken(credentials);
@@ -5517,22 +5568,7 @@ async function refreshKiroCredentials(credentials) {
5517
5568
  function getKiroApiKey(credentials) {
5518
5569
  return credentials.access;
5519
5570
  }
5520
- async function resolveRequestCredentials(hostKey) {
5521
- const stored = await resolveKiroCredentials();
5522
- const region = resolveApiRegion(stored?.region);
5523
- if (hostKey && (isApiKey(hostKey) || hostKey === stored?.access)) {
5524
- return { accessToken: hostKey, region, ...stored?.profileArn ? { profileArn: stored.profileArn } : {} };
5525
- }
5526
- if (stored) {
5527
- return {
5528
- accessToken: stored.access,
5529
- region,
5530
- ...stored.profileArn ? { profileArn: stored.profileArn } : {}
5531
- };
5532
- }
5533
- if (hostKey) return { accessToken: hostKey, region };
5534
- throw new Error("Kiro credentials not set. Run `kiro-cli login`, or set KIRO_API_KEY to a `ksk_` API key.");
5535
- }
5571
+ var resolveRequestCredentials = resolveKiroRequestCredentials;
5536
5572
 
5537
5573
  // src/models.ts
5538
5574
  function toOmpModelConfig(model) {
@@ -5564,22 +5600,81 @@ function kiroBaseUrl(region) {
5564
5600
  // src/stream.ts
5565
5601
  import * as PiAi from "@oh-my-pi/pi-ai";
5566
5602
 
5567
- // src/messages.ts
5603
+ // ../bridge-core/dist/pi/transcript.js
5604
+ function isSystemMessage(message) {
5605
+ return message?.role === "system";
5606
+ }
5607
+ function contentText(content) {
5608
+ if (typeof content === "string")
5609
+ return content;
5610
+ if (!Array.isArray(content))
5611
+ return "";
5612
+ return content.map((part) => part && typeof part === "object" && part.type === "text" ? String(part.text ?? "") : "").filter((text) => text.length > 0).join("\n");
5613
+ }
5614
+ function getCurrentTools(messages) {
5615
+ const tools = /* @__PURE__ */ new Map();
5616
+ for (const message of messages) {
5617
+ if (!isSystemMessage(message))
5618
+ continue;
5619
+ for (const tool of message.toolsRemoved ?? [])
5620
+ tools.delete(tool.name);
5621
+ for (const tool of message.toolsAdded ?? [])
5622
+ tools.set(tool.name, tool);
5623
+ }
5624
+ return [...tools.values()];
5625
+ }
5626
+ function getCurrentSystemPrompt(messages) {
5627
+ const content = [];
5628
+ const sections = /* @__PURE__ */ new Map();
5629
+ for (const message of messages) {
5630
+ if (!isSystemMessage(message))
5631
+ continue;
5632
+ const text = contentText(message.content);
5633
+ if (text.length > 0)
5634
+ content.push(text);
5635
+ for (const [name, value] of Object.entries(message.sections ?? {})) {
5636
+ if (value === null)
5637
+ sections.delete(name);
5638
+ else
5639
+ sections.set(name, value);
5640
+ }
5641
+ }
5642
+ return [content.join("\n\n"), ...sections.values()].filter((part) => part.length > 0).join("\n\n");
5643
+ }
5644
+
5645
+ // ../bridge-core/dist/pi/context.js
5568
5646
  function userContent(content) {
5569
- if (typeof content === "string") return content ? [{ type: "text", text: content }] : [];
5647
+ if (typeof content === "string")
5648
+ return content ? [{ type: "text", text: content }] : [];
5649
+ if (!Array.isArray(content))
5650
+ return [];
5570
5651
  const out = [];
5571
5652
  for (const block of content) {
5572
- if (block.type === "text") out.push({ type: "text", text: block.text });
5573
- else if (block.type === "image") {
5574
- const image = block;
5575
- out.push({ type: "image", data: image.data, mimeType: image.mimeType });
5653
+ if (block?.type === "text" && typeof block.text === "string")
5654
+ out.push({ type: "text", text: block.text });
5655
+ else if (block?.type === "image" && typeof block.data === "string") {
5656
+ out.push({ type: "image", data: block.data, mimeType: String(block.mimeType ?? "image/png") });
5576
5657
  }
5577
5658
  }
5578
5659
  return out;
5579
5660
  }
5580
- function toKiroMessages(messages) {
5661
+ var STOP_REASONS = /* @__PURE__ */ new Set(["stop", "length", "toolUse", "error", "aborted"]);
5662
+ function parseArguments(value) {
5663
+ if (typeof value === "string") {
5664
+ try {
5665
+ const parsed = JSON.parse(value || "{}");
5666
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
5667
+ } catch {
5668
+ return {};
5669
+ }
5670
+ }
5671
+ return value && typeof value === "object" ? value : {};
5672
+ }
5673
+ function toBridgeMessages(messages) {
5581
5674
  const out = [];
5582
5675
  for (const message of messages) {
5676
+ if (message.role === "system")
5677
+ continue;
5583
5678
  if (message.role === "user" || message.role === "developer") {
5584
5679
  out.push({ role: "user", content: userContent(message.content) });
5585
5680
  continue;
@@ -5587,103 +5682,147 @@ function toKiroMessages(messages) {
5587
5682
  if (message.role === "toolResult") {
5588
5683
  out.push({
5589
5684
  role: "toolResult",
5590
- toolCallId: message.toolCallId,
5591
- toolName: message.toolName,
5685
+ toolCallId: String(message.toolCallId ?? ""),
5686
+ toolName: String(message.toolName ?? "tool"),
5592
5687
  content: userContent(message.content),
5593
- isError: message.isError
5688
+ isError: message.isError === true
5594
5689
  });
5595
5690
  continue;
5596
5691
  }
5692
+ if (message.role !== "assistant")
5693
+ continue;
5597
5694
  const content = [];
5598
- for (const block of message.content) {
5599
- if (block.type === "text") content.push({ type: "text", text: block.text });
5600
- else if (block.type === "thinking") {
5601
- const thinking = block;
5695
+ for (const block of Array.isArray(message.content) ? message.content : []) {
5696
+ if (block?.type === "text" && typeof block.text === "string")
5697
+ content.push({ type: "text", text: block.text });
5698
+ else if (block?.type === "thinking" && typeof block.thinking === "string") {
5602
5699
  content.push({
5603
5700
  type: "thinking",
5604
- thinking: thinking.thinking,
5605
- ...thinking.thinkingSignature ? { thinkingSignature: thinking.thinkingSignature } : {}
5701
+ thinking: block.thinking,
5702
+ ...typeof block.thinkingSignature === "string" && block.thinkingSignature ? { thinkingSignature: block.thinkingSignature } : {}
5606
5703
  });
5607
- } else if (block.type === "toolCall") {
5608
- const call = block;
5704
+ } else if (block?.type === "toolCall") {
5609
5705
  content.push({
5610
5706
  type: "toolCall",
5611
- id: call.id,
5612
- name: call.name,
5613
- arguments: typeof call.arguments === "string" ? JSON.parse(call.arguments) : call.arguments
5707
+ id: String(block.id ?? ""),
5708
+ name: String(block.name ?? ""),
5709
+ arguments: parseArguments(block.arguments)
5614
5710
  });
5615
5711
  }
5616
5712
  }
5617
- out.push({ role: "assistant", content, stopReason: message.stopReason });
5713
+ out.push({
5714
+ role: "assistant",
5715
+ content,
5716
+ ...message.stopReason && STOP_REASONS.has(message.stopReason) ? { stopReason: message.stopReason } : {},
5717
+ ...typeof message.responseId === "string" && message.responseId ? { responseId: message.responseId } : {}
5718
+ });
5618
5719
  }
5619
5720
  return out;
5620
5721
  }
5621
- function toKiroTools(tools) {
5622
- if (!tools?.length) return void 0;
5722
+ function toBridgeTools(tools) {
5723
+ if (!tools?.length)
5724
+ return void 0;
5623
5725
  return tools.map((tool) => ({
5624
5726
  name: tool.name,
5625
- description: tool.description,
5626
- parameters: tool.parameters
5727
+ description: tool.description ?? "",
5728
+ parameters: tool.parameters ?? { type: "object", properties: {} },
5729
+ ...tool.strict !== void 0 ? { strict: tool.strict } : {}
5627
5730
  }));
5628
5731
  }
5732
+ function resolveSystemPrompt(context) {
5733
+ const parts = [];
5734
+ const declared = context.systemPrompt;
5735
+ if (typeof declared === "string")
5736
+ parts.push(declared);
5737
+ else if (Array.isArray(declared))
5738
+ parts.push(...declared.filter((part) => typeof part === "string"));
5739
+ const fromTranscript = getCurrentSystemPrompt(context.messages);
5740
+ if (fromTranscript)
5741
+ parts.push(fromTranscript);
5742
+ const joined = parts.filter((part) => part.trim().length > 0).join("\n\n");
5743
+ return joined || void 0;
5744
+ }
5745
+ function resolveTools(context) {
5746
+ if (context.tools?.length)
5747
+ return [...context.tools];
5748
+ return getCurrentTools(context.messages);
5749
+ }
5750
+ function toBridgeContext(context) {
5751
+ const systemPrompt = resolveSystemPrompt(context);
5752
+ const tools = toBridgeTools(resolveTools(context));
5753
+ return {
5754
+ ...systemPrompt ? { systemPrompt } : {},
5755
+ messages: toBridgeMessages(context.messages),
5756
+ ...tools ? { tools } : {}
5757
+ };
5758
+ }
5629
5759
 
5630
- // src/stream.ts
5631
- function newEventStream() {
5632
- const runtime = PiAi;
5633
- if (typeof runtime.AssistantMessageEventStream === "function") return new runtime.AssistantMessageEventStream();
5634
- if (typeof runtime.createAssistantMessageEventStream === "function") {
5635
- return runtime.createAssistantMessageEventStream();
5760
+ // ../bridge-core/dist/types.js
5761
+ var BRIDGE_EFFORT_ORDER = ["minimal", "low", "medium", "high", "xhigh", "max"];
5762
+
5763
+ // ../bridge-core/dist/pi/models.js
5764
+ var EFFORTS = new Set(BRIDGE_EFFORT_ORDER);
5765
+ function toBridgeEffort(reasoning) {
5766
+ return typeof reasoning === "string" && EFFORTS.has(reasoning) ? reasoning : void 0;
5767
+ }
5768
+
5769
+ // ../bridge-core/dist/errors.js
5770
+ var SECRET_PATTERNS = [
5771
+ /(authorization\s*[:=]\s*)(bearer\s+)?[^\s,;"']+/gi,
5772
+ /(bearer\s+)[A-Za-z0-9._~+/=$-]{12,}/gi,
5773
+ /\b(ksk_|devin-session-token\$|sk-)[A-Za-z0-9._~+/=$-]{8,}/g
5774
+ ];
5775
+ function formatErrorMessage(error2) {
5776
+ let text = error2 instanceof Error ? error2.message || error2.name : String(error2);
5777
+ for (const pattern of SECRET_PATTERNS) {
5778
+ text = text.replace(pattern, (_match, prefix) => `${prefix ?? ""}[redacted]`);
5636
5779
  }
5637
- throw new Error("This omp build exposes no AssistantMessageEventStream; omp-provider-kiro needs omp >= 18.1.");
5780
+ return text;
5638
5781
  }
5639
- var ZERO_USAGE = () => ({
5640
- input: 0,
5641
- output: 0,
5642
- cacheRead: 0,
5643
- cacheWrite: 0,
5644
- totalTokens: 0,
5645
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
5646
- });
5647
- function toKiroModel(model, region) {
5648
- const known = getCachedModels(region).find((candidate) => candidate.id === model.id);
5782
+
5783
+ // ../bridge-core/dist/pi/stream.js
5784
+ function zeroPiUsage() {
5649
5785
  return {
5650
- ...known ?? {
5651
- id: model.id,
5652
- // No fallback to `model.id`: that dash-spelled local id is not a valid
5653
- // Kiro wire id. Falls back to `""` rather than `model.id` — falsy, so
5654
- // `resolveKiroModel`'s `if (exactKiroModelId) return exactKiroModelId`
5655
- // treats it as no override and runs its own cache/bootstrap/dot-normalize
5656
- // resolution instead of being short-circuited by a value that is really
5657
- // just `model.id` again.
5658
- kiroModelId: model.kiroModelId ?? "",
5659
- name: model.name,
5660
- reasoning: model.reasoning,
5661
- input: [...model.input],
5662
- cost: { ...model.cost },
5663
- contextWindow: model.contextWindow ?? 2e5,
5664
- maxTokens: model.maxTokens ?? 8192
5665
- },
5666
- region,
5667
- ...model.kiroProfileArn ? { profileArn: model.kiroProfileArn } : {},
5668
- ...model.additionalModelRequestFieldsSchema ? { additionalModelRequestFieldsSchema: model.additionalModelRequestFieldsSchema } : {}
5786
+ input: 0,
5787
+ output: 0,
5788
+ cacheRead: 0,
5789
+ cacheWrite: 0,
5790
+ totalTokens: 0,
5791
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
5669
5792
  };
5670
5793
  }
5671
- function streamKiroForOmp(model, context, options) {
5672
- const stream = newEventStream();
5794
+ function applyBridgeUsage(target, usage) {
5795
+ target.input = usage.input;
5796
+ target.output = usage.output;
5797
+ target.totalTokens = usage.totalTokens;
5798
+ target.cacheRead = usage.cacheRead ?? 0;
5799
+ target.cacheWrite = usage.cacheWrite ?? 0;
5800
+ target.cost = { ...usage.cost };
5801
+ if (usage.contextPercent !== void 0)
5802
+ target.contextPercent = usage.contextPercent;
5803
+ if (usage.credits !== void 0)
5804
+ target.credits = usage.credits;
5805
+ if (usage.creditUnit !== void 0)
5806
+ target.creditUnit = usage.creditUnit;
5807
+ if (usage.cacheEstimated !== void 0)
5808
+ target.cacheEstimated = usage.cacheEstimated;
5809
+ }
5810
+ function streamToPi(options) {
5811
+ const stream = options.createStream();
5812
+ const formatError = options.formatError ?? formatErrorMessage;
5813
+ const discardOnReset = options.discardOnReset ?? true;
5814
+ const output = {
5815
+ role: "assistant",
5816
+ content: [],
5817
+ api: options.model.api,
5818
+ provider: options.model.provider,
5819
+ model: options.model.id,
5820
+ usage: zeroPiUsage(),
5821
+ stopReason: "stop",
5822
+ timestamp: Date.now()
5823
+ };
5673
5824
  (async () => {
5674
- const output = {
5675
- role: "assistant",
5676
- content: [],
5677
- api: model.api,
5678
- provider: model.provider,
5679
- model: model.id,
5680
- usage: ZERO_USAGE(),
5681
- stopReason: "stop",
5682
- timestamp: Date.now()
5683
- };
5684
5825
  try {
5685
- const hostKey = typeof options?.apiKey === "string" ? options.apiKey : void 0;
5686
- const credentials = await resolveRequestCredentials(hostKey);
5687
5826
  let indexes = /* @__PURE__ */ new Map();
5688
5827
  const open = (coreIndex, block) => {
5689
5828
  const contentIndex = output.content.length;
@@ -5692,121 +5831,114 @@ function streamKiroForOmp(model, context, options) {
5692
5831
  return contentIndex;
5693
5832
  };
5694
5833
  const at = (coreIndex) => indexes.get(coreIndex);
5695
- const region = model.kiroRegion ?? getKiroRegionFromEndpoint(model.baseUrl) ?? credentials.region;
5696
- for await (const event of streamKiro({
5697
- model: toKiroModel(model, region),
5698
- messages: toKiroMessages(context.messages),
5699
- systemPrompt: context.systemPrompt?.join("\n\n"),
5700
- tools: toKiroTools(context.tools),
5701
- effort: options?.reasoning,
5702
- accessToken: credentials.accessToken,
5703
- sessionId: options?.sessionId,
5704
- signal: options?.signal,
5705
- profileArn: model.kiroProfileArn ?? credentials.profileArn,
5706
- // OMP renders from `partial`, so discarding blocks is a matter of
5707
- // truncating the array the renderer already reads.
5708
- canDiscardEmittedBlocks: true
5709
- })) {
5834
+ let started = false;
5835
+ const start = () => {
5836
+ if (started)
5837
+ return;
5838
+ started = true;
5839
+ stream.push({ type: "start", partial: output });
5840
+ };
5841
+ for await (const event of await options.events()) {
5710
5842
  switch (event.type) {
5711
5843
  case "start":
5712
- stream.push({ type: "start", partial: output });
5844
+ start();
5713
5845
  break;
5714
5846
  case "reset":
5715
- output.content = [];
5716
- indexes = /* @__PURE__ */ new Map();
5847
+ if (discardOnReset) {
5848
+ output.content = [];
5849
+ indexes = /* @__PURE__ */ new Map();
5850
+ }
5717
5851
  break;
5718
5852
  case "text_start": {
5853
+ start();
5719
5854
  const contentIndex = open(event.index, { type: "text", text: "" });
5720
5855
  stream.push({ type: "text_start", contentIndex, partial: output });
5721
5856
  break;
5722
5857
  }
5723
5858
  case "text_delta": {
5724
5859
  const contentIndex = at(event.index);
5725
- if (contentIndex === void 0) break;
5860
+ if (contentIndex === void 0)
5861
+ break;
5726
5862
  output.content[contentIndex].text += event.delta;
5727
5863
  stream.push({ type: "text_delta", contentIndex, delta: event.delta, partial: output });
5728
5864
  break;
5729
5865
  }
5730
5866
  case "text_end": {
5731
5867
  const contentIndex = at(event.index);
5732
- if (contentIndex === void 0) break;
5868
+ if (contentIndex === void 0)
5869
+ break;
5733
5870
  output.content[contentIndex].text = event.text;
5734
5871
  stream.push({ type: "text_end", contentIndex, content: event.text, partial: output });
5735
5872
  break;
5736
5873
  }
5737
5874
  case "thinking_start": {
5875
+ start();
5738
5876
  const contentIndex = open(event.index, { type: "thinking", thinking: "" });
5739
5877
  stream.push({ type: "thinking_start", contentIndex, partial: output });
5740
5878
  break;
5741
5879
  }
5742
5880
  case "thinking_delta": {
5743
5881
  const contentIndex = at(event.index);
5744
- if (contentIndex === void 0) break;
5882
+ if (contentIndex === void 0)
5883
+ break;
5745
5884
  output.content[contentIndex].thinking += event.delta;
5746
5885
  stream.push({ type: "thinking_delta", contentIndex, delta: event.delta, partial: output });
5747
5886
  break;
5748
5887
  }
5749
5888
  case "thinking_end": {
5750
5889
  const contentIndex = at(event.index);
5751
- if (contentIndex === void 0) break;
5890
+ if (contentIndex === void 0)
5891
+ break;
5752
5892
  const block = output.content[contentIndex];
5753
5893
  block.thinking = event.thinking;
5754
- if (event.signature) block.thinkingSignature = event.signature;
5894
+ if (event.signature)
5895
+ block.thinkingSignature = event.signature;
5755
5896
  stream.push({ type: "thinking_end", contentIndex, content: event.thinking, partial: output });
5756
5897
  break;
5757
5898
  }
5758
5899
  case "tool_call_start": {
5759
- const toolCall = { type: "toolCall", id: event.id, name: event.name, arguments: {} };
5760
- const contentIndex = open(event.index, toolCall);
5900
+ start();
5901
+ const contentIndex = open(event.index, { type: "toolCall", id: event.id, name: event.name, arguments: {} });
5761
5902
  stream.push({ type: "toolcall_start", contentIndex, partial: output });
5762
5903
  break;
5763
5904
  }
5764
5905
  case "tool_call_delta": {
5765
5906
  const contentIndex = at(event.index);
5766
- if (contentIndex === void 0) break;
5907
+ if (contentIndex === void 0)
5908
+ break;
5767
5909
  stream.push({ type: "toolcall_delta", contentIndex, delta: event.argumentsDelta, partial: output });
5768
5910
  break;
5769
5911
  }
5770
5912
  case "tool_call_end": {
5771
5913
  const contentIndex = at(event.index);
5772
- if (contentIndex === void 0) break;
5914
+ if (contentIndex === void 0)
5915
+ break;
5773
5916
  const toolCall = output.content[contentIndex];
5917
+ toolCall.name = event.name;
5774
5918
  toolCall.arguments = event.arguments;
5775
5919
  stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: output });
5776
5920
  break;
5777
5921
  }
5778
- case "usage": {
5779
- output.usage.input = event.usage.input;
5780
- output.usage.output = event.usage.output;
5781
- output.usage.totalTokens = event.usage.totalTokens;
5782
- output.usage.cacheRead = event.usage.cacheRead ?? 0;
5783
- output.usage.cacheWrite = event.usage.cacheWrite ?? 0;
5784
- output.usage.cost = { ...event.usage.cost };
5785
- if (event.usage.contextPercent !== void 0) {
5786
- output.usage.contextPercent = event.usage.contextPercent;
5787
- }
5788
- if (event.usage.credits !== void 0) {
5789
- output.usage.credits = event.usage.credits;
5790
- }
5791
- if (event.usage.creditUnit !== void 0) {
5792
- output.usage.creditUnit = event.usage.creditUnit;
5793
- }
5794
- if (event.usage.cacheEstimated !== void 0) {
5795
- output.usage.cacheEstimated = event.usage.cacheEstimated;
5796
- }
5922
+ case "usage":
5923
+ applyBridgeUsage(output.usage, event.usage);
5797
5924
  break;
5798
- }
5799
5925
  case "done":
5926
+ start();
5800
5927
  output.stopReason = event.stopReason;
5801
- if (event.errorMessage) output.errorMessage = event.errorMessage;
5928
+ if (event.responseId)
5929
+ output.responseId = event.responseId;
5930
+ if (event.upstreamModel && event.upstreamModel !== output.model)
5931
+ output.responseModel = event.upstreamModel;
5932
+ if (event.errorMessage)
5933
+ output.errorMessage = event.errorMessage;
5802
5934
  stream.push({ type: "done", reason: event.stopReason, message: output });
5803
5935
  break;
5804
5936
  }
5805
5937
  }
5806
5938
  stream.end();
5807
5939
  } catch (error2) {
5808
- output.stopReason = options?.signal?.aborted ? "aborted" : "error";
5809
- output.errorMessage = formatSafeError(error2);
5940
+ output.stopReason = options.signal?.aborted ? "aborted" : "error";
5941
+ output.errorMessage = formatError(error2);
5810
5942
  stream.push({ type: "error", reason: output.stopReason, error: output });
5811
5943
  stream.end();
5812
5944
  }
@@ -5819,6 +5951,48 @@ function streamKiroForOmp(model, context, options) {
5819
5951
  return stream;
5820
5952
  }
5821
5953
 
5954
+ // src/stream.ts
5955
+ function newEventStream() {
5956
+ const runtime = PiAi;
5957
+ if (typeof runtime.AssistantMessageEventStream === "function") return new runtime.AssistantMessageEventStream();
5958
+ if (typeof runtime.createAssistantMessageEventStream === "function") {
5959
+ return runtime.createAssistantMessageEventStream();
5960
+ }
5961
+ throw new Error("This omp build exposes no AssistantMessageEventStream; omp-provider-kiro needs omp >= 18.1.");
5962
+ }
5963
+ function toKiroModel(model, region) {
5964
+ return toKiroModelForHost(model, region);
5965
+ }
5966
+ function streamKiroForOmp(model, context, options) {
5967
+ return streamToPi({
5968
+ model,
5969
+ createStream: newEventStream,
5970
+ signal: options?.signal,
5971
+ formatError: formatSafeError,
5972
+ // OMP renders from `partial`, so discarding blocks is a matter of
5973
+ // truncating the array the renderer already reads.
5974
+ discardOnReset: true,
5975
+ events: async () => {
5976
+ const hostKey = typeof options?.apiKey === "string" ? options.apiKey : void 0;
5977
+ const credentials = await resolveRequestCredentials(hostKey);
5978
+ const region = model.kiroRegion ?? getKiroRegionFromEndpoint(model.baseUrl) ?? credentials.region;
5979
+ const neutral = toBridgeContext(context);
5980
+ return streamKiro({
5981
+ model: toKiroModel(model, region),
5982
+ messages: neutral.messages,
5983
+ systemPrompt: neutral.systemPrompt,
5984
+ tools: neutral.tools,
5985
+ effort: toBridgeEffort(options?.reasoning),
5986
+ accessToken: credentials.accessToken,
5987
+ sessionId: options?.sessionId,
5988
+ signal: options?.signal,
5989
+ profileArn: model.kiroProfileArn ?? credentials.profileArn,
5990
+ canDiscardEmittedBlocks: true
5991
+ });
5992
+ }
5993
+ });
5994
+ }
5995
+
5822
5996
  // src/usage.ts
5823
5997
  function toUsageUnit(unit) {
5824
5998
  switch (unit?.toLowerCase()) {
@@ -5960,8 +6134,8 @@ export {
5960
6134
  index_default as default,
5961
6135
  kiroUsageProvider,
5962
6136
  streamKiroForOmp,
5963
- toKiroMessages,
5964
- toKiroTools,
6137
+ toBridgeMessages as toKiroMessages,
6138
+ toBridgeTools as toKiroTools,
5965
6139
  toOmpModelConfig,
5966
6140
  toUsageReport
5967
6141
  };
@@ -1,10 +1 @@
1
- import type { Message, Tool } from "@oh-my-pi/pi-ai";
2
- import type { KiroMessage, KiroTool } from "ns-kiro-core";
3
- /**
4
- * OMP carries block kinds this provider has no wire slot for — redacted
5
- * thinking, Anthropic fallback markers, verbatim server-tool blocks. They are
6
- * dropped rather than flattened into text: Kiro would read invented markup back
7
- * as the model's own prior speech.
8
- */
9
- export declare function toKiroMessages(messages: Message[]): KiroMessage[];
10
- export declare function toKiroTools(tools: Tool[] | undefined): KiroTool[] | undefined;
1
+ export { toBridgeMessages as toKiroMessages, toBridgeTools as toKiroTools } from "ns-bridge-core/pi";
package/dist/stream.d.ts CHANGED
@@ -7,18 +7,6 @@ export type KiroBackedModel = Model<Api> & {
7
7
  kiroProfileArn?: string;
8
8
  additionalModelRequestFieldsSchema?: Record<string, unknown>;
9
9
  };
10
- /**
11
- * Rebuild the core's model descriptor from what OMP hands back at request time.
12
- *
13
- * OMP's registry keeps its own `Model`, built from the config this provider
14
- * registered, so the catalog entry is looked up again here rather than carried
15
- * through: the ladder and the request-fields schema decide effort mapping, and
16
- * a request must use the same ones the catalog advertised.
17
- *
18
- * Looks up the region's authenticated cache, not the static bootstrap list:
19
- * models discovered only through the authenticated catalog (no bootstrap
20
- * entry) would otherwise miss this lookup and fall through to guessing a wire
21
- * id from the dash-spelled local `id`, which Kiro rejects as `INVALID_MODEL_ID`.
22
- */
10
+ /** Rebuild the core's model descriptor from what OMP hands back at request time (see kiro-core). */
23
11
  export declare function toKiroModel(model: KiroBackedModel, region: string): KiroModel;
24
12
  export declare function streamKiroForOmp(model: Model<Api>, context: Context, options?: SimpleStreamOptions): AssistantMessageEventStream;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ns-omp-provider-kiro",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "OMP (oh-my-pi) provider plugin: adds Kiro (AWS CodeWhisperer/Q) models via your kiro-cli login",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -8,7 +8,7 @@
8
8
  "license": "MIT",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "git+https://github.com/ngosangns/ns-kiro-provider.git",
11
+ "url": "git+https://github.com/ngosangns/ns-bridge.git",
12
12
  "directory": "packages/omp-provider-kiro"
13
13
  },
14
14
  "keywords": [
@@ -37,7 +37,7 @@
37
37
  "README.md"
38
38
  ],
39
39
  "dependencies": {
40
- "ns-kiro-core": "0.3.8"
40
+ "ns-kiro-core": "0.3.9"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@oh-my-pi/pi-ai": ">=18.2.0",
@@ -54,7 +54,8 @@
54
54
  "devDependencies": {
55
55
  "@oh-my-pi/pi-ai": "18.2.7",
56
56
  "@oh-my-pi/pi-coding-agent": "18.2.7",
57
- "@types/node": "^22.20.4"
57
+ "@types/node": "^22.20.4",
58
+ "ns-bridge-core": "0.1.0"
58
59
  },
59
60
  "scripts": {
60
61
  "build": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --banner:js=\"import{createRequire as __kiroCreateRequire}from'node:module';const require=__kiroCreateRequire(import.meta.url);\" --external:@oh-my-pi/pi-ai --external:@oh-my-pi/pi-catalog --external:@oh-my-pi/pi-coding-agent --external:@oh-my-pi/pi-tui && tsc -p tsconfig.json --emitDeclarationOnly",