scream-code 0.10.1 → 0.10.2
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.
|
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
|
|
|
6
6
|
import { a as __toESM, i as __require, r as __exportAll, t as __commonJSMin } from "./chunk-apG1qJts.mjs";
|
|
7
7
|
import "./suppress-sqlite-warning-C2VB0doZ.mjs";
|
|
8
8
|
import { C as join$1, D as resolve$1, E as relative$1, S as isAbsolute$1, T as parse$7, a as isSupportedFile, b as basename$1, i as ingestFile, r as ingestDirectory, t as multiSearch, w as normalize, x as dirname$2, y as KnowledgeStore } from "./src-oeUnRY3N.mjs";
|
|
9
|
-
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-
|
|
9
|
+
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-Cj7OClhs.mjs";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
12
12
|
import Jn, { access, appendFile, chmod, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
@@ -62543,8 +62543,8 @@ function parseSkillText(options) {
|
|
|
62543
62543
|
if (!isRecord$6(frontmatter)) throw new SkillParseError(`Frontmatter in ${options.skillMdPath} must be a mapping at the top level`);
|
|
62544
62544
|
const metadata = normalizeMetadata(frontmatter);
|
|
62545
62545
|
if (!isSupportedSkillType(metadata.type)) throw new UnsupportedSkillTypeError(metadata.type ?? String(frontmatter["type"]));
|
|
62546
|
-
const name = nonEmptyString$
|
|
62547
|
-
const description = nonEmptyString$
|
|
62546
|
+
const name = nonEmptyString$1(metadata.name);
|
|
62547
|
+
const description = nonEmptyString$1(metadata.description);
|
|
62548
62548
|
if (isDirectorySkill && (name === void 0 || description === void 0)) throw new SkillParseError(`Missing required frontmatter field ${name === void 0 ? "\"name\"" : "\"description\""} in ${options.skillMdPath}`);
|
|
62549
62549
|
const skillPath = resolve$1(options.skillMdPath);
|
|
62550
62550
|
const content = parsed.body.trim();
|
|
@@ -62604,11 +62604,11 @@ function normalizeMetadata(raw) {
|
|
|
62604
62604
|
const key = METADATA_ALIASES[rawKey] ?? rawKey;
|
|
62605
62605
|
out[key] = value;
|
|
62606
62606
|
}
|
|
62607
|
-
const type = nonEmptyString$
|
|
62607
|
+
const type = nonEmptyString$1(out["type"]);
|
|
62608
62608
|
if (type !== void 0) out["type"] = type;
|
|
62609
|
-
const name = nonEmptyString$
|
|
62609
|
+
const name = nonEmptyString$1(out["name"]);
|
|
62610
62610
|
if (name !== void 0) out["name"] = name;
|
|
62611
|
-
const description = nonEmptyString$
|
|
62611
|
+
const description = nonEmptyString$1(out["description"]);
|
|
62612
62612
|
if (description !== void 0) out["description"] = description;
|
|
62613
62613
|
return out;
|
|
62614
62614
|
}
|
|
@@ -62650,7 +62650,7 @@ function tokenizeArgs(raw) {
|
|
|
62650
62650
|
if (hasContent) out.push(current);
|
|
62651
62651
|
return out;
|
|
62652
62652
|
}
|
|
62653
|
-
function nonEmptyString$
|
|
62653
|
+
function nonEmptyString$1(value) {
|
|
62654
62654
|
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
62655
62655
|
}
|
|
62656
62656
|
function isRecord$6(value) {
|
|
@@ -69652,17 +69652,13 @@ const HookDefSchema = z.object({
|
|
|
69652
69652
|
command: z.string().min(1),
|
|
69653
69653
|
timeout: z.number().int().min(1).max(600).optional()
|
|
69654
69654
|
}).strict();
|
|
69655
|
-
const ScreamCliServiceConfigSchema = z.object({
|
|
69656
|
-
baseUrl: z.string().optional(),
|
|
69657
|
-
apiKey: z.string().optional(),
|
|
69658
|
-
oauth: OAuthRefSchema.optional(),
|
|
69659
|
-
customHeaders: StringRecordSchema.optional()
|
|
69660
|
-
});
|
|
69661
69655
|
const DuckDuckGoConfigSchema = z.object({ enabled: z.boolean().default(true) });
|
|
69656
|
+
const DomesticSearchConfigSchema = z.object({ enabled: z.boolean().default(true) });
|
|
69662
69657
|
const ServicesConfigSchema = z.object({
|
|
69663
|
-
|
|
69664
|
-
|
|
69665
|
-
|
|
69658
|
+
duckduckgo: DuckDuckGoConfigSchema.optional(),
|
|
69659
|
+
sogou: DomesticSearchConfigSchema.optional(),
|
|
69660
|
+
so360: DomesticSearchConfigSchema.optional(),
|
|
69661
|
+
baidu: DomesticSearchConfigSchema.optional()
|
|
69666
69662
|
});
|
|
69667
69663
|
const McpServerCommonFields = {
|
|
69668
69664
|
enabled: z.boolean().optional(),
|
|
@@ -69736,12 +69732,13 @@ const ThinkingConfigPatchSchema = ThinkingConfigSchema.partial();
|
|
|
69736
69732
|
const PermissionConfigPatchSchema = PermissionConfigSchema.partial();
|
|
69737
69733
|
const LoopControlPatchSchema = LoopControlSchema.partial();
|
|
69738
69734
|
const BackgroundConfigPatchSchema = BackgroundConfigSchema.partial();
|
|
69739
|
-
const ScreamCliServiceConfigPatchSchema = ScreamCliServiceConfigSchema.partial();
|
|
69740
69735
|
const DuckDuckGoConfigPatchSchema = DuckDuckGoConfigSchema.partial();
|
|
69736
|
+
const DomesticSearchConfigPatchSchema = DomesticSearchConfigSchema.partial();
|
|
69741
69737
|
const ServicesConfigPatchSchema = z.object({
|
|
69742
|
-
|
|
69743
|
-
|
|
69744
|
-
|
|
69738
|
+
duckduckgo: DuckDuckGoConfigPatchSchema.optional(),
|
|
69739
|
+
sogou: DomesticSearchConfigPatchSchema.optional(),
|
|
69740
|
+
so360: DomesticSearchConfigPatchSchema.optional(),
|
|
69741
|
+
baidu: DomesticSearchConfigPatchSchema.optional()
|
|
69745
69742
|
});
|
|
69746
69743
|
const ScreamConfigPatchSchema = z.object({
|
|
69747
69744
|
providers: z.record(z.string(), ProviderConfigPatchSchema).optional(),
|
|
@@ -78352,9 +78349,12 @@ var WebSearchTool = class {
|
|
|
78352
78349
|
execute: (ctx) => this.execution(args, ctx)
|
|
78353
78350
|
};
|
|
78354
78351
|
}
|
|
78355
|
-
async execution(args, { toolCallId }) {
|
|
78352
|
+
async execution(args, { signal, toolCallId }) {
|
|
78356
78353
|
try {
|
|
78357
|
-
const opts = {
|
|
78354
|
+
const opts = {
|
|
78355
|
+
signal,
|
|
78356
|
+
toolCallId
|
|
78357
|
+
};
|
|
78358
78358
|
if (args.limit !== void 0) opts.limit = args.limit;
|
|
78359
78359
|
if (args.include_content !== void 0) opts.includeContent = args.include_content;
|
|
78360
78360
|
const results = await this.provider.search(args.query, opts);
|
|
@@ -99968,17 +99968,18 @@ function permissionRuleToToml(rule) {
|
|
|
99968
99968
|
}
|
|
99969
99969
|
function servicesToToml(services, rawServices) {
|
|
99970
99970
|
const out = cloneRecord(rawServices);
|
|
99971
|
-
|
|
99972
|
-
|
|
99973
|
-
|
|
99974
|
-
|
|
99975
|
-
|
|
99976
|
-
|
|
99977
|
-
|
|
99978
|
-
|
|
99979
|
-
|
|
99980
|
-
|
|
99981
|
-
|
|
99971
|
+
for (const key of [
|
|
99972
|
+
"duckduckgo",
|
|
99973
|
+
"sogou",
|
|
99974
|
+
"so360",
|
|
99975
|
+
"baidu"
|
|
99976
|
+
]) {
|
|
99977
|
+
const toggle = services[key];
|
|
99978
|
+
if (toggle?.enabled !== void 0) out[key] = {
|
|
99979
|
+
...isPlainObject$1(out[key]) ? out[key] : {},
|
|
99980
|
+
enabled: toggle.enabled
|
|
99981
|
+
};
|
|
99982
|
+
}
|
|
99982
99983
|
return out;
|
|
99983
99984
|
}
|
|
99984
99985
|
function loopControlToToml(loopControl, rawLoopControl) {
|
|
@@ -117281,7 +117282,7 @@ function isPrivateIp(ip) {
|
|
|
117281
117282
|
}
|
|
117282
117283
|
return lower === "::1" || lower === "::" || lower.startsWith("fe80:") || lower.startsWith("fc") || lower.startsWith("fd");
|
|
117283
117284
|
}
|
|
117284
|
-
function cacheKey
|
|
117285
|
+
function cacheKey(url, allowPrivate, maxBytes, userAgent) {
|
|
117285
117286
|
return `local:${url}:${String(allowPrivate)}:${String(maxBytes)}:${userAgent}`;
|
|
117286
117287
|
}
|
|
117287
117288
|
const defaultDnsLookup = async (hostname) => {
|
|
@@ -117303,7 +117304,7 @@ var LocalFetchURLProvider = class {
|
|
|
117303
117304
|
this.dnsLookup = options.dnsLookup ?? defaultDnsLookup;
|
|
117304
117305
|
}
|
|
117305
117306
|
async fetch(url, _options) {
|
|
117306
|
-
const key = cacheKey
|
|
117307
|
+
const key = cacheKey(url, this.allowPrivateAddresses, this.maxBytes, this.userAgent);
|
|
117307
117308
|
const cached = this.cache.get(key);
|
|
117308
117309
|
if (cached !== void 0) return cached;
|
|
117309
117310
|
await assertSafeFetchTarget(url, this.allowPrivateAddresses, this.dnsLookup);
|
|
@@ -117362,268 +117363,220 @@ var LocalFetchURLProvider = class {
|
|
|
117362
117363
|
}
|
|
117363
117364
|
};
|
|
117364
117365
|
//#endregion
|
|
117365
|
-
//#region ../../packages/agent-core/src/tools/providers/
|
|
117366
|
+
//#region ../../packages/agent-core/src/tools/providers/search-timeout.ts
|
|
117366
117367
|
/**
|
|
117367
|
-
*
|
|
117368
|
+
* Hard timeout for outbound web-search requests (omp pattern).
|
|
117368
117369
|
*
|
|
117369
|
-
*
|
|
117370
|
-
*
|
|
117371
|
-
*
|
|
117372
|
-
*
|
|
117373
|
-
|
|
117374
|
-
|
|
117375
|
-
|
|
117376
|
-
|
|
117377
|
-
|
|
117378
|
-
|
|
117379
|
-
*/
|
|
117380
|
-
function cacheKey(baseUrl, url) {
|
|
117381
|
-
return `cli:${baseUrl}:${url}`;
|
|
117382
|
-
}
|
|
117383
|
-
var ScreamCliFetchURLProvider = class {
|
|
117384
|
-
tokenProvider;
|
|
117385
|
-
apiKey;
|
|
117386
|
-
baseUrl;
|
|
117387
|
-
defaultHeaders;
|
|
117388
|
-
customHeaders;
|
|
117389
|
-
localFallback;
|
|
117390
|
-
fetchImpl;
|
|
117391
|
-
cache;
|
|
117392
|
-
constructor(options) {
|
|
117393
|
-
this.tokenProvider = options.tokenProvider;
|
|
117394
|
-
this.apiKey = options.apiKey;
|
|
117395
|
-
this.baseUrl = options.baseUrl;
|
|
117396
|
-
this.defaultHeaders = options.defaultHeaders ?? {};
|
|
117397
|
-
this.customHeaders = options.customHeaders ?? {};
|
|
117398
|
-
this.localFallback = options.localFallback;
|
|
117399
|
-
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
117400
|
-
this.cache = options.cache ?? new FetchCache();
|
|
117401
|
-
}
|
|
117402
|
-
async fetch(url, options) {
|
|
117403
|
-
const key = cacheKey(this.baseUrl, url);
|
|
117404
|
-
const cached = this.cache.get(key);
|
|
117405
|
-
if (cached !== void 0) return cached;
|
|
117406
|
-
const result = await this.fetchFresh(url, options?.toolCallId);
|
|
117407
|
-
this.cache.set(key, result);
|
|
117408
|
-
return result;
|
|
117409
|
-
}
|
|
117410
|
-
async fetchFresh(url, toolCallId) {
|
|
117411
|
-
try {
|
|
117412
|
-
return {
|
|
117413
|
-
content: await this.fetchViaScreamCli(url, toolCallId),
|
|
117414
|
-
kind: "extracted"
|
|
117415
|
-
};
|
|
117416
|
-
} catch {
|
|
117417
|
-
return this.localFallback.fetch(url, { toolCallId });
|
|
117418
|
-
}
|
|
117419
|
-
}
|
|
117420
|
-
async fetchViaScreamCli(url, toolCallId) {
|
|
117421
|
-
const bodyJson = JSON.stringify({ url });
|
|
117422
|
-
const response = await this.post(bodyJson, toolCallId);
|
|
117423
|
-
if (response.status !== 200) {
|
|
117424
|
-
let detail = "";
|
|
117425
|
-
try {
|
|
117426
|
-
detail = await response.text();
|
|
117427
|
-
} catch {}
|
|
117428
|
-
throw new HttpFetchError(response.status, `ScreamCli fetch request failed: HTTP ${String(response.status)}. ${detail}`.trim());
|
|
117429
|
-
}
|
|
117430
|
-
return response.text();
|
|
117431
|
-
}
|
|
117432
|
-
async post(bodyJson, toolCallId) {
|
|
117433
|
-
const accessToken = await this.resolveApiKey();
|
|
117434
|
-
return this.fetchImpl(this.baseUrl, {
|
|
117435
|
-
method: "POST",
|
|
117436
|
-
headers: {
|
|
117437
|
-
...this.defaultHeaders,
|
|
117438
|
-
Authorization: `Bearer ${accessToken}`,
|
|
117439
|
-
Accept: "text/markdown",
|
|
117440
|
-
"Content-Type": "application/json",
|
|
117441
|
-
...toolCallId !== void 0 && toolCallId.length > 0 ? { "X-Msh-Tool-Call-Id": toolCallId } : {},
|
|
117442
|
-
...this.customHeaders
|
|
117443
|
-
},
|
|
117444
|
-
body: bodyJson
|
|
117445
|
-
});
|
|
117446
|
-
}
|
|
117447
|
-
async resolveApiKey() {
|
|
117448
|
-
if (this.tokenProvider !== void 0) try {
|
|
117449
|
-
const token = await this.tokenProvider.getAccessToken();
|
|
117450
|
-
if (token.trim().length > 0) return token;
|
|
117451
|
-
if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
|
|
117452
|
-
} catch (error) {
|
|
117453
|
-
if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
|
|
117454
|
-
throw error;
|
|
117455
|
-
}
|
|
117456
|
-
if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
|
|
117457
|
-
throw new Error("ScreamCli fetch service is not configured: missing API key or token provider.");
|
|
117458
|
-
}
|
|
117459
|
-
};
|
|
117370
|
+
* A stalled TCP/TLS connection can keep an undici fetch pending for minutes;
|
|
117371
|
+
* composing the caller's signal with a fixed ceiling guarantees the request
|
|
117372
|
+
* settles. Fires as a `TimeoutError` DOMException, which the tool layer maps
|
|
117373
|
+
* to "Search timed out".
|
|
117374
|
+
*/
|
|
117375
|
+
const SEARCH_HARD_TIMEOUT_MS = 6e4;
|
|
117376
|
+
function withHardTimeout(signal, ms = SEARCH_HARD_TIMEOUT_MS) {
|
|
117377
|
+
const timeout = AbortSignal.timeout(ms);
|
|
117378
|
+
return signal !== void 0 ? AbortSignal.any([signal, timeout]) : timeout;
|
|
117379
|
+
}
|
|
117460
117380
|
//#endregion
|
|
117461
117381
|
//#region ../../packages/agent-core/src/tools/providers/duckduckgo-search.ts
|
|
117382
|
+
const DUCKDUCKGO_HTML_URL = "https://html.duckduckgo.com/html/";
|
|
117383
|
+
/**
|
|
117384
|
+
* Browser-like UA so DDG serves the standard results page instead of the
|
|
117385
|
+
* mobile/noscript variants, and is less likely to trip bot detection.
|
|
117386
|
+
* DDG answers automation it suspects with HTTP 202 plus an anomaly modal;
|
|
117387
|
+
* the body check (not the status) is the reliable signal.
|
|
117388
|
+
*/
|
|
117389
|
+
const BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36";
|
|
117462
117390
|
var DuckDuckGoSearchProvider = class {
|
|
117391
|
+
name = "duckduckgo";
|
|
117463
117392
|
fetchImpl;
|
|
117464
117393
|
constructor(options = {}) {
|
|
117465
117394
|
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
117466
117395
|
}
|
|
117467
117396
|
async search(query, options) {
|
|
117468
117397
|
const limit = options?.limit ?? 5;
|
|
117469
|
-
|
|
117470
|
-
|
|
117471
|
-
|
|
117472
|
-
|
|
117473
|
-
|
|
117474
|
-
|
|
117475
|
-
|
|
117476
|
-
|
|
117477
|
-
|
|
117478
|
-
|
|
117479
|
-
|
|
117480
|
-
|
|
117481
|
-
|
|
117482
|
-
|
|
117483
|
-
|
|
117484
|
-
|
|
117485
|
-
|
|
117486
|
-
|
|
117487
|
-
|
|
117488
|
-
if (response.status !==
|
|
117489
|
-
|
|
117490
|
-
|
|
117491
|
-
}
|
|
117492
|
-
let json;
|
|
117493
|
-
try {
|
|
117494
|
-
json = await response.json();
|
|
117495
|
-
} catch {
|
|
117496
|
-
return [];
|
|
117497
|
-
}
|
|
117498
|
-
return this.buildApiResults(json, limit);
|
|
117499
|
-
}
|
|
117500
|
-
buildApiResults(json, limit) {
|
|
117501
|
-
const results = [];
|
|
117502
|
-
if (json.AbstractText && json.AbstractURL && json.AbstractText.trim().length > 0) results.push({
|
|
117503
|
-
title: json.Heading && json.Heading.trim().length > 0 ? json.Heading : json.AbstractSource ?? "DuckDuckGo",
|
|
117504
|
-
url: json.AbstractURL,
|
|
117505
|
-
snippet: json.AbstractText
|
|
117506
|
-
});
|
|
117507
|
-
if (json.RelatedTopics && json.RelatedTopics.length > 0) {
|
|
117508
|
-
const flattened = flattenRelatedTopics(json.RelatedTopics);
|
|
117509
|
-
for (const topic of flattened) {
|
|
117510
|
-
if (results.length >= limit) break;
|
|
117511
|
-
const url = topic.FirstURL ?? "";
|
|
117512
|
-
const text = topic.Text ?? "";
|
|
117513
|
-
if (url === "" || text === "") continue;
|
|
117514
|
-
results.push({
|
|
117515
|
-
title: text,
|
|
117516
|
-
url,
|
|
117517
|
-
snippet: text
|
|
117518
|
-
});
|
|
117519
|
-
}
|
|
117520
|
-
}
|
|
117521
|
-
return results.slice(0, limit);
|
|
117522
|
-
}
|
|
117523
|
-
/**
|
|
117524
|
-
* Scrape DuckDuckGo Lite search results.
|
|
117525
|
-
*
|
|
117526
|
-
* Lite is a minimal HTML table with no JavaScript, designed for basic
|
|
117527
|
-
* browsers. It may return a CAPTCHA page from datacenter IPs — in that
|
|
117528
|
-
* case we return `[]` so the caller can fall through.
|
|
117529
|
-
*/
|
|
117530
|
-
async searchViaLite(query, limit) {
|
|
117531
|
-
const url = `https://lite.duckduckgo.com/lite/?q=${encodeURIComponent(query)}`;
|
|
117532
|
-
let response;
|
|
117533
|
-
try {
|
|
117534
|
-
response = await this.fetchImpl(url);
|
|
117535
|
-
} catch {
|
|
117536
|
-
return [];
|
|
117537
|
-
}
|
|
117538
|
-
if (response.status !== 200) {
|
|
117539
|
-
await drainBody(response);
|
|
117540
|
-
return [];
|
|
117541
|
-
}
|
|
117542
|
-
let html;
|
|
117543
|
-
try {
|
|
117544
|
-
html = await response.text();
|
|
117545
|
-
} catch {
|
|
117546
|
-
return [];
|
|
117547
|
-
}
|
|
117548
|
-
if (isCaptchaPage(html)) return [];
|
|
117549
|
-
return parseLiteResults(html, limit);
|
|
117398
|
+
const form = new URLSearchParams({
|
|
117399
|
+
q: query,
|
|
117400
|
+
kl: "us-en"
|
|
117401
|
+
});
|
|
117402
|
+
form.set("b", "");
|
|
117403
|
+
const response = await this.fetchImpl(DUCKDUCKGO_HTML_URL, {
|
|
117404
|
+
method: "POST",
|
|
117405
|
+
body: form.toString(),
|
|
117406
|
+
headers: {
|
|
117407
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
|
|
117408
|
+
"Accept-Language": "en,en-US;q=0.9",
|
|
117409
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
117410
|
+
Referer: "https://html.duckduckgo.com/",
|
|
117411
|
+
"Upgrade-Insecure-Requests": "1",
|
|
117412
|
+
"User-Agent": BROWSER_USER_AGENT
|
|
117413
|
+
},
|
|
117414
|
+
signal: withHardTimeout(options?.signal)
|
|
117415
|
+
});
|
|
117416
|
+
const html = await response.text();
|
|
117417
|
+
if (!response.ok && response.status !== 202) throw new Error(`DuckDuckGo request failed: HTTP ${String(response.status)}`);
|
|
117418
|
+
if (isAnomalyResponse(html)) throw new Error("DuckDuckGo blocked the request with a bot-detection challenge (common from datacenter/shared-egress IPs)");
|
|
117419
|
+
return parseHtmlResults(html).slice(0, limit);
|
|
117550
117420
|
}
|
|
117551
117421
|
};
|
|
117552
|
-
/**
|
|
117553
|
-
|
|
117554
|
-
|
|
117555
|
-
* The API nests topics in two ways:
|
|
117556
|
-
* - A direct topic has `FirstURL` + `Text` (may lack `Topics`).
|
|
117557
|
-
* - A category group has a `Name` and nested `Topics[]`.
|
|
117558
|
-
* We walk both shapes, returning a flat list of leaf topics.
|
|
117559
|
-
*/
|
|
117560
|
-
function flattenRelatedTopics(topics) {
|
|
117561
|
-
const out = [];
|
|
117562
|
-
for (const topic of topics) if (topic.Topics && topic.Topics.length > 0) out.push(...flattenRelatedTopics(topic.Topics));
|
|
117563
|
-
else if (topic.FirstURL) out.push(topic);
|
|
117564
|
-
return out;
|
|
117422
|
+
/** `true` when DDG returned the bot-challenge modal instead of results. */
|
|
117423
|
+
function isAnomalyResponse(html) {
|
|
117424
|
+
return html.includes("anomaly-modal") || html.includes("anomaly.js");
|
|
117565
117425
|
}
|
|
117566
117426
|
/**
|
|
117567
|
-
*
|
|
117427
|
+
* Each result lives in a `<div class="result …">` container with
|
|
117428
|
+
* `<a class="result__a">` for the title link and an optional
|
|
117429
|
+
* `<a|div|span class="result__snippet">` sibling for the preview text.
|
|
117430
|
+
* Sponsored rows, missing snippets, and the pagination row are tolerated.
|
|
117568
117431
|
*/
|
|
117569
|
-
|
|
117570
|
-
|
|
117432
|
+
const RESULT_BLOCK_RE = /<div\b[^>]*\bclass="[^"]*\bresult\b[^"]*"[^>]*>([\s\S]*?)(?=<div\b[^>]*\bclass="[^"]*\bresult\b|<div\b[^>]*\bclass="[^"]*\bnav-link\b|$)/g;
|
|
117433
|
+
const RESULT_TITLE_RE = /<a\b[^>]*\bclass="[^"]*\bresult__a\b[^"]*"[^>]*\bhref="([^"]+)"[^>]*>([\s\S]*?)<\/a>/;
|
|
117434
|
+
const RESULT_SNIPPET_RE = /<(?:a|div|span)\b[^>]*\bclass="[^"]*\bresult__snippet\b[^"]*"[^>]*>([\s\S]*?)<\/(?:a|div|span)>/;
|
|
117435
|
+
/** Strip inline tags (DDG wraps query terms in `<b>`) and decode entities. */
|
|
117436
|
+
function decodeHtmlText$1(value) {
|
|
117437
|
+
return value.replace(/<[^>]*>/g, " ").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code))).replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCharCode(Number.parseInt(code, 16))).replace(/ /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, "\"").replace(/'|'/gi, "'").replace(/\s+/g, " ").trim();
|
|
117571
117438
|
}
|
|
117572
117439
|
/**
|
|
117573
|
-
*
|
|
117574
|
-
*
|
|
117575
|
-
*
|
|
117576
|
-
*
|
|
117577
|
-
* The Lite markup is intentionally minimal (no JS, no CSS classes beyond a
|
|
117578
|
-
* handful of utility names), so a DOM parser is unnecessary overhead.
|
|
117440
|
+
* Resolve a DDG result href to the underlying target URL. DDG routes
|
|
117441
|
+
* outbound clicks through `//duckduckgo.com/l/?uddg=<encoded>`; handles
|
|
117442
|
+
* redirect wrappers, protocol-relative links, and plain absolute URLs.
|
|
117579
117443
|
*/
|
|
117580
|
-
|
|
117581
|
-
|
|
117582
|
-
|
|
117583
|
-
|
|
117584
|
-
|
|
117585
|
-
|
|
117586
|
-
/** Unwrap a DuckDuckGo redirect URL to the real target if possible. */
|
|
117587
|
-
function unwrapDdgRedirect(url) {
|
|
117588
|
-
const uddgMatch = url.match(/[?&]uddg=([^&]+)/);
|
|
117589
|
-
if (uddgMatch?.[1]) try {
|
|
117590
|
-
return decodeURIComponent(uddgMatch[1]);
|
|
117444
|
+
function unwrapResultUrl(href) {
|
|
117445
|
+
if (href === "") return void 0;
|
|
117446
|
+
const decoded = href.replace(/&/gi, "&");
|
|
117447
|
+
const wrapMatch = decoded.match(/[?&]uddg=([^&]+)/);
|
|
117448
|
+
if (wrapMatch?.[1] !== void 0) try {
|
|
117449
|
+
return decodeURIComponent(wrapMatch[1]);
|
|
117591
117450
|
} catch {
|
|
117592
|
-
return
|
|
117451
|
+
return;
|
|
117593
117452
|
}
|
|
117594
|
-
return
|
|
117453
|
+
if (decoded.startsWith("//")) return `https:${decoded}`;
|
|
117454
|
+
if (decoded.startsWith("http://") || decoded.startsWith("https://")) return decoded;
|
|
117595
117455
|
}
|
|
117596
|
-
function
|
|
117456
|
+
function parseHtmlResults(html) {
|
|
117597
117457
|
const results = [];
|
|
117598
|
-
const
|
|
117599
|
-
for (const match of
|
|
117600
|
-
|
|
117601
|
-
const
|
|
117602
|
-
|
|
117603
|
-
|
|
117458
|
+
const seen = /* @__PURE__ */ new Set();
|
|
117459
|
+
for (const match of html.matchAll(RESULT_BLOCK_RE)) {
|
|
117460
|
+
const block = match[1] ?? "";
|
|
117461
|
+
const title = RESULT_TITLE_RE.exec(block);
|
|
117462
|
+
if (title === null) continue;
|
|
117463
|
+
const url = unwrapResultUrl(title[1] ?? "");
|
|
117464
|
+
if (url === void 0 || seen.has(url)) continue;
|
|
117465
|
+
const titleText = decodeHtmlText$1(title[2] ?? "");
|
|
117466
|
+
if (titleText === "") continue;
|
|
117467
|
+
seen.add(url);
|
|
117468
|
+
const snippet = RESULT_SNIPPET_RE.exec(block);
|
|
117469
|
+
const snippetText = snippet !== null ? decodeHtmlText$1(snippet[1] ?? "") : "";
|
|
117604
117470
|
results.push({
|
|
117605
|
-
title,
|
|
117606
|
-
url
|
|
117607
|
-
snippet:
|
|
117471
|
+
title: titleText,
|
|
117472
|
+
url,
|
|
117473
|
+
snippet: snippetText !== "" ? snippetText : titleText
|
|
117608
117474
|
});
|
|
117609
117475
|
}
|
|
117610
|
-
const snippetMatches = html.matchAll(LITE_SNIPPET_RE);
|
|
117611
|
-
let snippetIndex = 0;
|
|
117612
|
-
for (const match of snippetMatches) {
|
|
117613
|
-
if (snippetIndex >= results.length) break;
|
|
117614
|
-
const snippet = stripHtmlAndDecode(match[1] ?? "");
|
|
117615
|
-
const entry = results[snippetIndex];
|
|
117616
|
-
if (entry && snippet.length > 0) entry.snippet = snippet;
|
|
117617
|
-
snippetIndex++;
|
|
117618
|
-
}
|
|
117619
117476
|
return results;
|
|
117620
117477
|
}
|
|
117621
|
-
|
|
117622
|
-
|
|
117623
|
-
|
|
117624
|
-
|
|
117625
|
-
|
|
117478
|
+
//#endregion
|
|
117479
|
+
//#region ../../packages/agent-core/src/tools/providers/domestic-search.ts
|
|
117480
|
+
const BROWSER_HEADERS = {
|
|
117481
|
+
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
117482
|
+
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
|
117483
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36"
|
|
117484
|
+
};
|
|
117485
|
+
/** Strip inline tags and decode the common named/numeric entities. */
|
|
117486
|
+
function decodeHtmlText(value) {
|
|
117487
|
+
return value.replace(/<[^>]*>/g, " ").replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code))).replace(/&#x([0-9a-f]+);/gi, (_, code) => String.fromCharCode(Number.parseInt(code, 16))).replace(/ /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, "\"").replace(/'|'/gi, "'").replace(/\s+/g, " ").trim();
|
|
117488
|
+
}
|
|
117489
|
+
async function searchEngine(spec, fetchImpl, query, options) {
|
|
117490
|
+
const limit = options?.limit ?? 5;
|
|
117491
|
+
const response = await fetchImpl(spec.url(query), {
|
|
117492
|
+
headers: BROWSER_HEADERS,
|
|
117493
|
+
signal: withHardTimeout(options?.signal)
|
|
117494
|
+
});
|
|
117495
|
+
const html = await response.text();
|
|
117496
|
+
if (!response.ok) throw new Error(`${spec.label} request failed: HTTP ${String(response.status)}`);
|
|
117497
|
+
if (spec.botMarkers.some((m) => html.includes(m))) throw new Error(`${spec.label} blocked the request with an anti-bot challenge`);
|
|
117498
|
+
const results = [];
|
|
117499
|
+
const seen = /* @__PURE__ */ new Set();
|
|
117500
|
+
for (const match of html.matchAll(spec.blockRe)) {
|
|
117501
|
+
if (results.length >= limit) break;
|
|
117502
|
+
const block = match[1] ?? "";
|
|
117503
|
+
const title = spec.titleRe.exec(block);
|
|
117504
|
+
if (title === null) continue;
|
|
117505
|
+
const rawHref = (title[1] ?? "").replace(/&/gi, "&");
|
|
117506
|
+
const url = spec.normalizeUrl !== void 0 ? spec.normalizeUrl(rawHref) : rawHref;
|
|
117507
|
+
if (url === void 0 || url === "" || seen.has(url)) continue;
|
|
117508
|
+
const titleText = decodeHtmlText(title[2] ?? "");
|
|
117509
|
+
if (titleText === "") continue;
|
|
117510
|
+
seen.add(url);
|
|
117511
|
+
const snippetMatch = spec.snippetRe === void 0 ? null : spec.snippetRe.exec(block);
|
|
117512
|
+
const snippetText = snippetMatch !== null ? decodeHtmlText(snippetMatch[1] ?? "") : "";
|
|
117513
|
+
results.push({
|
|
117514
|
+
title: titleText,
|
|
117515
|
+
url,
|
|
117516
|
+
snippet: snippetText !== "" ? snippetText : titleText
|
|
117517
|
+
});
|
|
117518
|
+
}
|
|
117519
|
+
return results;
|
|
117626
117520
|
}
|
|
117521
|
+
const SOGOU = {
|
|
117522
|
+
label: "Sogou",
|
|
117523
|
+
url: (q) => `https://www.sogou.com/web?query=${encodeURIComponent(q)}`,
|
|
117524
|
+
blockRe: /<div\b[^>]*\bclass="[^"]*\b(?:vrwrap|rb)\b[^"]*"[^>]*>([\s\S]*?)(?=<div\b[^>]*\bclass="[^"]*\b(?:vrwrap|rb)\b|$)/g,
|
|
117525
|
+
titleRe: /<h3[^>]*>[\s\S]*?<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/,
|
|
117526
|
+
snippetRe: /<(?:p|div)\b[^>]*\bclass="[^"]*\b(?:str-text|str_info|text-layout)\b[^"]*"[^>]*>([\s\S]*?)<\/(?:p|div)>/,
|
|
117527
|
+
normalizeUrl: (href) => {
|
|
117528
|
+
if (href.startsWith("/link?")) return `https://www.sogou.com${href}`;
|
|
117529
|
+
if (href.includes("sogou.com/sogou?")) return void 0;
|
|
117530
|
+
if (href.startsWith("http://") || href.startsWith("https://")) return href;
|
|
117531
|
+
},
|
|
117532
|
+
botMarkers: ["antispider", "异常访问请求"]
|
|
117533
|
+
};
|
|
117534
|
+
var SogouSearchProvider = class {
|
|
117535
|
+
name = "sogou";
|
|
117536
|
+
fetchImpl;
|
|
117537
|
+
constructor(options = {}) {
|
|
117538
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
117539
|
+
}
|
|
117540
|
+
search(query, options) {
|
|
117541
|
+
return searchEngine(SOGOU, this.fetchImpl, query, options);
|
|
117542
|
+
}
|
|
117543
|
+
};
|
|
117544
|
+
const SO360 = {
|
|
117545
|
+
label: "360 Search",
|
|
117546
|
+
url: (q) => `https://www.so.com/s?q=${encodeURIComponent(q)}`,
|
|
117547
|
+
blockRe: /<li\b[^>]*\bclass="[^"]*\bres-list\b[^"]*"[^>]*>([\s\S]*?)(?=<li\b[^>]*\bclass="[^"]*\bres-list\b|$)/g,
|
|
117548
|
+
titleRe: /<h3[^>]*class="[^"]*\bres-title\b[^"]*"[^>]*>[\s\S]*?<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/,
|
|
117549
|
+
snippetRe: /<p\b[^>]*\bclass="[^"]*\bres-desc\b[^"]*"[^>]*>([\s\S]*?)<\/p>/,
|
|
117550
|
+
botMarkers: ["安全验证"]
|
|
117551
|
+
};
|
|
117552
|
+
var So360SearchProvider = class {
|
|
117553
|
+
name = "360";
|
|
117554
|
+
fetchImpl;
|
|
117555
|
+
constructor(options = {}) {
|
|
117556
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
117557
|
+
}
|
|
117558
|
+
search(query, options) {
|
|
117559
|
+
return searchEngine(SO360, this.fetchImpl, query, options);
|
|
117560
|
+
}
|
|
117561
|
+
};
|
|
117562
|
+
const BAIDU = {
|
|
117563
|
+
label: "Baidu",
|
|
117564
|
+
url: (q) => `https://www.baidu.com/s?wd=${encodeURIComponent(q)}`,
|
|
117565
|
+
blockRe: /<div\b[^>]*\bclass="[^"]*\bc-container\b[^"]*"[^>]*>([\s\S]*?)(?=<div\b[^>]*\bclass="[^"]*\bc-container\b|$)/g,
|
|
117566
|
+
titleRe: /<h3[^>]*class="[^"]*\bt\b[^"]*"[^>]*>[\s\S]*?<a[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/,
|
|
117567
|
+
snippetRe: /<(?:span|div)\b[^>]*\bclass="[^"]*\b(?:c-abstract|content-right)[^"]*\b[^"]*"[^>]*>([\s\S]*?)<\/(?:span|div)>/,
|
|
117568
|
+
botMarkers: ["百度安全验证", "wappass.baidu.com"]
|
|
117569
|
+
};
|
|
117570
|
+
var BaiduSearchProvider = class {
|
|
117571
|
+
name = "baidu";
|
|
117572
|
+
fetchImpl;
|
|
117573
|
+
constructor(options = {}) {
|
|
117574
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
117575
|
+
}
|
|
117576
|
+
search(query, options) {
|
|
117577
|
+
return searchEngine(BAIDU, this.fetchImpl, query, options);
|
|
117578
|
+
}
|
|
117579
|
+
};
|
|
117627
117580
|
//#endregion
|
|
117628
117581
|
//#region ../../packages/agent-core/src/tools/providers/fallback-search.ts
|
|
117629
117582
|
var FallbackSearchProvider = class {
|
|
@@ -117633,94 +117586,31 @@ var FallbackSearchProvider = class {
|
|
|
117633
117586
|
this.providers = providers;
|
|
117634
117587
|
}
|
|
117635
117588
|
async search(query, options) {
|
|
117636
|
-
|
|
117637
|
-
|
|
117638
|
-
|
|
117639
|
-
|
|
117640
|
-
|
|
117641
|
-
|
|
117642
|
-
|
|
117643
|
-
|
|
117644
|
-
|
|
117645
|
-
|
|
117646
|
-
|
|
117647
|
-
|
|
117648
|
-
|
|
117649
|
-
|
|
117650
|
-
|
|
117651
|
-
|
|
117652
|
-
|
|
117653
|
-
fetchImpl;
|
|
117654
|
-
constructor(options) {
|
|
117655
|
-
this.tokenProvider = options.tokenProvider;
|
|
117656
|
-
this.apiKey = options.apiKey;
|
|
117657
|
-
this.baseUrl = options.baseUrl;
|
|
117658
|
-
this.defaultHeaders = options.defaultHeaders ?? {};
|
|
117659
|
-
this.customHeaders = options.customHeaders ?? {};
|
|
117660
|
-
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
117661
|
-
}
|
|
117662
|
-
async search(query, options) {
|
|
117663
|
-
const body = {
|
|
117664
|
-
text_query: query,
|
|
117665
|
-
limit: options?.limit ?? 5,
|
|
117666
|
-
enable_page_crawling: options?.includeContent ?? false,
|
|
117667
|
-
timeout_seconds: 30
|
|
117668
|
-
};
|
|
117669
|
-
const bodyJson = JSON.stringify(body);
|
|
117670
|
-
const toolCallId = options?.toolCallId;
|
|
117671
|
-
const response = await this.post(bodyJson, toolCallId);
|
|
117672
|
-
if (response.status === 401) {
|
|
117673
|
-
const detail = await safeReadText(response);
|
|
117674
|
-
throw new Error(`ScreamCli search request failed: HTTP 401 (auth/unauthorized). ${detail}`.trim());
|
|
117675
|
-
}
|
|
117676
|
-
if (response.status !== 200) {
|
|
117677
|
-
const detail = await safeReadText(response);
|
|
117678
|
-
throw new Error(`ScreamCli search request failed: HTTP ${String(response.status)}. ${detail}`.trim());
|
|
117679
|
-
}
|
|
117680
|
-
const json = await response.json();
|
|
117681
|
-
return (Array.isArray(json.search_results) ? json.search_results : []).map((r) => {
|
|
117682
|
-
const out = {
|
|
117683
|
-
title: r.title ?? "",
|
|
117684
|
-
url: r.url ?? "",
|
|
117685
|
-
snippet: r.snippet ?? ""
|
|
117686
|
-
};
|
|
117687
|
-
if (typeof r.date === "string" && r.date.length > 0) out.date = r.date;
|
|
117688
|
-
if (typeof r.content === "string" && r.content.length > 0) out.content = r.content;
|
|
117689
|
-
return out;
|
|
117690
|
-
});
|
|
117691
|
-
}
|
|
117692
|
-
async post(bodyJson, toolCallId) {
|
|
117693
|
-
const accessToken = await this.resolveApiKey();
|
|
117694
|
-
return this.fetchImpl(this.baseUrl, {
|
|
117695
|
-
method: "POST",
|
|
117696
|
-
headers: {
|
|
117697
|
-
...this.defaultHeaders,
|
|
117698
|
-
Authorization: `Bearer ${accessToken}`,
|
|
117699
|
-
"Content-Type": "application/json",
|
|
117700
|
-
...toolCallId !== void 0 && toolCallId.length > 0 ? { "X-Msh-Tool-Call-Id": toolCallId } : {},
|
|
117701
|
-
...this.customHeaders
|
|
117702
|
-
},
|
|
117703
|
-
body: bodyJson
|
|
117704
|
-
});
|
|
117705
|
-
}
|
|
117706
|
-
async resolveApiKey() {
|
|
117707
|
-
if (this.tokenProvider !== void 0) try {
|
|
117708
|
-
return await this.tokenProvider.getAccessToken();
|
|
117709
|
-
} catch (error) {
|
|
117710
|
-
if (this.apiKey !== void 0 && this.apiKey.length > 0) return this.apiKey;
|
|
117711
|
-
throw error;
|
|
117589
|
+
const failures = [];
|
|
117590
|
+
for (const [index, provider] of this.providers.entries()) {
|
|
117591
|
+
options?.signal?.throwIfAborted();
|
|
117592
|
+
try {
|
|
117593
|
+
const results = await provider.search(query, options);
|
|
117594
|
+
if (results.length > 0) return results;
|
|
117595
|
+
failures.push({
|
|
117596
|
+
provider: provider.name ?? `provider ${String(index + 1)}`,
|
|
117597
|
+
reason: "no results"
|
|
117598
|
+
});
|
|
117599
|
+
} catch (error) {
|
|
117600
|
+
options?.signal?.throwIfAborted();
|
|
117601
|
+
failures.push({
|
|
117602
|
+
provider: provider.name ?? `provider ${String(index + 1)}`,
|
|
117603
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
117604
|
+
});
|
|
117605
|
+
}
|
|
117712
117606
|
}
|
|
117713
|
-
if (
|
|
117714
|
-
|
|
117607
|
+
if (failures.every((f) => f.reason === "no results")) return [];
|
|
117608
|
+
const last = failures[failures.length - 1];
|
|
117609
|
+
if (this.providers.length === 1 && last !== void 0) throw new Error(last.reason);
|
|
117610
|
+
const summary = failures.map((f) => `${f.provider}: ${f.reason}`).join("; ");
|
|
117611
|
+
throw new Error(`All web search providers failed — ${summary}`);
|
|
117715
117612
|
}
|
|
117716
117613
|
};
|
|
117717
|
-
async function safeReadText(response) {
|
|
117718
|
-
try {
|
|
117719
|
-
return await response.text();
|
|
117720
|
-
} catch {
|
|
117721
|
-
return "";
|
|
117722
|
-
}
|
|
117723
|
-
}
|
|
117724
117614
|
//#endregion
|
|
117725
117615
|
//#region ../../packages/agent-core/src/session/export/manifest.ts
|
|
117726
117616
|
const WIRE_PROTOCOL_VERSION = "1.3";
|
|
@@ -118910,7 +118800,7 @@ function providerApiKey(provider) {
|
|
|
118910
118800
|
case "openai_responses": return providerValue(provider.apiKey, provider.env, "OPENAI_API_KEY");
|
|
118911
118801
|
case "scream": return providerValue(provider.apiKey, provider.env, "SCREAM_API_KEY");
|
|
118912
118802
|
case "google-genai": return providerValue(provider.apiKey, provider.env, "GOOGLE_API_KEY");
|
|
118913
|
-
case "vertexai": return nonEmptyString
|
|
118803
|
+
case "vertexai": return nonEmptyString(provider.apiKey) ?? envValue(provider.env, "VERTEXAI_API_KEY") ?? envValue(provider.env, "GOOGLE_API_KEY");
|
|
118914
118804
|
default: {
|
|
118915
118805
|
const exhaustive = provider.type;
|
|
118916
118806
|
throw new ScreamError(ErrorCodes.MODEL_CONFIG_INVALID, `Unsupported provider type: ${String(exhaustive)}`);
|
|
@@ -118927,21 +118817,21 @@ function vertexAILocation(provider) {
|
|
|
118927
118817
|
return envValue(provider.env, "GOOGLE_CLOUD_LOCATION") ?? locationFromVertexAIBaseUrl(provider.baseUrl);
|
|
118928
118818
|
}
|
|
118929
118819
|
function providerValue(configured, env, envKey) {
|
|
118930
|
-
return nonEmptyString
|
|
118820
|
+
return nonEmptyString(configured) ?? envValue(env, envKey);
|
|
118931
118821
|
}
|
|
118932
118822
|
function envValue(env, key) {
|
|
118933
|
-
return nonEmptyString
|
|
118823
|
+
return nonEmptyString(env?.[key]);
|
|
118934
118824
|
}
|
|
118935
|
-
function nonEmptyString
|
|
118825
|
+
function nonEmptyString(value) {
|
|
118936
118826
|
const trimmed = value?.trim();
|
|
118937
118827
|
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
118938
118828
|
}
|
|
118939
118829
|
function locationFromVertexAIBaseUrl(baseUrl) {
|
|
118940
|
-
const url = nonEmptyString
|
|
118830
|
+
const url = nonEmptyString(baseUrl);
|
|
118941
118831
|
if (url === void 0) return void 0;
|
|
118942
118832
|
try {
|
|
118943
118833
|
const host = new URL(url).hostname;
|
|
118944
|
-
return host.endsWith("-aiplatform.googleapis.com") ? nonEmptyString
|
|
118834
|
+
return host.endsWith("-aiplatform.googleapis.com") ? nonEmptyString(host.slice(0, -26)) : void 0;
|
|
118945
118835
|
} catch {
|
|
118946
118836
|
return;
|
|
118947
118837
|
}
|
|
@@ -120486,7 +120376,6 @@ function waitForSpawn(child) {
|
|
|
120486
120376
|
new AsyncLocalStorage();
|
|
120487
120377
|
//#endregion
|
|
120488
120378
|
//#region ../../packages/agent-core/src/rpc/core-impl.ts
|
|
120489
|
-
const SCREAM_CODE_PROVIDER_NAME = "managed:scream-code";
|
|
120490
120379
|
var ScreamCore = class {
|
|
120491
120380
|
rpcClient;
|
|
120492
120381
|
sdk;
|
|
@@ -120967,11 +120856,7 @@ var ScreamCore = class {
|
|
|
120967
120856
|
}
|
|
120968
120857
|
async resolveRuntime(config) {
|
|
120969
120858
|
if (this.runtime !== void 0) return this.runtime;
|
|
120970
|
-
const runtime = await createRuntimeConfig({
|
|
120971
|
-
config,
|
|
120972
|
-
screamRequestHeaders: this.screamRequestHeaders,
|
|
120973
|
-
resolveOAuthTokenProvider: this.resolveOAuthTokenProvider
|
|
120974
|
-
});
|
|
120859
|
+
const runtime = await createRuntimeConfig({ config });
|
|
120975
120860
|
this.runtime = runtime;
|
|
120976
120861
|
return runtime;
|
|
120977
120862
|
}
|
|
@@ -121028,42 +120913,20 @@ var ScreamCore = class {
|
|
|
121028
120913
|
}
|
|
121029
120914
|
};
|
|
121030
120915
|
async function createRuntimeConfig(input) {
|
|
121031
|
-
const fetchCache = new FetchCache();
|
|
121032
|
-
const localFetcher = new LocalFetchURLProvider({ cache: fetchCache });
|
|
121033
|
-
const fetchService = input.config.services?.screamCliFetch;
|
|
121034
120916
|
return {
|
|
121035
|
-
urlFetcher:
|
|
121036
|
-
baseUrl: fetchService.baseUrl,
|
|
121037
|
-
localFallback: localFetcher,
|
|
121038
|
-
defaultHeaders: input.screamRequestHeaders,
|
|
121039
|
-
cache: fetchCache,
|
|
121040
|
-
...serviceCredentials(fetchService, input.resolveOAuthTokenProvider)
|
|
121041
|
-
}),
|
|
120917
|
+
urlFetcher: new LocalFetchURLProvider({ cache: new FetchCache() }),
|
|
121042
120918
|
webSearcher: buildWebSearcher(input)
|
|
121043
120919
|
};
|
|
121044
120920
|
}
|
|
121045
120921
|
function buildWebSearcher(input) {
|
|
121046
|
-
const
|
|
121047
|
-
const
|
|
121048
|
-
|
|
121049
|
-
|
|
121050
|
-
|
|
121051
|
-
|
|
121052
|
-
|
|
121053
|
-
|
|
121054
|
-
if (screamProvider && ddgProvider) return new FallbackSearchProvider([screamProvider, ddgProvider]);
|
|
121055
|
-
return screamProvider ?? ddgProvider;
|
|
121056
|
-
}
|
|
121057
|
-
function serviceCredentials(service, resolveOAuthTokenProvider) {
|
|
121058
|
-
return {
|
|
121059
|
-
apiKey: nonEmptyString(service.apiKey),
|
|
121060
|
-
tokenProvider: service.oauth !== void 0 ? resolveOAuthTokenProvider?.(SCREAM_CODE_PROVIDER_NAME, service.oauth) : void 0,
|
|
121061
|
-
customHeaders: service.customHeaders
|
|
121062
|
-
};
|
|
121063
|
-
}
|
|
121064
|
-
function nonEmptyString(value) {
|
|
121065
|
-
const trimmed = value?.trim();
|
|
121066
|
-
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
120922
|
+
const services = input.config.services;
|
|
120923
|
+
const providers = [];
|
|
120924
|
+
if (services?.duckduckgo?.enabled !== false) providers.push(new DuckDuckGoSearchProvider());
|
|
120925
|
+
if (services?.sogou?.enabled !== false) providers.push(new SogouSearchProvider());
|
|
120926
|
+
if (services?.so360?.enabled !== false) providers.push(new So360SearchProvider());
|
|
120927
|
+
if (services?.baidu?.enabled !== false) providers.push(new BaiduSearchProvider());
|
|
120928
|
+
if (providers.length === 0) return void 0;
|
|
120929
|
+
return providers.length === 1 ? providers[0] : new FallbackSearchProvider(providers);
|
|
121067
120930
|
}
|
|
121068
120931
|
function requiredWorkDir(operation, value) {
|
|
121069
120932
|
if (typeof value !== "string" || value.trim() === "") throw new ScreamError(ErrorCodes.REQUEST_WORK_DIR_REQUIRED, `${operation} requires workDir`);
|
|
@@ -122527,7 +122390,7 @@ function optionalBuildString(value) {
|
|
|
122527
122390
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
122528
122391
|
}
|
|
122529
122392
|
const SCREAM_BUILD_INFO = {
|
|
122530
|
-
version: optionalBuildString("0.10.
|
|
122393
|
+
version: optionalBuildString("0.10.2"),
|
|
122531
122394
|
channel: optionalBuildString(""),
|
|
122532
122395
|
commit: optionalBuildString(""),
|
|
122533
122396
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -123877,12 +123740,13 @@ function isManagedUsageProvider(providerKey) {
|
|
|
123877
123740
|
//#region src/tui/constant/streaming.ts
|
|
123878
123741
|
const STREAMING_ARGS_FIELD_RE = /"(path|file_path|command|pattern|query|url|description|title|name)"\s*:\s*"((?:\\.|[^"\\])*)"/g;
|
|
123879
123742
|
const STREAMING_ARGS_PREVIEW_MAX_CHARS = 8 * 1024;
|
|
123743
|
+
const STREAMING_ARGS_BUFFER_MAX_CHARS = 1024 * 1024;
|
|
123880
123744
|
//#endregion
|
|
123881
123745
|
//#region src/tui/utils/event-payload.ts
|
|
123882
123746
|
function appendStreamingArgsPreview(current, next) {
|
|
123883
|
-
const existing = (current ?? "").slice(0,
|
|
123747
|
+
const existing = (current ?? "").slice(0, STREAMING_ARGS_BUFFER_MAX_CHARS);
|
|
123884
123748
|
if (next === null || next === void 0 || next.length === 0) return existing;
|
|
123885
|
-
const remaining =
|
|
123749
|
+
const remaining = STREAMING_ARGS_BUFFER_MAX_CHARS - existing.length;
|
|
123886
123750
|
if (remaining <= 0) return existing;
|
|
123887
123751
|
return `${existing}${next.slice(0, remaining)}`;
|
|
123888
123752
|
}
|
|
@@ -125808,7 +125672,7 @@ var FooterComponent = class {
|
|
|
125808
125672
|
gitCache;
|
|
125809
125673
|
gitCacheWorkDir;
|
|
125810
125674
|
transientHint = null;
|
|
125811
|
-
|
|
125675
|
+
statusTimer = null;
|
|
125812
125676
|
/**
|
|
125813
125677
|
* Non-terminal background-task counts split by kind so the footer can
|
|
125814
125678
|
* render two distinct badges. `bashTasks` covers `bash-*` BPM tasks
|
|
@@ -125827,15 +125691,13 @@ var FooterComponent = class {
|
|
|
125827
125691
|
this.gitCache = createGitStatusCache(state.workDir, { onChange: this.onGitStatusChange });
|
|
125828
125692
|
}
|
|
125829
125693
|
setState(state) {
|
|
125830
|
-
const
|
|
125694
|
+
const prevPhase = this.state?.streamingPhase;
|
|
125831
125695
|
if (state.workDir !== this.gitCacheWorkDir) {
|
|
125832
125696
|
this.gitCacheWorkDir = state.workDir;
|
|
125833
125697
|
this.gitCache = createGitStatusCache(state.workDir, { onChange: this.onGitStatusChange });
|
|
125834
125698
|
}
|
|
125835
125699
|
this.state = state;
|
|
125836
|
-
|
|
125837
|
-
if (isThinking && !wasThinking) this.#startShimmer();
|
|
125838
|
-
else if (!isThinking && wasThinking) this.#stopShimmer();
|
|
125700
|
+
if (state.streamingPhase !== prevPhase) this.#restartStatusTimer(state.streamingPhase);
|
|
125839
125701
|
}
|
|
125840
125702
|
setColors(colors) {
|
|
125841
125703
|
this.colors = colors;
|
|
@@ -125860,29 +125722,29 @@ var FooterComponent = class {
|
|
|
125860
125722
|
}
|
|
125861
125723
|
invalidate() {}
|
|
125862
125724
|
/**
|
|
125863
|
-
* Stop the
|
|
125725
|
+
* Stop the status timer. Idempotent — safe to call even when
|
|
125864
125726
|
* the timer isn't running. Call this when the component is disposed.
|
|
125865
125727
|
*/
|
|
125866
125728
|
dispose() {
|
|
125867
|
-
this.#
|
|
125729
|
+
this.#stopStatusTimer();
|
|
125868
125730
|
}
|
|
125869
|
-
#
|
|
125870
|
-
|
|
125871
|
-
|
|
125872
|
-
|
|
125873
|
-
|
|
125731
|
+
#restartStatusTimer(phase) {
|
|
125732
|
+
this.#stopStatusTimer();
|
|
125733
|
+
if (phase === "idle") return;
|
|
125734
|
+
const intervalMs = phase === "thinking" ? 1e3 / 30 : SPINNER_TICK_MS;
|
|
125735
|
+
this.statusTimer = setInterval(() => {
|
|
125736
|
+
this.ui.requestComponentRender(this);
|
|
125737
|
+
}, intervalMs);
|
|
125874
125738
|
}
|
|
125875
|
-
#
|
|
125876
|
-
if (!this.
|
|
125877
|
-
clearInterval(this.
|
|
125878
|
-
this.
|
|
125739
|
+
#stopStatusTimer() {
|
|
125740
|
+
if (!this.statusTimer) return;
|
|
125741
|
+
clearInterval(this.statusTimer);
|
|
125742
|
+
this.statusTimer = null;
|
|
125879
125743
|
}
|
|
125880
125744
|
render(width) {
|
|
125881
125745
|
const colors = this.colors;
|
|
125882
125746
|
const state = this.state;
|
|
125883
125747
|
const left = [];
|
|
125884
|
-
if (state.permissionMode === "auto") left.push(chalk.hex(colors.warning).bold(t("badge.auto")));
|
|
125885
|
-
if (state.permissionMode === "yolo") left.push(chalk.hex(colors.warning).bold(t("badge.yes")));
|
|
125886
125748
|
if (state.planMode !== "off") {
|
|
125887
125749
|
const isFusion = state.planMode === "fusionplan";
|
|
125888
125750
|
left.push(chalk.hex(isFusion ? colors.fusionPlanMode : colors.planMode).bold(isFusion ? t("badge.fusion") : t("badge.plan")));
|
|
@@ -126766,7 +126628,6 @@ async function handleYoloCommand(host, args) {
|
|
|
126766
126628
|
}
|
|
126767
126629
|
await session.setPermission("yolo");
|
|
126768
126630
|
host.setAppState({ permissionMode: "yolo" });
|
|
126769
|
-
host.showNotice(t("config.yolo_on"), t("config.yolo_on_desc"));
|
|
126770
126631
|
return;
|
|
126771
126632
|
}
|
|
126772
126633
|
if (subcmd === "off") {
|
|
@@ -126776,17 +126637,14 @@ async function handleYoloCommand(host, args) {
|
|
|
126776
126637
|
}
|
|
126777
126638
|
await session.setPermission("manual");
|
|
126778
126639
|
host.setAppState({ permissionMode: "manual" });
|
|
126779
|
-
host.showNotice(t("config.yolo_off"));
|
|
126780
126640
|
return;
|
|
126781
126641
|
}
|
|
126782
126642
|
if (currentMode === "yolo") {
|
|
126783
126643
|
await session.setPermission("manual");
|
|
126784
126644
|
host.setAppState({ permissionMode: "manual" });
|
|
126785
|
-
host.showNotice(t("config.yolo_off"));
|
|
126786
126645
|
} else {
|
|
126787
126646
|
await session.setPermission("yolo");
|
|
126788
126647
|
host.setAppState({ permissionMode: "yolo" });
|
|
126789
|
-
host.showNotice(t("config.yolo_toggle_on"), t("config.yolo_toggle_on_desc"));
|
|
126790
126648
|
}
|
|
126791
126649
|
}
|
|
126792
126650
|
async function handleAutoCommand(host, args) {
|
|
@@ -126804,7 +126662,6 @@ async function handleAutoCommand(host, args) {
|
|
|
126804
126662
|
}
|
|
126805
126663
|
await session.setPermission("auto");
|
|
126806
126664
|
host.setAppState({ permissionMode: "auto" });
|
|
126807
|
-
host.showNotice(t("config.auto_on"), t("config.auto_on_desc"));
|
|
126808
126665
|
return;
|
|
126809
126666
|
}
|
|
126810
126667
|
if (subcmd === "off") {
|
|
@@ -126814,17 +126671,14 @@ async function handleAutoCommand(host, args) {
|
|
|
126814
126671
|
}
|
|
126815
126672
|
await session.setPermission("manual");
|
|
126816
126673
|
host.setAppState({ permissionMode: "manual" });
|
|
126817
|
-
host.showNotice(t("config.auto_off"));
|
|
126818
126674
|
return;
|
|
126819
126675
|
}
|
|
126820
126676
|
if (currentMode === "auto") {
|
|
126821
126677
|
await session.setPermission("manual");
|
|
126822
126678
|
host.setAppState({ permissionMode: "manual" });
|
|
126823
|
-
host.showNotice(t("config.auto_off"));
|
|
126824
126679
|
} else {
|
|
126825
126680
|
await session.setPermission("auto");
|
|
126826
126681
|
host.setAppState({ permissionMode: "auto" });
|
|
126827
|
-
host.showNotice(t("config.auto_toggle_on"), t("config.auto_toggle_on_desc"));
|
|
126828
126682
|
}
|
|
126829
126683
|
}
|
|
126830
126684
|
async function handleWolfpackCommand(host, args) {
|
|
@@ -127827,7 +127681,7 @@ async function createGoal(host, parsed) {
|
|
|
127827
127681
|
await showGoalConfigWizard(host, session, parsed.objective, parsed.replace);
|
|
127828
127682
|
}
|
|
127829
127683
|
async function showGoalConfigWizard(host, session, objective, replace) {
|
|
127830
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
127684
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-C8_8qYYi.mjs");
|
|
127831
127685
|
const turnInput = await promptNumber(host, TextInputDialogComponent, {
|
|
127832
127686
|
title: t("goal.wizard_title", { objective }),
|
|
127833
127687
|
subtitle: t("goal.budget_turns_hint"),
|
|
@@ -128576,7 +128430,7 @@ var AssistantMessageComponent = class {
|
|
|
128576
128430
|
const age = Date.now() - (this.fadeStartMs ?? 0);
|
|
128577
128431
|
this.cachedWidth = void 0;
|
|
128578
128432
|
this.cachedLines = void 0;
|
|
128579
|
-
this.ui?.
|
|
128433
|
+
this.ui?.requestComponentRender(this);
|
|
128580
128434
|
if (age >= 1200) this.stopFade();
|
|
128581
128435
|
}, FADE_TICK_MS);
|
|
128582
128436
|
this.ui.requestRender();
|
|
@@ -129057,7 +128911,7 @@ var ThinkingComponent = class {
|
|
|
129057
128911
|
if (this.ui === void 0 || this.spinnerInterval !== void 0) return;
|
|
129058
128912
|
this.spinnerInterval = setInterval(() => {
|
|
129059
128913
|
this.spinnerFrame = (this.spinnerFrame + 1) % BRAILLE_SPINNER_FRAMES.length;
|
|
129060
|
-
this.ui?.
|
|
128914
|
+
this.ui?.requestComponentRender(this);
|
|
129061
128915
|
}, 80);
|
|
129062
128916
|
}
|
|
129063
128917
|
stopSpinner() {
|
|
@@ -130244,23 +130098,20 @@ function unescapeJsonString(s) {
|
|
|
130244
130098
|
});
|
|
130245
130099
|
}
|
|
130246
130100
|
/**
|
|
130247
|
-
*
|
|
130248
|
-
*
|
|
130249
|
-
*
|
|
130250
|
-
*
|
|
130251
|
-
*
|
|
130101
|
+
* Scan a JSON string value starting at `start` (just after the opening
|
|
130102
|
+
* quote), unescaping into plain text. Stops at the closing quote or at
|
|
130103
|
+
* `end`, tolerating truncation (an incomplete escape at the boundary
|
|
130104
|
+
* simply ends the scan). Shared by the partial-args extractor and the
|
|
130105
|
+
* Write streaming tail preview.
|
|
130252
130106
|
*/
|
|
130253
|
-
function
|
|
130254
|
-
const match = new RegExp(`"${key}"\\s*:\\s*"`).exec(text);
|
|
130255
|
-
if (match === null) return void 0;
|
|
130256
|
-
const start = match.index + match[0].length;
|
|
130107
|
+
function unescapeJsonStringValue(text, start, end = text.length) {
|
|
130257
130108
|
let out = "";
|
|
130258
130109
|
let i = start;
|
|
130259
|
-
while (i <
|
|
130110
|
+
while (i < end) {
|
|
130260
130111
|
const ch = text[i];
|
|
130261
130112
|
if (ch === "\\") {
|
|
130262
130113
|
const next = text[i + 1];
|
|
130263
|
-
if (next === void 0) return out;
|
|
130114
|
+
if (next === void 0 || i + 1 >= end) return out;
|
|
130264
130115
|
switch (next) {
|
|
130265
130116
|
case "n":
|
|
130266
130117
|
out += "\n";
|
|
@@ -130287,7 +130138,7 @@ function extractPartialStringField(text, key) {
|
|
|
130287
130138
|
out += "/";
|
|
130288
130139
|
break;
|
|
130289
130140
|
case "u": {
|
|
130290
|
-
if (i + 5 >=
|
|
130141
|
+
if (i + 5 >= end) return out;
|
|
130291
130142
|
const hex = text.slice(i + 2, i + 6);
|
|
130292
130143
|
const code = Number.parseInt(hex, 16);
|
|
130293
130144
|
if (Number.isNaN(code)) return out;
|
|
@@ -130306,6 +130157,16 @@ function extractPartialStringField(text, key) {
|
|
|
130306
130157
|
}
|
|
130307
130158
|
return out;
|
|
130308
130159
|
}
|
|
130160
|
+
/**
|
|
130161
|
+
* Pull the live value of a JSON string field out of partially-streamed
|
|
130162
|
+
* arguments, even if the closing quote hasn't arrived yet. Returns
|
|
130163
|
+
* `undefined` if the field hasn't started streaming yet.
|
|
130164
|
+
*/
|
|
130165
|
+
function extractPartialStringField(text, key) {
|
|
130166
|
+
const match = new RegExp(`"${key}"\\s*:\\s*"`).exec(text);
|
|
130167
|
+
if (match === null) return void 0;
|
|
130168
|
+
return unescapeJsonStringValue(text, match.index + match[0].length);
|
|
130169
|
+
}
|
|
130309
130170
|
function parseArgsPreview(value) {
|
|
130310
130171
|
const previewText = value.slice(0, STREAMING_ARGS_PREVIEW_MAX_CHARS);
|
|
130311
130172
|
if (previewText.trim().length === 0) return {};
|
|
@@ -130448,6 +130309,11 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
|
|
|
130448
130309
|
subagentEndedAtMs;
|
|
130449
130310
|
progressLines = [];
|
|
130450
130311
|
static MAX_PROGRESS_LINES = 24;
|
|
130312
|
+
writeStreamContentStart = -1;
|
|
130313
|
+
writeStreamNlScanOffset = 0;
|
|
130314
|
+
writeStreamNlCount = 0;
|
|
130315
|
+
writeStreamLang;
|
|
130316
|
+
static WRITE_STREAM_TAIL_RAW_CHARS = 4096;
|
|
130451
130317
|
/**
|
|
130452
130318
|
* Registered by a group container (`AgentGroupComponent` or
|
|
130453
130319
|
* `ReadGroupComponent`) when this component is borrowed as a hidden state
|
|
@@ -131217,21 +131083,12 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
|
|
|
131217
131083
|
const name = this.toolCall.name;
|
|
131218
131084
|
const previewText = streamText.slice(0, STREAMING_ARGS_PREVIEW_MAX_CHARS);
|
|
131219
131085
|
if (name === "Write") {
|
|
131220
|
-
|
|
131221
|
-
if (content === void 0 || content.length === 0) return;
|
|
131222
|
-
const allLines = highlightLines(content, langFromPath(extractPartialStringField(previewText, "file_path") ?? extractPartialStringField(previewText, "path") ?? ""));
|
|
131223
|
-
const maxLines = 10;
|
|
131224
|
-
const scrollLines = allLines.length > maxLines ? allLines.slice(allLines.length - maxLines) : allLines;
|
|
131225
|
-
for (const [i, line] of scrollLines.entries()) {
|
|
131226
|
-
const originalLineNumber = allLines.length > maxLines ? allLines.length - maxLines + i : i;
|
|
131227
|
-
const lineNum = chalk.dim(String(originalLineNumber + 1).padStart(4) + " ");
|
|
131228
|
-
this.addChild(new Text(lineNum + line, 2, 0));
|
|
131229
|
-
}
|
|
131086
|
+
this.buildWriteStreamingPreview(streamText);
|
|
131230
131087
|
return;
|
|
131231
131088
|
}
|
|
131232
131089
|
if (name === "Edit") {
|
|
131233
131090
|
const filePath = extractPartialStringField(previewText, "file_path") ?? extractPartialStringField(previewText, "path") ?? "";
|
|
131234
|
-
const bytes = Buffer.byteLength(
|
|
131091
|
+
const bytes = Buffer.byteLength(streamText, "utf8");
|
|
131235
131092
|
const startedAtMs = this.toolCall.streamingStartedAtMs;
|
|
131236
131093
|
const elapsedSeconds = startedAtMs === void 0 ? 0 : Math.max(0, Math.floor((Date.now() - startedAtMs) / 1e3));
|
|
131237
131094
|
const progress = t("toolcall.preparing_changes", {
|
|
@@ -131253,6 +131110,54 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
|
|
|
131253
131110
|
}));
|
|
131254
131111
|
}
|
|
131255
131112
|
}
|
|
131113
|
+
/**
|
|
131114
|
+
* Live Write preview: renders the last COMMAND_PREVIEW_LINES lines of the
|
|
131115
|
+
* file being written, reading from the TAIL of the accumulating args JSON
|
|
131116
|
+
* so the window keeps scrolling for arbitrarily large files (the old
|
|
131117
|
+
* head-bounded preview visibly froze once args passed 8KB). Line numbers
|
|
131118
|
+
* stay exact via an incremental newline counter. Per-frame cost is
|
|
131119
|
+
* O(delta + tail), independent of total file size.
|
|
131120
|
+
*/
|
|
131121
|
+
buildWriteStreamingPreview(streamText) {
|
|
131122
|
+
if (streamText.length < this.writeStreamNlScanOffset) {
|
|
131123
|
+
this.writeStreamContentStart = -1;
|
|
131124
|
+
this.writeStreamLang = void 0;
|
|
131125
|
+
}
|
|
131126
|
+
if (this.writeStreamContentStart < 0) {
|
|
131127
|
+
const opener = /"content"\s*:\s*"/.exec(streamText);
|
|
131128
|
+
if (opener === null) return;
|
|
131129
|
+
this.writeStreamContentStart = opener.index + opener[0].length;
|
|
131130
|
+
this.writeStreamNlScanOffset = this.writeStreamContentStart;
|
|
131131
|
+
this.writeStreamNlCount = 0;
|
|
131132
|
+
const filePath = extractPartialStringField(streamText, "file_path") ?? extractPartialStringField(streamText, "path") ?? "";
|
|
131133
|
+
this.writeStreamLang = langFromPath(filePath);
|
|
131134
|
+
}
|
|
131135
|
+
const contentStart = this.writeStreamContentStart;
|
|
131136
|
+
const tailStart = Math.max(contentStart, streamText.length - ToolCallComponent.WRITE_STREAM_TAIL_RAW_CHARS);
|
|
131137
|
+
while (this.writeStreamNlScanOffset < tailStart) {
|
|
131138
|
+
const idx = streamText.indexOf("\\n", this.writeStreamNlScanOffset);
|
|
131139
|
+
if (idx === -1 || idx >= tailStart) break;
|
|
131140
|
+
this.writeStreamNlCount++;
|
|
131141
|
+
this.writeStreamNlScanOffset = idx + 2;
|
|
131142
|
+
}
|
|
131143
|
+
let fragmentStart = tailStart;
|
|
131144
|
+
if (tailStart > contentStart) {
|
|
131145
|
+
const firstNl = streamText.indexOf("\\n", tailStart);
|
|
131146
|
+
if (firstNl === -1) return;
|
|
131147
|
+
fragmentStart = firstNl + 2;
|
|
131148
|
+
}
|
|
131149
|
+
const fragmentEnd = Math.min(streamText.length, fragmentStart + ToolCallComponent.WRITE_STREAM_TAIL_RAW_CHARS);
|
|
131150
|
+
const fragment = unescapeJsonStringValue(streamText, fragmentStart, fragmentEnd);
|
|
131151
|
+
if (fragment.length === 0) return;
|
|
131152
|
+
const lines = highlightLines(fragment, this.writeStreamLang);
|
|
131153
|
+
const displayLines = lines.slice(-10);
|
|
131154
|
+
const firstFragmentLineNo = this.writeStreamNlCount + (tailStart > contentStart ? 2 : 1);
|
|
131155
|
+
const skipped = lines.length - displayLines.length;
|
|
131156
|
+
for (const [i, line] of displayLines.entries()) {
|
|
131157
|
+
const lineNum = chalk.dim(String(firstFragmentLineNo + skipped + i).padStart(4) + " ");
|
|
131158
|
+
this.addChild(new Text(lineNum + line, 2, 0));
|
|
131159
|
+
}
|
|
131160
|
+
}
|
|
131256
131161
|
buildPlanPreview() {
|
|
131257
131162
|
const plan = this.resolvePlanForPreview();
|
|
131258
131163
|
if (plan.length === 0) return;
|
|
@@ -133232,7 +133137,7 @@ var MoonLoader = class extends Text {
|
|
|
133232
133137
|
const frame = this.frames[this.currentFrame];
|
|
133233
133138
|
const coloredFrame = this.colorFn ? this.colorFn(frame) : frame;
|
|
133234
133139
|
this.setText(this.label ? `${coloredFrame} ${this.label}` : coloredFrame);
|
|
133235
|
-
this.ui.
|
|
133140
|
+
this.ui.requestComponentRender(this);
|
|
133236
133141
|
}
|
|
133237
133142
|
};
|
|
133238
133143
|
//#endregion
|
|
@@ -141506,7 +141411,7 @@ var PulseWaveLoader = class extends Text {
|
|
|
141506
141411
|
2
|
|
141507
141412
|
].map((idx) => this.renderCell(idx, step.active, step.forward));
|
|
141508
141413
|
this.setText(cells.join(" "));
|
|
141509
|
-
this.ui.
|
|
141414
|
+
this.ui.requestComponentRender(this);
|
|
141510
141415
|
}
|
|
141511
141416
|
renderCell(index, active, forward) {
|
|
141512
141417
|
const distance = forward ? active - index : index - active;
|
|
@@ -143105,6 +143010,8 @@ var CustomEditor = class extends Editor {
|
|
|
143105
143010
|
thinking = false;
|
|
143106
143011
|
/** Current thinking effort level (e.g. low, medium, high). Used to annotate the think label. */
|
|
143107
143012
|
thinkingLevel = "off";
|
|
143013
|
+
/** Current permission mode — always shown as a badge at the top-left of the input box border. */
|
|
143014
|
+
permissionMode = "manual";
|
|
143108
143015
|
/** Current border colour hex — kept in sync with borderColor by the host. */
|
|
143109
143016
|
borderHex = "";
|
|
143110
143017
|
consumingPaste = false;
|
|
@@ -143165,7 +143072,13 @@ var CustomEditor = class extends Editor {
|
|
|
143165
143072
|
const withPrompt = injectPromptSymbol(firstContent);
|
|
143166
143073
|
if (withPrompt !== void 0) lines[firstContentIdx] = withPrompt;
|
|
143167
143074
|
}
|
|
143168
|
-
|
|
143075
|
+
injectBorderBadges(lines, width, {
|
|
143076
|
+
mode: this.permissionMode,
|
|
143077
|
+
thinking: this.thinking,
|
|
143078
|
+
thinkingLevel: this.thinkingLevel,
|
|
143079
|
+
paint: this.borderColor ?? ((s) => s),
|
|
143080
|
+
borderHex: this.borderHex
|
|
143081
|
+
});
|
|
143169
143082
|
return lines;
|
|
143170
143083
|
}
|
|
143171
143084
|
handleInput(data) {
|
|
@@ -143298,34 +143211,47 @@ function injectPromptSymbol(line) {
|
|
|
143298
143211
|
return "> " + line.slice(2);
|
|
143299
143212
|
}
|
|
143300
143213
|
const THINK_LABEL_MIN_WIDTH = 14;
|
|
143214
|
+
const MODE_BADGE_MIN_WIDTH = 10;
|
|
143301
143215
|
function isLightBg(hex) {
|
|
143302
143216
|
const r = parseInt(hex.slice(1, 3), 16);
|
|
143303
143217
|
const g = parseInt(hex.slice(3, 5), 16);
|
|
143304
143218
|
const b = parseInt(hex.slice(5, 7), 16);
|
|
143305
143219
|
return (.299 * r + .587 * g + .114 * b) / 255 > .5;
|
|
143306
143220
|
}
|
|
143221
|
+
function makeBadge(label, bgHex) {
|
|
143222
|
+
return bgHex ? chalk.bgHex(bgHex).hex(isLightBg(bgHex) ? "#000000" : "#FFFFFF")(label) : chalk.bgBlack.white(label);
|
|
143223
|
+
}
|
|
143307
143224
|
/**
|
|
143308
|
-
*
|
|
143309
|
-
*
|
|
143310
|
-
*
|
|
143311
|
-
*
|
|
143312
|
-
*
|
|
143313
|
-
*
|
|
143314
|
-
*
|
|
143225
|
+
* Compose the editor's top border line with up to two badges: the
|
|
143226
|
+
* permission mode badge at the left (always shown) and the think badge at
|
|
143227
|
+
* the right (only when thinking is enabled). Each badge is a solid colour
|
|
143228
|
+
* block with black/white text picked by background luminance. When the
|
|
143229
|
+
* editor is scrolled, pi-tui's `↑ N more` indicator keeps the line — it
|
|
143230
|
+
* signals hidden content, which matters more than the badges. On narrow
|
|
143231
|
+
* terminals the think badge drops first, then the mode badge.
|
|
143315
143232
|
*/
|
|
143316
|
-
function
|
|
143317
|
-
if (width < THINK_LABEL_MIN_WIDTH) return;
|
|
143233
|
+
function injectBorderBadges(lines, width, opts) {
|
|
143318
143234
|
const topIdx = lines.findIndex((line) => {
|
|
143319
143235
|
const plain = stripSgr(line);
|
|
143320
143236
|
return plain.length > 0 && plain[0] === "─";
|
|
143321
143237
|
});
|
|
143322
143238
|
if (topIdx === -1) return;
|
|
143323
|
-
|
|
143324
|
-
const
|
|
143325
|
-
|
|
143326
|
-
|
|
143327
|
-
if (
|
|
143328
|
-
|
|
143239
|
+
if (stripSgr(lines[topIdx]).includes("↑")) return;
|
|
143240
|
+
const { paint } = opts;
|
|
143241
|
+
let left = "";
|
|
143242
|
+
let right = paint("─");
|
|
143243
|
+
if (width >= MODE_BADGE_MIN_WIDTH) {
|
|
143244
|
+
const badgeText = ` ${opts.mode} `;
|
|
143245
|
+
left = paint("──") + (opts.borderHex ? chalk.hex(opts.borderHex).bold(badgeText) : paint(badgeText));
|
|
143246
|
+
}
|
|
143247
|
+
if (opts.thinking && width >= THINK_LABEL_MIN_WIDTH) right = makeBadge(opts.thinkingLevel !== "off" ? ` Think ${opts.thinkingLevel} ` : " Think ", opts.borderHex) + paint("─");
|
|
143248
|
+
const fill = width - visibleWidth(left) - visibleWidth(right);
|
|
143249
|
+
if (fill < 0) {
|
|
143250
|
+
const modeOnlyFill = width - visibleWidth(left) - 1;
|
|
143251
|
+
lines[topIdx] = modeOnlyFill >= 0 ? left + paint("─".repeat(modeOnlyFill)) + paint("─") : paint("─".repeat(width));
|
|
143252
|
+
return;
|
|
143253
|
+
}
|
|
143254
|
+
lines[topIdx] = left + paint("─".repeat(fill)) + right;
|
|
143329
143255
|
}
|
|
143330
143256
|
//#endregion
|
|
143331
143257
|
//#region src/tui/utils/terminal-state.ts
|
|
@@ -143566,6 +143492,7 @@ function createTUIState(options) {
|
|
|
143566
143492
|
const editor = new CustomEditor(ui, theme.colors);
|
|
143567
143493
|
editor.thinking = initialAppState.thinkingLevel !== "off";
|
|
143568
143494
|
editor.thinkingLevel = initialAppState.thinkingLevel;
|
|
143495
|
+
editor.permissionMode = initialAppState.permissionMode ?? "manual";
|
|
143569
143496
|
return {
|
|
143570
143497
|
ui,
|
|
143571
143498
|
terminal,
|
|
@@ -146661,6 +146588,7 @@ var ScreamTUI = class {
|
|
|
146661
146588
|
this.state.editor.thinking = patch.thinkingLevel !== "off";
|
|
146662
146589
|
this.state.editor.thinkingLevel = patch.thinkingLevel ?? "off";
|
|
146663
146590
|
}
|
|
146591
|
+
if ("permissionMode" in patch) this.state.editor.permissionMode = patch.permissionMode ?? "manual";
|
|
146664
146592
|
if ("streamingPhase" in patch && patch.streamingPhase !== "idle") {
|
|
146665
146593
|
this.transcriptController.stopWelcomeBreathing();
|
|
146666
146594
|
this.inputController.stopBreathingForStreaming();
|