ns-omp-provider 0.2.0 → 0.3.1
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 +28 -0
- package/README.md +19 -12
- package/dist/config.d.ts +12 -3
- package/dist/index.js +264 -11
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.1
|
|
4
|
+
|
|
5
|
+
Bundles `@ngosangns/ns-pi-provider@0.2.2`.
|
|
6
|
+
|
|
7
|
+
- fix(grok): Grok's own tool activity is now visible in OMP. The Grok agent edits
|
|
8
|
+
files and runs commands inside its own ACP process; those tools were dropped, so
|
|
9
|
+
turns looked like read-only reasoning even when files changed. Each tool now shows
|
|
10
|
+
as a line in the reply (`` - Edit `src/a.ts` (+3 −1) ``, `` - Run `npm test` ``,
|
|
11
|
+
`✗ … failed`). Text only — OMP never re-runs them. `PI_GROK_SDK_SHOW_TOOLS=0` hides.
|
|
12
|
+
- fix(grok): cold-start prompts carry a short bridge note so Grok acts with its
|
|
13
|
+
built-in tools instead of describing OMP tool calls (`PI_GROK_SDK_BRIDGE_NOTE=0`
|
|
14
|
+
disables).
|
|
15
|
+
|
|
16
|
+
## 0.3.0
|
|
17
|
+
|
|
18
|
+
- **Breaking: `kiro` is now opt-in.** By default only `devin` and `grok` (+ `grok-sdk`)
|
|
19
|
+
are registered, so this package no longer shadows the dedicated
|
|
20
|
+
[`ns-omp-provider-kiro`](https://www.npmjs.com/package/ns-omp-provider-kiro) plugin
|
|
21
|
+
(the recommended Kiro path in OMP). Opt in with `NS_OMP_PROVIDER_ENABLE=kiro` or by
|
|
22
|
+
listing it in `NS_OMP_PROVIDERS` (e.g. `NS_OMP_PROVIDERS=devin,grok,kiro`).
|
|
23
|
+
`NS_OMP_PROVIDER_DISABLE` still works and is applied last; the old
|
|
24
|
+
`NS_OMP_PROVIDER_DISABLE=kiro` workaround is no longer needed.
|
|
25
|
+
- Bundles `@ngosangns/ns-pi-provider@0.2.1`: Kiro now honours `metadataEvent` stop
|
|
26
|
+
reasons — `MAX_TOKENS` → `length` (OMP skips truncated tool calls instead of running
|
|
27
|
+
them with partial arguments), `CONTENT_FILTERED` / `MODEL_CONTEXT_WINDOW_EXCEEDED` /
|
|
28
|
+
`PAUSE_TURN` and mid-stream exception frames end as errors, and reported token usage
|
|
29
|
+
is read. Only relevant when `kiro` is opted in.
|
|
30
|
+
|
|
3
31
|
## 0.2.0
|
|
4
32
|
|
|
5
33
|
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 |
|
|
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
|
-
###
|
|
39
|
+
### Choosing providers (`kiro` is opt-in)
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
46
|
-
export
|
|
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
|
-
|
|
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`** —
|
|
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.
|
|
91
|
+
- **Upstream pin** — this release bundles `@ngosangns/ns-pi-provider@0.2.2`.
|
|
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.
|
|
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.
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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.
|
|
1
|
+
// ns-omp-provider 0.3.1 — bundles @ngosangns/ns-pi-provider@0.2.2 (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
|
|
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
|
|
|
@@ -3730,9 +3798,14 @@ function mapReasoningEffort(reasoning) {
|
|
|
3730
3798
|
}
|
|
3731
3799
|
return void 0;
|
|
3732
3800
|
}
|
|
3733
|
-
function
|
|
3734
|
-
const
|
|
3735
|
-
|
|
3801
|
+
function envFlagDefault(names, fallback, env = process.env) {
|
|
3802
|
+
for (const name of names) {
|
|
3803
|
+
const raw = env[name]?.trim().toLowerCase();
|
|
3804
|
+
if (!raw) continue;
|
|
3805
|
+
if (raw === "1" || raw === "true" || raw === "yes" || raw === "on") return true;
|
|
3806
|
+
if (raw === "0" || raw === "false" || raw === "no" || raw === "off") return false;
|
|
3807
|
+
}
|
|
3808
|
+
return fallback;
|
|
3736
3809
|
}
|
|
3737
3810
|
|
|
3738
3811
|
// node_modules/@ngosangns/ns-pi-provider/src/grok/models.ts
|
|
@@ -3917,6 +3990,8 @@ var PiContentEmitter = class {
|
|
|
3917
3990
|
started = false;
|
|
3918
3991
|
thinkingContentIndex = -1;
|
|
3919
3992
|
textContentIndex = -1;
|
|
3993
|
+
/** Last visible text appended was a tool-activity list line. */
|
|
3994
|
+
lastTextWasTool = false;
|
|
3920
3995
|
mutuallyExclusive;
|
|
3921
3996
|
/** Current immutable view (for external reads). */
|
|
3922
3997
|
snapshot() {
|
|
@@ -3957,6 +4032,7 @@ var PiContentEmitter = class {
|
|
|
3957
4032
|
const contentIndex = this.textContentIndex;
|
|
3958
4033
|
const block = this.partial.content[contentIndex];
|
|
3959
4034
|
this.textContentIndex = -1;
|
|
4035
|
+
this.lastTextWasTool = false;
|
|
3960
4036
|
if (block?.type !== "text") return "";
|
|
3961
4037
|
this.push({
|
|
3962
4038
|
type: "text_end",
|
|
@@ -3995,6 +4071,32 @@ var PiContentEmitter = class {
|
|
|
3995
4071
|
}
|
|
3996
4072
|
appendText(delta, options) {
|
|
3997
4073
|
if (!delta) return;
|
|
4074
|
+
if (this.lastTextWasTool && this.textContentIndex >= 0) {
|
|
4075
|
+
this.lastTextWasTool = false;
|
|
4076
|
+
const open = this.partial.content[this.textContentIndex];
|
|
4077
|
+
if (open?.type === "text" && !open.text.endsWith("\n\n")) {
|
|
4078
|
+
delta = `${open.text.endsWith("\n") ? "\n" : "\n\n"}${delta}`;
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
this.lastTextWasTool = false;
|
|
4082
|
+
this.appendTextRaw(delta, options);
|
|
4083
|
+
}
|
|
4084
|
+
/**
|
|
4085
|
+
* Visible tool-activity line(s) ("- Edit `a.ts` (+1 −1)\n") in the answer
|
|
4086
|
+
* text, separated from surrounding prose so Markdown renders them as a list.
|
|
4087
|
+
*/
|
|
4088
|
+
appendToolActivity(lines, options) {
|
|
4089
|
+
if (!lines) return;
|
|
4090
|
+
let delta = lines.endsWith("\n") ? lines : `${lines}
|
|
4091
|
+
`;
|
|
4092
|
+
const open = this.textContentIndex >= 0 ? this.partial.content[this.textContentIndex] : void 0;
|
|
4093
|
+
if (!this.lastTextWasTool && open?.type === "text" && open.text && !open.text.endsWith("\n\n")) {
|
|
4094
|
+
delta = `${open.text.endsWith("\n") ? "\n" : "\n\n"}${delta}`;
|
|
4095
|
+
}
|
|
4096
|
+
this.appendTextRaw(delta, options);
|
|
4097
|
+
this.lastTextWasTool = true;
|
|
4098
|
+
}
|
|
4099
|
+
appendTextRaw(delta, options) {
|
|
3998
4100
|
this.ensureStart();
|
|
3999
4101
|
if (options?.closeThinking ?? this.mutuallyExclusive) this.closeThinking();
|
|
4000
4102
|
if (this.textContentIndex < 0) {
|
|
@@ -4119,6 +4221,15 @@ var StreamingUpdatePump = class {
|
|
|
4119
4221
|
|
|
4120
4222
|
// node_modules/@ngosangns/ns-pi-provider/src/grok/prompt.ts
|
|
4121
4223
|
init_pi_ai();
|
|
4224
|
+
var GROK_BRIDGE_NOTE = [
|
|
4225
|
+
"[grok bridge] You are the Grok coding agent, running behind an external coding harness that only relays your messages.",
|
|
4226
|
+
"Tools named in the harness instructions below (read, bash, edit, write, \u2026) are not callable from here.",
|
|
4227
|
+
"Use your own built-in tools (read_file, grep, search_replace, write, run_terminal_command, \u2026) directly in the working directory.",
|
|
4228
|
+
"When the task needs file changes or commands, perform them with those tools instead of describing them or writing tool calls as text."
|
|
4229
|
+
].join("\n");
|
|
4230
|
+
function bridgeNoteEnabled() {
|
|
4231
|
+
return envFlagDefault(["PI_GROK_SDK_BRIDGE_NOTE"], true);
|
|
4232
|
+
}
|
|
4122
4233
|
function contentToText(content) {
|
|
4123
4234
|
if (typeof content === "string") return content;
|
|
4124
4235
|
if (!Array.isArray(content)) return "";
|
|
@@ -4155,6 +4266,7 @@ ${contentToText(message2.content)}`);
|
|
|
4155
4266
|
${contentToText(message2.content)}`);
|
|
4156
4267
|
}
|
|
4157
4268
|
}
|
|
4269
|
+
if (parts.length > 0 && bridgeNoteEnabled()) parts.unshift(GROK_BRIDGE_NOTE);
|
|
4158
4270
|
return parts.join("\n\n");
|
|
4159
4271
|
}
|
|
4160
4272
|
function buildIncrementalPrompt(context) {
|
|
@@ -4601,6 +4713,140 @@ async function withSessionAgent(params, fn) {
|
|
|
4601
4713
|
});
|
|
4602
4714
|
}
|
|
4603
4715
|
|
|
4716
|
+
// node_modules/@ngosangns/ns-pi-provider/src/grok/tool-activity.ts
|
|
4717
|
+
var MAX_LABEL = 160;
|
|
4718
|
+
function toolMeta(update) {
|
|
4719
|
+
const meta = update._meta?.["x.ai/tool"];
|
|
4720
|
+
if (!meta || typeof meta !== "object") return {};
|
|
4721
|
+
const m = meta;
|
|
4722
|
+
return {
|
|
4723
|
+
name: typeof m.name === "string" ? m.name : void 0,
|
|
4724
|
+
kind: typeof m.kind === "string" ? m.kind : void 0,
|
|
4725
|
+
label: typeof m.label === "string" ? m.label : void 0
|
|
4726
|
+
};
|
|
4727
|
+
}
|
|
4728
|
+
function oneLine(text2, max = MAX_LABEL) {
|
|
4729
|
+
const flat = text2.replace(/\s*\n\s*/g, " \u23CE ").replace(/\s+/g, " ").trim();
|
|
4730
|
+
return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
|
|
4731
|
+
}
|
|
4732
|
+
function countLines(text2) {
|
|
4733
|
+
if (typeof text2 !== "string" || text2.length === 0) return 0;
|
|
4734
|
+
const body = text2.endsWith("\n") ? text2.slice(0, -1) : text2;
|
|
4735
|
+
return body.split("\n").length;
|
|
4736
|
+
}
|
|
4737
|
+
function diffStat(content) {
|
|
4738
|
+
if (!Array.isArray(content)) return "";
|
|
4739
|
+
let added = 0;
|
|
4740
|
+
let removed = 0;
|
|
4741
|
+
let seen = false;
|
|
4742
|
+
for (const item of content) {
|
|
4743
|
+
if (!item || typeof item !== "object") continue;
|
|
4744
|
+
const c = item;
|
|
4745
|
+
if (c.type !== "diff") continue;
|
|
4746
|
+
seen = true;
|
|
4747
|
+
added += countLines(c.newText);
|
|
4748
|
+
removed += countLines(c.oldText);
|
|
4749
|
+
}
|
|
4750
|
+
return seen ? `+${added} \u2212${removed}` : "";
|
|
4751
|
+
}
|
|
4752
|
+
function firstText(content) {
|
|
4753
|
+
if (!Array.isArray(content)) return "";
|
|
4754
|
+
for (const item of content) {
|
|
4755
|
+
if (!item || typeof item !== "object") continue;
|
|
4756
|
+
const c = item;
|
|
4757
|
+
const inner = c.content;
|
|
4758
|
+
if (c.type === "content" && inner && typeof inner.text === "string" && inner.text.trim()) {
|
|
4759
|
+
return inner.text;
|
|
4760
|
+
}
|
|
4761
|
+
}
|
|
4762
|
+
return "";
|
|
4763
|
+
}
|
|
4764
|
+
var ToolActivityTracker = class {
|
|
4765
|
+
constructor(cwd) {
|
|
4766
|
+
this.cwd = cwd;
|
|
4767
|
+
}
|
|
4768
|
+
cwd;
|
|
4769
|
+
tools = /* @__PURE__ */ new Map();
|
|
4770
|
+
relativize(text2) {
|
|
4771
|
+
const cwd = this.cwd?.replace(/\/+$/, "");
|
|
4772
|
+
if (!cwd || cwd === "/") return text2;
|
|
4773
|
+
let out = text2;
|
|
4774
|
+
const roots = /* @__PURE__ */ new Set([cwd, cwd.replace(/^\/private\//, "/")]);
|
|
4775
|
+
for (const root of [...roots]) roots.add(`/private${root}`);
|
|
4776
|
+
for (const root of [...roots].sort((a, b) => b.length - a.length)) {
|
|
4777
|
+
out = out.split(`${root}/`).join("");
|
|
4778
|
+
out = out.split(`\`${root}\``).join("`.`");
|
|
4779
|
+
}
|
|
4780
|
+
return out;
|
|
4781
|
+
}
|
|
4782
|
+
describe(entry) {
|
|
4783
|
+
const input = entry.rawInput ?? {};
|
|
4784
|
+
let label;
|
|
4785
|
+
if (entry.kind === "execute" && typeof input.command === "string") {
|
|
4786
|
+
label = `Run \`${oneLine(String(input.command), MAX_LABEL - 8)}\``;
|
|
4787
|
+
} else if (entry.title && entry.title !== entry.name) {
|
|
4788
|
+
label = oneLine(entry.title);
|
|
4789
|
+
} else {
|
|
4790
|
+
const verb = entry.label ?? entry.name ?? "Tool";
|
|
4791
|
+
const target = typeof input.file_path === "string" && input.file_path || typeof input.target_file === "string" && input.target_file || typeof input.path === "string" && input.path || typeof input.pattern === "string" && input.pattern || typeof input.query === "string" && input.query || "";
|
|
4792
|
+
label = target ? `${verb} \`${oneLine(String(target), MAX_LABEL - 8)}\`` : verb;
|
|
4793
|
+
}
|
|
4794
|
+
return this.relativize(label);
|
|
4795
|
+
}
|
|
4796
|
+
/**
|
|
4797
|
+
* Feed one ACP `session/update`. Returns Markdown line(s) to show (each
|
|
4798
|
+
* ending in "\n"), or undefined when nothing new is visible yet.
|
|
4799
|
+
*/
|
|
4800
|
+
handle(update) {
|
|
4801
|
+
const kind = update.sessionUpdate;
|
|
4802
|
+
if (kind !== "tool_call" && kind !== "tool_call_update") return void 0;
|
|
4803
|
+
const id = update.toolCallId;
|
|
4804
|
+
if (!id) return void 0;
|
|
4805
|
+
let entry = this.tools.get(id);
|
|
4806
|
+
if (!entry) {
|
|
4807
|
+
entry = { announced: false, finished: false };
|
|
4808
|
+
this.tools.set(id, entry);
|
|
4809
|
+
}
|
|
4810
|
+
const meta = toolMeta(update);
|
|
4811
|
+
entry.name ??= meta.name ?? (kind === "tool_call" ? update.title : void 0);
|
|
4812
|
+
entry.label ??= meta.label;
|
|
4813
|
+
if (meta.kind) entry.kind = meta.kind === "write" ? "edit" : meta.kind;
|
|
4814
|
+
if (update.kind) entry.kind = update.kind;
|
|
4815
|
+
if (update.rawInput && typeof update.rawInput === "object") {
|
|
4816
|
+
entry.rawInput = { ...entry.rawInput ?? {}, ...update.rawInput };
|
|
4817
|
+
}
|
|
4818
|
+
if (update.title) entry.title = update.title;
|
|
4819
|
+
if (entry.finished) return void 0;
|
|
4820
|
+
const lines = [];
|
|
4821
|
+
const status = update.status;
|
|
4822
|
+
const done = status === "completed" || status === "failed";
|
|
4823
|
+
const stat = diffStat(update.content);
|
|
4824
|
+
if (stat) entry.stat = stat;
|
|
4825
|
+
const isEdit = entry.kind === "edit" || entry.kind === "delete" || entry.kind === "move";
|
|
4826
|
+
const enriched = kind === "tool_call_update" && Boolean(update.title || update.kind);
|
|
4827
|
+
const ready = isEdit ? done : enriched || done || status === "in_progress";
|
|
4828
|
+
if (!entry.announced && ready) {
|
|
4829
|
+
entry.announced = true;
|
|
4830
|
+
lines.push(`- ${this.describe(entry)}${isEdit && entry.stat ? ` (${entry.stat})` : ""}`);
|
|
4831
|
+
if (done) {
|
|
4832
|
+
entry.finished = true;
|
|
4833
|
+
if (status === "failed") lines.push(` - \u2717 failed${this.failureDetail(update)}`);
|
|
4834
|
+
}
|
|
4835
|
+
} else if (entry.announced && done) {
|
|
4836
|
+
entry.finished = true;
|
|
4837
|
+
if (status === "failed") {
|
|
4838
|
+
lines.push(` - \u2717 ${this.describe(entry)} failed${this.failureDetail(update)}`);
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
return lines.length ? `${lines.join("\n")}
|
|
4842
|
+
` : void 0;
|
|
4843
|
+
}
|
|
4844
|
+
failureDetail(update) {
|
|
4845
|
+
const text2 = firstText(update.content);
|
|
4846
|
+
return text2 ? `: ${this.relativize(oneLine(text2, 200))}` : "";
|
|
4847
|
+
}
|
|
4848
|
+
};
|
|
4849
|
+
|
|
4604
4850
|
// node_modules/@ngosangns/ns-pi-provider/src/grok/acp-mode.ts
|
|
4605
4851
|
function streamViaAcp(model, context, options) {
|
|
4606
4852
|
const stream = createAssistantMessageEventStream();
|
|
@@ -4630,7 +4876,11 @@ function streamViaAcp(model, context, options) {
|
|
|
4630
4876
|
}
|
|
4631
4877
|
const reasoning = mapReasoningEffort(options?.reasoning);
|
|
4632
4878
|
const cwd = getSessionCwd();
|
|
4633
|
-
const showTools =
|
|
4879
|
+
const showTools = envFlagDefault(
|
|
4880
|
+
["PI_GROK_SDK_SHOW_TOOLS", "PI_GROK_AGENT_SHOW_TOOLS"],
|
|
4881
|
+
true
|
|
4882
|
+
);
|
|
4883
|
+
const tools = new ToolActivityTracker(cwd);
|
|
4634
4884
|
const cliModelId = resolveCliModelId(model.id);
|
|
4635
4885
|
emitter.ensureStart();
|
|
4636
4886
|
await withSessionAgent(
|
|
@@ -4658,9 +4908,9 @@ function streamViaAcp(model, context, options) {
|
|
|
4658
4908
|
emitter.appendThinking(piece);
|
|
4659
4909
|
} else if (kind === "agent_message_chunk") {
|
|
4660
4910
|
emitter.appendText(piece);
|
|
4661
|
-
} else if (showTools && kind === "tool_call") {
|
|
4662
|
-
const
|
|
4663
|
-
emitter.
|
|
4911
|
+
} else if (showTools && (kind === "tool_call" || kind === "tool_call_update")) {
|
|
4912
|
+
const lines = tools.handle(update);
|
|
4913
|
+
if (lines) emitter.appendToolActivity(lines);
|
|
4664
4914
|
}
|
|
4665
4915
|
});
|
|
4666
4916
|
}
|
|
@@ -5196,12 +5446,15 @@ function parseList(value2) {
|
|
|
5196
5446
|
value2.split(/[\s,]+/).map((item) => item.trim().toLowerCase()).filter(Boolean)
|
|
5197
5447
|
);
|
|
5198
5448
|
}
|
|
5449
|
+
var OPT_IN_PROVIDERS = /* @__PURE__ */ new Set(["kiro"]);
|
|
5199
5450
|
function resolveProviderSelection(env = process.env) {
|
|
5200
5451
|
const allow = parseList(env.NS_OMP_PROVIDERS);
|
|
5452
|
+
const enable = parseList(env.NS_OMP_PROVIDER_ENABLE) ?? /* @__PURE__ */ new Set();
|
|
5201
5453
|
const deny = parseList(env.NS_OMP_PROVIDER_DISABLE) ?? /* @__PURE__ */ new Set();
|
|
5202
5454
|
const selection = {};
|
|
5203
5455
|
for (const key of PROVIDER_KEYS) {
|
|
5204
|
-
|
|
5456
|
+
const base = allow ? allow.has(key) : !OPT_IN_PROVIDERS.has(key);
|
|
5457
|
+
selection[key] = (base || enable.has(key)) && !deny.has(key);
|
|
5205
5458
|
}
|
|
5206
5459
|
return selection;
|
|
5207
5460
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ns-omp-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
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.
|
|
73
|
+
"@ngosangns/ns-pi-provider": "0.2.2",
|
|
74
74
|
"@types/node": "^22.18.6",
|
|
75
75
|
"esbuild": "^0.28.2",
|
|
76
76
|
"typescript": "^5.9.2",
|