@ycodium-ai/agent-grok 0.2.3252 → 0.2.3265
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/dist/index.js +451 -76
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -159,6 +159,9 @@ var GROK_CARD = {
|
|
|
159
159
|
|
|
160
160
|
// src/grokInstance.ts
|
|
161
161
|
import * as NodeCrypto3 from "node:crypto";
|
|
162
|
+
import {
|
|
163
|
+
raceAbort as raceAbort2
|
|
164
|
+
} from "@ycodium-ai/plugin-api/executor";
|
|
162
165
|
import { nodeStreamsFromHandle as nodeStreamsFromHandle2 } from "@ycodium-ai/plugin-api/process-stdin";
|
|
163
166
|
|
|
164
167
|
// ../../packages/ycodium-acp-executor/src/executor.ts
|
|
@@ -25191,11 +25194,15 @@ function createAcpConnection(options) {
|
|
|
25191
25194
|
});
|
|
25192
25195
|
};
|
|
25193
25196
|
const close = (error62) => shutdown(error62 ?? new AcpConnectionClosedError());
|
|
25197
|
+
const abandonPending = (error62) => {
|
|
25198
|
+
rejectPending(error62);
|
|
25199
|
+
};
|
|
25194
25200
|
return {
|
|
25195
25201
|
request,
|
|
25196
25202
|
begin,
|
|
25197
25203
|
notify,
|
|
25198
25204
|
awaitInboundIdle,
|
|
25205
|
+
abandonPending,
|
|
25199
25206
|
close,
|
|
25200
25207
|
closed
|
|
25201
25208
|
};
|
|
@@ -25661,10 +25668,11 @@ function createExecutorPrompt({
|
|
|
25661
25668
|
const prompt = async (input2) => {
|
|
25662
25669
|
failIfPromptUnavailable();
|
|
25663
25670
|
const started = requireStarted();
|
|
25671
|
+
let sessionId = started.sessionId;
|
|
25664
25672
|
let record3;
|
|
25665
25673
|
const settle2 = (rest) => settleTurn({
|
|
25666
25674
|
expectedGeneration: started.generation,
|
|
25667
|
-
expectedSessionId:
|
|
25675
|
+
expectedSessionId: sessionId,
|
|
25668
25676
|
record: record3,
|
|
25669
25677
|
...rest
|
|
25670
25678
|
});
|
|
@@ -25748,12 +25756,32 @@ function createExecutorPrompt({
|
|
|
25748
25756
|
const queuedTurnId = turnId;
|
|
25749
25757
|
const isStale = () => interruptedTurnIds.has(queuedTurnId) || input2.expectedActiveTurnId !== void 0 && shared.activeTurnId !== queuedTurnId;
|
|
25750
25758
|
if (!isStale()) {
|
|
25751
|
-
|
|
25752
|
-
|
|
25753
|
-
|
|
25754
|
-
|
|
25755
|
-
|
|
25756
|
-
|
|
25759
|
+
try {
|
|
25760
|
+
const applied = await applyRequestedModel({
|
|
25761
|
+
state: shared.modelSelectionState,
|
|
25762
|
+
acpSessionId: sessionId,
|
|
25763
|
+
setup: shared.startResult?.sessionSetupResult ?? {},
|
|
25764
|
+
requestedModelId: input2.requestedModelId,
|
|
25765
|
+
expectedGeneration: started.generation
|
|
25766
|
+
});
|
|
25767
|
+
shared.modelSelectionState = applied.state;
|
|
25768
|
+
sessionId = applied.sessionId;
|
|
25769
|
+
record3.sessionId = applied.sessionId;
|
|
25770
|
+
} catch (error62) {
|
|
25771
|
+
const failureCategory = readAcpFailureCategory(error62);
|
|
25772
|
+
if (failureCategory === "model-unavailable") {
|
|
25773
|
+
const errorMessage = error62 instanceof Error && error62.message.trim().length > 0 ? error62.message : "Requested model is not in this agent's catalog. This turn was not sent.";
|
|
25774
|
+
settle2({
|
|
25775
|
+
turnId,
|
|
25776
|
+
stopReason: "error",
|
|
25777
|
+
state: "failed",
|
|
25778
|
+
errorMessage,
|
|
25779
|
+
failureCategory
|
|
25780
|
+
});
|
|
25781
|
+
throw error62 instanceof Error ? withFailureCategory(error62, failureCategory) : error62;
|
|
25782
|
+
}
|
|
25783
|
+
throw error62;
|
|
25784
|
+
}
|
|
25757
25785
|
}
|
|
25758
25786
|
failIfDisposed();
|
|
25759
25787
|
if (isStale()) {
|
|
@@ -25764,7 +25792,7 @@ function createExecutorPrompt({
|
|
|
25764
25792
|
let written = false;
|
|
25765
25793
|
try {
|
|
25766
25794
|
response = await sendPromptRpc(
|
|
25767
|
-
|
|
25795
|
+
sessionId,
|
|
25768
25796
|
promptParts,
|
|
25769
25797
|
() => {
|
|
25770
25798
|
written = true;
|
|
@@ -25814,7 +25842,7 @@ function createExecutorPrompt({
|
|
|
25814
25842
|
if (hooks.readContextWindow !== void 0) {
|
|
25815
25843
|
try {
|
|
25816
25844
|
contextWindow = await hooks.readContextWindow({
|
|
25817
|
-
sessionId
|
|
25845
|
+
sessionId,
|
|
25818
25846
|
promptResponse: response
|
|
25819
25847
|
}) ?? void 0;
|
|
25820
25848
|
} catch {
|
|
@@ -26457,6 +26485,56 @@ function acpInitializePayload(clientInfo, overrides) {
|
|
|
26457
26485
|
};
|
|
26458
26486
|
}
|
|
26459
26487
|
|
|
26488
|
+
// ../../packages/ycodium-acp-executor/src/refreshDeadline.ts
|
|
26489
|
+
var MODEL_CATALOG_REFRESH_TIMEOUT_MS = 9e4;
|
|
26490
|
+
|
|
26491
|
+
// ../../packages/ycodium-acp-executor/src/model-send.ts
|
|
26492
|
+
function judgeRequestedModel(input2) {
|
|
26493
|
+
const requested = normalizeModelId(input2.requestedModelId);
|
|
26494
|
+
const current = input2.state.currentModelId;
|
|
26495
|
+
if (requested === void 0 || requested === current) return { kind: "keep" };
|
|
26496
|
+
const hasCatalog = input2.state.advertisedModelValues.length > 0;
|
|
26497
|
+
const hasCurrent = current !== void 0;
|
|
26498
|
+
if (!hasCatalog && !hasCurrent) return { kind: "keep" };
|
|
26499
|
+
const canSwitch = input2.state.strategy === "session/set_model" && input2.state.sessionModelsAdvertised || input2.state.strategy === "config-option" && input2.state.modelConfigId !== void 0;
|
|
26500
|
+
const value = hasCatalog ? matchAdvertisedModelValue(requested, input2.state.advertisedModelValues) : void 0;
|
|
26501
|
+
if (value !== void 0 && value === current) return { kind: "keep" };
|
|
26502
|
+
if (value !== void 0 && canSwitch) return { kind: "switch", value };
|
|
26503
|
+
return input2.refreshed ? { kind: "unavailable" } : { kind: "refresh" };
|
|
26504
|
+
}
|
|
26505
|
+
function modelUnavailableError(requestedModelId, currentModelId) {
|
|
26506
|
+
const current = currentModelId ?? "its own model";
|
|
26507
|
+
return withFailureCategory(
|
|
26508
|
+
new Error(
|
|
26509
|
+
`Requested model "${requestedModelId}" is not in this agent's catalog; it is on "${current}". This turn was not sent. Refresh the model list in this agent's settings and try again.`
|
|
26510
|
+
),
|
|
26511
|
+
"model-unavailable"
|
|
26512
|
+
);
|
|
26513
|
+
}
|
|
26514
|
+
var ModelCatalogRefreshTimeoutError = class extends Error {
|
|
26515
|
+
constructor() {
|
|
26516
|
+
super("model catalog refresh timed out");
|
|
26517
|
+
this.name = "ModelCatalogRefreshTimeoutError";
|
|
26518
|
+
}
|
|
26519
|
+
};
|
|
26520
|
+
function modelRefreshTimedOutError(requestedModelId) {
|
|
26521
|
+
return withFailureCategory(
|
|
26522
|
+
new Error(
|
|
26523
|
+
`Refreshing this agent's model list timed out, so this turn was not sent. Requested model "${requestedModelId}".`
|
|
26524
|
+
),
|
|
26525
|
+
"model-unavailable"
|
|
26526
|
+
);
|
|
26527
|
+
}
|
|
26528
|
+
function modelRefreshFailedError(requestedModelId, error62) {
|
|
26529
|
+
const detail = error62 instanceof Error && error62.message.trim().length > 0 ? error62.message : "the model list could not be read";
|
|
26530
|
+
return withFailureCategory(
|
|
26531
|
+
new Error(
|
|
26532
|
+
`Refreshing this agent's model list failed (${detail}), so this turn was not sent. Requested model "${requestedModelId}".`
|
|
26533
|
+
),
|
|
26534
|
+
"model-unavailable"
|
|
26535
|
+
);
|
|
26536
|
+
}
|
|
26537
|
+
|
|
26460
26538
|
// ../../packages/ycodium-acp-executor/src/runtime-mode.ts
|
|
26461
26539
|
function isRecord5(value) {
|
|
26462
26540
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -26487,43 +26565,131 @@ function createExecutorSession({
|
|
|
26487
26565
|
failIfDisposed,
|
|
26488
26566
|
shared
|
|
26489
26567
|
}) {
|
|
26490
|
-
const
|
|
26491
|
-
const
|
|
26492
|
-
if (
|
|
26493
|
-
|
|
26494
|
-
}
|
|
26495
|
-
const value = matchAdvertisedModelValue(requestedModelId, input2.state.advertisedModelValues);
|
|
26496
|
-
if (value === void 0) {
|
|
26497
|
-
if (requestedModelId !== shared.lastUnmatchedModelId) {
|
|
26498
|
-
shared.lastUnmatchedModelId = requestedModelId;
|
|
26499
|
-
options.log?.info("requested model is not in the agent catalog; keeping its own model", {
|
|
26500
|
-
requestedModelId,
|
|
26501
|
-
currentModelId: input2.state.currentModelId,
|
|
26502
|
-
advertisedModelValues: input2.state.advertisedModelValues
|
|
26503
|
-
});
|
|
26504
|
-
}
|
|
26505
|
-
return input2.state;
|
|
26568
|
+
const openFreshSession = async (expectedGeneration) => {
|
|
26569
|
+
const initialized = shared.initializeResult;
|
|
26570
|
+
if (initialized === void 0) {
|
|
26571
|
+
throw new Error("no handshake");
|
|
26506
26572
|
}
|
|
26507
|
-
|
|
26508
|
-
|
|
26573
|
+
const mcpServers = filterMcpServersByAgentCapabilities(
|
|
26574
|
+
options.mcpServers ?? [],
|
|
26575
|
+
initialized.agentCapabilities
|
|
26576
|
+
);
|
|
26577
|
+
const additionalDirectories = options.additionalDirectories !== void 0 && options.additionalDirectories.length > 0 ? { additionalDirectories: options.additionalDirectories } : {};
|
|
26578
|
+
const created = await connection.request("session/new", {
|
|
26579
|
+
cwd: options.cwd,
|
|
26580
|
+
mcpServers,
|
|
26581
|
+
...additionalDirectories
|
|
26582
|
+
});
|
|
26583
|
+
if (!isLive(expectedGeneration)) {
|
|
26584
|
+
throw new AcpExecutorDisposedError();
|
|
26509
26585
|
}
|
|
26586
|
+
if (created.sessionId === void 0 || created.sessionId.length === 0) {
|
|
26587
|
+
throw new Error("session/new did not return a session id");
|
|
26588
|
+
}
|
|
26589
|
+
return { sessionId: created.sessionId, setup: created };
|
|
26590
|
+
};
|
|
26591
|
+
const switchModel = async (input2) => {
|
|
26510
26592
|
let configOptions = input2.state.configOptions;
|
|
26511
26593
|
if (input2.state.strategy === "config-option") {
|
|
26512
26594
|
const switched = await connection.request(
|
|
26513
26595
|
"session/set_config_option",
|
|
26514
|
-
{ sessionId: input2.
|
|
26596
|
+
{ sessionId: input2.sessionId, configId: input2.state.modelConfigId, value: input2.value }
|
|
26515
26597
|
);
|
|
26516
26598
|
configOptions = switched?.configOptions ?? configOptions;
|
|
26517
26599
|
} else {
|
|
26518
26600
|
await connection.request("session/set_model", {
|
|
26519
|
-
sessionId: input2.
|
|
26520
|
-
modelId: value
|
|
26601
|
+
sessionId: input2.sessionId,
|
|
26602
|
+
modelId: input2.value
|
|
26521
26603
|
});
|
|
26522
26604
|
}
|
|
26523
26605
|
if (!isLive(input2.expectedGeneration)) {
|
|
26524
26606
|
throw new AcpExecutorDisposedError();
|
|
26525
26607
|
}
|
|
26526
|
-
return { ...input2.state, currentModelId: value, configOptions };
|
|
26608
|
+
return { ...input2.state, currentModelId: input2.value, configOptions };
|
|
26609
|
+
};
|
|
26610
|
+
const adoptReopenedSession = (applied) => {
|
|
26611
|
+
if (shared.sessionId === void 0 && shared.startResult === void 0) return;
|
|
26612
|
+
const setup = applied.setup;
|
|
26613
|
+
shared.sessionId = applied.sessionId;
|
|
26614
|
+
shared.modelSelectionState = applied.state;
|
|
26615
|
+
shared.modeState = parseSessionModeState(setup);
|
|
26616
|
+
if (shared.startResult !== void 0) {
|
|
26617
|
+
shared.startResult = {
|
|
26618
|
+
...shared.startResult,
|
|
26619
|
+
sessionId: applied.sessionId,
|
|
26620
|
+
sessionSetupResult: setup,
|
|
26621
|
+
modelConfigId: extractModelConfigId(setup),
|
|
26622
|
+
modelId: applied.state.currentModelId
|
|
26623
|
+
};
|
|
26624
|
+
}
|
|
26625
|
+
};
|
|
26626
|
+
const applyRequestedModel = async (input2) => {
|
|
26627
|
+
async function applyOnce(state, sessionId, setup, refreshed) {
|
|
26628
|
+
const requestedModelId = normalizeModelId(input2.requestedModelId) ?? "";
|
|
26629
|
+
const judgment = judgeRequestedModel({
|
|
26630
|
+
state,
|
|
26631
|
+
requestedModelId: input2.requestedModelId,
|
|
26632
|
+
refreshed
|
|
26633
|
+
});
|
|
26634
|
+
if (judgment.kind === "keep") return { state, sessionId, setup };
|
|
26635
|
+
if (judgment.kind === "unavailable") {
|
|
26636
|
+
throw modelUnavailableError(requestedModelId, state.currentModelId);
|
|
26637
|
+
}
|
|
26638
|
+
if (judgment.kind === "switch") {
|
|
26639
|
+
return {
|
|
26640
|
+
state: await switchModel({
|
|
26641
|
+
state,
|
|
26642
|
+
sessionId,
|
|
26643
|
+
value: judgment.value,
|
|
26644
|
+
expectedGeneration: input2.expectedGeneration
|
|
26645
|
+
}),
|
|
26646
|
+
sessionId,
|
|
26647
|
+
setup
|
|
26648
|
+
};
|
|
26649
|
+
}
|
|
26650
|
+
return refreshAndRejudge(requestedModelId);
|
|
26651
|
+
}
|
|
26652
|
+
async function refreshAndRejudge(requestedModelId) {
|
|
26653
|
+
const timeoutMs = options.modelCatalogRefreshTimeoutMs ?? MODEL_CATALOG_REFRESH_TIMEOUT_MS;
|
|
26654
|
+
let timedOut = false;
|
|
26655
|
+
const timeoutError = new ModelCatalogRefreshTimeoutError();
|
|
26656
|
+
let timer;
|
|
26657
|
+
const work = (async () => {
|
|
26658
|
+
await options.refreshCatalogForSend?.();
|
|
26659
|
+
if (timedOut) throw timeoutError;
|
|
26660
|
+
const reopened = await openFreshSession(input2.expectedGeneration);
|
|
26661
|
+
if (timedOut) throw timeoutError;
|
|
26662
|
+
const next = modelSelectionStateFromSetup(reopened.setup);
|
|
26663
|
+
const applied = await applyOnce(next, reopened.sessionId, reopened.setup, true);
|
|
26664
|
+
if (timedOut) throw timeoutError;
|
|
26665
|
+
adoptReopenedSession(applied);
|
|
26666
|
+
return applied;
|
|
26667
|
+
})();
|
|
26668
|
+
void work.then(
|
|
26669
|
+
() => void 0,
|
|
26670
|
+
() => void 0
|
|
26671
|
+
);
|
|
26672
|
+
const deadline = new Promise((_resolve, reject) => {
|
|
26673
|
+
timer = setTimeout(() => {
|
|
26674
|
+
timedOut = true;
|
|
26675
|
+
connection.abandonPending(timeoutError);
|
|
26676
|
+
reject(timeoutError);
|
|
26677
|
+
}, timeoutMs);
|
|
26678
|
+
});
|
|
26679
|
+
try {
|
|
26680
|
+
return await Promise.race([work, deadline]);
|
|
26681
|
+
} catch (error62) {
|
|
26682
|
+
if (error62 instanceof AcpExecutorDisposedError) throw error62;
|
|
26683
|
+
if (timedOut || error62 instanceof ModelCatalogRefreshTimeoutError) {
|
|
26684
|
+
throw modelRefreshTimedOutError(requestedModelId);
|
|
26685
|
+
}
|
|
26686
|
+
if (readAcpFailureCategory(error62) === "model-unavailable") throw error62;
|
|
26687
|
+
throw modelRefreshFailedError(requestedModelId, error62);
|
|
26688
|
+
} finally {
|
|
26689
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
26690
|
+
}
|
|
26691
|
+
}
|
|
26692
|
+
return applyOnce(input2.state, input2.acpSessionId, input2.setup, false);
|
|
26527
26693
|
};
|
|
26528
26694
|
const startOnce = async (expectedGeneration) => {
|
|
26529
26695
|
const initializePayload = acpInitializePayload(options.clientInfo, options.clientCapabilities);
|
|
@@ -26592,12 +26758,17 @@ function createExecutorSession({
|
|
|
26592
26758
|
categories: unhandledCategories
|
|
26593
26759
|
});
|
|
26594
26760
|
}
|
|
26595
|
-
const
|
|
26596
|
-
|
|
26761
|
+
const openedSetup = setup;
|
|
26762
|
+
const appliedModel = await applyRequestedModel({
|
|
26763
|
+
state: modelSelectionStateFromSetup(openedSetup),
|
|
26597
26764
|
acpSessionId: nextSessionId,
|
|
26765
|
+
setup: openedSetup,
|
|
26598
26766
|
requestedModelId: options.requestedModelId,
|
|
26599
26767
|
expectedGeneration
|
|
26600
26768
|
});
|
|
26769
|
+
nextSessionId = appliedModel.sessionId;
|
|
26770
|
+
setup = appliedModel.setup;
|
|
26771
|
+
const nextModelSelectionState = appliedModel.state;
|
|
26601
26772
|
await applyRequestedEffort({
|
|
26602
26773
|
connection,
|
|
26603
26774
|
log: options.log,
|
|
@@ -27132,7 +27303,6 @@ function createAcpExecutor(options) {
|
|
|
27132
27303
|
currentModelId: void 0,
|
|
27133
27304
|
advertisedModelValues: []
|
|
27134
27305
|
},
|
|
27135
|
-
lastUnmatchedModelId: void 0,
|
|
27136
27306
|
initializeResult: void 0,
|
|
27137
27307
|
startResult: void 0,
|
|
27138
27308
|
startInFlight: void 0,
|
|
@@ -28696,7 +28866,8 @@ var runGrokInspect = async (input2) => {
|
|
|
28696
28866
|
args: GROK_INSPECT_ARGS,
|
|
28697
28867
|
cwd,
|
|
28698
28868
|
owner: { bindingId: host.bindingId },
|
|
28699
|
-
...Object.keys(env).length > 0 ? { env } : {}
|
|
28869
|
+
...Object.keys(env).length > 0 ? { env } : {},
|
|
28870
|
+
...input2.signal === void 0 ? {} : { signal: input2.signal }
|
|
28700
28871
|
});
|
|
28701
28872
|
const run = await collectProbeProcess(handle, {
|
|
28702
28873
|
timeoutMs: INSPECT_TIMEOUT_MS,
|
|
@@ -28782,10 +28953,21 @@ function grokOpenCommands(session) {
|
|
|
28782
28953
|
};
|
|
28783
28954
|
}
|
|
28784
28955
|
|
|
28956
|
+
// src/modelCacheRefresh.ts
|
|
28957
|
+
import * as NodeFS4 from "node:fs";
|
|
28958
|
+
import * as NodePath6 from "node:path";
|
|
28959
|
+
import {
|
|
28960
|
+
ExecutorSpawnAbortedError as ExecutorSpawnAbortedError2,
|
|
28961
|
+
ExecutorSpawnRefusedError as ExecutorSpawnRefusedError2,
|
|
28962
|
+
throwIfExecutorSpawnAborted
|
|
28963
|
+
} from "@ycodium-ai/plugin-api/executor";
|
|
28964
|
+
|
|
28785
28965
|
// src/probeModels.ts
|
|
28786
28966
|
import {
|
|
28787
28967
|
describeExecutorSpawnFailure,
|
|
28788
|
-
|
|
28968
|
+
ExecutorSpawnAbortedError,
|
|
28969
|
+
ExecutorSpawnRefusedError,
|
|
28970
|
+
raceAbort
|
|
28789
28971
|
} from "@ycodium-ai/plugin-api/executor";
|
|
28790
28972
|
import { nodeStreamsFromHandle } from "@ycodium-ai/plugin-api/process-stdin";
|
|
28791
28973
|
var PROBE_REQUEST_TIMEOUT_MS = 2e4;
|
|
@@ -28809,21 +28991,27 @@ async function probeGrokModels(input2) {
|
|
|
28809
28991
|
args: tokenizeLaunchArgs(recipe.launchArgs),
|
|
28810
28992
|
cwd: process.cwd(),
|
|
28811
28993
|
owner: { bindingId: host.bindingId },
|
|
28812
|
-
...Object.keys(env).length > 0 ? { env } : {}
|
|
28994
|
+
...Object.keys(env).length > 0 ? { env } : {},
|
|
28995
|
+
...input2.signal === void 0 ? {} : { signal: input2.signal }
|
|
28813
28996
|
});
|
|
28814
28997
|
} catch (error62) {
|
|
28815
|
-
if (error62 instanceof ExecutorSpawnRefusedError
|
|
28998
|
+
if (error62 instanceof ExecutorSpawnRefusedError || error62 instanceof ExecutorSpawnAbortedError) {
|
|
28999
|
+
throw error62;
|
|
29000
|
+
}
|
|
28816
29001
|
return { kind: "not-started", ...describeExecutorSpawnFailure(error62) };
|
|
28817
29002
|
}
|
|
28818
29003
|
let handshake;
|
|
28819
29004
|
try {
|
|
28820
29005
|
const { readable, writable } = nodeStreamsFromHandle(handle);
|
|
28821
|
-
handshake = await
|
|
28822
|
-
|
|
28823
|
-
|
|
28824
|
-
|
|
28825
|
-
|
|
28826
|
-
|
|
29006
|
+
handshake = await raceAbort(
|
|
29007
|
+
openAcpProbeHandshake({
|
|
29008
|
+
readable,
|
|
29009
|
+
writable,
|
|
29010
|
+
clientInfo: GROK_CLIENT_INFO,
|
|
29011
|
+
requestTimeoutMs: PROBE_REQUEST_TIMEOUT_MS
|
|
29012
|
+
}),
|
|
29013
|
+
input2.signal
|
|
29014
|
+
);
|
|
28827
29015
|
return { kind: "answered", models: grokProbeModels(handshake.initializeResult) };
|
|
28828
29016
|
} catch (error62) {
|
|
28829
29017
|
return { kind: "failed", detail: failureDetail(error62) };
|
|
@@ -28833,34 +29021,195 @@ async function probeGrokModels(input2) {
|
|
|
28833
29021
|
}
|
|
28834
29022
|
}
|
|
28835
29023
|
async function probeGrokVersion(input2) {
|
|
28836
|
-
const { recipe, host, env } = input2;
|
|
29024
|
+
const { recipe, host, env, signal } = input2;
|
|
28837
29025
|
return probeCliVersion({
|
|
28838
29026
|
spawn: () => host.process.spawn({
|
|
28839
29027
|
program: recipe.binaryPath,
|
|
28840
29028
|
args: ["--version"],
|
|
28841
29029
|
owner: { bindingId: host.bindingId },
|
|
28842
|
-
...Object.keys(env).length > 0 ? { env } : {}
|
|
29030
|
+
...Object.keys(env).length > 0 ? { env } : {},
|
|
29031
|
+
...signal === void 0 ? {} : { signal }
|
|
28843
29032
|
}),
|
|
28844
29033
|
timeoutMs: PROBE_REQUEST_TIMEOUT_MS
|
|
28845
29034
|
});
|
|
28846
29035
|
}
|
|
28847
29036
|
|
|
29037
|
+
// src/modelCacheRefresh.ts
|
|
29038
|
+
var GROK_MODEL_CACHE_FILE = "models_cache.json";
|
|
29039
|
+
var GROK_MODEL_CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
29040
|
+
var GROK_MODELS_COMMAND_TIMEOUT_MS = 6e4;
|
|
29041
|
+
function grokModelCachePath(accountHome) {
|
|
29042
|
+
return NodePath6.join(accountHome, GROK_MODEL_CACHE_FILE);
|
|
29043
|
+
}
|
|
29044
|
+
function readGrokModelCacheMtimeMs(accountHome) {
|
|
29045
|
+
try {
|
|
29046
|
+
return NodeFS4.statSync(grokModelCachePath(accountHome)).mtimeMs;
|
|
29047
|
+
} catch {
|
|
29048
|
+
return null;
|
|
29049
|
+
}
|
|
29050
|
+
}
|
|
29051
|
+
function grokModelCacheNeedsRefresh(input2) {
|
|
29052
|
+
if (input2.force) return true;
|
|
29053
|
+
if (input2.cacheMtimeMs === null) return true;
|
|
29054
|
+
if (input2.nowMs - input2.cacheMtimeMs > GROK_MODEL_CACHE_MAX_AGE_MS) return true;
|
|
29055
|
+
return input2.lastSeenCliVersion !== null && input2.cliVersion !== null && input2.cliVersion !== input2.lastSeenCliVersion;
|
|
29056
|
+
}
|
|
29057
|
+
async function drain(stream) {
|
|
29058
|
+
for await (const chunk of stream) {
|
|
29059
|
+
void chunk;
|
|
29060
|
+
}
|
|
29061
|
+
}
|
|
29062
|
+
async function runGrokModelsCommand(input2) {
|
|
29063
|
+
const { recipe, host, env } = input2;
|
|
29064
|
+
throwIfExecutorSpawnAborted(input2.signal);
|
|
29065
|
+
let handle;
|
|
29066
|
+
try {
|
|
29067
|
+
handle = await host.process.spawn({
|
|
29068
|
+
program: recipe.binaryPath,
|
|
29069
|
+
args: ["models"],
|
|
29070
|
+
cwd: process.cwd(),
|
|
29071
|
+
owner: { bindingId: host.bindingId },
|
|
29072
|
+
...Object.keys(env).length > 0 ? { env } : {},
|
|
29073
|
+
...input2.signal === void 0 ? {} : { signal: input2.signal }
|
|
29074
|
+
});
|
|
29075
|
+
} catch (error62) {
|
|
29076
|
+
if (error62 instanceof ExecutorSpawnAbortedError2) throw error62;
|
|
29077
|
+
host.log.warn("grok models could not be started; keeping the previous model list", {
|
|
29078
|
+
detail: error62 instanceof ExecutorSpawnRefusedError2 ? "process limit" : String(error62)
|
|
29079
|
+
});
|
|
29080
|
+
return false;
|
|
29081
|
+
}
|
|
29082
|
+
const finished = Promise.all([drain(handle.stdout), drain(handle.stderr), handle.exited]).then(
|
|
29083
|
+
([, , code]) => code
|
|
29084
|
+
);
|
|
29085
|
+
let timer;
|
|
29086
|
+
const timeout = new Promise((resolve2) => {
|
|
29087
|
+
timer = setTimeout(() => resolve2("timeout"), GROK_MODELS_COMMAND_TIMEOUT_MS);
|
|
29088
|
+
timer.unref?.();
|
|
29089
|
+
});
|
|
29090
|
+
try {
|
|
29091
|
+
const outcome = await Promise.race([finished, timeout]);
|
|
29092
|
+
if (outcome === "timeout") {
|
|
29093
|
+
finished.catch(() => void 0);
|
|
29094
|
+
host.log.warn("grok models timed out; keeping the previous model list", {
|
|
29095
|
+
timeoutMs: GROK_MODELS_COMMAND_TIMEOUT_MS
|
|
29096
|
+
});
|
|
29097
|
+
return false;
|
|
29098
|
+
}
|
|
29099
|
+
if (outcome !== 0) {
|
|
29100
|
+
host.log.warn("grok models exited non-zero; keeping the previous model list", {
|
|
29101
|
+
exitCode: outcome
|
|
29102
|
+
});
|
|
29103
|
+
return false;
|
|
29104
|
+
}
|
|
29105
|
+
return true;
|
|
29106
|
+
} finally {
|
|
29107
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
29108
|
+
await handle.kill("SIGTERM").catch(() => void 0);
|
|
29109
|
+
}
|
|
29110
|
+
}
|
|
29111
|
+
var modelsCommandSlots = /* @__PURE__ */ new Map();
|
|
29112
|
+
function runCoalescedModelsCommand(cachePath, force, run) {
|
|
29113
|
+
const existing = modelsCommandSlots.get(cachePath);
|
|
29114
|
+
if (existing !== void 0) {
|
|
29115
|
+
if (force && existing.followUp === void 0) {
|
|
29116
|
+
const followUp = existing.current.then(
|
|
29117
|
+
() => run(),
|
|
29118
|
+
() => run()
|
|
29119
|
+
);
|
|
29120
|
+
existing.followUp = followUp;
|
|
29121
|
+
void followUp.finally(() => {
|
|
29122
|
+
if (modelsCommandSlots.get(cachePath)?.followUp === followUp) {
|
|
29123
|
+
modelsCommandSlots.delete(cachePath);
|
|
29124
|
+
}
|
|
29125
|
+
});
|
|
29126
|
+
return followUp;
|
|
29127
|
+
}
|
|
29128
|
+
return existing.followUp ?? existing.current;
|
|
29129
|
+
}
|
|
29130
|
+
const current = run();
|
|
29131
|
+
const slot = { current };
|
|
29132
|
+
modelsCommandSlots.set(cachePath, slot);
|
|
29133
|
+
void current.finally(() => {
|
|
29134
|
+
if (slot.followUp !== void 0) return;
|
|
29135
|
+
if (modelsCommandSlots.get(cachePath) === slot) modelsCommandSlots.delete(cachePath);
|
|
29136
|
+
});
|
|
29137
|
+
return current;
|
|
29138
|
+
}
|
|
29139
|
+
function createGrokModelCatalog(input2) {
|
|
29140
|
+
let lastSeenCliVersion = null;
|
|
29141
|
+
let skipUnforced = false;
|
|
29142
|
+
const nowMs = input2.nowMs ?? (() => Date.now());
|
|
29143
|
+
const readCliVersion = (signal) => {
|
|
29144
|
+
if (input2.recipe.binaryPath.length === 0) return Promise.resolve(null);
|
|
29145
|
+
return probeGrokVersion({
|
|
29146
|
+
recipe: input2.recipe,
|
|
29147
|
+
host: input2.host,
|
|
29148
|
+
env: input2.spawnEnv(),
|
|
29149
|
+
...signal === void 0 ? {} : { signal }
|
|
29150
|
+
});
|
|
29151
|
+
};
|
|
29152
|
+
const refreshModelCatalog = async (request) => {
|
|
29153
|
+
try {
|
|
29154
|
+
const cliVersion = request.cliVersion !== void 0 ? request.cliVersion : await readCliVersion();
|
|
29155
|
+
if (!request.force && skipUnforced) {
|
|
29156
|
+
skipUnforced = false;
|
|
29157
|
+
if (cliVersion !== null) lastSeenCliVersion = cliVersion;
|
|
29158
|
+
return;
|
|
29159
|
+
}
|
|
29160
|
+
const needs = grokModelCacheNeedsRefresh({
|
|
29161
|
+
cacheMtimeMs: readGrokModelCacheMtimeMs(input2.accountHome()),
|
|
29162
|
+
nowMs: nowMs(),
|
|
29163
|
+
lastSeenCliVersion,
|
|
29164
|
+
cliVersion,
|
|
29165
|
+
force: request.force
|
|
29166
|
+
});
|
|
29167
|
+
let commandOk = true;
|
|
29168
|
+
if (needs) {
|
|
29169
|
+
commandOk = await runCoalescedModelsCommand(
|
|
29170
|
+
grokModelCachePath(input2.accountHome()),
|
|
29171
|
+
request.force,
|
|
29172
|
+
() => runGrokModelsCommand({
|
|
29173
|
+
recipe: input2.recipe,
|
|
29174
|
+
host: input2.host,
|
|
29175
|
+
env: input2.spawnEnv(),
|
|
29176
|
+
...request.signal === void 0 ? {} : { signal: request.signal }
|
|
29177
|
+
})
|
|
29178
|
+
);
|
|
29179
|
+
}
|
|
29180
|
+
if (cliVersion !== null) lastSeenCliVersion = cliVersion;
|
|
29181
|
+
if (request.force && request.armProbeSkip === true) skipUnforced = true;
|
|
29182
|
+
if (!commandOk && request.surfaceErrors === true) {
|
|
29183
|
+
throw new Error("grok models failed");
|
|
29184
|
+
}
|
|
29185
|
+
} catch (error62) {
|
|
29186
|
+
if (error62 instanceof ExecutorSpawnAbortedError2) throw error62;
|
|
29187
|
+
if (request.surfaceErrors === true) throw error62;
|
|
29188
|
+
input2.host.log.warn("grok model cache refresh failed; keeping the previous model list", {
|
|
29189
|
+
detail: error62 instanceof Error ? error62.message : String(error62)
|
|
29190
|
+
});
|
|
29191
|
+
}
|
|
29192
|
+
};
|
|
29193
|
+
const refreshForSend = () => refreshModelCatalog({ force: true, surfaceErrors: true });
|
|
29194
|
+
return { readCliVersion, refreshModelCatalog, refreshForSend };
|
|
29195
|
+
}
|
|
29196
|
+
|
|
28848
29197
|
// src/grokHome.ts
|
|
28849
29198
|
import * as NodeCrypto2 from "node:crypto";
|
|
28850
29199
|
import * as NodeOS3 from "node:os";
|
|
28851
|
-
import * as
|
|
29200
|
+
import * as NodePath7 from "node:path";
|
|
28852
29201
|
import { mergeLaunchEnv } from "@ycodium-ai/plugin-api/executor";
|
|
28853
29202
|
function expandHomePath(value) {
|
|
28854
29203
|
if (value === "~") return NodeOS3.homedir();
|
|
28855
29204
|
if (value.startsWith("~/") || value.startsWith("~\\")) {
|
|
28856
|
-
return
|
|
29205
|
+
return NodePath7.join(NodeOS3.homedir(), value.slice(2));
|
|
28857
29206
|
}
|
|
28858
29207
|
return value;
|
|
28859
29208
|
}
|
|
28860
29209
|
function resolveGrokHomePath(value) {
|
|
28861
29210
|
const trimmed3 = value.trim();
|
|
28862
|
-
if (trimmed3.length === 0) return
|
|
28863
|
-
return
|
|
29211
|
+
if (trimmed3.length === 0) return NodePath7.join(NodeOS3.homedir(), ".grok");
|
|
29212
|
+
return NodePath7.resolve(expandHomePath(trimmed3));
|
|
28864
29213
|
}
|
|
28865
29214
|
function probeGrokAccount(accountPath) {
|
|
28866
29215
|
const resolved = resolveGrokHomePath(accountPath);
|
|
@@ -28879,12 +29228,12 @@ function grokContinuationKey(recipe) {
|
|
|
28879
29228
|
function resolveAccountHomePath(recipe, configuredHome) {
|
|
28880
29229
|
const fromEnvironment = recipe.environment["GROK_HOME"]?.trim();
|
|
28881
29230
|
if (fromEnvironment !== void 0 && fromEnvironment.length > 0) {
|
|
28882
|
-
return
|
|
29231
|
+
return NodePath7.resolve(expandHomePath(fromEnvironment));
|
|
28883
29232
|
}
|
|
28884
29233
|
return resolveGrokHomePath(configuredHome);
|
|
28885
29234
|
}
|
|
28886
29235
|
function absentPath() {
|
|
28887
|
-
return
|
|
29236
|
+
return NodePath7.join(
|
|
28888
29237
|
NodeOS3.tmpdir(),
|
|
28889
29238
|
`ycodium-grok-no-auth-${NodeCrypto2.randomUUID()}`,
|
|
28890
29239
|
"auth.json"
|
|
@@ -29012,7 +29361,22 @@ var createGrokInstance = (config2, host) => {
|
|
|
29012
29361
|
return { status: "unknown" };
|
|
29013
29362
|
}
|
|
29014
29363
|
};
|
|
29015
|
-
const
|
|
29364
|
+
const spawnEnv = () => {
|
|
29365
|
+
const configuredHome = recipe.homePath;
|
|
29366
|
+
return buildSpawnEnv(
|
|
29367
|
+
recipe,
|
|
29368
|
+
resolveGrokHomePath(configuredHome),
|
|
29369
|
+
configuredHome.trim().length > 0
|
|
29370
|
+
);
|
|
29371
|
+
};
|
|
29372
|
+
const modelCatalog = createGrokModelCatalog({
|
|
29373
|
+
recipe,
|
|
29374
|
+
host,
|
|
29375
|
+
spawnEnv,
|
|
29376
|
+
accountHome: () => resolveAccountHomePath(recipe, recipe.homePath)
|
|
29377
|
+
});
|
|
29378
|
+
const probe = async (signal) => {
|
|
29379
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
29016
29380
|
if (recipe.binaryPath.length === 0) {
|
|
29017
29381
|
return {
|
|
29018
29382
|
installed: false,
|
|
@@ -29022,17 +29386,35 @@ var createGrokInstance = (config2, host) => {
|
|
|
29022
29386
|
detail: "Grok program path is not configured"
|
|
29023
29387
|
};
|
|
29024
29388
|
}
|
|
29025
|
-
const
|
|
29026
|
-
const
|
|
29027
|
-
|
|
29028
|
-
|
|
29029
|
-
|
|
29389
|
+
const env = spawnEnv();
|
|
29390
|
+
const version2 = await raceAbort2(modelCatalog.readCliVersion(signal), signal);
|
|
29391
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
29392
|
+
await raceAbort2(
|
|
29393
|
+
modelCatalog.refreshModelCatalog({
|
|
29394
|
+
force: false,
|
|
29395
|
+
cliVersion: version2,
|
|
29396
|
+
...signal === void 0 ? {} : { signal }
|
|
29397
|
+
}),
|
|
29398
|
+
signal
|
|
29399
|
+
);
|
|
29400
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
29401
|
+
const [outcome, skills] = await raceAbort2(
|
|
29402
|
+
Promise.all([
|
|
29403
|
+
probeGrokModels({
|
|
29404
|
+
recipe,
|
|
29405
|
+
host,
|
|
29406
|
+
env,
|
|
29407
|
+
...signal === void 0 ? {} : { signal }
|
|
29408
|
+
}),
|
|
29409
|
+
probeGrokSkills({
|
|
29410
|
+
recipe,
|
|
29411
|
+
host,
|
|
29412
|
+
env,
|
|
29413
|
+
...signal === void 0 ? {} : { signal }
|
|
29414
|
+
})
|
|
29415
|
+
]),
|
|
29416
|
+
signal
|
|
29030
29417
|
);
|
|
29031
|
-
const [outcome, version2, skills] = await Promise.all([
|
|
29032
|
-
probeGrokModels({ recipe, host, env }),
|
|
29033
|
-
probeGrokVersion({ recipe, host, env }),
|
|
29034
|
-
probeGrokSkills({ recipe, host, env })
|
|
29035
|
-
]);
|
|
29036
29418
|
if (outcome.kind !== "answered") {
|
|
29037
29419
|
return {
|
|
29038
29420
|
installed: outcome.kind === "failed",
|
|
@@ -29057,21 +29439,18 @@ var createGrokInstance = (config2, host) => {
|
|
|
29057
29439
|
};
|
|
29058
29440
|
const instance = {
|
|
29059
29441
|
probe,
|
|
29442
|
+
readCliVersion: () => modelCatalog.readCliVersion(),
|
|
29443
|
+
refreshModelCatalog: ({ force }) => modelCatalog.refreshModelCatalog({ force, armProbeSkip: force }),
|
|
29060
29444
|
// 项目里的技能:探测在 home 下跑 inspect 只看得到整机的;这里在项目目录里再跑一次。
|
|
29061
29445
|
workspaceInventory: ({ cwd }) => {
|
|
29062
29446
|
if (recipe.binaryPath.length === 0) {
|
|
29063
29447
|
return Promise.reject(new Error("Grok program path is not configured"));
|
|
29064
29448
|
}
|
|
29065
|
-
const configuredHome = recipe.homePath;
|
|
29066
29449
|
return inspectGrokWorkspace({
|
|
29067
29450
|
recipe,
|
|
29068
29451
|
host,
|
|
29069
29452
|
cwd,
|
|
29070
|
-
env:
|
|
29071
|
-
recipe,
|
|
29072
|
-
resolveGrokHomePath(configuredHome),
|
|
29073
|
-
configuredHome.trim().length > 0
|
|
29074
|
-
)
|
|
29453
|
+
env: spawnEnv()
|
|
29075
29454
|
});
|
|
29076
29455
|
},
|
|
29077
29456
|
// 有意不处理 input.readOnlyDirectories:grok 的 read_file 在每一档都不弹审批、
|
|
@@ -29090,12 +29469,7 @@ var createGrokInstance = (config2, host) => {
|
|
|
29090
29469
|
if (disposed) throw new Error("instance disposed");
|
|
29091
29470
|
throwIfStartAborted(signal);
|
|
29092
29471
|
}
|
|
29093
|
-
const
|
|
29094
|
-
const env = buildSpawnEnv(
|
|
29095
|
-
recipe,
|
|
29096
|
-
resolveGrokHomePath(configuredHome),
|
|
29097
|
-
configuredHome.trim().length > 0
|
|
29098
|
-
);
|
|
29472
|
+
const env = spawnEnv();
|
|
29099
29473
|
const effort = modelSelectionStringOption(input2.modelSelection, GROK_REASONING_OPTION_ID);
|
|
29100
29474
|
const baseArgs = tokenizeLaunchArgs(recipe.launchArgs);
|
|
29101
29475
|
const insertAt = baseArgs.length <= 1 ? baseArgs.length : baseArgs.length - 1;
|
|
@@ -29185,6 +29559,7 @@ var createGrokInstance = (config2, host) => {
|
|
|
29185
29559
|
}
|
|
29186
29560
|
},
|
|
29187
29561
|
...requestedModelId === void 0 ? {} : { requestedModelId },
|
|
29562
|
+
refreshCatalogForSend: () => modelCatalog.refreshForSend(),
|
|
29188
29563
|
...input2.bindingGeneration === void 0 ? {} : { originBindingGeneration: input2.bindingGeneration },
|
|
29189
29564
|
// 内置编排那一条排第一,后面跟用户在插件中心配的;缺了这一栏,
|
|
29190
29565
|
// `mcp__ycodium__*` 整套工具(浏览器等)在这只手的会话里都不存在。
|
|
@@ -29401,7 +29776,7 @@ var executors = [
|
|
|
29401
29776
|
];
|
|
29402
29777
|
var index_default = definePlugin({
|
|
29403
29778
|
id: "agent-grok",
|
|
29404
|
-
apiVersion:
|
|
29779
|
+
apiVersion: 31,
|
|
29405
29780
|
executors,
|
|
29406
29781
|
activate(ctx) {
|
|
29407
29782
|
ctx.log.info("grok executor ready");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ycodium-ai/agent-grok",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3265",
|
|
4
4
|
"description": "Grok executor: grok CLI over Agent Client Protocol stdio.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ycodium",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"@types/node": "24.12.4",
|
|
22
22
|
"esbuild": "0.28.1",
|
|
23
23
|
"vite-plus": "0.2.2",
|
|
24
|
-
"ycodium-
|
|
25
|
-
"
|
|
24
|
+
"@ycodium-ai/plugin-api": "0.2.3265",
|
|
25
|
+
"ycodium-acp-executor": "0.2.3265",
|
|
26
26
|
"ycodium-executor-testing": "0.0.0"
|
|
27
27
|
},
|
|
28
28
|
"ycodium": {
|