ns-omp-provider 0.2.0 → 0.3.0

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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ - **Breaking: `kiro` is now opt-in.** By default only `devin` and `grok` (+ `grok-sdk`)
6
+ are registered, so this package no longer shadows the dedicated
7
+ [`ns-omp-provider-kiro`](https://www.npmjs.com/package/ns-omp-provider-kiro) plugin
8
+ (the recommended Kiro path in OMP). Opt in with `NS_OMP_PROVIDER_ENABLE=kiro` or by
9
+ listing it in `NS_OMP_PROVIDERS` (e.g. `NS_OMP_PROVIDERS=devin,grok,kiro`).
10
+ `NS_OMP_PROVIDER_DISABLE` still works and is applied last; the old
11
+ `NS_OMP_PROVIDER_DISABLE=kiro` workaround is no longer needed.
12
+ - Bundles `@ngosangns/ns-pi-provider@0.2.1`: Kiro now honours `metadataEvent` stop
13
+ reasons — `MAX_TOKENS` → `length` (OMP skips truncated tool calls instead of running
14
+ them with partial arguments), `CONTENT_FILTERED` / `MODEL_CONTEXT_WINDOW_EXCEEDED` /
15
+ `PAUSE_TURN` and mid-stream exception frames end as errors, and reported token usage
16
+ is read. Only relevant when `kiro` is opted in.
17
+
3
18
  ## 0.2.0
4
19
 
5
20
  Bundles `@ngosangns/ns-pi-provider@0.2.0` (was 0.1.7).
package/README.md CHANGED
@@ -10,7 +10,7 @@ compat layer.
10
10
 
11
11
  | Provider | Auth | Notes |
12
12
  |----------|------|-------|
13
- | `kiro` | OAuth (`/login kiro`) + `KIRO_API_KEY` / SSO cache | Overlaps with [`ns-omp-provider-kiro`](https://www.npmjs.com/package/ns-omp-provider-kiro) — see below |
13
+ | `kiro` (**opt-in**) | OAuth (`/login kiro`) + `KIRO_API_KEY` / SSO cache | Off by default — use [`ns-omp-provider-kiro`](https://www.npmjs.com/package/ns-omp-provider-kiro); see below |
14
14
  | `devin` | OAuth (`/login devin`) + `credentials.toml` / env | Swe-2 catalog when authenticated |
15
15
  | `grok` (+ `grok-sdk` alias) | Local `grok` CLI / `~/.grok` / `XAI_API_KEY` | Commands: `/grok status \| models \| refresh` |
16
16
 
@@ -36,17 +36,24 @@ omp -e ./dist/index.js ...
36
36
  Then `/login kiro` or `/login devin` as needed. Grok uses the local `grok`
37
37
  CLI / `~/.grok` auth (or `XAI_API_KEY`).
38
38
 
39
- ### Avoiding a duplicate `kiro` provider
39
+ ### Choosing providers (`kiro` is opt-in)
40
40
 
41
- If you already use [`ns-omp-provider-kiro`](https://www.npmjs.com/package/ns-omp-provider-kiro),
42
- disable this package's kiro registration:
41
+ By default this package registers **`devin`** and **`grok`** (+ `grok-sdk`) only.
42
+ For Kiro in OMP, the recommended path is the dedicated
43
+ [`ns-omp-provider-kiro`](https://www.npmjs.com/package/ns-omp-provider-kiro) plugin;
44
+ this package's `kiro` stays off so it never shadows that plugin.
43
45
 
44
46
  ```bash
45
- # in the shell that launches omp, or via your shell profile / omp env
46
- export NS_OMP_PROVIDER_DISABLE=kiro
47
+ # opt in to this package's kiro (in the environment that launches omp)
48
+ export NS_OMP_PROVIDER_ENABLE=kiro
49
+ # or allow-list exactly what you want
50
+ export NS_OMP_PROVIDERS=devin,grok,kiro
51
+ # deny-list, applied last (e.g. drop grok)
52
+ export NS_OMP_PROVIDER_DISABLE=grok
47
53
  ```
48
54
 
49
- Or allow-list only what you want: `NS_OMP_PROVIDERS=devin,grok`.
55
+ Don't opt in to `kiro` while `ns-omp-provider-kiro` is installed — both would
56
+ register the same `kiro` provider id.
50
57
 
51
58
  ## How the wrapper works
52
59
 
@@ -72,16 +79,16 @@ Or allow-list only what you want: `NS_OMP_PROVIDERS=devin,grok`.
72
79
 
73
80
  - **`session_info_changed`** — OMP has no equivalent; the grok handler is a
74
81
  documented no-op (name changes do not re-key the agent pool).
75
- - **Duplicate `kiro`** — see `NS_OMP_PROVIDER_DISABLE` above.
82
+ - **Duplicate `kiro`** — `kiro` is opt-in; don't enable it alongside `ns-omp-provider-kiro`.
76
83
  - **Unauthenticated catalogs** — without credentials, OMP may hide a provider's
77
84
  models from `omp models` even though the provider is registered. After
78
85
  `/login` (or with CLI creds present) the live catalog, including Devin
79
86
  `swe-2-*`, appears via `fetchDynamicModels`.
80
- - **Kiro catalog** — models come only from Kiro's `ListAvailableModels`
87
+ - **Kiro catalog** (when opted in) — models come only from Kiro's `ListAvailableModels`
81
88
  (since ns-pi-provider 0.2.0): with no Kiro credential and no saved catalog
82
89
  snapshot, `kiro` registers zero models, and `-1m` long-context variants are
83
90
  not offered.
84
- - **Upstream pin** — this release bundles `@ngosangns/ns-pi-provider@0.2.0`.
91
+ - **Upstream pin** — this release bundles `@ngosangns/ns-pi-provider@0.2.1`.
85
92
  Bumping it is a deliberate change (rebuild + retest).
86
93
 
87
94
  ## Development
@@ -99,14 +106,14 @@ your real `~/.omp`.
99
106
 
100
107
  ## Publish
101
108
 
102
- Push a tag matching `package.json` (e.g. `v0.2.0`) to run
109
+ Push a tag matching `package.json` (e.g. `v0.3.0`) to run
103
110
  [`.github/workflows/publish.yml`](.github/workflows/publish.yml), which
104
111
  publishes with **provenance** from a GitHub-hosted runner:
105
112
 
106
113
  ```bash
107
114
  # bump "version" in package.json first, then
108
115
  npm run check
109
- git tag v0.2.0 && git push origin v0.2.0
116
+ git tag v0.3.0 && git push origin v0.3.0
110
117
  ```
111
118
 
112
119
  Auth, in the order npm tries it:
package/dist/config.d.ts CHANGED
@@ -3,8 +3,17 @@ export declare const PROVIDER_KEYS: readonly ["kiro", "devin", "grok"];
3
3
  export type ProviderKey = (typeof PROVIDER_KEYS)[number];
4
4
  export type ProviderSelection = Record<ProviderKey, boolean>;
5
5
  /**
6
- * - `NS_OMP_PROVIDERS=devin,grok` — allow-list (only these are registered).
7
- * - `NS_OMP_PROVIDER_DISABLE=kiro` — deny-list, applied after the allow-list.
8
- * Default: all three.
6
+ * Providers that stay off unless explicitly requested. `kiro` is opt-in because
7
+ * the dedicated `ns-omp-provider-kiro` plugin is the recommended Kiro path in
8
+ * OMP; registering a second `kiro` here would shadow it.
9
+ */
10
+ export declare const OPT_IN_PROVIDERS: ReadonlySet<ProviderKey>;
11
+ /**
12
+ * - `NS_OMP_PROVIDERS=devin,grok,kiro` — allow-list (only these are registered;
13
+ * listing `kiro` here opts it in).
14
+ * - `NS_OMP_PROVIDER_ENABLE=kiro` — opt in to opt-in providers on top of the
15
+ * default set (or the allow-list).
16
+ * - `NS_OMP_PROVIDER_DISABLE=grok` — deny-list, applied last.
17
+ * Default: `devin` and `grok`; `kiro` is opt-in.
9
18
  */
10
19
  export declare function resolveProviderSelection(env?: NodeJS.ProcessEnv): ProviderSelection;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // ns-omp-provider 0.2.0 — bundles @ngosangns/ns-pi-provider@0.2.0 (MIT). See NOTICE.
1
+ // ns-omp-provider 0.3.0 — bundles @ngosangns/ns-pi-provider@0.2.1 (MIT). See NOTICE.
2
2
  import { createRequire as __nsOmpCreateRequire } from 'node:module';
3
3
  const require = __nsOmpCreateRequire(import.meta.url);
4
4
  var __defProp = Object.defineProperty;
@@ -882,6 +882,65 @@ function buildUsage(model, tokens) {
882
882
  calculateCost(model, usage);
883
883
  return usage;
884
884
  }
885
+ function optionalCount(value2) {
886
+ return typeof value2 === "number" && Number.isFinite(value2) && value2 >= 0 ? value2 : void 0;
887
+ }
888
+ function firstCount(record2, keys) {
889
+ for (const key of keys) {
890
+ const value2 = optionalCount(record2[key]);
891
+ if (value2 !== void 0) return value2;
892
+ }
893
+ return void 0;
894
+ }
895
+ function applyKiroMetadata(state2, model, payload) {
896
+ const metadata = isRecord(payload.metadataEvent) ? payload.metadataEvent : payload;
897
+ const stopReason = nonEmptyString(metadata.stopReason);
898
+ if (stopReason) state2.wireStopReason = stopReason;
899
+ if (isRecord(metadata.stopDetails)) state2.wireStopDetails = metadata.stopDetails;
900
+ const tokenUsage = isRecord(metadata.tokenUsage) ? metadata.tokenUsage : void 0;
901
+ if (!tokenUsage) return;
902
+ const percentage2 = numberFrom(tokenUsage.contextUsagePercentage);
903
+ if (percentage2 > 0) state2.contextUsagePercentage = percentage2;
904
+ const next = {
905
+ input: optionalCount(tokenUsage.uncachedInputTokens) ?? optionalCount(tokenUsage.inputTokens),
906
+ output: optionalCount(tokenUsage.outputTokens),
907
+ cacheRead: firstCount(tokenUsage, CACHE_READ_KEYS),
908
+ cacheWrite: firstCount(tokenUsage, CACHE_WRITE_KEYS)
909
+ };
910
+ const merged = { ...state2.wireTokens ?? {} };
911
+ for (const [key, value2] of Object.entries(next)) {
912
+ if (value2 !== void 0) merged[key] = value2;
913
+ }
914
+ state2.wireTokens = merged;
915
+ const tokens = { input: merged.input ?? 0, output: merged.output ?? 0, cacheRead: merged.cacheRead ?? 0, cacheWrite: merged.cacheWrite ?? 0 };
916
+ if (tokens.input > 0 || tokens.output > 0 || tokens.cacheRead > 0 || tokens.cacheWrite > 0) {
917
+ state2.usage = buildUsage(model, tokens);
918
+ }
919
+ }
920
+ function clampForDiagnostic(text2) {
921
+ return text2.length <= DIAGNOSTIC_QUOTE_LIMIT ? text2 : `${text2.slice(0, DIAGNOSTIC_QUOTE_LIMIT)}\u2026 (truncated)`;
922
+ }
923
+ function throwOnTerminalWireStop(state2) {
924
+ switch (state2.wireStopReason) {
925
+ case "MODEL_CONTEXT_WINDOW_EXCEEDED":
926
+ throw new Error("Kiro API error: context_length_exceeded (MODEL_CONTEXT_WINDOW_EXCEEDED): the model context window exceeded its limit");
927
+ case "CONTENT_FILTERED":
928
+ throw new Error(`Kiro content filtered (CONTENT_FILTERED): ${clampForDiagnostic(redactSensitiveString(JSON.stringify(state2.wireStopDetails ?? {})))}`);
929
+ case "PAUSE_TURN":
930
+ throw new Error("Kiro paused the turn (PAUSE_TURN); automatic continuation is not supported");
931
+ }
932
+ }
933
+ function resolveStopReason(state2) {
934
+ if (state2.wireStopReason === "MAX_TOKENS") return "length";
935
+ return state2.hasToolCalls ? "toolUse" : "stop";
936
+ }
937
+ function throwOnExceptionFrame(headers, payload) {
938
+ const messageType = headers[":message-type"];
939
+ if (messageType !== "exception" && messageType !== "error") return;
940
+ const kind = headers[":exception-type"] ?? headers[":error-code"] ?? "error";
941
+ const message2 = (payload ? nonEmptyString(payload.message) ?? nonEmptyString(payload.Message) ?? nonEmptyString(payload.error) : void 0) ?? headers[":error-message"] ?? "";
942
+ throw new Error(`Kiro stream ${kind}: ${clampForDiagnostic(redactSensitiveString(message2 || "no details"))}`);
943
+ }
885
944
  function usageFromMetrics(model, metrics) {
886
945
  const input = numberFrom(metrics.inputTokens);
887
946
  const output = numberFrom(metrics.outputTokens);
@@ -1065,6 +1124,10 @@ function handleEvent(stream, output, state2, model, eventType, payload) {
1065
1124
  if (percentage2 > 0) state2.contextUsagePercentage = percentage2;
1066
1125
  return;
1067
1126
  }
1127
+ if (eventType === "metadataEvent") {
1128
+ applyKiroMetadata(state2, model, payload);
1129
+ return;
1130
+ }
1068
1131
  if (eventType === "metricsEvent") {
1069
1132
  const metrics = isRecord(payload.metricsEvent) ? payload.metricsEvent : payload;
1070
1133
  state2.usage = usageFromMetrics(model, metrics);
@@ -1092,15 +1155,17 @@ async function consumeKiroEventStream(response, stream, output, model, logger) {
1092
1155
  if (!frameBytes) break;
1093
1156
  const frame = parseEventFrame(frameBytes, logger);
1094
1157
  if (!frame) continue;
1158
+ throwOnExceptionFrame(frame.headers, frame.payload);
1095
1159
  handleEvent(stream, output, state2, model, frame.headers[":event-type"] ?? "", frame.payload);
1096
1160
  }
1097
1161
  if (iterations >= 1e3) logger.warn("eventstream_iteration_limit_reached", { remainingBytes: queue.length });
1098
1162
  }
1163
+ throwOnTerminalWireStop(state2);
1099
1164
  closeThinkingBlock(stream, output, state2);
1100
1165
  closeTextBlock(stream, output, state2);
1101
1166
  closeToolCalls(stream, output, state2);
1102
1167
  output.usage = state2.usage ?? estimatedUsage(model, state2) ?? output.usage;
1103
- output.stopReason = state2.hasToolCalls ? "toolUse" : "stop";
1168
+ output.stopReason = resolveStopReason(state2);
1104
1169
  stream.push({ type: "done", reason: output.stopReason, message: output });
1105
1170
  stream.end(output);
1106
1171
  }
@@ -1147,7 +1212,7 @@ function createKiroStream(config, _runtime, logger) {
1147
1212
  return stream;
1148
1213
  };
1149
1214
  }
1150
- var ENV_VAR_PATTERN, DEFAULT_MAX_OUTPUT_TOKENS, API_MAX_OUTPUT_TOKENS, KIRO_NAMESPACE, KIRO_STREAMING_TARGET, KIRO_CODEWHISPERER_SDK_USER_AGENT, KIRO_CODEWHISPERER_AMZ_USER_AGENT, KIRO_Q_SDK_USER_AGENT, KIRO_Q_AMZ_USER_AGENT, KIRO_CLI_CONTEXT_BLOCK_PATTERN, KIRO_CLI_USER_MESSAGE_PATTERN, KiroAuthFailureError;
1215
+ var ENV_VAR_PATTERN, DEFAULT_MAX_OUTPUT_TOKENS, API_MAX_OUTPUT_TOKENS, KIRO_NAMESPACE, KIRO_STREAMING_TARGET, KIRO_CODEWHISPERER_SDK_USER_AGENT, KIRO_CODEWHISPERER_AMZ_USER_AGENT, KIRO_Q_SDK_USER_AGENT, KIRO_Q_AMZ_USER_AGENT, KIRO_CLI_CONTEXT_BLOCK_PATTERN, KIRO_CLI_USER_MESSAGE_PATTERN, KiroAuthFailureError, CACHE_READ_KEYS, CACHE_WRITE_KEYS, DIAGNOSTIC_QUOTE_LIMIT;
1151
1216
  var init_stream_oauth = __esm({
1152
1217
  "node_modules/@ngosangns/ns-pi-provider/src/kiro/stream-oauth.ts"() {
1153
1218
  init_pi_ai();
@@ -1174,6 +1239,9 @@ var init_stream_oauth = __esm({
1174
1239
  this.kiroAuth = { ...metadata };
1175
1240
  }
1176
1241
  };
1242
+ CACHE_READ_KEYS = ["cacheReadInputTokens", "cache_read_input_tokens", "cacheReadTokens"];
1243
+ CACHE_WRITE_KEYS = ["cacheWriteInputTokens", "cache_creation_input_tokens", "cacheCreationInputTokens", "cacheCreationTokens"];
1244
+ DIAGNOSTIC_QUOTE_LIMIT = 500;
1177
1245
  }
1178
1246
  });
1179
1247
 
@@ -5196,12 +5264,15 @@ function parseList(value2) {
5196
5264
  value2.split(/[\s,]+/).map((item) => item.trim().toLowerCase()).filter(Boolean)
5197
5265
  );
5198
5266
  }
5267
+ var OPT_IN_PROVIDERS = /* @__PURE__ */ new Set(["kiro"]);
5199
5268
  function resolveProviderSelection(env = process.env) {
5200
5269
  const allow = parseList(env.NS_OMP_PROVIDERS);
5270
+ const enable = parseList(env.NS_OMP_PROVIDER_ENABLE) ?? /* @__PURE__ */ new Set();
5201
5271
  const deny = parseList(env.NS_OMP_PROVIDER_DISABLE) ?? /* @__PURE__ */ new Set();
5202
5272
  const selection = {};
5203
5273
  for (const key of PROVIDER_KEYS) {
5204
- selection[key] = (allow ? allow.has(key) : true) && !deny.has(key);
5274
+ const base = allow ? allow.has(key) : !OPT_IN_PROVIDERS.has(key);
5275
+ selection[key] = (base || enable.has(key)) && !deny.has(key);
5205
5276
  }
5206
5277
  return selection;
5207
5278
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ns-omp-provider",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "OMP (oh-my-pi) plugin: Kiro, Devin, and Grok providers from @ngosangns/ns-pi-provider, wrapped for OMP's provider/extension API.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -70,7 +70,7 @@
70
70
  }
71
71
  },
72
72
  "devDependencies": {
73
- "@ngosangns/ns-pi-provider": "0.2.0",
73
+ "@ngosangns/ns-pi-provider": "0.2.1",
74
74
  "@types/node": "^22.18.6",
75
75
  "esbuild": "^0.28.2",
76
76
  "typescript": "^5.9.2",