@gajae-code/coding-agent 0.13.0 → 0.13.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.
- package/CHANGELOG.md +34 -0
- package/dist/types/config/file-lock.d.ts +2 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/models-config-schema.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +17 -5
- package/dist/types/config/settings.d.ts +10 -1
- package/dist/types/edit/streaming.d.ts +5 -5
- package/dist/types/eval/py/kernel.d.ts +2 -2
- package/dist/types/eval/py/runtime.d.ts +5 -1
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +3 -1
- package/dist/types/modes/components/model-selector.d.ts +3 -2
- package/dist/types/runtime/process-lifecycle.d.ts +12 -31
- package/dist/types/sdk/broker/identity.d.ts +2 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +17 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +11 -6
- package/dist/types/sdk/broker/startup-failure.d.ts +32 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +4 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +10 -1
- package/dist/types/sdk/bus/reconciliation-store.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +13 -2
- package/dist/types/sdk/bus/telegram-daemon.d.ts +17 -1
- package/dist/types/sdk/host/query/cursor.d.ts +4 -0
- package/dist/types/sdk/host/query/handlers.d.ts +6 -0
- package/dist/types/sdk/host/session-runtime.d.ts +7 -6
- package/dist/types/sdk/host/websocket-transport.d.ts +1 -1
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/turn-result.d.ts +37 -0
- package/dist/types/session/agent-session.d.ts +8 -11
- package/dist/types/session/blob-store.d.ts +6 -3
- package/dist/types/session/internal/managed-session-storage.d.ts +13 -0
- package/dist/types/slash-commands/helpers/fast-status-report.d.ts +12 -3
- package/dist/types/task/executor.d.ts +4 -7
- package/dist/types/tools/index.d.ts +1 -1
- package/dist/types/tools/read.d.ts +1 -1
- package/dist/types/tools/search.d.ts +1 -1
- package/dist/types/utils/edit-mode.d.ts +69 -0
- package/package.json +8 -8
- package/scripts/generate-hotkeys-docs.ts +8 -6
- package/src/commands/sdk.ts +23 -2
- package/src/config/file-lock.ts +25 -5
- package/src/config/keybindings.ts +4 -2
- package/src/config/model-registry.ts +20 -0
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +29 -22
- package/src/config/settings.ts +42 -10
- package/src/coordinator-mcp/server.ts +87 -106
- package/src/edit/index.ts +5 -19
- package/src/edit/renderer.ts +3 -2
- package/src/edit/streaming.ts +28 -15
- package/src/eval/py/executor.ts +108 -10
- package/src/eval/py/kernel.ts +86 -12
- package/src/eval/py/runtime.ts +200 -16
- package/src/exec/bash-executor.ts +36 -37
- package/src/gjc-runtime/gc-runtime.ts +25 -3
- package/src/gjc-runtime/managed-owner-supervisor.ts +27 -1
- package/src/gjc-runtime/session-state-sidecar.ts +110 -4
- package/src/gjc-runtime/tmux-sessions.ts +34 -5
- package/src/internal-urls/docs-index.generated.ts +9 -9
- package/src/modes/acp/acp-agent.ts +10 -2
- package/src/modes/components/model-selector.ts +28 -8
- package/src/modes/controllers/plan-mode-controller.ts +3 -3
- package/src/modes/controllers/selector-controller.ts +4 -2
- package/src/modes/interactive-mode.ts +4 -0
- package/src/runtime/process-lifecycle.ts +126 -27
- package/src/runtime-mcp/transports/stdio.ts +11 -6
- package/src/sdk/acp/adapter.ts +18 -10
- package/src/sdk/broker/broker.ts +55 -4
- package/src/sdk/broker/ensure.ts +10 -7
- package/src/sdk/broker/identity.ts +10 -0
- package/src/sdk/broker/lifecycle-ledger.ts +56 -6
- package/src/sdk/broker/lifecycle.ts +53 -10
- package/src/sdk/broker/process-incarnation.ts +6 -1
- package/src/sdk/broker/session-index.ts +28 -11
- package/src/sdk/broker/startup-failure.ts +101 -0
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/bus/chat-daemon-control.ts +4 -3
- package/src/sdk/bus/chat-daemon-runtime.ts +8 -1
- package/src/sdk/bus/conversation-store.ts +171 -50
- package/src/sdk/bus/index.ts +101 -47
- package/src/sdk/bus/kind-aware-reconciliation.ts +52 -2
- package/src/sdk/bus/notification-service.ts +3 -3
- package/src/sdk/bus/reconciliation-store.ts +16 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +13 -2
- package/src/sdk/bus/telegram-daemon.ts +151 -41
- package/src/sdk/bus/topic-registry.ts +38 -9
- package/src/sdk/cli/session-cli.ts +20 -7
- package/src/sdk/client/discovery.ts +23 -2
- package/src/sdk/host/query/cursor.ts +4 -0
- package/src/sdk/host/query/handlers.ts +99 -100
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -1
- package/src/sdk/host/session-runtime.test.ts +98 -1
- package/src/sdk/host/session-runtime.ts +57 -15
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +192 -2
- package/src/sdk/host/websocket-transport.ts +54 -30
- package/src/sdk/index.ts +1 -0
- package/src/sdk/mcp/server.ts +19 -6
- package/src/sdk/protocol/operation-inventory.generated.json +1 -1
- package/src/sdk/protocol/operation-registry.ts +8 -12
- package/src/sdk/session.ts +5 -14
- package/src/sdk/turn-result.ts +53 -0
- package/src/session/agent-session.ts +112 -69
- package/src/session/blob-store.ts +91 -21
- package/src/session/internal/managed-session-storage.ts +136 -21
- package/src/session/session-manager.ts +408 -81
- package/src/session/session-storage.ts +68 -10
- package/src/slash-commands/builtin-registry.ts +43 -5
- package/src/slash-commands/helpers/fast-status-report.ts +24 -8
- package/src/task/executor.ts +25 -8
- package/src/task/index.ts +5 -4
- package/src/tools/bash.ts +11 -9
- package/src/tools/index.ts +1 -1
- package/src/tools/read.ts +12 -11
- package/src/tools/search.ts +7 -7
- package/src/utils/edit-mode.ts +180 -9
- package/src/web/search/providers/insane.ts +55 -1
package/src/utils/edit-mode.ts
CHANGED
|
@@ -2,8 +2,15 @@ import { $pickenv } from "@gajae-code/utils/env";
|
|
|
2
2
|
|
|
3
3
|
export type EditMode = "replace" | "patch" | "hashline" | "vim" | "apply_patch";
|
|
4
4
|
|
|
5
|
+
/** Setting value for `edit.mode`: an executable mode or `auto` (model-family routing). */
|
|
6
|
+
export type EditModeSetting = "auto" | EditMode;
|
|
7
|
+
|
|
8
|
+
/** Fallback executable mode used when automatic routing cannot identify the model. */
|
|
5
9
|
export const DEFAULT_EDIT_MODE: EditMode = "hashline";
|
|
6
10
|
|
|
11
|
+
/** Default `edit.mode` setting value. */
|
|
12
|
+
export const DEFAULT_EDIT_MODE_SETTING: EditModeSetting = "auto";
|
|
13
|
+
|
|
7
14
|
const EDIT_MODE_IDS = {
|
|
8
15
|
apply_patch: "apply_patch",
|
|
9
16
|
hashline: "hashline",
|
|
@@ -14,29 +21,193 @@ const EDIT_MODE_IDS = {
|
|
|
14
21
|
|
|
15
22
|
export const EDIT_MODES = Object.keys(EDIT_MODE_IDS) as EditMode[];
|
|
16
23
|
|
|
24
|
+
export const EDIT_MODE_SETTINGS: EditModeSetting[] = ["auto", ...EDIT_MODES];
|
|
25
|
+
|
|
17
26
|
export function normalizeEditMode(mode?: string | null): EditMode | undefined {
|
|
18
27
|
if (!mode) return undefined;
|
|
19
|
-
return EDIT_MODE_IDS[mode as keyof typeof EDIT_MODE_IDS];
|
|
28
|
+
return Object.hasOwn(EDIT_MODE_IDS, mode) ? EDIT_MODE_IDS[mode as keyof typeof EDIT_MODE_IDS] : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
32
|
+
// Model-family detection (automatic routing)
|
|
33
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
export type ModelEditFamily = "gpt" | "codex" | "claude" | "deepseek" | "qwen" | "minimax" | "glm" | "kimi" | "unknown";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Built-in family → edit mode mapping used by `edit.mode: auto`.
|
|
39
|
+
*
|
|
40
|
+
* GPT/Codex models are trained on the OpenAI patch grammar; Claude, DeepSeek,
|
|
41
|
+
* and Qwen perform best with exact search/replace; MiniMax, GLM, and
|
|
42
|
+
* Kimi/Moonshot benchmark best with hash-anchored edits. Unknown models fall
|
|
43
|
+
* back to hashline, the safest fail-closed default.
|
|
44
|
+
*/
|
|
45
|
+
const FAMILY_EDIT_MODES: Record<Exclude<ModelEditFamily, "unknown">, EditMode> = {
|
|
46
|
+
gpt: "apply_patch",
|
|
47
|
+
codex: "apply_patch",
|
|
48
|
+
claude: "replace",
|
|
49
|
+
deepseek: "replace",
|
|
50
|
+
qwen: "replace",
|
|
51
|
+
minimax: "hashline",
|
|
52
|
+
glm: "hashline",
|
|
53
|
+
kimi: "hashline",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Detect the model family from a model ID, ignoring the provider prefix so
|
|
58
|
+
* equivalent models served through different providers route identically
|
|
59
|
+
* (`openai/gpt-5.4`, `openrouter/openai/gpt-5.4`, `company-gateway/gpt-5.4`).
|
|
60
|
+
*
|
|
61
|
+
* Matching only examines the final path segment. A direct family prefix or a
|
|
62
|
+
* known provider-qualified family name is accepted, while arbitrary token
|
|
63
|
+
* substrings return `unknown` instead of guessing.
|
|
64
|
+
*/
|
|
65
|
+
export function detectModelEditFamily(modelId: string | undefined): ModelEditFamily {
|
|
66
|
+
if (!modelId) return "unknown";
|
|
67
|
+
const normalized = modelId.trim().toLowerCase();
|
|
68
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
69
|
+
const model = segments.at(-1) ?? normalized;
|
|
70
|
+
|
|
71
|
+
if (/^codex(?:$|[-_.\d])|^(?:gpt|(?:duo-chat|openai)-gpt)-\d+(?:[-_.][\w]+)*[-_.]codex(?:$|[-_.\d])/.test(model))
|
|
72
|
+
return "codex";
|
|
73
|
+
if (
|
|
74
|
+
/^(?:gpt-\d+(?:$|[-_.])|gpt-oss(?:$|[-_.\d])|chatgpt(?:$|[-_.]\d))|(?:^|\.)openai\.(?:gpt|chatgpt)(?:$|[-_.\d])/.test(
|
|
75
|
+
model,
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
return "gpt";
|
|
79
|
+
if (/^claude(?:$|[-_.\d])|(?:^|\.)anthropic\.claude(?:$|[-_.\d])/.test(model)) return "claude";
|
|
80
|
+
if (/^deepseek(?:$|[-_.\d])|(?:^|\.)deepseek(?:$|[-_.\d])/.test(model)) return "deepseek";
|
|
81
|
+
if (/^qwen(?:$|[-_.\d])|(?:^|\.)qwen\.qwen(?:$|[-_.\d])/.test(model)) return "qwen";
|
|
82
|
+
if (/^minimax(?:$|[-_.\d])/.test(model)) return "minimax";
|
|
83
|
+
if (/^glm(?:$|[-_.\d])/.test(model)) return "glm";
|
|
84
|
+
if (/^(?:kimi|moonshot)(?:$|[-_.\d])/.test(model)) return "kimi";
|
|
85
|
+
|
|
86
|
+
return "unknown";
|
|
20
87
|
}
|
|
21
88
|
|
|
89
|
+
/** Built-in mode for a detected family; `undefined` for `unknown`. */
|
|
90
|
+
export function builtinEditModeForFamily(family: ModelEditFamily): EditMode | undefined {
|
|
91
|
+
if (family === "unknown") return undefined;
|
|
92
|
+
return FAMILY_EDIT_MODES[family];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
96
|
+
// Resolution
|
|
97
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
export type EditModeSource = "environment" | "model-override" | "setting" | "catalog" | "builtin-family" | "fallback";
|
|
100
|
+
|
|
101
|
+
export type ResolvedEditModeDetails = {
|
|
102
|
+
mode: EditMode;
|
|
103
|
+
source: EditModeSource;
|
|
104
|
+
modelId?: string;
|
|
105
|
+
family?: ModelEditFamily;
|
|
106
|
+
matchedRule?: string;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/** Raw `edit.modelVariants` match: pattern plus the configured (unvalidated) value. */
|
|
110
|
+
export type EditVariantMatch = {
|
|
111
|
+
pattern: string;
|
|
112
|
+
value: string;
|
|
113
|
+
};
|
|
114
|
+
|
|
22
115
|
export interface EditModeSettingsLike {
|
|
23
116
|
get(key: "edit.mode"): unknown;
|
|
117
|
+
/** Legacy accessor: first *valid* matching `edit.modelVariants` value. */
|
|
24
118
|
getEditVariantForModel?(model: string | undefined): EditMode | null;
|
|
119
|
+
/**
|
|
120
|
+
* Discriminated accessor: first matching `edit.modelVariants` rule with its
|
|
121
|
+
* raw value. Lets the resolver fail closed on invalid matched values
|
|
122
|
+
* instead of silently falling through to another mode.
|
|
123
|
+
*/
|
|
124
|
+
matchEditVariantForModel?(model: string | undefined): EditVariantMatch | null;
|
|
25
125
|
}
|
|
26
126
|
|
|
27
127
|
export interface EditModeSessionLike {
|
|
28
128
|
settings: EditModeSettingsLike;
|
|
29
129
|
getActiveModelString?: () => string | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* Optional model-catalog edit recommendation; beats the built-in family
|
|
132
|
+
* mapping when present.
|
|
133
|
+
*
|
|
134
|
+
* Extension point: production sessions do not wire this yet — the model
|
|
135
|
+
* catalog does not expose an edit-protocol recommendation (`applyPatchToolType`
|
|
136
|
+
* is the OpenAI wire representation of an already-selected `apply_patch`
|
|
137
|
+
* mode, not a mode selector). Catalog metadata generation is deliberately
|
|
138
|
+
* deferred until the family-based path is proven for arbitrary custom-provider
|
|
139
|
+
* IDs; when it lands, sessions can expose this hook without a resolver change.
|
|
140
|
+
*/
|
|
141
|
+
getCatalogEditMode?: () => EditMode | undefined;
|
|
30
142
|
}
|
|
31
143
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Read the forced edit mode from `GJC_EDIT_VARIANT`/`PI_EDIT_VARIANT`.
|
|
146
|
+
* The environment force is the emergency kill switch and beats every other
|
|
147
|
+
* source. Invalid values fail fast; empty and `auto` mean "not forced".
|
|
148
|
+
*/
|
|
149
|
+
export function resolveForcedEnvEditMode(): EditMode | undefined {
|
|
150
|
+
const raw = $pickenv("GJC_EDIT_VARIANT", "PI_EDIT_VARIANT");
|
|
151
|
+
if (!raw || raw === "auto") return undefined;
|
|
152
|
+
const mode = normalizeEditMode(raw);
|
|
153
|
+
if (!mode) {
|
|
154
|
+
throw new Error(`Invalid GJC_EDIT_VARIANT: ${raw}`);
|
|
155
|
+
}
|
|
156
|
+
return mode;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Resolve the active edit mode with provenance.
|
|
161
|
+
*
|
|
162
|
+
* Precedence:
|
|
163
|
+
* 1. `GJC_EDIT_VARIANT`/`PI_EDIT_VARIANT` environment force (invalid → throw).
|
|
164
|
+
* 2. Matching user `edit.modelVariants` rule (matched invalid → throw).
|
|
165
|
+
* 3. Explicit `edit.mode` setting when it is not `auto`.
|
|
166
|
+
* 4. Model-catalog edit recommendation, when the session exposes one.
|
|
167
|
+
* 5. Built-in model-family mapping.
|
|
168
|
+
* 6. `hashline` fallback.
|
|
169
|
+
*/
|
|
170
|
+
export function resolveEditModeDetails(session: EditModeSessionLike): ResolvedEditModeDetails {
|
|
171
|
+
const modelId = session.getActiveModelString?.();
|
|
172
|
+
|
|
173
|
+
const envMode = resolveForcedEnvEditMode();
|
|
174
|
+
if (envMode) return { mode: envMode, source: "environment", modelId };
|
|
175
|
+
|
|
176
|
+
const settings = session.settings;
|
|
177
|
+
if (settings.matchEditVariantForModel) {
|
|
178
|
+
const match = settings.matchEditVariantForModel(modelId);
|
|
179
|
+
if (match) {
|
|
180
|
+
const mode = normalizeEditMode(match.value);
|
|
181
|
+
if (!mode) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`Invalid edit.modelVariants value "${match.value}" for pattern "${match.pattern}" (expected one of: ${EDIT_MODES.join(", ")})`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return { mode, source: "model-override", modelId, matchedRule: match.pattern };
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
const legacyVariant = settings.getEditVariantForModel?.(modelId);
|
|
190
|
+
if (legacyVariant) return { mode: legacyVariant, source: "model-override", modelId };
|
|
191
|
+
}
|
|
36
192
|
|
|
37
|
-
const
|
|
38
|
-
|
|
193
|
+
const rawSetting = settings.get("edit.mode");
|
|
194
|
+
const settingValue = typeof rawSetting === "string" ? rawSetting : undefined;
|
|
195
|
+
if (settingValue && settingValue !== "auto") {
|
|
196
|
+
const settingsMode = normalizeEditMode(settingValue);
|
|
197
|
+
if (settingsMode) return { mode: settingsMode, source: "setting", modelId };
|
|
198
|
+
}
|
|
39
199
|
|
|
40
|
-
const
|
|
41
|
-
|
|
200
|
+
const family = detectModelEditFamily(modelId);
|
|
201
|
+
|
|
202
|
+
const catalogMode = session.getCatalogEditMode?.();
|
|
203
|
+
if (catalogMode) return { mode: catalogMode, source: "catalog", modelId, family };
|
|
204
|
+
|
|
205
|
+
const builtinMode = builtinEditModeForFamily(family);
|
|
206
|
+
if (builtinMode) return { mode: builtinMode, source: "builtin-family", modelId, family };
|
|
207
|
+
|
|
208
|
+
return { mode: DEFAULT_EDIT_MODE, source: "fallback", modelId, family };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function resolveEditMode(session: EditModeSessionLike): EditMode {
|
|
212
|
+
return resolveEditModeDetails(session).mode;
|
|
42
213
|
}
|
|
@@ -25,6 +25,7 @@ const DEFAULT_NUM_RESULTS = 10;
|
|
|
25
25
|
const MAX_NUM_RESULTS = 20;
|
|
26
26
|
const PUBLIC_ROUTE_TIMEOUT_MS = 15_000;
|
|
27
27
|
const DISCOVERY_LIMIT = 8;
|
|
28
|
+
const MAX_PUBLIC_ROUTE_RESPONSE_BYTES = 1024 * 1024;
|
|
28
29
|
|
|
29
30
|
const USER_AGENT = "Gajae-Code insane-search safe-public-routes/1.0 (+https://github.com/Yeachan-Heo/gajae-code)";
|
|
30
31
|
|
|
@@ -146,6 +147,59 @@ function attempt(
|
|
|
146
147
|
return { platform, route, ok, status, bytes: new TextEncoder().encode(body).byteLength, note };
|
|
147
148
|
}
|
|
148
149
|
|
|
150
|
+
class ResponseTooLargeError extends Error {
|
|
151
|
+
override readonly name = "response_too_large";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function cancelBody(body: ReadableStream<Uint8Array> | null): Promise<void> {
|
|
155
|
+
if (!body) return;
|
|
156
|
+
try {
|
|
157
|
+
await body.cancel();
|
|
158
|
+
} catch {
|
|
159
|
+
// Best-effort teardown; preserve the stable size-limit diagnostic.
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function readResponseText(response: Response): Promise<string> {
|
|
164
|
+
const contentLength = response.headers.get("content-length");
|
|
165
|
+
if (contentLength !== null) {
|
|
166
|
+
const declaredBytes = Number.parseInt(contentLength, 10);
|
|
167
|
+
if (Number.isFinite(declaredBytes) && declaredBytes > MAX_PUBLIC_ROUTE_RESPONSE_BYTES) {
|
|
168
|
+
await cancelBody(response.body);
|
|
169
|
+
throw new ResponseTooLargeError();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const reader = response.body?.getReader();
|
|
174
|
+
if (!reader) return "";
|
|
175
|
+
|
|
176
|
+
const decoder = new TextDecoder();
|
|
177
|
+
let totalBytes = 0;
|
|
178
|
+
let text = "";
|
|
179
|
+
try {
|
|
180
|
+
while (true) {
|
|
181
|
+
const { done, value } = await reader.read();
|
|
182
|
+
if (done) break;
|
|
183
|
+
if (!value || value.byteLength === 0) continue;
|
|
184
|
+
|
|
185
|
+
totalBytes += value.byteLength;
|
|
186
|
+
if (totalBytes > MAX_PUBLIC_ROUTE_RESPONSE_BYTES) {
|
|
187
|
+
try {
|
|
188
|
+
await reader.cancel();
|
|
189
|
+
} catch {
|
|
190
|
+
// Best-effort teardown; preserve the stable size-limit diagnostic.
|
|
191
|
+
}
|
|
192
|
+
throw new ResponseTooLargeError();
|
|
193
|
+
}
|
|
194
|
+
text += decoder.decode(value, { stream: true });
|
|
195
|
+
}
|
|
196
|
+
text += decoder.decode();
|
|
197
|
+
return text;
|
|
198
|
+
} finally {
|
|
199
|
+
reader.releaseLock();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
149
203
|
async function fetchText(
|
|
150
204
|
url: string,
|
|
151
205
|
signal?: AbortSignal,
|
|
@@ -158,7 +212,7 @@ async function fetchText(
|
|
|
158
212
|
redirect: "follow",
|
|
159
213
|
signal: withHardTimeout(signal, PUBLIC_ROUTE_TIMEOUT_MS),
|
|
160
214
|
});
|
|
161
|
-
const text = await response
|
|
215
|
+
const text = await readResponseText(response);
|
|
162
216
|
return { status: response.status, text, contentType: response.headers.get("content-type") ?? "" };
|
|
163
217
|
}
|
|
164
218
|
|