@yanlinglabs/winter-agent-runtime 0.0.38 → 0.0.40
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/embedded-worker.js +3 -3
- package/dist/embedded.js +3 -3
- package/dist/engine.d.ts +8 -0
- package/dist/{index-5xe59vkt.js → index-a8j104mg.js} +2133 -218
- package/dist/{index-8x4yjdg7.js → index-r7yjcppm.js} +2 -2
- package/dist/{index-z3970wsj.js → index-rhxkckyn.js} +8 -2
- package/dist/index.js +1 -1
- package/dist/messaging/host-port.d.ts +19 -0
- package/dist/messaging/reference-adapter.d.ts +8 -1
- package/dist/permissions/bash-read-only.d.ts +26 -0
- package/dist/permissions/grammar.d.ts +32 -6
- package/dist/production-wiring.d.ts +3 -1
- package/dist/rpc/bridge.d.ts +1 -0
- package/dist/store/dialect.d.ts +1 -1
- package/dist/subagents/child-engine.d.ts +2 -0
- package/dist/subagents/register-default-factory.d.ts +2 -0
- package/dist/testing.js +2 -2
- package/dist/tools/concurrency.d.ts +48 -0
- package/dist/tools/impl/task-stop.d.ts +2 -0
- package/dist/tools/registry.d.ts +13 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -61,12 +61,24 @@ function createRpcBridge2(output, bridgeOpts = {}) {
|
|
|
61
61
|
const onAbort = () => {
|
|
62
62
|
if (pending.get(requestId) !== entry)
|
|
63
63
|
return;
|
|
64
|
-
pending.delete(requestId);
|
|
65
64
|
if (entry.timer !== undefined)
|
|
66
65
|
clearTimeout(entry.timer);
|
|
67
|
-
|
|
66
|
+
const wasHeld = held?.delete(requestId) === true;
|
|
67
|
+
if (!wasHeld)
|
|
68
68
|
output.write({ type: "control_cancel_request", requestId });
|
|
69
|
-
|
|
69
|
+
const cancelled = () => {
|
|
70
|
+
if (pending.get(requestId) !== entry)
|
|
71
|
+
return;
|
|
72
|
+
pending.delete(requestId);
|
|
73
|
+
reject(new WinterRpcError("cancelled", `the '${subtype}' request was cancelled: the runtime stopped waiting for it`));
|
|
74
|
+
};
|
|
75
|
+
const grace = opts?.cancelGraceMs;
|
|
76
|
+
if (grace === undefined || wasHeld) {
|
|
77
|
+
cancelled();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
entry.timer = setTimeout(cancelled, grace);
|
|
81
|
+
entry.timer.unref?.();
|
|
70
82
|
};
|
|
71
83
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
72
84
|
entry.detach = () => signal.removeEventListener("abort", onAbort);
|
|
@@ -265,10 +277,28 @@ function leadingWord(s) {
|
|
|
265
277
|
function joinLineContinuations(command) {
|
|
266
278
|
if (!command.includes("\\\n"))
|
|
267
279
|
return command;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
280
|
+
let out = "";
|
|
281
|
+
let i = 0;
|
|
282
|
+
while (i < command.length) {
|
|
283
|
+
if (command[i] !== "\\") {
|
|
284
|
+
out += command[i];
|
|
285
|
+
i++;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
let end = i;
|
|
289
|
+
while (end < command.length && command[end] === "\\")
|
|
290
|
+
end++;
|
|
291
|
+
const run = end - i;
|
|
292
|
+
if (command[end] === `
|
|
293
|
+
` && run % 2 === 1) {
|
|
294
|
+
out += "\\".repeat(run - 1);
|
|
295
|
+
i = end + 1;
|
|
296
|
+
} else {
|
|
297
|
+
out += "\\".repeat(run);
|
|
298
|
+
i = end;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return out;
|
|
272
302
|
}
|
|
273
303
|
function splitCompound(rawCommand) {
|
|
274
304
|
const command = joinLineContinuations(rawCommand);
|
|
@@ -350,25 +380,34 @@ function stripLeadingAssignmentsAt(s, info, start, direction) {
|
|
|
350
380
|
}
|
|
351
381
|
return pos;
|
|
352
382
|
}
|
|
353
|
-
function
|
|
383
|
+
function stripWrappersDetailed(rawCmd, direction) {
|
|
354
384
|
const cmd = joinLineContinuations(rawCmd);
|
|
355
385
|
const info = scanShellLike(cmd);
|
|
386
|
+
const finish = (pos, ambiguousForAllow = false) => {
|
|
387
|
+
const carried = info.ok ? redirectWriteSpans(cmd, info).filter((span) => span.start < pos).map((span) => cmd.slice(span.start, span.end)) : [];
|
|
388
|
+
return { inner: cmd.slice(pos), carried, ambiguousForAllow };
|
|
389
|
+
};
|
|
356
390
|
let pos = 0;
|
|
357
391
|
for (;; ) {
|
|
358
392
|
pos = stripLeadingAssignmentsAt(cmd, info, pos, direction);
|
|
359
393
|
const { word: rawWord, end: afterWordEnd } = leadingWordAt(cmd, info, pos);
|
|
360
394
|
if (rawWord === undefined)
|
|
361
|
-
return
|
|
362
|
-
const
|
|
395
|
+
return finish(pos);
|
|
396
|
+
const isWrapperName = (r) => r === XARGS || FIXED_WRAPPERS.has(r);
|
|
397
|
+
const readings = shellWordReadings(rawWord);
|
|
398
|
+
const exact = readings[0];
|
|
399
|
+
if (direction === "allow" && !isWrapperName(exact) && readings.some(isWrapperName))
|
|
400
|
+
return finish(pos, true);
|
|
401
|
+
const word = direction === "allow" ? exact : readings.find(isWrapperName) ?? exact;
|
|
363
402
|
if (word === XARGS) {
|
|
364
403
|
const { word: next } = leadingWordAt(cmd, info, afterWordEnd);
|
|
365
404
|
if (next !== undefined && next.startsWith("-"))
|
|
366
|
-
return
|
|
405
|
+
return finish(pos);
|
|
367
406
|
pos = afterWordEnd;
|
|
368
407
|
continue;
|
|
369
408
|
}
|
|
370
409
|
if (!FIXED_WRAPPERS.has(word))
|
|
371
|
-
return
|
|
410
|
+
return finish(pos);
|
|
372
411
|
let remainderPos = afterWordEnd;
|
|
373
412
|
for (;; ) {
|
|
374
413
|
const { word: flag, end: afterFlagEnd } = leadingWordAt(cmd, info, remainderPos);
|
|
@@ -384,6 +423,17 @@ function stripWrappers(rawCmd, direction) {
|
|
|
384
423
|
pos = remainderPos;
|
|
385
424
|
}
|
|
386
425
|
}
|
|
426
|
+
var joinedStrip = (strip) => [strip.inner, ...strip.carried].filter((part) => part !== "").join(" ");
|
|
427
|
+
function stripWrappers(rawCmd, direction) {
|
|
428
|
+
const strip = stripWrappersDetailed(rawCmd, direction);
|
|
429
|
+
return strip.ambiguousForAllow ? joinLineContinuations(rawCmd).trimStart() : joinedStrip(strip);
|
|
430
|
+
}
|
|
431
|
+
function ruleMatchTexts(command, direction) {
|
|
432
|
+
const strip = stripWrappersDetailed(command, direction);
|
|
433
|
+
if (strip.ambiguousForAllow)
|
|
434
|
+
return [];
|
|
435
|
+
return direction === "allow" ? [strip.inner] : [joinedStrip(strip), strip.inner];
|
|
436
|
+
}
|
|
387
437
|
var ANSI_C_SIMPLE_ESCAPES = { n: `
|
|
388
438
|
`, t: "\t", r: "\r", a: "\x07", b: "\b", e: "\x1B", E: "\x1B", f: "\f", v: "\v", "\\": "\\", "'": "'", '"': '"', "?": "?" };
|
|
389
439
|
function decodeAnsiCEscape(s, i) {
|
|
@@ -413,7 +463,9 @@ function decodeAnsiCEscape(s, i) {
|
|
|
413
463
|
return { text: String.fromCharCode(s.charCodeAt(i + 1) & 31), next: i + 2 };
|
|
414
464
|
return { text: `\\${ch}`, next: i + 1 };
|
|
415
465
|
}
|
|
416
|
-
|
|
466
|
+
var DOUBLE_QUOTE_ESCAPABLE = new Set(["$", "`", '"', "\\", `
|
|
467
|
+
`]);
|
|
468
|
+
function shellWords(s, split = true, reading = "broad") {
|
|
417
469
|
const words = [];
|
|
418
470
|
let cur = "";
|
|
419
471
|
let start = -1;
|
|
@@ -455,9 +507,12 @@ function shellWords(s, split = true) {
|
|
|
455
507
|
if (ch === '"')
|
|
456
508
|
quote = null;
|
|
457
509
|
else if (ch === "\\" && i + 1 < s.length) {
|
|
458
|
-
|
|
510
|
+
const next = s[i + 1];
|
|
511
|
+
if (reading === "bash" && !DOUBLE_QUOTE_ESCAPABLE.has(next))
|
|
512
|
+
cur += ch + next;
|
|
513
|
+
else if (next !== `
|
|
459
514
|
`)
|
|
460
|
-
cur +=
|
|
515
|
+
cur += next;
|
|
461
516
|
i++;
|
|
462
517
|
} else
|
|
463
518
|
cur += ch;
|
|
@@ -498,7 +553,12 @@ function shellWords(s, split = true) {
|
|
|
498
553
|
return words;
|
|
499
554
|
}
|
|
500
555
|
function dequoteShellWord(word) {
|
|
501
|
-
return shellWords(word, false)[0]?.word ?? "";
|
|
556
|
+
return shellWords(word, false, "bash")[0]?.word ?? "";
|
|
557
|
+
}
|
|
558
|
+
function shellWordReadings(word) {
|
|
559
|
+
const exact = dequoteShellWord(word);
|
|
560
|
+
const broad = shellWords(word, false, "broad")[0]?.word ?? "";
|
|
561
|
+
return broad === exact ? [exact] : [exact, broad];
|
|
502
562
|
}
|
|
503
563
|
var REDIRECT_WORD_STOP = /[\s;&|<>()]/;
|
|
504
564
|
function redirectWordAt(s, info, start) {
|
|
@@ -515,12 +575,23 @@ function extractRedirectWrites(rawCommand) {
|
|
|
515
575
|
const info = scanShellLike(command);
|
|
516
576
|
if (!info.ok)
|
|
517
577
|
return [];
|
|
518
|
-
const { topLevel } = info;
|
|
519
578
|
const writes = [];
|
|
520
|
-
const
|
|
521
|
-
|
|
579
|
+
const pushReadings = (raw) => {
|
|
580
|
+
for (const target of shellWordReadings(raw))
|
|
581
|
+
writes.push({ raw, target });
|
|
582
|
+
};
|
|
583
|
+
for (const { target: raw } of redirectWriteSpans(command, info)) {
|
|
584
|
+
pushReadings(raw);
|
|
522
585
|
if (raw.length > 1 && raw.startsWith("!"))
|
|
523
|
-
|
|
586
|
+
pushReadings(raw.slice(1));
|
|
587
|
+
}
|
|
588
|
+
return writes;
|
|
589
|
+
}
|
|
590
|
+
function redirectWriteSpans(command, info) {
|
|
591
|
+
const { topLevel } = info;
|
|
592
|
+
const spans = [];
|
|
593
|
+
const push = (start, target, end) => {
|
|
594
|
+
spans.push({ start, end, target });
|
|
524
595
|
};
|
|
525
596
|
let i = 0;
|
|
526
597
|
while (i < command.length) {
|
|
@@ -566,10 +637,10 @@ function extractRedirectWrites(rawCommand) {
|
|
|
566
637
|
continue;
|
|
567
638
|
}
|
|
568
639
|
if (!(descriptorCopy && /^(?:[0-9]+|-)$/.test(dequoteShellWord(word))))
|
|
569
|
-
push(word);
|
|
640
|
+
push(i, word, end);
|
|
570
641
|
i = end;
|
|
571
642
|
}
|
|
572
|
-
return
|
|
643
|
+
return spans;
|
|
573
644
|
}
|
|
574
645
|
function extractRedirectTargets(command) {
|
|
575
646
|
return extractRedirectWrites(command).map((w) => w.target);
|
|
@@ -583,8 +654,8 @@ function isRecognizedReadOnly(command) {
|
|
|
583
654
|
const { word: first, afterWord } = leadingWord(stripped);
|
|
584
655
|
if (first === undefined)
|
|
585
656
|
return false;
|
|
586
|
-
const
|
|
587
|
-
const hasFlag = (pattern) =>
|
|
657
|
+
const views = [afterWord, ...["broad", "bash"].map((reading) => shellWords(afterWord, true, reading).map((w) => w.word).join(" "))];
|
|
658
|
+
const hasFlag = (pattern) => views.some((view) => pattern.test(view));
|
|
588
659
|
if (first === "git") {
|
|
589
660
|
const { word: sub } = leadingWord(afterWord);
|
|
590
661
|
return sub !== undefined && GIT_READ_ONLY_SUBCOMMANDS.has(sub) && !hasFlag(GIT_WRITE_CAPABLE_FLAGS);
|
|
@@ -794,8 +865,8 @@ function matchesRule(rule, call, opts) {
|
|
|
794
865
|
case "pattern": {
|
|
795
866
|
const raw = call.input["command"];
|
|
796
867
|
const cmd = typeof raw === "string" ? raw : "";
|
|
797
|
-
const
|
|
798
|
-
return
|
|
868
|
+
const pattern = compilePattern(rule.specifier.source);
|
|
869
|
+
return ruleMatchTexts(cmd, opts.direction).some((text) => pattern.test(text));
|
|
799
870
|
}
|
|
800
871
|
}
|
|
801
872
|
}
|
|
@@ -1112,6 +1183,10 @@ function dedupeSameBatch(server, tools) {
|
|
|
1112
1183
|
}
|
|
1113
1184
|
return dropped ? out : tools;
|
|
1114
1185
|
}
|
|
1186
|
+
var CONCURRENCY_LANE = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
1187
|
+
function isValidConcurrencyLane(lane) {
|
|
1188
|
+
return typeof lane === "string" && CONCURRENCY_LANE.test(lane);
|
|
1189
|
+
}
|
|
1115
1190
|
function registerMcpServerTools(server, incomingTools, opts) {
|
|
1116
1191
|
if (RESERVED_MCP_SERVER_NAMES.has(server)) {
|
|
1117
1192
|
throw new Error(`registerMcpServerTools: "${server}" is a RESERVED server name -- the standing Winter server ` + `(its name is defined in mcp/winter-server.ts) owns this identity and is registry-native; it is never installed through ` + `this live-mutation function, and nothing else may register live tools under it either.`);
|
|
@@ -1166,7 +1241,9 @@ function registerMcpServerTools(server, incomingTools, opts) {
|
|
|
1166
1241
|
for (const tool of tools) {
|
|
1167
1242
|
const canonicalName = registeredName(tool);
|
|
1168
1243
|
const existingEntry = registry.get(canonicalName);
|
|
1169
|
-
const
|
|
1244
|
+
const built = buildMcpToolDescriptor(server, tool, opts, plainNameFor(tool));
|
|
1245
|
+
const lane = opts.toolLanes !== undefined && Object.hasOwn(opts.toolLanes, tool.name) ? opts.toolLanes[tool.name] : undefined;
|
|
1246
|
+
const descriptor = isValidConcurrencyLane(lane) ? { ...built, concurrencyLane: lane } : built;
|
|
1170
1247
|
registry.set(canonicalName, existingEntry ? { ...existingEntry, descriptor } : { descriptor });
|
|
1171
1248
|
mcpToolOwner.set(canonicalName, server);
|
|
1172
1249
|
nowOwned.add(canonicalName);
|
|
@@ -4554,7 +4631,7 @@ function parentOf(dir) {
|
|
|
4554
4631
|
// src/engine.ts
|
|
4555
4632
|
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
4556
4633
|
import { homedir, release as osRelease, type as osType } from "node:os";
|
|
4557
|
-
import { join as
|
|
4634
|
+
import { join as join26, resolve as resolve12 } from "node:path";
|
|
4558
4635
|
import {
|
|
4559
4636
|
PROTOCOL_VERSION,
|
|
4560
4637
|
resolveWebToolsConfig,
|
|
@@ -5155,7 +5232,25 @@ function createDefaultMessagingRuntime(opts = {}) {
|
|
|
5155
5232
|
subscribers,
|
|
5156
5233
|
now
|
|
5157
5234
|
});
|
|
5158
|
-
|
|
5235
|
+
const hostPorts = new Map;
|
|
5236
|
+
return {
|
|
5237
|
+
seam,
|
|
5238
|
+
adapter,
|
|
5239
|
+
notifications,
|
|
5240
|
+
loopGuard: createLoopGuard(),
|
|
5241
|
+
subscribers,
|
|
5242
|
+
now,
|
|
5243
|
+
peers,
|
|
5244
|
+
classifyDeliveryError,
|
|
5245
|
+
hostMessaging: (sessionId) => hostPorts.get(sessionId),
|
|
5246
|
+
registerHostMessagingPort(sessionId, port) {
|
|
5247
|
+
hostPorts.set(sessionId, port);
|
|
5248
|
+
return () => {
|
|
5249
|
+
if (hostPorts.get(sessionId) === port)
|
|
5250
|
+
hostPorts.delete(sessionId);
|
|
5251
|
+
};
|
|
5252
|
+
}
|
|
5253
|
+
};
|
|
5159
5254
|
}
|
|
5160
5255
|
function getDefaultMessagingRuntime() {
|
|
5161
5256
|
const runtime = getMessagingRuntime();
|
|
@@ -7394,6 +7489,40 @@ function transformChildFrame(frame, correlation, forwardSubagentText) {
|
|
|
7394
7489
|
}
|
|
7395
7490
|
var SESSION_LEVEL_SYSTEM_SUBTYPES = new Set(["task_started", "task_progress", "task_notification", "task_updated", "background_tasks_changed"]);
|
|
7396
7491
|
|
|
7492
|
+
// src/messaging/host-port.ts
|
|
7493
|
+
import { HOST_MESSAGE_LIST_SUBTYPE, HOST_MESSAGE_SEND_SUBTYPE, HOST_SESSION_STOP_SUBTYPE } from "@yanlinglabs/winter-agent-sdk";
|
|
7494
|
+
import { isHostMessageSendAnswer, isHostSessionStopAnswer, normaliseHostMessageListAnswer } from "@yanlinglabs/winter-agent-sdk/messaging";
|
|
7495
|
+
var HOST_MESSAGE_SEND_TIMEOUT_MS = 180000;
|
|
7496
|
+
var HOST_MESSAGE_LIST_TIMEOUT_MS = 15000;
|
|
7497
|
+
var HOST_SESSION_STOP_TIMEOUT_MS = 30000;
|
|
7498
|
+
|
|
7499
|
+
class HostMessagingAnswerError extends Error {
|
|
7500
|
+
constructor(message) {
|
|
7501
|
+
super(message);
|
|
7502
|
+
this.name = "HostMessagingAnswerError";
|
|
7503
|
+
}
|
|
7504
|
+
}
|
|
7505
|
+
function createHostMessagingPort(sender) {
|
|
7506
|
+
return {
|
|
7507
|
+
async send(request, opts) {
|
|
7508
|
+
const answer = await sender.request(HOST_MESSAGE_SEND_SUBTYPE, request, { timeoutMs: HOST_MESSAGE_SEND_TIMEOUT_MS, ...opts?.signal !== undefined ? { signal: opts.signal } : {} });
|
|
7509
|
+
if (!isHostMessageSendAnswer(answer))
|
|
7510
|
+
throw new HostMessagingAnswerError("the host answered host_message_send with a malformed outcome");
|
|
7511
|
+
return answer;
|
|
7512
|
+
},
|
|
7513
|
+
async list(request, opts) {
|
|
7514
|
+
const answer = await sender.request(HOST_MESSAGE_LIST_SUBTYPE, request, { timeoutMs: HOST_MESSAGE_LIST_TIMEOUT_MS, ...opts?.signal !== undefined ? { signal: opts.signal } : {} });
|
|
7515
|
+
return normaliseHostMessageListAnswer(answer) ?? { sessions: [] };
|
|
7516
|
+
},
|
|
7517
|
+
async stop(request, opts) {
|
|
7518
|
+
const answer = await sender.request(HOST_SESSION_STOP_SUBTYPE, request, { timeoutMs: HOST_SESSION_STOP_TIMEOUT_MS, ...opts?.signal !== undefined ? { signal: opts.signal } : {} });
|
|
7519
|
+
if (!isHostSessionStopAnswer(answer))
|
|
7520
|
+
throw new HostMessagingAnswerError("the host answered host_session_stop with a malformed answer");
|
|
7521
|
+
return answer;
|
|
7522
|
+
}
|
|
7523
|
+
};
|
|
7524
|
+
}
|
|
7525
|
+
|
|
7397
7526
|
// src/permissions/ruleset.ts
|
|
7398
7527
|
import {
|
|
7399
7528
|
readFileSync as readFileSync6,
|
|
@@ -8338,9 +8467,10 @@ function naiveWriteWords(command) {
|
|
|
8338
8467
|
const out = [];
|
|
8339
8468
|
for (const m of command.matchAll(/(?:&>>?|[0-9]*>>?\|?|[0-9]*>&|<>)[ \t]*([^\s;&|()<>]+)/g)) {
|
|
8340
8469
|
const raw = m[1];
|
|
8341
|
-
const word
|
|
8342
|
-
|
|
8343
|
-
|
|
8470
|
+
for (const word of shellWordReadings(raw)) {
|
|
8471
|
+
if (!/^(?:[0-9]+|-)$/.test(word))
|
|
8472
|
+
out.push({ word, raw, quoted: word !== raw });
|
|
8473
|
+
}
|
|
8344
8474
|
}
|
|
8345
8475
|
for (const piece of naiveCommandPieces(command)) {
|
|
8346
8476
|
const stripped = stripWrappers(piece, "denyAsk");
|
|
@@ -18802,6 +18932,7 @@ replaceExecutor("TaskOutput", taskOutputExecutor);
|
|
|
18802
18932
|
|
|
18803
18933
|
// src/tools/impl/task-stop.ts
|
|
18804
18934
|
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
18935
|
+
import { boundedHostNote } from "@yanlinglabs/winter-agent-sdk/messaging";
|
|
18805
18936
|
function parseTaskStopInput(input) {
|
|
18806
18937
|
if (typeof input !== "object" || input === null)
|
|
18807
18938
|
return { error: "TaskStop: input must be an object" };
|
|
@@ -18831,6 +18962,9 @@ var taskStopExecutor = {
|
|
|
18831
18962
|
const id = parsed.task_id ?? parsed.shell_id;
|
|
18832
18963
|
const task = getTask(id);
|
|
18833
18964
|
if (!task) {
|
|
18965
|
+
const host = getMessagingRuntime()?.hostMessaging?.(ctx.sessionId);
|
|
18966
|
+
if (host !== undefined)
|
|
18967
|
+
return stopHostSession(host, id, ctx.agentId, ctx.signal);
|
|
18834
18968
|
return { output: `Error: TaskStop: unknown task_id "${id}"`, isError: true };
|
|
18835
18969
|
}
|
|
18836
18970
|
if (task.status !== "running") {
|
|
@@ -18852,6 +18986,24 @@ var taskStopExecutor = {
|
|
|
18852
18986
|
return { output: formatResult(`stopped task ${id}`, id, task.kind, task.command) };
|
|
18853
18987
|
}
|
|
18854
18988
|
};
|
|
18989
|
+
var HOST_SESSION_TASK_TYPE = "session";
|
|
18990
|
+
async function stopHostSession(host, id, agentId, signal) {
|
|
18991
|
+
let answer;
|
|
18992
|
+
try {
|
|
18993
|
+
answer = await host.stop({ id, ...agentId !== undefined ? { fromAgentId: agentId } : {} }, signal !== undefined ? { signal } : {});
|
|
18994
|
+
} catch (err) {
|
|
18995
|
+
return { output: `Error: TaskStop: the host did not answer the stop for "${id}" (${err instanceof Error ? err.name : "error"}); it may or may not have stopped`, isError: true };
|
|
18996
|
+
}
|
|
18997
|
+
const note = boundedHostNote(answer.note);
|
|
18998
|
+
const withNote = (message) => note === undefined ? message : `${message}. ${note}`;
|
|
18999
|
+
if (answer.status === "stopped")
|
|
19000
|
+
return { output: JSON.stringify({ message: withNote(`stopped session ${id}: its running turn was interrupted`), task_id: id, task_type: HOST_SESSION_TASK_TYPE }) };
|
|
19001
|
+
if (answer.status === "not_running")
|
|
19002
|
+
return { output: JSON.stringify({ message: withNote(`session ${id} has no turn running -- nothing to stop`), task_id: id, task_type: HOST_SESSION_TASK_TYPE }) };
|
|
19003
|
+
if (answer.status === "not_found")
|
|
19004
|
+
return { output: `Error: TaskStop: unknown task_id "${id}" (${answer.reason ?? "not found"})`, isError: true };
|
|
19005
|
+
return { output: `Error: TaskStop: ${answer.reason ?? answer.status}`, isError: true };
|
|
19006
|
+
}
|
|
18855
19007
|
replaceExecutor("TaskStop", taskStopExecutor);
|
|
18856
19008
|
|
|
18857
19009
|
// src/tools/impl/todo-write.ts
|
|
@@ -19448,7 +19600,8 @@ function callerContextFrom(ctx) {
|
|
|
19448
19600
|
return {
|
|
19449
19601
|
sessionId: ctx.sessionId,
|
|
19450
19602
|
...ctx.agentId !== undefined ? { agentId: ctx.agentId } : {},
|
|
19451
|
-
...ctx.toolUseId !== undefined ? { toolUseId: ctx.toolUseId } : {}
|
|
19603
|
+
...ctx.toolUseId !== undefined ? { toolUseId: ctx.toolUseId } : {},
|
|
19604
|
+
...ctx.signal !== undefined ? { signal: ctx.signal } : {}
|
|
19452
19605
|
};
|
|
19453
19606
|
}
|
|
19454
19607
|
|
|
@@ -22366,130 +22519,1711 @@ Sources:
|
|
|
22366
22519
|
}
|
|
22367
22520
|
replaceExecutor(SEARCH_CANONICAL_NAME, createSearchExecutor());
|
|
22368
22521
|
|
|
22369
|
-
// src/
|
|
22370
|
-
import {
|
|
22371
|
-
|
|
22372
|
-
|
|
22373
|
-
|
|
22374
|
-
|
|
22375
|
-
|
|
22376
|
-
|
|
22377
|
-
|
|
22378
|
-
|
|
22379
|
-
|
|
22380
|
-
|
|
22381
|
-
|
|
22382
|
-
|
|
22383
|
-
|
|
22384
|
-
|
|
22522
|
+
// src/permissions/bash-read-only.ts
|
|
22523
|
+
import { statSync as statSync10 } from "node:fs";
|
|
22524
|
+
import { join as join24 } from "node:path";
|
|
22525
|
+
var READ_ONLY_LENGTH_LIMIT = 4096;
|
|
22526
|
+
var WHOLE_TEXT_REFUSALS = [
|
|
22527
|
+
(text) => text.length > Math.min(PARSE_LIMIT, READ_ONLY_LENGTH_LIMIT),
|
|
22528
|
+
(text) => text.trim() === "",
|
|
22529
|
+
(text) => splitCompound(text) === null,
|
|
22530
|
+
(text) => !passesSecurityValidators(text),
|
|
22531
|
+
(text, platform) => containsVulnerableUncPath(text, platform)
|
|
22532
|
+
];
|
|
22533
|
+
function isBashCommandReadOnly(command, ctx) {
|
|
22534
|
+
const platform = process.platform;
|
|
22535
|
+
if (typeof command !== "string" || WHOLE_TEXT_REFUSALS.some((refuses) => refuses(command, platform)))
|
|
22536
|
+
return false;
|
|
22537
|
+
const joined = joinLineContinuations(command);
|
|
22538
|
+
if ((substitutionBodies(joined) ?? [""]).length > 0)
|
|
22539
|
+
return false;
|
|
22540
|
+
const commands = readSimpleCommands(joined) ?? [];
|
|
22541
|
+
if (commands.length === 0 || gitRunsUnsafely(joined, commands, ctx))
|
|
22542
|
+
return false;
|
|
22543
|
+
return commands.every((simple) => passesSecurityValidators(simple.text) && isSimpleCommandReadOnly(simple, platform));
|
|
22544
|
+
}
|
|
22545
|
+
var SHELL_OPERATORS = ["<<<", "&&", "||", "|&", ";;", ";&", "&>", ">>", ">&", ">|", "<<", "<&", "<>", ">", "<", "&", "|", ";"];
|
|
22546
|
+
var COMMAND_SEPARATORS = new Set([";", "&&", "||", "|"]);
|
|
22547
|
+
function throwsOutputAway(operator, descriptor, target) {
|
|
22548
|
+
if (operator === ">")
|
|
22549
|
+
return target === "/dev/null" && (descriptor === undefined || ["0", "1", "2"].includes(descriptor));
|
|
22550
|
+
return operator === ">&" && descriptor === "2" && target === "1";
|
|
22551
|
+
}
|
|
22552
|
+
function readSimpleCommands(text) {
|
|
22553
|
+
const lx = lex(text);
|
|
22554
|
+
if (lx.unterminated || text.includes("\r"))
|
|
22555
|
+
return null;
|
|
22556
|
+
let lastNonBlank = text.length - 1;
|
|
22557
|
+
while (lastNonBlank >= 0 && isBlank(text[lastNonBlank]))
|
|
22558
|
+
lastNonBlank--;
|
|
22559
|
+
const commands = [];
|
|
22560
|
+
let words = [];
|
|
22561
|
+
let wordStart = -1;
|
|
22562
|
+
let awaiting;
|
|
22563
|
+
let endedBySemicolon = false;
|
|
22564
|
+
const closeWord = (end) => {
|
|
22565
|
+
if (wordStart === -1)
|
|
22566
|
+
return true;
|
|
22567
|
+
const raw = text.slice(wordStart, end);
|
|
22568
|
+
wordStart = -1;
|
|
22569
|
+
if (awaiting !== undefined) {
|
|
22570
|
+
const harmless = throwsOutputAway(awaiting.operator, awaiting.descriptor, raw);
|
|
22571
|
+
awaiting = undefined;
|
|
22572
|
+
return harmless;
|
|
22573
|
+
}
|
|
22574
|
+
words.push({ raw, value: dequoteShellWord(raw) });
|
|
22575
|
+
return true;
|
|
22385
22576
|
};
|
|
22577
|
+
const opensSubstitution = (i, quoteForms) => {
|
|
22578
|
+
const next = text[i + 1];
|
|
22579
|
+
return text[i] === "$" && next !== undefined && (next === "(" || next === "{" || next === "[" || quoteForms && (next === "'" || next === '"'));
|
|
22580
|
+
};
|
|
22581
|
+
for (let i = 0;i < text.length; i++) {
|
|
22582
|
+
const ch = text[i];
|
|
22583
|
+
if (!isBare(lx, i)) {
|
|
22584
|
+
if (lx.role[i] === 0 /* Text */ && lx.quoting[i] === 2 /* Double */ && (ch === "`" || opensSubstitution(i, false)))
|
|
22585
|
+
return null;
|
|
22586
|
+
if (wordStart === -1)
|
|
22587
|
+
wordStart = i;
|
|
22588
|
+
continue;
|
|
22589
|
+
}
|
|
22590
|
+
if (ch === " " || ch === "\t" || ch === `
|
|
22591
|
+
`) {
|
|
22592
|
+
if (!closeWord(i))
|
|
22593
|
+
return null;
|
|
22594
|
+
if (ch === `
|
|
22595
|
+
` && i < lastNonBlank)
|
|
22596
|
+
return null;
|
|
22597
|
+
continue;
|
|
22598
|
+
}
|
|
22599
|
+
if (ch === "`" || ch === "(" || ch === ")" || ch === "\\")
|
|
22600
|
+
return null;
|
|
22601
|
+
if (opensSubstitution(i, true))
|
|
22602
|
+
return null;
|
|
22603
|
+
if (ch === "#" && wordStart === -1)
|
|
22604
|
+
return null;
|
|
22605
|
+
if (!";&|<>".includes(ch)) {
|
|
22606
|
+
if (wordStart === -1)
|
|
22607
|
+
wordStart = i;
|
|
22608
|
+
continue;
|
|
22609
|
+
}
|
|
22610
|
+
const operator = SHELL_OPERATORS.find((op) => text.startsWith(op, i));
|
|
22611
|
+
const glued = wordStart === -1 ? undefined : text.slice(wordStart, i);
|
|
22612
|
+
let descriptor;
|
|
22613
|
+
if (glued !== undefined && /^\d+$/.test(glued) && (operator[0] === ">" || operator[0] === "<")) {
|
|
22614
|
+
descriptor = glued;
|
|
22615
|
+
wordStart = -1;
|
|
22616
|
+
} else if (!closeWord(i)) {
|
|
22617
|
+
return null;
|
|
22618
|
+
}
|
|
22619
|
+
i += operator.length - 1;
|
|
22620
|
+
if (awaiting !== undefined)
|
|
22621
|
+
return null;
|
|
22622
|
+
if (descriptor === undefined && COMMAND_SEPARATORS.has(operator)) {
|
|
22623
|
+
if (words.length === 0)
|
|
22624
|
+
return null;
|
|
22625
|
+
commands.push({ words, text: words.map((w) => w.raw).join(" ") });
|
|
22626
|
+
words = [];
|
|
22627
|
+
endedBySemicolon = operator === ";";
|
|
22628
|
+
continue;
|
|
22629
|
+
}
|
|
22630
|
+
awaiting = descriptor === undefined ? { operator } : { operator, descriptor };
|
|
22631
|
+
}
|
|
22632
|
+
if (!closeWord(text.length) || awaiting !== undefined)
|
|
22633
|
+
return null;
|
|
22634
|
+
if (words.length > 0)
|
|
22635
|
+
commands.push({ words, text: words.map((w) => w.raw).join(" ") });
|
|
22636
|
+
else if (!(commands.length > 0 && endedBySemicolon))
|
|
22637
|
+
return null;
|
|
22638
|
+
return commands;
|
|
22639
|
+
}
|
|
22640
|
+
var DIRECTORY_CHANGERS = new Set(["cd", "pushd", "popd"]);
|
|
22641
|
+
function programsRunBy(simple) {
|
|
22642
|
+
const programs = new Set([simple.words[0].value]);
|
|
22643
|
+
const inner = leadingWord(stripWrappers(simple.text, "denyAsk")).word;
|
|
22644
|
+
if (inner !== undefined)
|
|
22645
|
+
programs.add(dequoteShellWord(inner));
|
|
22646
|
+
if (simple.words[0].value === "xargs")
|
|
22647
|
+
for (const word of simple.words)
|
|
22648
|
+
programs.add(word.value);
|
|
22649
|
+
return programs;
|
|
22650
|
+
}
|
|
22651
|
+
function entryKind(path) {
|
|
22652
|
+
try {
|
|
22653
|
+
const entry = statSync10(path);
|
|
22654
|
+
return entry.isFile() ? "file" : entry.isDirectory() ? "directory" : "other";
|
|
22655
|
+
} catch {
|
|
22656
|
+
return "missing";
|
|
22657
|
+
}
|
|
22386
22658
|
}
|
|
22387
|
-
|
|
22388
|
-
|
|
22389
|
-
|
|
22390
|
-
|
|
22391
|
-
|
|
22392
|
-
|
|
22393
|
-
|
|
22394
|
-
const
|
|
22395
|
-
if (
|
|
22659
|
+
var BARE_REPOSITORY_MARKERS = [
|
|
22660
|
+
["HEAD", "file"],
|
|
22661
|
+
["objects", "directory"],
|
|
22662
|
+
["refs", "directory"]
|
|
22663
|
+
];
|
|
22664
|
+
function isCurrentDirectoryBareGitRepo(cwd) {
|
|
22665
|
+
const dotGit = entryKind(join24(cwd, ".git"));
|
|
22666
|
+
const workingRepository = dotGit === "file" || dotGit === "directory" && entryKind(join24(cwd, ".git", "HEAD")) === "file";
|
|
22667
|
+
if (workingRepository)
|
|
22396
22668
|
return false;
|
|
22397
|
-
return
|
|
22398
|
-
}
|
|
22399
|
-
|
|
22400
|
-
// src/provider/first-party.ts
|
|
22401
|
-
var FIRST_PARTY_ANTHROPIC_PROVIDER_IDS = new Set(["anthropic", "console"]);
|
|
22402
|
-
function isFirstPartyAnthropic(providerId) {
|
|
22403
|
-
return providerId !== undefined && FIRST_PARTY_ANTHROPIC_PROVIDER_IDS.has(providerId);
|
|
22404
|
-
}
|
|
22405
|
-
|
|
22406
|
-
// src/engine.ts
|
|
22407
|
-
var SKILL_TOOL_ADVERTISED_NAME = "Skill";
|
|
22408
|
-
function undeclaredToolDefinitionsText(definitions) {
|
|
22409
|
-
if (definitions.length === 0)
|
|
22410
|
-
return "";
|
|
22411
|
-
const lines = definitions.map((d) => `<function>${JSON.stringify({ name: d.name, description: d.description, parameters: d.inputSchema })}</function>`);
|
|
22412
|
-
return `
|
|
22413
|
-
|
|
22414
|
-
These loaded tools are not in this conversation's tool list, but you can call them by name; each one's input must match its parameters schema:
|
|
22415
|
-
<functions>
|
|
22416
|
-
${lines.join(`
|
|
22417
|
-
`)}
|
|
22418
|
-
</functions>`;
|
|
22419
|
-
}
|
|
22420
|
-
function providerMessageContentToText(content) {
|
|
22421
|
-
if (typeof content === "string")
|
|
22422
|
-
return content;
|
|
22423
|
-
return content.map((block) => {
|
|
22424
|
-
if (block.type === "text")
|
|
22425
|
-
return block.text;
|
|
22426
|
-
if (block.type === "tool_use")
|
|
22427
|
-
return `[called ${block.name}]`;
|
|
22428
|
-
if (block.type === "tool_reference")
|
|
22429
|
-
return `[tools now callable: ${block.tool_names.join(", ")}]`;
|
|
22430
|
-
if (block.type === "thinking")
|
|
22431
|
-
return `[thinking: ${block.thinking.length} chars]`;
|
|
22432
|
-
if (block.type === "redacted_thinking")
|
|
22433
|
-
return "[redacted thinking]";
|
|
22434
|
-
if (block.type === "image")
|
|
22435
|
-
return `[image: ${block.source.media_type}]`;
|
|
22436
|
-
return typeof block.content === "string" ? block.content : providerMessageContentToText(block.content);
|
|
22437
|
-
}).join(`
|
|
22438
|
-
`);
|
|
22669
|
+
return BARE_REPOSITORY_MARKERS.some(([name, kind]) => entryKind(join24(cwd, name)) === kind);
|
|
22439
22670
|
}
|
|
22440
|
-
|
|
22441
|
-
|
|
22671
|
+
var GIT_INTERNAL_TOP_LEVEL = new Set(["objects", "refs", "hooks"]);
|
|
22672
|
+
function namesGitInternals(path) {
|
|
22673
|
+
const segments = path.split("/").filter((segment) => segment !== "" && segment !== ".");
|
|
22674
|
+
const top = segments[0];
|
|
22675
|
+
if (top === undefined)
|
|
22442
22676
|
return false;
|
|
22443
|
-
return
|
|
22677
|
+
return top === "HEAD" && segments.length === 1 || GIT_INTERNAL_TOP_LEVEL.has(top);
|
|
22444
22678
|
}
|
|
22445
|
-
|
|
22446
|
-
|
|
22679
|
+
var CREATING_COMMANDS = new Set(["mkdir", "touch", "cp", "mv"]);
|
|
22680
|
+
function gitRunsUnsafely(joined, commands, ctx) {
|
|
22681
|
+
const programs = commands.map(programsRunBy);
|
|
22682
|
+
if (!programs.some((set) => set.has("git")))
|
|
22447
22683
|
return false;
|
|
22448
|
-
if (
|
|
22684
|
+
if (programs.some((set) => [...DIRECTORY_CHANGERS].some((changer) => set.has(changer))))
|
|
22449
22685
|
return true;
|
|
22450
|
-
|
|
22686
|
+
if (isCurrentDirectoryBareGitRepo(ctx.cwd))
|
|
22687
|
+
return true;
|
|
22688
|
+
if (extractRedirectTargets(joined).some(namesGitInternals))
|
|
22689
|
+
return true;
|
|
22690
|
+
const createsInternals = commands.some((simple) => CREATING_COMMANDS.has(simple.words[0].value) && simple.words.slice(1).some((w) => !w.value.startsWith("-") && namesGitInternals(w.value)));
|
|
22691
|
+
return createsInternals || ctx.sandboxEnabled && ctx.cwd !== ctx.originalCwd;
|
|
22451
22692
|
}
|
|
22452
|
-
|
|
22453
|
-
|
|
22454
|
-
|
|
22455
|
-
|
|
22456
|
-
if (errorParam(text)?.startsWith("tool_choice") === true)
|
|
22693
|
+
var WEBDAV_MARKERS = [/@SSL@\d+/i, /@\d+@SSL/i, /DavWWWRoot/i];
|
|
22694
|
+
var isPathSeparator = (ch) => ch === "/" || ch === "\\";
|
|
22695
|
+
function containsVulnerableUncPath(text, platform = process.platform) {
|
|
22696
|
+
if (platform !== "win32")
|
|
22457
22697
|
return false;
|
|
22458
|
-
|
|
22459
|
-
|
|
22460
|
-
|
|
22461
|
-
|
|
22698
|
+
if (WEBDAV_MARKERS.some((marker) => marker.test(text)))
|
|
22699
|
+
return true;
|
|
22700
|
+
let i = 0;
|
|
22701
|
+
while (i < text.length) {
|
|
22702
|
+
if (!isPathSeparator(text[i])) {
|
|
22703
|
+
i++;
|
|
22704
|
+
continue;
|
|
22705
|
+
}
|
|
22706
|
+
const runStart = i;
|
|
22707
|
+
while (i < text.length && isPathSeparator(text[i]))
|
|
22708
|
+
i++;
|
|
22709
|
+
const host = text[i];
|
|
22710
|
+
if (i - runStart < 2 || host === undefined || /\s/.test(host))
|
|
22711
|
+
continue;
|
|
22712
|
+
const urlScheme = i - runStart === 2 && text[runStart] === "/" && text[runStart + 1] === "/" && text[runStart - 1] === ":";
|
|
22713
|
+
if (!urlScheme)
|
|
22714
|
+
return true;
|
|
22715
|
+
}
|
|
22716
|
+
return false;
|
|
22462
22717
|
}
|
|
22463
|
-
function
|
|
22464
|
-
|
|
22465
|
-
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
|
|
22469
|
-
|
|
22470
|
-
|
|
22718
|
+
function lex(text) {
|
|
22719
|
+
const quoting = new Uint8Array(text.length);
|
|
22720
|
+
const role = new Uint8Array(text.length);
|
|
22721
|
+
let open = 0 /* None */;
|
|
22722
|
+
for (let i = 0;i < text.length; i++) {
|
|
22723
|
+
const ch = text[i];
|
|
22724
|
+
if (open === 1 /* Single */) {
|
|
22725
|
+
quoting[i] = 1 /* Single */;
|
|
22726
|
+
if (ch === "'") {
|
|
22727
|
+
role[i] = 1 /* QuoteMark */;
|
|
22728
|
+
open = 0 /* None */;
|
|
22729
|
+
}
|
|
22730
|
+
continue;
|
|
22731
|
+
}
|
|
22732
|
+
if (ch === "\\" && i + 1 < text.length) {
|
|
22733
|
+
quoting[i] = quoting[i + 1] = open;
|
|
22734
|
+
role[i] = 2 /* Escape */;
|
|
22735
|
+
role[i + 1] = 3 /* Escaped */;
|
|
22736
|
+
i++;
|
|
22737
|
+
continue;
|
|
22738
|
+
}
|
|
22739
|
+
if (open === 2 /* Double */) {
|
|
22740
|
+
quoting[i] = 2 /* Double */;
|
|
22741
|
+
if (ch === '"') {
|
|
22742
|
+
role[i] = 1 /* QuoteMark */;
|
|
22743
|
+
open = 0 /* None */;
|
|
22744
|
+
}
|
|
22745
|
+
continue;
|
|
22746
|
+
}
|
|
22747
|
+
if (ch === "'" || ch === '"') {
|
|
22748
|
+
open = ch === "'" ? 1 /* Single */ : 2 /* Double */;
|
|
22749
|
+
quoting[i] = open;
|
|
22750
|
+
role[i] = 1 /* QuoteMark */;
|
|
22751
|
+
}
|
|
22752
|
+
}
|
|
22753
|
+
return { text, quoting, role, unterminated: open !== 0 /* None */ };
|
|
22754
|
+
}
|
|
22755
|
+
var isBare = (lx, i) => lx.quoting[i] === 0 /* None */ && lx.role[i] === 0 /* Text */;
|
|
22756
|
+
var isBlank = (ch) => ch !== undefined && /\s/.test(ch);
|
|
22757
|
+
function viewsOf(lx, keepDoubleMarks) {
|
|
22758
|
+
let outsideSingle = "";
|
|
22759
|
+
let bare = "";
|
|
22760
|
+
let bareWithMarks = "";
|
|
22761
|
+
for (let i = 0;i < lx.text.length; i++) {
|
|
22762
|
+
const ch = lx.text[i];
|
|
22763
|
+
if (lx.role[i] === 1 /* QuoteMark */) {
|
|
22764
|
+
bareWithMarks += ch;
|
|
22765
|
+
if (ch === '"' && keepDoubleMarks)
|
|
22766
|
+
outsideSingle += ch;
|
|
22767
|
+
continue;
|
|
22768
|
+
}
|
|
22769
|
+
if (lx.quoting[i] === 1 /* Single */)
|
|
22770
|
+
continue;
|
|
22771
|
+
outsideSingle += ch;
|
|
22772
|
+
if (lx.quoting[i] === 0 /* None */) {
|
|
22773
|
+
bare += ch;
|
|
22774
|
+
bareWithMarks += ch;
|
|
22775
|
+
}
|
|
22776
|
+
}
|
|
22777
|
+
return { outsideSingle, bare, bareWithMarks };
|
|
22471
22778
|
}
|
|
22472
|
-
function
|
|
22473
|
-
|
|
22779
|
+
function wordSpans(lx) {
|
|
22780
|
+
const spans = [];
|
|
22781
|
+
let start = -1;
|
|
22782
|
+
for (let i = 0;i <= lx.text.length; i++) {
|
|
22783
|
+
const cut = i === lx.text.length || isBare(lx, i) && isBlank(lx.text[i]);
|
|
22784
|
+
if (cut) {
|
|
22785
|
+
if (start !== -1)
|
|
22786
|
+
spans.push({ start, end: i });
|
|
22787
|
+
start = -1;
|
|
22788
|
+
} else if (start === -1)
|
|
22789
|
+
start = i;
|
|
22790
|
+
}
|
|
22791
|
+
return spans;
|
|
22474
22792
|
}
|
|
22475
|
-
function
|
|
22476
|
-
|
|
22793
|
+
function withoutHarmlessRedirections(view) {
|
|
22794
|
+
const drop = new Uint8Array(view.length);
|
|
22795
|
+
const markFrom = (from, to) => {
|
|
22796
|
+
for (let k = from;k < to; k++)
|
|
22797
|
+
drop[k] = 1;
|
|
22798
|
+
};
|
|
22799
|
+
const skipBlanks = (k) => {
|
|
22800
|
+
while (isBlank(view[k]))
|
|
22801
|
+
k++;
|
|
22802
|
+
return k;
|
|
22803
|
+
};
|
|
22804
|
+
const endsWord = (k) => k === view.length || isBlank(view[k]);
|
|
22805
|
+
const devNullAt = (k) => view.startsWith("/dev/null", k) && endsWord(k + 9) ? k + 9 : -1;
|
|
22806
|
+
for (let i = 0;i < view.length; i++) {
|
|
22807
|
+
const ch = view[i];
|
|
22808
|
+
if (ch === "<") {
|
|
22809
|
+
const end = devNullAt(skipBlanks(i + 1));
|
|
22810
|
+
if (end !== -1)
|
|
22811
|
+
markFrom(i, end);
|
|
22812
|
+
} else if (ch === ">" && view[i + 1] === "&") {
|
|
22813
|
+
const one = skipBlanks(i + 2);
|
|
22814
|
+
let two = i - 1;
|
|
22815
|
+
while (two >= 0 && isBlank(view[two]))
|
|
22816
|
+
two--;
|
|
22817
|
+
if (view[one] === "1" && endsWord(one + 1) && view[two] === "2" && isBlank(view[two - 1]))
|
|
22818
|
+
markFrom(two, one + 1);
|
|
22819
|
+
} else if (ch === ">") {
|
|
22820
|
+
const end = devNullAt(skipBlanks(i + 1));
|
|
22821
|
+
if (end !== -1)
|
|
22822
|
+
markFrom(i, end);
|
|
22823
|
+
}
|
|
22824
|
+
}
|
|
22825
|
+
let out = "";
|
|
22826
|
+
for (let i = 0;i < view.length; i++)
|
|
22827
|
+
if (drop[i] === 0)
|
|
22828
|
+
out += view[i];
|
|
22829
|
+
return out;
|
|
22477
22830
|
}
|
|
22478
|
-
|
|
22479
|
-
|
|
22480
|
-
|
|
22831
|
+
var CONTROL_CHARACTERS = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/;
|
|
22832
|
+
var UNICODE_WHITESPACE = /[\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/;
|
|
22833
|
+
function singleQuoteEndsInBackslash(lx) {
|
|
22834
|
+
let lastSingleQuote = -1;
|
|
22835
|
+
for (let i = lx.text.length - 1;i >= 0; i--) {
|
|
22836
|
+
if (lx.text[i] === "'") {
|
|
22837
|
+
lastSingleQuote = i;
|
|
22838
|
+
break;
|
|
22839
|
+
}
|
|
22840
|
+
}
|
|
22841
|
+
let inside = false;
|
|
22842
|
+
let trailing = 0;
|
|
22843
|
+
for (let i = 0;i < lx.text.length; i++) {
|
|
22844
|
+
if (lx.quoting[i] !== 1 /* Single */)
|
|
22845
|
+
continue;
|
|
22846
|
+
if (lx.role[i] === 1 /* QuoteMark */) {
|
|
22847
|
+
if (inside && (trailing % 2 === 1 || trailing > 0 && lastSingleQuote > i))
|
|
22848
|
+
return true;
|
|
22849
|
+
inside = !inside;
|
|
22850
|
+
trailing = 0;
|
|
22851
|
+
continue;
|
|
22852
|
+
}
|
|
22853
|
+
trailing = lx.text[i] === "\\" ? trailing + 1 : 0;
|
|
22854
|
+
}
|
|
22855
|
+
return false;
|
|
22481
22856
|
}
|
|
22482
|
-
|
|
22483
|
-
|
|
22484
|
-
|
|
22485
|
-
const
|
|
22486
|
-
return
|
|
22857
|
+
var MIDWAY_OPENINGS = ["-", ";", ">", "<", "&&", "||"];
|
|
22858
|
+
function startsMidway(text) {
|
|
22859
|
+
const body = text.trimStart();
|
|
22860
|
+
const indentation = text.slice(0, text.length - body.length);
|
|
22861
|
+
return indentation.includes("\t") || MIDWAY_OPENINGS.some((opening) => body.startsWith(opening));
|
|
22487
22862
|
}
|
|
22488
|
-
|
|
22489
|
-
|
|
22490
|
-
|
|
22863
|
+
function jqReachesBeyondItsFilter(text, base) {
|
|
22864
|
+
if (base !== "jq")
|
|
22865
|
+
return false;
|
|
22866
|
+
for (let at = text.indexOf("system");at !== -1; at = text.indexOf("system", at + 1)) {
|
|
22867
|
+
if (at > 0 && /\w/.test(text[at - 1]))
|
|
22868
|
+
continue;
|
|
22869
|
+
let k = at + 6;
|
|
22870
|
+
while (isBlank(text[k]))
|
|
22871
|
+
k++;
|
|
22872
|
+
if (text[k] === "(")
|
|
22873
|
+
return true;
|
|
22874
|
+
}
|
|
22875
|
+
const words = text.split(/\s+/).slice(1);
|
|
22876
|
+
return words.some((word) => {
|
|
22877
|
+
if (["--from-file", "--rawfile", "--slurpfile", "--library-path"].some((long) => word.startsWith(long)))
|
|
22878
|
+
return true;
|
|
22879
|
+
if (!word.startsWith("-") || word.startsWith("--"))
|
|
22880
|
+
return false;
|
|
22881
|
+
const letters = /^-([A-Za-z]*)/.exec(word)[1];
|
|
22882
|
+
return letters.includes("f") || letters.includes("L");
|
|
22883
|
+
});
|
|
22491
22884
|
}
|
|
22492
|
-
|
|
22885
|
+
var QUOTE_MARK = /['"]/;
|
|
22886
|
+
var GLUED_QUOTED_VALUE_OPTION = { cut: "-d" };
|
|
22887
|
+
function quoteInsideFlagName(text, start, base) {
|
|
22888
|
+
let end = start;
|
|
22889
|
+
while (end < text.length && !isBlank(text[end]) && text[end] !== "=")
|
|
22890
|
+
end++;
|
|
22891
|
+
const head = text.slice(start, end);
|
|
22892
|
+
const mark = head.search(/['"]/);
|
|
22893
|
+
if (mark === -1)
|
|
22894
|
+
return false;
|
|
22895
|
+
if (Object.hasOwn(GLUED_QUOTED_VALUE_OPTION, base) && GLUED_QUOTED_VALUE_OPTION[base] === head.slice(0, mark))
|
|
22896
|
+
return false;
|
|
22897
|
+
const follower = text[start + mark + 1];
|
|
22898
|
+
return follower === undefined || /[\w'"-]/.test(follower);
|
|
22899
|
+
}
|
|
22900
|
+
function quoteRunNearDash(text) {
|
|
22901
|
+
for (const run of text.matchAll(/['"]+/g)) {
|
|
22902
|
+
const before = text[run.index - 1];
|
|
22903
|
+
const after = run.index + run[0].length;
|
|
22904
|
+
const opensWord = before === undefined || isBlank(before);
|
|
22905
|
+
let solid = after;
|
|
22906
|
+
while (isBlank(text[solid]))
|
|
22907
|
+
solid++;
|
|
22908
|
+
if (run[0].length >= 2 && (opensWord || before === "$") && text[solid] === "-")
|
|
22909
|
+
return true;
|
|
22910
|
+
if (run[0].length >= 3 && (opensWord || text[after] === "-"))
|
|
22911
|
+
return true;
|
|
22912
|
+
}
|
|
22913
|
+
return false;
|
|
22914
|
+
}
|
|
22915
|
+
function flagSpelledWithQuotes(lx, base) {
|
|
22916
|
+
const text = lx.text;
|
|
22917
|
+
if (base === "echo" && !/[|&;]/.test(text))
|
|
22918
|
+
return false;
|
|
22919
|
+
if (text.includes("$'") || text.includes('$"') || quoteRunNearDash(text))
|
|
22920
|
+
return true;
|
|
22921
|
+
return wordSpans(lx).some(({ start, end }) => {
|
|
22922
|
+
if (lx.role[start] === 1 /* QuoteMark */)
|
|
22923
|
+
return dequoteShellWord(text.slice(start, end)).startsWith("-");
|
|
22924
|
+
return text[start] === "-" && quoteInsideFlagName(text, start, base);
|
|
22925
|
+
});
|
|
22926
|
+
}
|
|
22927
|
+
function nextQuoteMark(s, from) {
|
|
22928
|
+
for (let k = from;k < s.length; k++)
|
|
22929
|
+
if (s[k] === "'" || s[k] === '"')
|
|
22930
|
+
return k;
|
|
22931
|
+
return -1;
|
|
22932
|
+
}
|
|
22933
|
+
function separatorInsideQuoteMarks(outsideSingle) {
|
|
22934
|
+
const s = outsideSingle;
|
|
22935
|
+
const spanHas = (open, chars, mustEndWord) => {
|
|
22936
|
+
const close = nextQuoteMark(s, open + 1);
|
|
22937
|
+
if (close === -1)
|
|
22938
|
+
return false;
|
|
22939
|
+
if (mustEndWord && !(close + 1 === s.length || isBlank(s[close + 1])))
|
|
22940
|
+
return false;
|
|
22941
|
+
for (let k = open + 1;k < close; k++)
|
|
22942
|
+
if (chars.includes(s[k]))
|
|
22943
|
+
return true;
|
|
22944
|
+
return false;
|
|
22945
|
+
};
|
|
22946
|
+
for (let i = 0;i < s.length; i++) {
|
|
22947
|
+
if ((s[i] === "'" || s[i] === '"') && (i === 0 || isBlank(s[i - 1])) && spanHas(i, ";&", true))
|
|
22948
|
+
return true;
|
|
22949
|
+
}
|
|
22950
|
+
for (const [predicate, chars] of [["-name", ";|&"], ["-path", ";|&"], ["-iname", ";|&"], ["-regex", ";&"]]) {
|
|
22951
|
+
for (let at = s.indexOf(predicate);at !== -1; at = s.indexOf(predicate, at + 1)) {
|
|
22952
|
+
let k = at + predicate.length;
|
|
22953
|
+
if (!isBlank(s[k]))
|
|
22954
|
+
continue;
|
|
22955
|
+
while (isBlank(s[k]))
|
|
22956
|
+
k++;
|
|
22957
|
+
if ((s[k] === "'" || s[k] === '"') && spanHas(k, chars, false))
|
|
22958
|
+
return true;
|
|
22959
|
+
}
|
|
22960
|
+
}
|
|
22961
|
+
return false;
|
|
22962
|
+
}
|
|
22963
|
+
function variableBesideRedirectionOrPipe(view) {
|
|
22964
|
+
const solidBefore = [];
|
|
22965
|
+
let last;
|
|
22966
|
+
for (let i = 0;i < view.length; i++) {
|
|
22967
|
+
solidBefore.push(last);
|
|
22968
|
+
if (!isBlank(view[i]))
|
|
22969
|
+
last = view[i];
|
|
22970
|
+
}
|
|
22971
|
+
const solidFrom = new Array(view.length + 1);
|
|
22972
|
+
for (let i = view.length - 1;i >= 0; i--)
|
|
22973
|
+
solidFrom[i] = isBlank(view[i]) ? solidFrom[i + 1] : view[i];
|
|
22974
|
+
const isRouting = (ch) => ch === "<" || ch === ">" || ch === "|";
|
|
22975
|
+
for (const match of view.matchAll(/\$[A-Za-z_]\w*/g)) {
|
|
22976
|
+
if (isRouting(solidBefore[match.index]) || isRouting(solidFrom[match.index + match[0].length]))
|
|
22977
|
+
return true;
|
|
22978
|
+
}
|
|
22979
|
+
return false;
|
|
22980
|
+
}
|
|
22981
|
+
function commentHoldsQuoteMark(lx) {
|
|
22982
|
+
let inComment = false;
|
|
22983
|
+
for (let i = 0;i < lx.text.length; i++) {
|
|
22984
|
+
const ch = lx.text[i];
|
|
22985
|
+
if (inComment) {
|
|
22986
|
+
if (ch === `
|
|
22987
|
+
`)
|
|
22988
|
+
inComment = false;
|
|
22989
|
+
else if (ch === "'" || ch === '"')
|
|
22990
|
+
return true;
|
|
22991
|
+
continue;
|
|
22992
|
+
}
|
|
22993
|
+
if (ch === "#" && isBare(lx, i))
|
|
22994
|
+
inComment = true;
|
|
22995
|
+
}
|
|
22996
|
+
return false;
|
|
22997
|
+
}
|
|
22998
|
+
function quotedNewlineBeforeCommentLine(lx) {
|
|
22999
|
+
if (!lx.text.includes(`
|
|
23000
|
+
`) || !lx.text.includes("#"))
|
|
23001
|
+
return false;
|
|
23002
|
+
for (let i = 0;i < lx.text.length; i++) {
|
|
23003
|
+
if (lx.text[i] !== `
|
|
23004
|
+
` || lx.quoting[i] === 0 /* None */ || lx.role[i] !== 0 /* Text */)
|
|
23005
|
+
continue;
|
|
23006
|
+
let k = i + 1;
|
|
23007
|
+
while (k < lx.text.length && lx.text[k] !== `
|
|
23008
|
+
` && isBlank(lx.text[k]))
|
|
23009
|
+
k++;
|
|
23010
|
+
if (lx.text[k] === "#")
|
|
23011
|
+
return true;
|
|
23012
|
+
}
|
|
23013
|
+
return false;
|
|
23014
|
+
}
|
|
23015
|
+
function lineBreakStartsCommand(bare) {
|
|
23016
|
+
let lastNonBlank = -1;
|
|
23017
|
+
for (let i = bare.length - 1;i >= 0; i--) {
|
|
23018
|
+
if (!isBlank(bare[i])) {
|
|
23019
|
+
lastNonBlank = i;
|
|
23020
|
+
break;
|
|
23021
|
+
}
|
|
23022
|
+
}
|
|
23023
|
+
for (let i = 0;i < lastNonBlank; i++) {
|
|
23024
|
+
if (bare[i] !== `
|
|
23025
|
+
` && bare[i] !== "\r")
|
|
23026
|
+
continue;
|
|
23027
|
+
const continuation = i >= 2 && bare[i - 1] === "\\" && isBlank(bare[i - 2]);
|
|
23028
|
+
if (!continuation)
|
|
23029
|
+
return true;
|
|
23030
|
+
}
|
|
23031
|
+
return false;
|
|
23032
|
+
}
|
|
23033
|
+
function mentionsIfs(text) {
|
|
23034
|
+
if (text.includes("$IFS"))
|
|
23035
|
+
return true;
|
|
23036
|
+
return text.split("}").some((stretch) => {
|
|
23037
|
+
const open = stretch.indexOf("${");
|
|
23038
|
+
return open !== -1 && stretch.includes("IFS", open + 2);
|
|
23039
|
+
});
|
|
23040
|
+
}
|
|
23041
|
+
function readsProcessEnvironment(text) {
|
|
23042
|
+
return text.split(/[\n\r\u2028\u2029]/).some((line) => {
|
|
23043
|
+
const proc = line.indexOf("/proc/");
|
|
23044
|
+
return proc !== -1 && line.includes("/environ", proc + "/proc/".length);
|
|
23045
|
+
});
|
|
23046
|
+
}
|
|
23047
|
+
function hasLiveBacktick(lx) {
|
|
23048
|
+
for (let i = 0;i < lx.text.length; i++) {
|
|
23049
|
+
if (lx.text[i] === "`" && lx.quoting[i] !== 1 /* Single */ && lx.role[i] !== 3 /* Escaped */)
|
|
23050
|
+
return true;
|
|
23051
|
+
}
|
|
23052
|
+
return false;
|
|
23053
|
+
}
|
|
23054
|
+
function escapesBlankOrOperator(lx) {
|
|
23055
|
+
for (let i = 0;i < lx.text.length; i++) {
|
|
23056
|
+
if (lx.role[i] !== 2 /* Escape */ || lx.quoting[i] !== 0 /* None */)
|
|
23057
|
+
continue;
|
|
23058
|
+
const next = lx.text[i + 1];
|
|
23059
|
+
if (next === " " || next === "\t" || ";|&<>".includes(next))
|
|
23060
|
+
return true;
|
|
23061
|
+
}
|
|
23062
|
+
return false;
|
|
23063
|
+
}
|
|
23064
|
+
function hashInsideWord(bareWithMarks) {
|
|
23065
|
+
const glued = (s) => s.split(/\s+/).some((word) => {
|
|
23066
|
+
for (let at = word.indexOf("#", 1);at !== -1; at = word.indexOf("#", at + 1)) {
|
|
23067
|
+
if (!word.slice(0, at).endsWith("${"))
|
|
23068
|
+
return true;
|
|
23069
|
+
}
|
|
23070
|
+
return false;
|
|
23071
|
+
});
|
|
23072
|
+
return glued(bareWithMarks) || glued(joinLineContinuations(bareWithMarks));
|
|
23073
|
+
}
|
|
23074
|
+
function hasBraceExpansion(bare, text) {
|
|
23075
|
+
const events = [];
|
|
23076
|
+
let run = 0;
|
|
23077
|
+
for (let i = 0;i < bare.length; i++) {
|
|
23078
|
+
const ch = bare[i];
|
|
23079
|
+
const escaped = run % 2 === 1;
|
|
23080
|
+
run = ch === "\\" ? run + 1 : 0;
|
|
23081
|
+
if ((ch === "{" || ch === "}") && !escaped)
|
|
23082
|
+
events.push(ch === "{" ? "open" : "close");
|
|
23083
|
+
else if (ch === "," || ch === "." && bare[i + 1] === ".")
|
|
23084
|
+
events.push("separator");
|
|
23085
|
+
}
|
|
23086
|
+
const opens = events.filter((e) => e === "open").length;
|
|
23087
|
+
const closes = events.filter((e) => e === "close").length;
|
|
23088
|
+
if (opens > 0 && closes > opens)
|
|
23089
|
+
return true;
|
|
23090
|
+
const owner = [];
|
|
23091
|
+
const ownsSeparator = new Set;
|
|
23092
|
+
for (const [id, event] of events.entries()) {
|
|
23093
|
+
if (event === "open")
|
|
23094
|
+
owner.push(id);
|
|
23095
|
+
else if (event === "separator") {
|
|
23096
|
+
if (owner.length > 0)
|
|
23097
|
+
ownsSeparator.add(owner[owner.length - 1]);
|
|
23098
|
+
} else if (owner.length > 0 && ownsSeparator.has(owner.pop()))
|
|
23099
|
+
return true;
|
|
23100
|
+
}
|
|
23101
|
+
if (opens === 0)
|
|
23102
|
+
return false;
|
|
23103
|
+
for (let i = 0;i + 2 < text.length; i++) {
|
|
23104
|
+
if ((text[i + 1] === "{" || text[i + 1] === "}") && QUOTE_MARK.test(text[i]) && QUOTE_MARK.test(text[i + 2]))
|
|
23105
|
+
return true;
|
|
23106
|
+
}
|
|
23107
|
+
return false;
|
|
23108
|
+
}
|
|
23109
|
+
var ZSH_DANGEROUS_COMMANDS = new Set([
|
|
23110
|
+
"zmodload",
|
|
23111
|
+
"emulate",
|
|
23112
|
+
"sysopen",
|
|
23113
|
+
"sysread",
|
|
23114
|
+
"syswrite",
|
|
23115
|
+
"sysseek",
|
|
23116
|
+
"zpty",
|
|
23117
|
+
"ztcp",
|
|
23118
|
+
"zsocket",
|
|
23119
|
+
"mapfile",
|
|
23120
|
+
"zf_rm",
|
|
23121
|
+
"zf_mv",
|
|
23122
|
+
"zf_ln",
|
|
23123
|
+
"zf_chmod",
|
|
23124
|
+
"zf_chown",
|
|
23125
|
+
"zf_mkdir",
|
|
23126
|
+
"zf_rmdir",
|
|
23127
|
+
"zf_chgrp"
|
|
23128
|
+
]);
|
|
23129
|
+
var ZSH_PRECOMMAND_MODIFIERS = new Set(["command", "builtin", "noglob", "nocorrect"]);
|
|
23130
|
+
var PROGRAM_AFTER_PREFIXES = new RegExp(`^\\s*(?:(?:[A-Za-z_]\\w*=\\S*|${[...ZSH_PRECOMMAND_MODIFIERS].join("|")})\\s+)*(\\S+)([\\s\\S]*)$`);
|
|
23131
|
+
function runsZshBuiltin(text) {
|
|
23132
|
+
const named = PROGRAM_AFTER_PREFIXES.exec(text);
|
|
23133
|
+
if (named === null)
|
|
23134
|
+
return false;
|
|
23135
|
+
const [, program, rest] = named;
|
|
23136
|
+
if (ZSH_DANGEROUS_COMMANDS.has(program))
|
|
23137
|
+
return true;
|
|
23138
|
+
return program === "fc" && rest.split(/\s+/).some((option) => option[0] === "-" && option.indexOf("e", 1) !== -1);
|
|
23139
|
+
}
|
|
23140
|
+
var occurrences = (s, ch) => {
|
|
23141
|
+
let n = 0;
|
|
23142
|
+
for (const c of s)
|
|
23143
|
+
if (c === ch)
|
|
23144
|
+
n++;
|
|
23145
|
+
return n;
|
|
23146
|
+
};
|
|
23147
|
+
var unescapedOccurrences = (s, ch) => {
|
|
23148
|
+
let n = 0;
|
|
23149
|
+
for (let i = 0;i < s.length; i++)
|
|
23150
|
+
if (s[i] === ch && s[i - 1] !== "\\")
|
|
23151
|
+
n++;
|
|
23152
|
+
return n;
|
|
23153
|
+
};
|
|
23154
|
+
function unbalancedWordNearSeparator(lx, bare) {
|
|
23155
|
+
if (!(bare.includes(";") || bare.includes("&&") || bare.includes("||")))
|
|
23156
|
+
return false;
|
|
23157
|
+
if (lx.unterminated)
|
|
23158
|
+
return true;
|
|
23159
|
+
let start = -1;
|
|
23160
|
+
for (let i = 0;i <= lx.text.length; i++) {
|
|
23161
|
+
const cut = i === lx.text.length || isBare(lx, i) && (isBlank(lx.text[i]) || ";&|<>()".includes(lx.text[i]));
|
|
23162
|
+
if (!cut) {
|
|
23163
|
+
if (start === -1)
|
|
23164
|
+
start = i;
|
|
23165
|
+
continue;
|
|
23166
|
+
}
|
|
23167
|
+
if (start === -1)
|
|
23168
|
+
continue;
|
|
23169
|
+
const word = shellWords(lx.text.slice(start, i), false, "broad")[0]?.word ?? "";
|
|
23170
|
+
start = -1;
|
|
23171
|
+
if (occurrences(word, "{") !== occurrences(word, "}") || occurrences(word, "(") !== occurrences(word, ")") || occurrences(word, "[") !== occurrences(word, "]") || unescapedOccurrences(word, '"') % 2 !== 0 || unescapedOccurrences(word, "'") % 2 !== 0) {
|
|
23172
|
+
return true;
|
|
23173
|
+
}
|
|
23174
|
+
}
|
|
23175
|
+
return false;
|
|
23176
|
+
}
|
|
23177
|
+
var SUBSTITUTION_PATTERNS = [
|
|
23178
|
+
/<\(/,
|
|
23179
|
+
/>\(/,
|
|
23180
|
+
/=\(/,
|
|
23181
|
+
/(?:^|[\s;&|])=[a-zA-Z_]/,
|
|
23182
|
+
/\$\(/,
|
|
23183
|
+
/\$\{/,
|
|
23184
|
+
/\$\[/,
|
|
23185
|
+
/~\[/,
|
|
23186
|
+
/\(e:/,
|
|
23187
|
+
/\(\+/,
|
|
23188
|
+
/\}\s*always\s*\{/,
|
|
23189
|
+
/<#/
|
|
23190
|
+
];
|
|
23191
|
+
var PRE_CHECKS = {
|
|
23192
|
+
unreadable: [
|
|
23193
|
+
(s) => s.text.includes("\r"),
|
|
23194
|
+
(s) => CONTROL_CHARACTERS.test(s.text) || UNICODE_WHITESPACE.test(s.text),
|
|
23195
|
+
(s) => startsMidway(s.text)
|
|
23196
|
+
],
|
|
23197
|
+
hidesAWord: [
|
|
23198
|
+
(s) => flagSpelledWithQuotes(s.lx, s.base),
|
|
23199
|
+
(s) => hashInsideWord(s.bareWithMarks),
|
|
23200
|
+
(s) => commentHoldsQuoteMark(s.lx),
|
|
23201
|
+
(s) => quotedNewlineBeforeCommentLine(s.lx),
|
|
23202
|
+
(s) => singleQuoteEndsInBackslash(s.lx),
|
|
23203
|
+
(s) => unbalancedWordNearSeparator(s.lx, s.bare)
|
|
23204
|
+
],
|
|
23205
|
+
smugglesAnOperator: [
|
|
23206
|
+
(s) => lineBreakStartsCommand(s.bare),
|
|
23207
|
+
(s) => s.cleaned.includes("<") || s.cleaned.includes(">"),
|
|
23208
|
+
(s) => variableBesideRedirectionOrPipe(s.cleaned),
|
|
23209
|
+
(s) => escapesBlankOrOperator(s.lx),
|
|
23210
|
+
(s) => separatorInsideQuoteMarks(s.outsideSingle)
|
|
23211
|
+
],
|
|
23212
|
+
expandsAtRunTime: [
|
|
23213
|
+
(s) => hasLiveBacktick(s.lx),
|
|
23214
|
+
(s) => SUBSTITUTION_PATTERNS.some((pattern) => pattern.test(s.outsideSingle)),
|
|
23215
|
+
(s) => hasBraceExpansion(s.bare, s.text),
|
|
23216
|
+
(s) => mentionsIfs(s.text)
|
|
23217
|
+
],
|
|
23218
|
+
reachesBeyondItsArguments: [
|
|
23219
|
+
(s) => jqReachesBeyondItsFilter(s.text, s.base),
|
|
23220
|
+
(s) => runsZshBuiltin(s.text),
|
|
23221
|
+
(s) => readsProcessEnvironment(s.text)
|
|
23222
|
+
]
|
|
23223
|
+
};
|
|
23224
|
+
function passesSecurityValidators(command) {
|
|
23225
|
+
const program = /^[^ ]*/.exec(command)[0];
|
|
23226
|
+
const lx = lex(command);
|
|
23227
|
+
const views = viewsOf(lx, /^jq$/.test(program));
|
|
23228
|
+
const scanned = { ...views, text: command, lx, base: program, cleaned: withoutHarmlessRedirections(views.bare) };
|
|
23229
|
+
return Object.values(PRE_CHECKS).every((group) => group.every((fires) => !fires(scanned)));
|
|
23230
|
+
}
|
|
23231
|
+
var PARAMETER_START = /[A-Za-z_@*#?!$0-9-]/;
|
|
23232
|
+
function expandsWhenRun(text) {
|
|
23233
|
+
const lx = lex(text);
|
|
23234
|
+
for (let i = 0;i < text.length; i++) {
|
|
23235
|
+
if (lx.role[i] !== 0 /* Text */)
|
|
23236
|
+
continue;
|
|
23237
|
+
const ch = text[i];
|
|
23238
|
+
if (lx.quoting[i] === 0 /* None */ && (ch === "*" || ch === "?" || ch === "[" || ch === "]"))
|
|
23239
|
+
return true;
|
|
23240
|
+
const next = text[i + 1];
|
|
23241
|
+
if (ch === "$" && lx.quoting[i] !== 1 /* Single */ && next !== undefined && PARAMETER_START.test(next))
|
|
23242
|
+
return true;
|
|
23243
|
+
}
|
|
23244
|
+
return false;
|
|
23245
|
+
}
|
|
23246
|
+
function takesPatterns(simple) {
|
|
23247
|
+
const [first, second] = simple.words.map((w) => w.value);
|
|
23248
|
+
return ["grep", "rg", "fd", "fdfind", "find", "sed"].includes(first ?? "") || first === "git" && second === "grep";
|
|
23249
|
+
}
|
|
23250
|
+
function keptBackslashMisleads(simple) {
|
|
23251
|
+
const patterns = takesPatterns(simple);
|
|
23252
|
+
return simple.words.some((word) => {
|
|
23253
|
+
if (!word.value.startsWith("\\"))
|
|
23254
|
+
return false;
|
|
23255
|
+
const broad = shellWords(word.raw, false, "broad")[0]?.word ?? "";
|
|
23256
|
+
if (broad.startsWith("\\"))
|
|
23257
|
+
return false;
|
|
23258
|
+
const after = word.value[1];
|
|
23259
|
+
return !patterns || after !== undefined && "-+{}".includes(after);
|
|
23260
|
+
});
|
|
23261
|
+
}
|
|
23262
|
+
var GIT_CONFIGURING_OPTION = /\s(?:-c|--exec-path|--config-env)[\s=]/;
|
|
23263
|
+
function matchesReadOnlyPattern(simple) {
|
|
23264
|
+
if (simple.words.some((w) => w.value.startsWith("-") && w.raw.includes("\\")))
|
|
23265
|
+
return false;
|
|
23266
|
+
const matched = READ_ONLY_REGEXES.some((regex) => regex.test(simple.text)) || findOnlyLists(simple);
|
|
23267
|
+
return matched && !(simple.text.includes("git") && GIT_CONFIGURING_OPTION.test(simple.text));
|
|
23268
|
+
}
|
|
23269
|
+
function isSimpleCommandReadOnly(simple, platform) {
|
|
23270
|
+
const refused = containsVulnerableUncPath(simple.text, platform) || expandsWhenRun(simple.text) || keptBackslashMisleads(simple);
|
|
23271
|
+
return !refused && (isSafeViaFlagAllowlist(simple, platform) || matchesReadOnlyPattern(simple));
|
|
23272
|
+
}
|
|
23273
|
+
var takes = (kind, ...names) => Object.fromEntries(names.map((name) => [name, kind]));
|
|
23274
|
+
var GIT_REF_SELECTION = takes("none", "--all", "--branches", "--tags", "--remotes");
|
|
23275
|
+
var GIT_DATE_FILTERS = takes("string", "--since", "--after", "--until", "--before");
|
|
23276
|
+
var GIT_LOG_DISPLAY = { ...takes("none", "--oneline", "--graph", "--decorate", "--no-decorate", "--relative-date"), ...takes("string", "--date") };
|
|
23277
|
+
var GIT_COUNT = takes("number", "--max-count", "-n");
|
|
23278
|
+
var GIT_STAT = takes("none", "--stat", "--numstat", "--shortstat", "--name-only", "--name-status");
|
|
23279
|
+
var GIT_COLOR = takes("none", "--color", "--no-color");
|
|
23280
|
+
var GIT_PATCH = takes("none", "--patch", "-p", "--no-patch", "--no-ext-diff", "-s");
|
|
23281
|
+
var GIT_AUTHOR_FILTERS = takes("string", "--author", "--committer", "--grep");
|
|
23282
|
+
var REFLOG_WRITING_SUBCOMMANDS = new Set(["expire", "delete", "exists"]);
|
|
23283
|
+
var reflogWrites = (_text, args) => args.some((arg) => REFLOG_WRITING_SUBCOMMANDS.has(arg));
|
|
23284
|
+
function namesNewRef(args, flags, optionalValue) {
|
|
23285
|
+
let listing = false;
|
|
23286
|
+
let optionsOver = false;
|
|
23287
|
+
let lastOption = "";
|
|
23288
|
+
for (let i = 0;i < args.length; i++) {
|
|
23289
|
+
const token = args[i];
|
|
23290
|
+
if (token === "")
|
|
23291
|
+
continue;
|
|
23292
|
+
const word = optionsOver ? { form: "operand" } : readOptionWord(token);
|
|
23293
|
+
if (word.form === "end-of-options") {
|
|
23294
|
+
optionsOver = true;
|
|
23295
|
+
lastOption = "";
|
|
23296
|
+
} else if (word.form === "operand") {
|
|
23297
|
+
if (!listing && !optionalValue.has(lastOption))
|
|
23298
|
+
return true;
|
|
23299
|
+
} else {
|
|
23300
|
+
lastOption = word.form === "long" ? word.name : `-${word.letters}`;
|
|
23301
|
+
if (word.inline !== undefined)
|
|
23302
|
+
continue;
|
|
23303
|
+
if (lastOption === "--list" || word.form === "short" && word.letters.includes("l"))
|
|
23304
|
+
listing = true;
|
|
23305
|
+
const kind = kindOf(flags, lastOption);
|
|
23306
|
+
if (kind === "number" || kind === "string" || kind === "char")
|
|
23307
|
+
i++;
|
|
23308
|
+
}
|
|
23309
|
+
}
|
|
23310
|
+
return false;
|
|
23311
|
+
}
|
|
23312
|
+
var BRANCH_OPTIONAL_COMMIT = new Set(["--merged", "--no-merged"]);
|
|
23313
|
+
var GIT_TAG_FLAGS = {
|
|
23314
|
+
...takes("none", "-l", "--list", "--column", "--no-column", "-i", "--ignore-case"),
|
|
23315
|
+
...takes("number", "-n"),
|
|
23316
|
+
...takes("string", "--contains", "--no-contains", "--merged", "--no-merged", "--sort", "--format", "--points-at")
|
|
23317
|
+
};
|
|
23318
|
+
var GIT_BRANCH_FLAGS = {
|
|
23319
|
+
...takes("none", "-l", "--list", "-a", "--all", "-r", "--remotes", "-v", "-vv", "--verbose", "--color", "--no-color", "--column", "--no-column", "--no-abbrev", "--merged", "--no-merged", "--show-current", "-i", "--ignore-case"),
|
|
23320
|
+
...takes("attached", "--abbrev"),
|
|
23321
|
+
...takes("string", "--contains", "--no-contains", "--points-at", "--sort")
|
|
23322
|
+
};
|
|
23323
|
+
var BASE64_FLAGS = {
|
|
23324
|
+
...takes("none", "-d", "-D", "--decode", "--ignore-garbage", "-h", "--help", "--version"),
|
|
23325
|
+
...takes("number", "-b", "--break", "-w", "--wrap"),
|
|
23326
|
+
...takes("string", "-i", "--input")
|
|
23327
|
+
};
|
|
23328
|
+
var DATE_FLAGS = {
|
|
23329
|
+
...takes("string", "-d", "--date", "-r", "--reference"),
|
|
23330
|
+
...takes("attached", "--iso-8601", "--rfc-3339"),
|
|
23331
|
+
...takes("none", "-u", "--utc", "--universal", "-I", "-R", "--rfc-email", "--debug", "--help", "--version")
|
|
23332
|
+
};
|
|
23333
|
+
function dateSetsClock(_text, args) {
|
|
23334
|
+
return operandsOf(args, DATE_FLAGS).some((operand) => !operand.startsWith("+"));
|
|
23335
|
+
}
|
|
23336
|
+
var TPUT_DANGEROUS_CAPABILITIES = new Set([
|
|
23337
|
+
"init",
|
|
23338
|
+
"reset",
|
|
23339
|
+
"rs1",
|
|
23340
|
+
"rs2",
|
|
23341
|
+
"rs3",
|
|
23342
|
+
"is1",
|
|
23343
|
+
"is2",
|
|
23344
|
+
"is3",
|
|
23345
|
+
"iprog",
|
|
23346
|
+
"if",
|
|
23347
|
+
"rf",
|
|
23348
|
+
"clear",
|
|
23349
|
+
"flash",
|
|
23350
|
+
"mc0",
|
|
23351
|
+
"mc4",
|
|
23352
|
+
"mc5",
|
|
23353
|
+
"mc5i",
|
|
23354
|
+
"mc5p",
|
|
23355
|
+
"pfkey",
|
|
23356
|
+
"pfloc",
|
|
23357
|
+
"pfx",
|
|
23358
|
+
"pfxl",
|
|
23359
|
+
"smcup",
|
|
23360
|
+
"rmcup"
|
|
23361
|
+
]);
|
|
23362
|
+
var TPUT_FLAGS = { ...takes("string", "-T"), ...takes("none", "-V", "-x") };
|
|
23363
|
+
function tputChangesTerminal(_text, args) {
|
|
23364
|
+
const { options, operands } = partitionArguments(args, TPUT_FLAGS);
|
|
23365
|
+
const readsStdin = options.some((option) => !option.startsWith("--") && option.split("=")[0].includes("S"));
|
|
23366
|
+
return readsStdin || operands.some((operand) => TPUT_DANGEROUS_CAPABILITIES.has(operand));
|
|
23367
|
+
}
|
|
23368
|
+
var REMOTE_NAME = /^[a-zA-Z0-9_-]+$/;
|
|
23369
|
+
function remoteShowReachesBeyondOneRemote(_text, args) {
|
|
23370
|
+
let names = 0;
|
|
23371
|
+
for (const arg of args) {
|
|
23372
|
+
if (arg === "-n")
|
|
23373
|
+
continue;
|
|
23374
|
+
if (!REMOTE_NAME.test(arg))
|
|
23375
|
+
return true;
|
|
23376
|
+
names++;
|
|
23377
|
+
}
|
|
23378
|
+
return names !== 1;
|
|
23379
|
+
}
|
|
23380
|
+
var REMOTE_LISTING_OPTIONS = new Set(["-v", "--verbose"]);
|
|
23381
|
+
var remoteDoesMoreThanList = (_text, args) => !args.every((arg) => REMOTE_LISTING_OPTIONS.has(arg));
|
|
23382
|
+
var psPrintsEnvironments = (_text, args) => args.some((arg) => /^[a-zA-Z]+$/.test(arg) && arg.includes("e"));
|
|
23383
|
+
var lsofWritesMountSupplement = (_text, args) => args.some((arg) => arg.slice(0, 2) === "+m");
|
|
23384
|
+
var pyrightWatches = (_text, args) => args.includes("--watch") || args.includes("-w");
|
|
23385
|
+
var FD_FLAGS = {
|
|
23386
|
+
...takes("none", "-h", "--help", "-V", "--version", "-H", "--hidden", "-I", "--no-ignore", "--no-ignore-vcs", "--no-ignore-parent", "-s", "--case-sensitive", "-i", "--ignore-case", "-g", "--glob", "--regex", "-F", "--fixed-strings", "-a", "--absolute-path", "-L", "--follow", "-p", "--full-path", "-0", "--print0", "-1", "-q", "--quiet", "--show-errors", "--strip-cwd-prefix", "--one-file-system", "--prune", "--no-require-git"),
|
|
23387
|
+
...takes("number", "-d", "--max-depth", "--min-depth", "--exact-depth", "-j", "--threads", "--max-results", "--batch-size"),
|
|
23388
|
+
...takes("string", "-t", "--type", "-e", "--extension", "-S", "--size", "--changed-within", "--changed-before", "-o", "--owner", "-E", "--exclude", "--ignore-file", "-c", "--color", "--max-buffer-time", "--search-path", "--base-directory", "--path-separator", "--hyperlink", "--and", "--format")
|
|
23389
|
+
};
|
|
23390
|
+
var CHECKSUM_FLAGS = takes("none", "-b", "--binary", "-t", "--text", "-c", "--check", "--ignore-missing", "--quiet", "--status", "--strict", "-w", "--warn", "--tag", "-z", "--zero", "--help", "--version");
|
|
23391
|
+
var COMMAND_SPECS = [
|
|
23392
|
+
[
|
|
23393
|
+
"xargs",
|
|
23394
|
+
{
|
|
23395
|
+
flags: { ...takes("{}", "-I"), ...takes("number", "-n", "-P", "-L", "-s"), ...takes("EOF", "-E"), ...takes("none", "-0", "-t", "-r", "-x"), ...takes("char", "-d") }
|
|
23396
|
+
}
|
|
23397
|
+
],
|
|
23398
|
+
[
|
|
23399
|
+
"git diff",
|
|
23400
|
+
{
|
|
23401
|
+
flags: {
|
|
23402
|
+
...GIT_STAT,
|
|
23403
|
+
...GIT_COLOR,
|
|
23404
|
+
...takes("none", "--dirstat", "--summary", "--patch-with-stat", "--word-diff", "--color-words", "--no-renames", "--no-ext-diff", "--check", "--full-index", "--binary", "--break-rewrites", "--find-renames", "--find-copies", "--find-copies-harder", "--irreversible-delete", "--histogram", "--patience", "--minimal", "--ignore-space-at-eol", "--ignore-space-change", "--ignore-all-space", "--ignore-blank-lines", "--function-context", "--exit-code", "--quiet", "--cached", "--staged", "--pickaxe-regex", "--pickaxe-all", "--no-index", "-p", "-u", "-s", "-M", "-C", "-B", "-D", "-l", "-R"),
|
|
23405
|
+
...takes("string", "--word-diff-regex", "--ws-error-highlight", "--diff-algorithm", "--relative", "--diff-filter", "-S", "-G", "-O"),
|
|
23406
|
+
...takes("number", "--abbrev", "--inter-hunk-context")
|
|
23407
|
+
}
|
|
23408
|
+
}
|
|
23409
|
+
],
|
|
23410
|
+
[
|
|
23411
|
+
"git log",
|
|
23412
|
+
{
|
|
23413
|
+
flags: {
|
|
23414
|
+
...GIT_LOG_DISPLAY,
|
|
23415
|
+
...GIT_REF_SELECTION,
|
|
23416
|
+
...GIT_DATE_FILTERS,
|
|
23417
|
+
...GIT_COUNT,
|
|
23418
|
+
...GIT_STAT,
|
|
23419
|
+
...GIT_COLOR,
|
|
23420
|
+
...GIT_PATCH,
|
|
23421
|
+
...GIT_AUTHOR_FILTERS,
|
|
23422
|
+
...takes("none", "--abbrev-commit", "--full-history", "--dense", "--sparse", "--simplify-merges", "--ancestry-path", "--source", "--first-parent", "--merges", "--no-merges", "--reverse", "--walk-reflogs", "--no-min-parents", "--no-max-parents", "--follow", "--no-walk", "--left-right", "--cherry-mark", "--cherry-pick", "--boundary", "--topo-order", "--date-order", "--author-date-order", "--pickaxe-regex", "--pickaxe-all"),
|
|
23423
|
+
...takes("number", "--skip", "--max-age", "--min-age"),
|
|
23424
|
+
...takes("string", "--pretty", "--format", "--diff-filter", "-S", "-G")
|
|
23425
|
+
}
|
|
23426
|
+
}
|
|
23427
|
+
],
|
|
23428
|
+
[
|
|
23429
|
+
"git show",
|
|
23430
|
+
{
|
|
23431
|
+
flags: {
|
|
23432
|
+
...GIT_LOG_DISPLAY,
|
|
23433
|
+
...GIT_STAT,
|
|
23434
|
+
...GIT_COLOR,
|
|
23435
|
+
...GIT_PATCH,
|
|
23436
|
+
...takes("none", "--abbrev-commit", "--word-diff", "--color-words", "--first-parent", "--raw", "-m", "--quiet"),
|
|
23437
|
+
...takes("string", "--word-diff-regex", "--pretty", "--format", "--diff-filter")
|
|
23438
|
+
}
|
|
23439
|
+
}
|
|
23440
|
+
],
|
|
23441
|
+
[
|
|
23442
|
+
"git shortlog",
|
|
23443
|
+
{
|
|
23444
|
+
flags: {
|
|
23445
|
+
...GIT_REF_SELECTION,
|
|
23446
|
+
...GIT_DATE_FILTERS,
|
|
23447
|
+
...takes("none", "-s", "--summary", "-n", "--numbered", "-e", "--email", "-c", "--committer", "--no-merges"),
|
|
23448
|
+
...takes("string", "--group", "--format", "--author")
|
|
23449
|
+
}
|
|
23450
|
+
}
|
|
23451
|
+
],
|
|
23452
|
+
["git reflog", { respectsDoubleDash: false, flags: { ...GIT_LOG_DISPLAY, ...GIT_REF_SELECTION, ...GIT_DATE_FILTERS, ...GIT_COUNT, ...GIT_AUTHOR_FILTERS }, dangerous: reflogWrites }],
|
|
23453
|
+
["git stash list", { respectsDoubleDash: false, flags: { ...GIT_LOG_DISPLAY, ...GIT_REF_SELECTION, ...GIT_COUNT } }],
|
|
23454
|
+
[
|
|
23455
|
+
"git ls-remote",
|
|
23456
|
+
{
|
|
23457
|
+
respectsDoubleDash: false,
|
|
23458
|
+
flags: {
|
|
23459
|
+
...takes("none", "--branches", "-b", "--tags", "-t", "--heads", "-h", "--refs", "--quiet", "-q", "--exit-code", "--get-url", "--symref"),
|
|
23460
|
+
...takes("string", "--sort")
|
|
23461
|
+
}
|
|
23462
|
+
}
|
|
23463
|
+
],
|
|
23464
|
+
[
|
|
23465
|
+
"git status",
|
|
23466
|
+
{
|
|
23467
|
+
flags: {
|
|
23468
|
+
...takes("none", "--short", "-s", "--branch", "-b", "--porcelain", "--long", "--verbose", "-v", "--ignored", "--column", "--no-column", "--ahead-behind", "--no-ahead-behind", "--renames", "--no-renames"),
|
|
23469
|
+
...takes("string", "--untracked-files", "-u", "--ignore-submodules", "--find-renames", "-M")
|
|
23470
|
+
}
|
|
23471
|
+
}
|
|
23472
|
+
],
|
|
23473
|
+
[
|
|
23474
|
+
"git blame",
|
|
23475
|
+
{
|
|
23476
|
+
flags: {
|
|
23477
|
+
...GIT_COLOR,
|
|
23478
|
+
...takes("string", "-L", "--date", "--ignore-rev", "--ignore-revs-file"),
|
|
23479
|
+
...takes("none", "--porcelain", "-p", "--line-porcelain", "--incremental", "--root", "--show-stats", "--show-name", "--show-number", "-n", "--show-email", "-e", "-f", "-w", "-M", "-C", "--score-debug", "-s", "-l", "-t"),
|
|
23480
|
+
...takes("number", "--abbrev")
|
|
23481
|
+
}
|
|
23482
|
+
}
|
|
23483
|
+
],
|
|
23484
|
+
[
|
|
23485
|
+
"git ls-files",
|
|
23486
|
+
{
|
|
23487
|
+
flags: {
|
|
23488
|
+
...takes("none", "--cached", "-c", "--deleted", "-d", "--modified", "-m", "--others", "-o", "--ignored", "-i", "--stage", "-s", "--killed", "-k", "--unmerged", "-u", "--directory", "--no-empty-directory", "--eol", "--full-name", "--debug", "-z", "-t", "-v", "-f", "--exclude-standard", "--error-unmatch", "--recurse-submodules"),
|
|
23489
|
+
...takes("number", "--abbrev"),
|
|
23490
|
+
...takes("string", "--exclude", "-x", "--exclude-from", "-X", "--exclude-per-directory")
|
|
23491
|
+
}
|
|
23492
|
+
}
|
|
23493
|
+
],
|
|
23494
|
+
[
|
|
23495
|
+
"git config --get",
|
|
23496
|
+
{
|
|
23497
|
+
flags: {
|
|
23498
|
+
...takes("none", "--local", "--global", "--system", "--worktree", "--bool", "--int", "--bool-or-int", "--path", "--expiry-date", "-z", "--null", "--name-only", "--show-origin", "--show-scope"),
|
|
23499
|
+
...takes("string", "--default", "--type")
|
|
23500
|
+
}
|
|
23501
|
+
}
|
|
23502
|
+
],
|
|
23503
|
+
[
|
|
23504
|
+
"git remote show",
|
|
23505
|
+
{
|
|
23506
|
+
flags: takes("none", "-n"),
|
|
23507
|
+
dangerous: remoteShowReachesBeyondOneRemote
|
|
23508
|
+
}
|
|
23509
|
+
],
|
|
23510
|
+
["git remote", { flags: takes("none", "-v", "--verbose"), dangerous: remoteDoesMoreThanList }],
|
|
23511
|
+
["git merge-base", { flags: takes("none", "--is-ancestor", "--fork-point", "--octopus", "--independent", "--all") }],
|
|
23512
|
+
[
|
|
23513
|
+
"git rev-parse",
|
|
23514
|
+
{
|
|
23515
|
+
flags: {
|
|
23516
|
+
...takes("none", "--verify", "--abbrev-ref", "--symbolic", "--symbolic-full-name", "--show-toplevel", "--show-cdup", "--show-prefix", "--git-dir", "--git-common-dir", "--absolute-git-dir", "--show-superproject-working-tree", "--is-inside-work-tree", "--is-inside-git-dir", "--is-bare-repository", "--is-shallow-repository", "--is-shallow-update", "--path-prefix"),
|
|
23517
|
+
...takes("string", "--short")
|
|
23518
|
+
}
|
|
23519
|
+
}
|
|
23520
|
+
],
|
|
23521
|
+
[
|
|
23522
|
+
"git rev-list",
|
|
23523
|
+
{
|
|
23524
|
+
flags: {
|
|
23525
|
+
...GIT_REF_SELECTION,
|
|
23526
|
+
...GIT_DATE_FILTERS,
|
|
23527
|
+
...GIT_COUNT,
|
|
23528
|
+
...GIT_AUTHOR_FILTERS,
|
|
23529
|
+
...takes("none", "--count", "--reverse", "--first-parent", "--ancestry-path", "--merges", "--no-merges", "--no-min-parents", "--no-max-parents", "--walk-reflogs", "--oneline", "--abbrev-commit", "--full-history", "--dense", "--sparse", "--source", "--graph"),
|
|
23530
|
+
...takes("number", "--min-parents", "--max-parents", "--skip", "--max-age", "--min-age", "--abbrev"),
|
|
23531
|
+
...takes("string", "--pretty", "--format")
|
|
23532
|
+
}
|
|
23533
|
+
}
|
|
23534
|
+
],
|
|
23535
|
+
[
|
|
23536
|
+
"git describe",
|
|
23537
|
+
{
|
|
23538
|
+
flags: {
|
|
23539
|
+
...takes("none", "--tags", "--long", "--always", "--contains", "--first-match", "--exact-match", "--dirty", "--broken"),
|
|
23540
|
+
...takes("string", "--match", "--exclude"),
|
|
23541
|
+
...takes("number", "--abbrev", "--candidates")
|
|
23542
|
+
}
|
|
23543
|
+
}
|
|
23544
|
+
],
|
|
23545
|
+
["git cat-file", { flags: takes("none", "-t", "-s", "-p", "-e", "--batch-check", "--allow-undetermined-type") }],
|
|
23546
|
+
[
|
|
23547
|
+
"git for-each-ref",
|
|
23548
|
+
{
|
|
23549
|
+
flags: {
|
|
23550
|
+
...takes("string", "--format", "--sort", "--contains", "--no-contains", "--merged", "--no-merged", "--points-at"),
|
|
23551
|
+
...takes("number", "--count")
|
|
23552
|
+
}
|
|
23553
|
+
}
|
|
23554
|
+
],
|
|
23555
|
+
[
|
|
23556
|
+
"git grep",
|
|
23557
|
+
{
|
|
23558
|
+
flags: {
|
|
23559
|
+
...takes("string", "-e"),
|
|
23560
|
+
...takes("none", "-E", "--extended-regexp", "-G", "--basic-regexp", "-F", "--fixed-strings", "-P", "--perl-regexp", "-i", "--ignore-case", "-v", "--invert-match", "-w", "--word-regexp", "-n", "--line-number", "-c", "--count", "-l", "--files-with-matches", "-L", "--files-without-match", "-h", "-H", "--heading", "--break", "--full-name", "--color", "--no-color", "-o", "--only-matching", "--and", "--or", "--not", "--untracked", "--no-index", "--recurse-submodules", "--cached", "-q", "--quiet"),
|
|
23561
|
+
...takes("number", "-A", "--after-context", "-B", "--before-context", "-C", "--context", "--max-depth", "--threads")
|
|
23562
|
+
}
|
|
23563
|
+
}
|
|
23564
|
+
],
|
|
23565
|
+
[
|
|
23566
|
+
"git stash show",
|
|
23567
|
+
{
|
|
23568
|
+
respectsDoubleDash: false,
|
|
23569
|
+
flags: {
|
|
23570
|
+
...GIT_STAT,
|
|
23571
|
+
...GIT_COLOR,
|
|
23572
|
+
...GIT_PATCH,
|
|
23573
|
+
...takes("none", "--word-diff"),
|
|
23574
|
+
...takes("string", "--word-diff-regex", "--diff-filter"),
|
|
23575
|
+
...takes("number", "--abbrev")
|
|
23576
|
+
}
|
|
23577
|
+
}
|
|
23578
|
+
],
|
|
23579
|
+
["git worktree list", { flags: { ...takes("none", "--porcelain", "-v", "--verbose"), ...takes("string", "--expire") } }],
|
|
23580
|
+
["git tag", { flags: GIT_TAG_FLAGS, dangerous: (_text, args) => namesNewRef(args, GIT_TAG_FLAGS, new Set) }],
|
|
23581
|
+
["git branch", { flags: GIT_BRANCH_FLAGS, dangerous: (_text, args) => namesNewRef(args, GIT_BRANCH_FLAGS, BRANCH_OPTIONAL_COMMIT) }],
|
|
23582
|
+
[
|
|
23583
|
+
"file",
|
|
23584
|
+
{
|
|
23585
|
+
flags: {
|
|
23586
|
+
...takes("none", "--brief", "-b", "--mime", "-i", "--mime-type", "--mime-encoding", "--apple", "--check-encoding", "-c", "--print0", "-0", "--help", "--version", "-v", "--no-dereference", "-h", "--dereference", "-L", "--keep-going", "-k", "--list", "-l", "--no-buffer", "-n", "--preserve-date", "-p", "--raw", "-r", "-s", "--special-files", "--uncompress", "-z"),
|
|
23587
|
+
...takes("string", "--exclude", "--exclude-quiet", "-f", "-F", "--separator", "--magic-file", "-m")
|
|
23588
|
+
}
|
|
23589
|
+
}
|
|
23590
|
+
],
|
|
23591
|
+
[
|
|
23592
|
+
"sed",
|
|
23593
|
+
{
|
|
23594
|
+
flags: {
|
|
23595
|
+
...takes("string", "--expression", "-e"),
|
|
23596
|
+
...takes("none", "--quiet", "--silent", "-n", "--regexp-extended", "-r", "--posix", "-E", "--zero-terminated", "-z", "--separate", "-s", "--unbuffered", "-u", "--debug", "--help", "--version"),
|
|
23597
|
+
...takes("number", "--line-length", "-l")
|
|
23598
|
+
},
|
|
23599
|
+
dangerous: (text, args) => !sedIsAllowed(text, args)
|
|
23600
|
+
}
|
|
23601
|
+
],
|
|
23602
|
+
[
|
|
23603
|
+
"sort",
|
|
23604
|
+
{
|
|
23605
|
+
flags: {
|
|
23606
|
+
...takes("none", "--ignore-leading-blanks", "-b", "--dictionary-order", "-d", "--ignore-case", "-f", "--general-numeric-sort", "-g", "--human-numeric-sort", "-h", "--ignore-nonprinting", "-i", "--month-sort", "-M", "--numeric-sort", "-n", "--random-sort", "-R", "--reverse", "-r", "--stable", "-s", "--unique", "-u", "--version-sort", "-V", "--zero-terminated", "-z", "--check", "-c", "--check-char-order", "-C", "--merge", "-m", "--help", "--version"),
|
|
23607
|
+
...takes("string", "--sort", "--key", "-k", "--field-separator", "-t", "--buffer-size", "-S"),
|
|
23608
|
+
...takes("number", "--parallel", "--batch-size")
|
|
23609
|
+
}
|
|
23610
|
+
}
|
|
23611
|
+
],
|
|
23612
|
+
["man", { flags: { ...takes("none", "-a", "--all", "-d", "-f", "--whatis", "-h", "-k", "--apropos", "-w"), ...takes("string", "-l", "-S", "-s") } }],
|
|
23613
|
+
["help", { flags: takes("none", "-d", "-m", "-s") }],
|
|
23614
|
+
["netstat", { flags: { ...takes("none", "-a", "-L", "-l", "-n", "-g", "-i", "-s", "-r", "-m", "-v"), ...takes("string", "-f", "-I") } }],
|
|
23615
|
+
[
|
|
23616
|
+
"ps",
|
|
23617
|
+
{
|
|
23618
|
+
flags: {
|
|
23619
|
+
...takes("none", "-e", "-A", "-a", "-d", "-N", "--deselect", "-f", "-F", "-l", "-j", "-y", "-w", "-ww", "-c", "-H", "--forest", "--headers", "--no-headers", "-L", "-T", "-m", "--help", "--info", "-V", "--version"),
|
|
23620
|
+
...takes("number", "--width"),
|
|
23621
|
+
...takes("string", "-n", "--sort", "-C", "-G", "-g", "-p", "--pid", "-q", "--quick-pid", "-s", "--sid", "-t", "--tty", "-U", "-u", "--user")
|
|
23622
|
+
},
|
|
23623
|
+
dangerous: psPrintsEnvironments
|
|
23624
|
+
}
|
|
23625
|
+
],
|
|
23626
|
+
[
|
|
23627
|
+
"base64",
|
|
23628
|
+
{
|
|
23629
|
+
respectsDoubleDash: false,
|
|
23630
|
+
flags: BASE64_FLAGS,
|
|
23631
|
+
dangerous: (_text, args) => operandsOf(args, BASE64_FLAGS).length > 1
|
|
23632
|
+
}
|
|
23633
|
+
],
|
|
23634
|
+
[
|
|
23635
|
+
"grep",
|
|
23636
|
+
{
|
|
23637
|
+
flags: {
|
|
23638
|
+
...takes("string", "-e", "--regexp", "-f", "--file", "--color", "--colour", "--label", "--group-separator", "--binary-files", "-D", "--devices", "-d", "--directories", "--exclude", "--exclude-from", "--exclude-dir", "--include"),
|
|
23639
|
+
...takes("none", "-F", "--fixed-strings", "-G", "--basic-regexp", "-E", "--extended-regexp", "-P", "--perl-regexp", "-i", "--ignore-case", "--no-ignore-case", "-v", "--invert-match", "-w", "--word-regexp", "-x", "--line-regexp", "-c", "--count", "-L", "--files-without-match", "-l", "--files-with-matches", "-o", "--only-matching", "-q", "--quiet", "--silent", "-s", "--no-messages", "-b", "--byte-offset", "-H", "--with-filename", "-h", "--no-filename", "-n", "--line-number", "-T", "--initial-tab", "-u", "--unix-byte-offsets", "-Z", "--null", "-z", "--null-data", "--no-group-separator", "-a", "--text", "-r", "--recursive", "-R", "--dereference-recursive", "--line-buffered", "-U", "--binary", "--help", "-V", "--version"),
|
|
23640
|
+
...takes("number", "-m", "--max-count", "-A", "--after-context", "-B", "--before-context", "-C", "--context")
|
|
23641
|
+
}
|
|
23642
|
+
}
|
|
23643
|
+
],
|
|
23644
|
+
[
|
|
23645
|
+
"rg",
|
|
23646
|
+
{
|
|
23647
|
+
flags: {
|
|
23648
|
+
...takes("string", "-e", "--regexp", "-f", "-g", "--glob", "-t", "--type", "-T", "--type-not", "--color"),
|
|
23649
|
+
...takes("none", "-i", "--ignore-case", "-S", "--smart-case", "-F", "--fixed-strings", "-w", "--word-regexp", "-v", "--invert-match", "-c", "--count", "-l", "--files-with-matches", "--files-without-match", "-n", "--line-number", "-o", "--only-matching", "-H", "-h", "--heading", "--no-heading", "-q", "--quiet", "--column", "--type-list", "--hidden", "--no-ignore", "-u", "-a", "--text", "-z", "-L", "--follow", "--json", "--stats", "--help", "--version", "--debug", "--"),
|
|
23650
|
+
...takes("number", "-A", "--after-context", "-B", "--before-context", "-C", "--context", "-m", "--max-count", "-d", "--max-depth")
|
|
23651
|
+
}
|
|
23652
|
+
}
|
|
23653
|
+
],
|
|
23654
|
+
["sha256sum", { flags: CHECKSUM_FLAGS }],
|
|
23655
|
+
["sha1sum", { flags: CHECKSUM_FLAGS }],
|
|
23656
|
+
["md5sum", { flags: CHECKSUM_FLAGS }],
|
|
23657
|
+
[
|
|
23658
|
+
"tree",
|
|
23659
|
+
{
|
|
23660
|
+
respectsDoubleDash: false,
|
|
23661
|
+
flags: {
|
|
23662
|
+
...takes("none", "-a", "-d", "-l", "-f", "-x", "--gitignore", "--ignore-case", "--matchdirs", "--metafirst", "--prune", "--info", "--noreport", "-q", "-N", "-Q", "-p", "-u", "-g", "-s", "-h", "--si", "--du", "-D", "-F", "--inodes", "--device", "-v", "-t", "-c", "-U", "-r", "--dirsfirst", "--filesfirst", "-i", "-A", "-S", "-n", "-C", "-X", "-J", "--nolinks", "--hyperlink", "--fromfile", "--fromtabfile", "--fflinks", "--help", "--version"),
|
|
23663
|
+
...takes("number", "-L", "--filelimit"),
|
|
23664
|
+
...takes("string", "-P", "-I", "--gitfile", "--infofile", "--charset", "--timefmt", "--sort", "-H", "--hintro", "--houtro", "-T", "--scheme", "--authority")
|
|
23665
|
+
}
|
|
23666
|
+
}
|
|
23667
|
+
],
|
|
23668
|
+
[
|
|
23669
|
+
"date",
|
|
23670
|
+
{
|
|
23671
|
+
flags: DATE_FLAGS,
|
|
23672
|
+
dangerous: dateSetsClock
|
|
23673
|
+
}
|
|
23674
|
+
],
|
|
23675
|
+
[
|
|
23676
|
+
"hostname",
|
|
23677
|
+
{
|
|
23678
|
+
flags: takes("none", "-f", "--fqdn", "--long", "-s", "--short", "-i", "--ip-address", "-I", "--all-ip-addresses", "-a", "--alias", "-d", "--domain", "-A", "--all-fqdns", "-v", "--verbose", "-h", "--help", "-V", "--version"),
|
|
23679
|
+
pattern: /^hostname(?:\s+(?:-[a-zA-Z]|--[a-zA-Z-]+))*\s*$/
|
|
23680
|
+
}
|
|
23681
|
+
],
|
|
23682
|
+
[
|
|
23683
|
+
"info",
|
|
23684
|
+
{
|
|
23685
|
+
flags: {
|
|
23686
|
+
...takes("string", "-f", "--file", "-d", "--directory", "-n", "--node", "-k", "--apropos"),
|
|
23687
|
+
...takes("none", "-a", "--all", "-w", "--where", "--location", "--show-options", "--vi-keys", "--subnodes", "-h", "--help", "--usage", "--version")
|
|
23688
|
+
}
|
|
23689
|
+
}
|
|
23690
|
+
],
|
|
23691
|
+
[
|
|
23692
|
+
"lsof",
|
|
23693
|
+
{
|
|
23694
|
+
respectsDoubleDash: false,
|
|
23695
|
+
flags: {
|
|
23696
|
+
...takes("none", "-?", "-h", "-v", "-a", "-b", "-C", "-l", "-n", "-N", "-O", "-P", "-Q", "-R", "-t", "-U", "-V", "-X", "-H", "-E", "-F", "-g", "-i", "-K", "-L", "-o", "-r", "-s", "-S", "-T", "-x"),
|
|
23697
|
+
...takes("string", "-A", "-c", "-d", "-e", "-k", "-p", "-u")
|
|
23698
|
+
},
|
|
23699
|
+
dangerous: lsofWritesMountSupplement
|
|
23700
|
+
}
|
|
23701
|
+
],
|
|
23702
|
+
[
|
|
23703
|
+
"pgrep",
|
|
23704
|
+
{
|
|
23705
|
+
flags: {
|
|
23706
|
+
...takes("string", "-d", "--delimiter", "-g", "--pgroup", "-G", "--group", "-O", "--older", "-P", "--parent", "-s", "--session", "-t", "--terminal", "-u", "--euid", "-U", "--uid", "-F", "--pidfile", "-r", "--runstates", "--ns", "--nslist"),
|
|
23707
|
+
...takes("none", "-l", "--list-name", "-a", "--list-full", "-v", "--inverse", "-w", "--lightweight", "-c", "--count", "-f", "--full", "-i", "--ignore-case", "-n", "--newest", "-o", "--oldest", "-x", "--exact", "-L", "--logpidfile", "--help", "-V", "--version")
|
|
23708
|
+
}
|
|
23709
|
+
}
|
|
23710
|
+
],
|
|
23711
|
+
["tput", { flags: TPUT_FLAGS, dangerous: tputChangesTerminal }],
|
|
23712
|
+
[
|
|
23713
|
+
"ss",
|
|
23714
|
+
{
|
|
23715
|
+
flags: {
|
|
23716
|
+
...takes("none", "-h", "--help", "-V", "--version", "-n", "--numeric", "-r", "--resolve", "-a", "--all", "-l", "--listening", "-o", "--options", "-e", "--extended", "-m", "--memory", "-p", "--processes", "-i", "--info", "-s", "--summary", "-4", "--ipv4", "-6", "--ipv6", "-0", "--packet", "-t", "--tcp", "-M", "--mptcp", "-S", "--sctp", "-u", "--udp", "-d", "--dccp", "-w", "--raw", "-x", "--unix", "--tipc", "--vsock", "-Z", "--context", "-z", "--contexts", "-b", "--bpf", "-E", "--events", "-H", "--no-header", "-O", "--oneline", "--tipcinfo", "--tos", "--cgroup", "--inet-sockopt"),
|
|
23717
|
+
...takes("string", "-f", "--family", "-A", "--query", "--socket")
|
|
23718
|
+
}
|
|
23719
|
+
}
|
|
23720
|
+
],
|
|
23721
|
+
["fd", { flags: FD_FLAGS }],
|
|
23722
|
+
["fdfind", { flags: FD_FLAGS }],
|
|
23723
|
+
[
|
|
23724
|
+
"pyright",
|
|
23725
|
+
{
|
|
23726
|
+
respectsDoubleDash: false,
|
|
23727
|
+
flags: {
|
|
23728
|
+
...takes("none", "--outputjson", "--stats", "--verbose", "--version", "--dependencies", "--warnings"),
|
|
23729
|
+
...takes("string", "--project", "-p", "--pythonversion", "--pythonplatform", "--typeshedpath", "--venvpath", "--level")
|
|
23730
|
+
},
|
|
23731
|
+
dangerous: pyrightWatches
|
|
23732
|
+
}
|
|
23733
|
+
],
|
|
23734
|
+
["docker logs", { flags: { ...takes("none", "--follow", "-f", "--timestamps", "-t", "--details"), ...takes("string", "--tail", "-n", "--since", "--until") } }],
|
|
23735
|
+
["docker inspect", { flags: { ...takes("string", "--format", "-f", "--type"), ...takes("none", "--size", "-s") } }]
|
|
23736
|
+
];
|
|
23737
|
+
var XARGS_SAFE_TARGETS = new Set(["echo", "printf", "wc", "grep", "head", "tail"]);
|
|
23738
|
+
function readOptionWord(token) {
|
|
23739
|
+
if (token === "--")
|
|
23740
|
+
return { form: "end-of-options" };
|
|
23741
|
+
if (token.length < 2 || !token.startsWith("-"))
|
|
23742
|
+
return { form: "operand" };
|
|
23743
|
+
const eq = token.indexOf("=");
|
|
23744
|
+
const head = eq === -1 ? token : token.slice(0, eq);
|
|
23745
|
+
const inline = eq === -1 ? undefined : token.slice(eq + 1);
|
|
23746
|
+
if (head.startsWith("--"))
|
|
23747
|
+
return inline === undefined ? { form: "long", name: head } : { form: "long", name: head, inline };
|
|
23748
|
+
const letters = head.slice(1);
|
|
23749
|
+
return inline === undefined ? { form: "short", letters } : { form: "short", letters, inline };
|
|
23750
|
+
}
|
|
23751
|
+
var looksLikeOption = (token) => token.length > 1 && token.startsWith("-");
|
|
23752
|
+
function kindOf(flags, name) {
|
|
23753
|
+
return Object.hasOwn(flags, name) ? flags[name] : undefined;
|
|
23754
|
+
}
|
|
23755
|
+
function valueFits(kind, value, flag, command) {
|
|
23756
|
+
switch (kind) {
|
|
23757
|
+
case "number":
|
|
23758
|
+
return /^\d+$/.test(value);
|
|
23759
|
+
case "string":
|
|
23760
|
+
case "attached":
|
|
23761
|
+
return !value.startsWith("-") || command === "git" && flag === "--sort" && /^-[a-zA-Z]/.test(value);
|
|
23762
|
+
case "char":
|
|
23763
|
+
return value.length === 1;
|
|
23764
|
+
case "{}":
|
|
23765
|
+
case "EOF":
|
|
23766
|
+
return value === kind;
|
|
23767
|
+
default:
|
|
23768
|
+
return false;
|
|
23769
|
+
}
|
|
23770
|
+
}
|
|
23771
|
+
function acceptOption(tokens, i, flags, command) {
|
|
23772
|
+
const word = readOptionWord(tokens[i]);
|
|
23773
|
+
if (word.form !== "long" && word.form !== "short")
|
|
23774
|
+
return null;
|
|
23775
|
+
const name = word.form === "long" ? word.name : `-${word.letters}`;
|
|
23776
|
+
const kind = kindOf(flags, name);
|
|
23777
|
+
if (kind !== undefined) {
|
|
23778
|
+
if (kind === "none")
|
|
23779
|
+
return word.inline === undefined ? 1 : null;
|
|
23780
|
+
if (kind === "attached")
|
|
23781
|
+
return word.inline !== undefined && valueFits(kind, word.inline, name, command) ? 1 : null;
|
|
23782
|
+
if (word.inline !== undefined)
|
|
23783
|
+
return valueFits(kind, word.inline, name, command) ? 1 : null;
|
|
23784
|
+
const value = tokens[i + 1];
|
|
23785
|
+
if (value === undefined || looksLikeOption(value))
|
|
23786
|
+
return null;
|
|
23787
|
+
return valueFits(kind, value, name, command) ? 2 : null;
|
|
23788
|
+
}
|
|
23789
|
+
if (word.form === "long" || word.inline !== undefined)
|
|
23790
|
+
return null;
|
|
23791
|
+
if (command === "git" && /^\d+$/.test(word.letters))
|
|
23792
|
+
return 1;
|
|
23793
|
+
if (command === "grep" || command === "rg") {
|
|
23794
|
+
const leading = kindOf(flags, `-${word.letters[0]}`);
|
|
23795
|
+
if ((leading === "number" || leading === "string") && /^\d+$/.test(word.letters.slice(1)))
|
|
23796
|
+
return 1;
|
|
23797
|
+
}
|
|
23798
|
+
for (const letter of word.letters) {
|
|
23799
|
+
if (kindOf(flags, `-${letter}`) !== "none")
|
|
23800
|
+
return null;
|
|
23801
|
+
}
|
|
23802
|
+
return 1;
|
|
23803
|
+
}
|
|
23804
|
+
function optionsAllowed(tokens, start, spec, command) {
|
|
23805
|
+
let i = start;
|
|
23806
|
+
while (i < tokens.length) {
|
|
23807
|
+
const word = readOptionWord(tokens[i]);
|
|
23808
|
+
if (word.form === "operand") {
|
|
23809
|
+
i++;
|
|
23810
|
+
continue;
|
|
23811
|
+
}
|
|
23812
|
+
if (word.form === "end-of-options") {
|
|
23813
|
+
if (spec.respectsDoubleDash !== false)
|
|
23814
|
+
return true;
|
|
23815
|
+
i++;
|
|
23816
|
+
continue;
|
|
23817
|
+
}
|
|
23818
|
+
const used = acceptOption(tokens, i, spec.flags, command);
|
|
23819
|
+
if (used === null)
|
|
23820
|
+
return false;
|
|
23821
|
+
i += used;
|
|
23822
|
+
}
|
|
23823
|
+
return true;
|
|
23824
|
+
}
|
|
23825
|
+
function xargsAllowed(tokens, start, spec) {
|
|
23826
|
+
let i = start;
|
|
23827
|
+
while (i < tokens.length) {
|
|
23828
|
+
const word = readOptionWord(tokens[i]);
|
|
23829
|
+
if (word.form === "end-of-options") {
|
|
23830
|
+
const target = tokens[i + 1];
|
|
23831
|
+
return target !== undefined && XARGS_SAFE_TARGETS.has(target);
|
|
23832
|
+
}
|
|
23833
|
+
if (word.form === "operand")
|
|
23834
|
+
return XARGS_SAFE_TARGETS.has(tokens[i]);
|
|
23835
|
+
const used = acceptOption(tokens, i, spec.flags, "xargs");
|
|
23836
|
+
if (used === null)
|
|
23837
|
+
return false;
|
|
23838
|
+
i += used;
|
|
23839
|
+
}
|
|
23840
|
+
return true;
|
|
23841
|
+
}
|
|
23842
|
+
function operandsOf(args, flags) {
|
|
23843
|
+
return partitionArguments(args, flags).operands;
|
|
23844
|
+
}
|
|
23845
|
+
function partitionArguments(args, flags) {
|
|
23846
|
+
const options = [];
|
|
23847
|
+
const operands = [];
|
|
23848
|
+
let optionsOver = false;
|
|
23849
|
+
for (let i = 0;i < args.length; i++) {
|
|
23850
|
+
const token = args[i];
|
|
23851
|
+
const word = optionsOver ? { form: "operand" } : readOptionWord(token);
|
|
23852
|
+
if (word.form === "end-of-options")
|
|
23853
|
+
optionsOver = true;
|
|
23854
|
+
else if (word.form === "operand")
|
|
23855
|
+
operands.push(token);
|
|
23856
|
+
else {
|
|
23857
|
+
options.push(token);
|
|
23858
|
+
const kind = word.inline === undefined ? kindOf(flags, word.form === "long" ? word.name : `-${word.letters}`) : undefined;
|
|
23859
|
+
if (kind === "number" || kind === "string" || kind === "char" || kind === "{}" || kind === "EOF")
|
|
23860
|
+
i++;
|
|
23861
|
+
}
|
|
23862
|
+
}
|
|
23863
|
+
return { options, operands };
|
|
23864
|
+
}
|
|
23865
|
+
var SPEC_LEADING_WORDS = COMMAND_SPECS.map(([name, spec]) => ({ leading: name.split(" "), spec }));
|
|
23866
|
+
function commandSpecFor(tokens, platform) {
|
|
23867
|
+
const entry = SPEC_LEADING_WORDS.find(({ leading }) => leading.every((word, k) => tokens[k] === word) && !(leading[0] === "xargs" && platform === "win32"));
|
|
23868
|
+
return entry && { spec: entry.spec, words: entry.leading.length };
|
|
23869
|
+
}
|
|
23870
|
+
var namesRemoteLocation = (token) => token.length > 0 && !token.startsWith("-") && (token.includes("://") || token.includes("@") || token.includes(":") || token.includes("$"));
|
|
23871
|
+
var expandsAtRunTime = (token) => token.includes("$") || token.includes("{") && (token.includes(",") || token.includes(".."));
|
|
23872
|
+
function isSafeViaFlagAllowlist(sub, platform) {
|
|
23873
|
+
const tokens = sub.words.map((w) => w.value);
|
|
23874
|
+
const match = commandSpecFor(tokens, platform);
|
|
23875
|
+
if (match === undefined)
|
|
23876
|
+
return false;
|
|
23877
|
+
const { spec, words } = match;
|
|
23878
|
+
const args = tokens.slice(words);
|
|
23879
|
+
if (args.some(expandsAtRunTime))
|
|
23880
|
+
return false;
|
|
23881
|
+
if (tokens[0] === "git" && tokens[1] === "ls-remote" && args.some(namesRemoteLocation))
|
|
23882
|
+
return false;
|
|
23883
|
+
const optionsOk = tokens[0] === "xargs" ? xargsAllowed(tokens, words, spec) : optionsAllowed(tokens, words, spec, tokens[0]);
|
|
23884
|
+
if (!optionsOk)
|
|
23885
|
+
return false;
|
|
23886
|
+
if (spec.pattern !== undefined) {
|
|
23887
|
+
if (!spec.pattern.test(sub.text))
|
|
23888
|
+
return false;
|
|
23889
|
+
} else if (sub.text.includes("`") || (tokens[0] === "rg" || tokens[0] === "grep") && /[\n\r]/.test(sub.text)) {
|
|
23890
|
+
return false;
|
|
23891
|
+
}
|
|
23892
|
+
return !(spec.dangerous?.(sub.text, args) ?? false);
|
|
23893
|
+
}
|
|
23894
|
+
var SED_QUIET = ["-n", "--quiet", "--silent"];
|
|
23895
|
+
var SED_PRINT_OPTIONS = { long: ["--quiet", "--silent", "--regexp-extended", "--zero-terminated", "--posix"], letters: "nErz" };
|
|
23896
|
+
var SED_SUBSTITUTE_OPTIONS = { long: ["--regexp-extended", "--posix"], letters: "Er" };
|
|
23897
|
+
function sedOptionsWithin(options, allowed) {
|
|
23898
|
+
return options.every((option) => option.startsWith("--") ? allowed.long.includes(option) : option.length > 1 && [...option.slice(1)].every((letter) => allowed.letters.includes(letter)));
|
|
23899
|
+
}
|
|
23900
|
+
var SED_PRINT_SCRIPT = /^(?:\d+(?:,\d+)?)?p\s*(?:;\s*(?:\d+(?:,\d+)?)?p\s*)*$/;
|
|
23901
|
+
function sedPrintScript(script) {
|
|
23902
|
+
const trimmed = script.trim();
|
|
23903
|
+
return !trimmed.includes(`
|
|
23904
|
+
`) && SED_PRINT_SCRIPT.test(trimmed);
|
|
23905
|
+
}
|
|
23906
|
+
function sedSubstituteScript(script) {
|
|
23907
|
+
const s = script.trim();
|
|
23908
|
+
if (!s.startsWith("s/") || occurrences(s, "/") !== 3)
|
|
23909
|
+
return false;
|
|
23910
|
+
const delimiters = [];
|
|
23911
|
+
for (let i = 0;i < s.length; i++) {
|
|
23912
|
+
if (s[i] === "\\") {
|
|
23913
|
+
if (s[i + 1] === "/")
|
|
23914
|
+
return false;
|
|
23915
|
+
i++;
|
|
23916
|
+
continue;
|
|
23917
|
+
}
|
|
23918
|
+
if (s[i] === "/")
|
|
23919
|
+
delimiters.push(i);
|
|
23920
|
+
}
|
|
23921
|
+
if (delimiters.length !== 3)
|
|
23922
|
+
return false;
|
|
23923
|
+
if (!/^[gpimIM]*[1-9]?[gpimIM]*$/.test(s.slice(delimiters[2] + 1)))
|
|
23924
|
+
return false;
|
|
23925
|
+
for (let i = 0;i < s.length; i++) {
|
|
23926
|
+
const ch = s[i];
|
|
23927
|
+
const code = ch.charCodeAt(0);
|
|
23928
|
+
if (code === 0 || code > 127)
|
|
23929
|
+
return false;
|
|
23930
|
+
if (`
|
|
23931
|
+
{}#;!~`.includes(ch))
|
|
23932
|
+
return false;
|
|
23933
|
+
if (ch === "\\" && s[i - 1] === "s")
|
|
23934
|
+
return false;
|
|
23935
|
+
if (ch === "\\" && "|#%@".includes(s[i + 1] ?? ""))
|
|
23936
|
+
return false;
|
|
23937
|
+
if (isBlank(ch) && "wWeE".includes(s[i + 1] ?? " "))
|
|
23938
|
+
return false;
|
|
23939
|
+
if (ch === ",") {
|
|
23940
|
+
let k = i + 1;
|
|
23941
|
+
while (isBlank(s[k]))
|
|
23942
|
+
k++;
|
|
23943
|
+
if (s[k] === "+" || s[k] === "-")
|
|
23944
|
+
return false;
|
|
23945
|
+
}
|
|
23946
|
+
}
|
|
23947
|
+
const transliterates = /y[^\\\n]/.test(s);
|
|
23948
|
+
return !(transliterates && /[wWeE]/.test(s));
|
|
23949
|
+
}
|
|
23950
|
+
function sedIsAllowed(text, args) {
|
|
23951
|
+
if (!text.startsWith("sed "))
|
|
23952
|
+
return false;
|
|
23953
|
+
const argumentText = text.slice(4);
|
|
23954
|
+
if (/-e[wWe]|-w[eE]/.test(argumentText))
|
|
23955
|
+
return false;
|
|
23956
|
+
const options = args.filter((a) => a.startsWith("-") && a !== "--");
|
|
23957
|
+
const operands = args.filter((a) => !a.startsWith("-"));
|
|
23958
|
+
const script = operands[0];
|
|
23959
|
+
if (script === undefined)
|
|
23960
|
+
return false;
|
|
23961
|
+
const quiet = options.some((o) => SED_QUIET.includes(o) || !o.startsWith("--") && o.includes("n"));
|
|
23962
|
+
if (quiet && sedOptionsWithin(options, SED_PRINT_OPTIONS))
|
|
23963
|
+
return sedPrintScript(script);
|
|
23964
|
+
return operands.length === 1 && sedOptionsWithin(options, SED_SUBSTITUTE_OPTIONS) && sedSubstituteScript(script);
|
|
23965
|
+
}
|
|
23966
|
+
var PLAIN_READ_ONLY_COMMANDS = [
|
|
23967
|
+
"docker ps",
|
|
23968
|
+
"docker images",
|
|
23969
|
+
"cal",
|
|
23970
|
+
"uptime",
|
|
23971
|
+
"cat",
|
|
23972
|
+
"head",
|
|
23973
|
+
"tail",
|
|
23974
|
+
"wc",
|
|
23975
|
+
"stat",
|
|
23976
|
+
"strings",
|
|
23977
|
+
"hexdump",
|
|
23978
|
+
"od",
|
|
23979
|
+
"nl",
|
|
23980
|
+
"id",
|
|
23981
|
+
"uname",
|
|
23982
|
+
"free",
|
|
23983
|
+
"df",
|
|
23984
|
+
"du",
|
|
23985
|
+
"locale",
|
|
23986
|
+
"groups",
|
|
23987
|
+
"nproc",
|
|
23988
|
+
"basename",
|
|
23989
|
+
"dirname",
|
|
23990
|
+
"realpath",
|
|
23991
|
+
"cut",
|
|
23992
|
+
"paste",
|
|
23993
|
+
"tr",
|
|
23994
|
+
"column",
|
|
23995
|
+
"tac",
|
|
23996
|
+
"rev",
|
|
23997
|
+
"fold",
|
|
23998
|
+
"expand",
|
|
23999
|
+
"unexpand",
|
|
24000
|
+
"fmt",
|
|
24001
|
+
"comm",
|
|
24002
|
+
"cmp",
|
|
24003
|
+
"numfmt",
|
|
24004
|
+
"readlink",
|
|
24005
|
+
"diff",
|
|
24006
|
+
"true",
|
|
24007
|
+
"false",
|
|
24008
|
+
"sleep",
|
|
24009
|
+
"which",
|
|
24010
|
+
"type",
|
|
24011
|
+
"expr",
|
|
24012
|
+
"test",
|
|
24013
|
+
"getconf",
|
|
24014
|
+
"seq",
|
|
24015
|
+
"tsort",
|
|
24016
|
+
"pr"
|
|
24017
|
+
];
|
|
24018
|
+
var PLAIN_ARGUMENTS = "(?:\\s|$)[^<>()$`|{}&;\\n\\r]*$";
|
|
24019
|
+
var FIND_ACTIONS = /-(?:delete|exec|execdir|ok|okdir|fprint0?|fls|fprintf)\b/;
|
|
24020
|
+
var FIND_FORBIDDEN_SYNTAX = "<>()$`|{}&;\n\r";
|
|
24021
|
+
function findOnlyLists(simple) {
|
|
24022
|
+
if (simple.words[0].raw !== "find")
|
|
24023
|
+
return false;
|
|
24024
|
+
const args = simple.text.slice("find".length);
|
|
24025
|
+
for (let i = 0;i < args.length; i++) {
|
|
24026
|
+
if (args[i] === "\\" && (args[i + 1] === "(" || args[i + 1] === ")")) {
|
|
24027
|
+
i++;
|
|
24028
|
+
continue;
|
|
24029
|
+
}
|
|
24030
|
+
if (FIND_FORBIDDEN_SYNTAX.includes(args[i]))
|
|
24031
|
+
return false;
|
|
24032
|
+
}
|
|
24033
|
+
return !FIND_ACTIONS.test(args) && simple.words.every((w) => !FIND_ACTIONS.test(w.value));
|
|
24034
|
+
}
|
|
24035
|
+
var READ_ONLY_REGEXES = [
|
|
24036
|
+
...PLAIN_READ_ONLY_COMMANDS.map((name) => new RegExp("^" + name + PLAIN_ARGUMENTS)),
|
|
24037
|
+
/^echo(?:\s+(?:'[^']*'|"[^"$<>\n\r]*"|[^|;&`$(){}><#\\!"'\s]+))*(?:\s+2>&1)?\s*$/,
|
|
24038
|
+
/^claude -h$/,
|
|
24039
|
+
/^claude --help$/,
|
|
24040
|
+
/^uniq(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+(?:=\S+)?|-[fsw]\s+\d+))*(?:\s|$)\s*$/,
|
|
24041
|
+
/^pwd$/,
|
|
24042
|
+
/^whoami$/,
|
|
24043
|
+
/^node -v$/,
|
|
24044
|
+
/^node --version$/,
|
|
24045
|
+
/^python --version$/,
|
|
24046
|
+
/^python3 --version$/,
|
|
24047
|
+
/^history(?:\s+\d+)?\s*$/,
|
|
24048
|
+
/^alias$/,
|
|
24049
|
+
/^arch(?:\s+(?:--help|-h))?\s*$/,
|
|
24050
|
+
/^ip addr$/,
|
|
24051
|
+
/^ifconfig(?:\s+[a-zA-Z][a-zA-Z0-9_-]*)?\s*$/,
|
|
24052
|
+
/^jq(?!\s+.*(?:-f\b|--from-file|--rawfile|--slurpfile|--run-tests|-L\b|--library-path|\benv\b|\$ENV\b))(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+(?:=\S+)?))*(?:\s+'[^'`]*'|\s+"[^"`]*"|\s+[^-\s'"][^\s]*)+\s*$/,
|
|
24053
|
+
/^cd(?:\s+(?:'[^']*'|"[^"]*"|[^\s;|&`$(){}><#\\]+))?$/,
|
|
24054
|
+
/^ls(?:\s+[^<>()$`|{}&;\n\r]*)?$/
|
|
24055
|
+
];
|
|
24056
|
+
|
|
24057
|
+
// src/tools/concurrency.ts
|
|
24058
|
+
var MAX_TOOL_CONCURRENCY = 10;
|
|
24059
|
+
var CONCURRENCY_SAFE_BUILTINS = new Set(["Read", "Glob", "Grep", "LSP", "WebFetch", "WebSearch", "Search", "Agent"]);
|
|
24060
|
+
function schedulingForCall(name, input, bash) {
|
|
24061
|
+
const descriptor = getRegisteredTool(name)?.descriptor;
|
|
24062
|
+
if (descriptor === undefined)
|
|
24063
|
+
return { kind: "serial" };
|
|
24064
|
+
if (descriptor.source === "builtin") {
|
|
24065
|
+
if (CONCURRENCY_SAFE_BUILTINS.has(descriptor.canonicalName))
|
|
24066
|
+
return { kind: "concurrent" };
|
|
24067
|
+
if (descriptor.canonicalName === "Bash" && bash !== undefined) {
|
|
24068
|
+
const command = typeof input === "object" && input !== null ? input.command : undefined;
|
|
24069
|
+
const escapes = typeof input === "object" && input !== null && input.dangerouslyDisableSandbox === true;
|
|
24070
|
+
if (typeof command === "string" && !escapes && isBashCommandReadOnly(command, bash))
|
|
24071
|
+
return { kind: "concurrent" };
|
|
24072
|
+
}
|
|
24073
|
+
return { kind: "serial" };
|
|
24074
|
+
}
|
|
24075
|
+
if (descriptor.source === "mcp" || descriptor.source === "sdk" || descriptor.source === "plugin") {
|
|
24076
|
+
if (descriptor.annotations?.readOnlyHint === true)
|
|
24077
|
+
return { kind: "concurrent" };
|
|
24078
|
+
if (descriptor.concurrencyLane !== undefined)
|
|
24079
|
+
return { kind: "lane", lane: descriptor.concurrencyLane };
|
|
24080
|
+
}
|
|
24081
|
+
return { kind: "serial" };
|
|
24082
|
+
}
|
|
24083
|
+
var SDK_MCP_CANCEL_GRACE_MS = 5000;
|
|
24084
|
+
function createToolLaneTails() {
|
|
24085
|
+
return new Map;
|
|
24086
|
+
}
|
|
24087
|
+
function enterToolLane(lanes, lane, tail) {
|
|
24088
|
+
const previous = lanes.get(lane);
|
|
24089
|
+
const chained = previous === undefined ? tail : Promise.all([previous, tail]).then(() => {
|
|
24090
|
+
return;
|
|
24091
|
+
});
|
|
24092
|
+
lanes.set(lane, chained);
|
|
24093
|
+
chained.finally(() => {
|
|
24094
|
+
if (lanes.get(lane) === chained)
|
|
24095
|
+
lanes.delete(lane);
|
|
24096
|
+
});
|
|
24097
|
+
return previous;
|
|
24098
|
+
}
|
|
24099
|
+
|
|
24100
|
+
// src/tools/read-state.ts
|
|
24101
|
+
import { isAbsolute as isAbsolute6, join as join25, normalize as normalize3 } from "node:path";
|
|
24102
|
+
function createSessionReadState(opts) {
|
|
24103
|
+
const cwd = opts.cwd;
|
|
24104
|
+
const reads = new Map;
|
|
24105
|
+
function canonicalKey(p) {
|
|
24106
|
+
const abs = isAbsolute6(p) ? p : join25(cwd, p);
|
|
24107
|
+
return resolveRealTarget(normalize3(abs));
|
|
24108
|
+
}
|
|
24109
|
+
return {
|
|
24110
|
+
recordRead(p, o) {
|
|
24111
|
+
reads.set(canonicalKey(p), { complete: o.complete, mtimeMs: o.mtimeMs });
|
|
24112
|
+
},
|
|
24113
|
+
lookup(p) {
|
|
24114
|
+
return reads.get(canonicalKey(p));
|
|
24115
|
+
}
|
|
24116
|
+
};
|
|
24117
|
+
}
|
|
24118
|
+
|
|
24119
|
+
// src/provider/lean-prompt.ts
|
|
24120
|
+
var FULL_PROMPT_OPUS_MINORS = new Set(["0", "1", "5", "6", "7"]);
|
|
24121
|
+
function claudeModelTakesFullPrompt(modelKey) {
|
|
24122
|
+
const id = modelKey.slice(modelKey.lastIndexOf("/") + 1).toLowerCase();
|
|
24123
|
+
if (id.includes("claude-3-") || id.includes("haiku") || id.includes("sonnet"))
|
|
24124
|
+
return true;
|
|
24125
|
+
const opus = /^claude-opus-4(?:[-.](\d{1,2}))?(?:-\d{8})?$/.exec(id);
|
|
24126
|
+
if (opus === null)
|
|
24127
|
+
return false;
|
|
24128
|
+
return FULL_PROMPT_OPUS_MINORS.has(opus[1] ?? "0");
|
|
24129
|
+
}
|
|
24130
|
+
|
|
24131
|
+
// src/provider/first-party.ts
|
|
24132
|
+
var FIRST_PARTY_ANTHROPIC_PROVIDER_IDS = new Set(["anthropic", "console"]);
|
|
24133
|
+
function isFirstPartyAnthropic(providerId) {
|
|
24134
|
+
return providerId !== undefined && FIRST_PARTY_ANTHROPIC_PROVIDER_IDS.has(providerId);
|
|
24135
|
+
}
|
|
24136
|
+
|
|
24137
|
+
// src/engine.ts
|
|
24138
|
+
var SKILL_TOOL_ADVERTISED_NAME = "Skill";
|
|
24139
|
+
function undeclaredToolDefinitionsText(definitions) {
|
|
24140
|
+
if (definitions.length === 0)
|
|
24141
|
+
return "";
|
|
24142
|
+
const lines = definitions.map((d) => `<function>${JSON.stringify({ name: d.name, description: d.description, parameters: d.inputSchema })}</function>`);
|
|
24143
|
+
return `
|
|
24144
|
+
|
|
24145
|
+
These loaded tools are not in this conversation's tool list, but you can call them by name; each one's input must match its parameters schema:
|
|
24146
|
+
<functions>
|
|
24147
|
+
${lines.join(`
|
|
24148
|
+
`)}
|
|
24149
|
+
</functions>`;
|
|
24150
|
+
}
|
|
24151
|
+
function providerMessageContentToText(content) {
|
|
24152
|
+
if (typeof content === "string")
|
|
24153
|
+
return content;
|
|
24154
|
+
return content.map((block) => {
|
|
24155
|
+
if (block.type === "text")
|
|
24156
|
+
return block.text;
|
|
24157
|
+
if (block.type === "tool_use")
|
|
24158
|
+
return `[called ${block.name}]`;
|
|
24159
|
+
if (block.type === "tool_reference")
|
|
24160
|
+
return `[tools now callable: ${block.tool_names.join(", ")}]`;
|
|
24161
|
+
if (block.type === "thinking")
|
|
24162
|
+
return `[thinking: ${block.thinking.length} chars]`;
|
|
24163
|
+
if (block.type === "redacted_thinking")
|
|
24164
|
+
return "[redacted thinking]";
|
|
24165
|
+
if (block.type === "image")
|
|
24166
|
+
return `[image: ${block.source.media_type}]`;
|
|
24167
|
+
return typeof block.content === "string" ? block.content : providerMessageContentToText(block.content);
|
|
24168
|
+
}).join(`
|
|
24169
|
+
`);
|
|
24170
|
+
}
|
|
24171
|
+
function isPerMessageEffortRejection(err) {
|
|
24172
|
+
if (!isProviderTurnError(err) || err.status !== 400)
|
|
24173
|
+
return false;
|
|
24174
|
+
return /mid-conversation-output-config|per-turn effort|configuration_update/i.test(err instanceof Error ? err.message : "");
|
|
24175
|
+
}
|
|
24176
|
+
function isToolChangeRejection(err, carriedChangeMessage = false) {
|
|
24177
|
+
if (!isProviderTurnError(err) || err.status !== 400 && err.status !== 422)
|
|
24178
|
+
return false;
|
|
24179
|
+
if (carriedChangeMessage && /system message|role.{0,4}system|must (immediately )?(follow|precede)|end the array|paused assistant turn/i.test(err instanceof Error ? err.message : ""))
|
|
24180
|
+
return true;
|
|
24181
|
+
return /mid-conversation-tool-changes|inline-tools|tool_addition|tool_removal|tool_definition|tool_reference_unresolved|tool_name_conflict|available_tools_limit_exceeded|cannot yet be defined in a message|additional_tools/i.test(err instanceof Error ? err.message : "");
|
|
24182
|
+
}
|
|
24183
|
+
function isToolSearchRejection(err) {
|
|
24184
|
+
if (!isProviderTurnError(err) || err.status !== 400 && err.status !== 422)
|
|
24185
|
+
return false;
|
|
24186
|
+
const text = err instanceof Error ? err.message : "";
|
|
24187
|
+
if (errorParam(text)?.startsWith("tool_choice") === true)
|
|
24188
|
+
return false;
|
|
24189
|
+
return /tool_search|defer_loading|namespace/i.test(text);
|
|
24190
|
+
}
|
|
24191
|
+
function errorParam(text) {
|
|
24192
|
+
return /"param"\s*:\s*"([^"]*)"/.exec(text)?.[1];
|
|
24193
|
+
}
|
|
24194
|
+
function isAllowedToolsRejection(err) {
|
|
24195
|
+
if (!isProviderTurnError(err) || err.status !== 400 && err.status !== 422)
|
|
24196
|
+
return false;
|
|
24197
|
+
const text = err instanceof Error ? err.message : "";
|
|
24198
|
+
const param = errorParam(text);
|
|
24199
|
+
if (param !== undefined)
|
|
24200
|
+
return param.startsWith("tool_choice");
|
|
24201
|
+
return /allowed_tools|tool_choice|tool_search/i.test(text);
|
|
24202
|
+
}
|
|
24203
|
+
function promptCacheTtlFor(config) {
|
|
24204
|
+
return config.promptCacheTtl === "1h" ? "1h" : "5m";
|
|
24205
|
+
}
|
|
24206
|
+
function promptCacheKeyFor(config) {
|
|
24207
|
+
return config.agentId === undefined ? config.sessionId : `${config.sessionId}:${config.agentId}`;
|
|
24208
|
+
}
|
|
24209
|
+
function withoutLoadedTools(block) {
|
|
24210
|
+
const { loadedTools: _loaded, loadedToolDefinitions: _definitions, ...rest } = block;
|
|
24211
|
+
return rest;
|
|
24212
|
+
}
|
|
24213
|
+
function hostToolResultContent(blocks, icons) {
|
|
24214
|
+
return withHostSiteIcons(toolResultsForHost(blocks.map((b) => b.type === "tool_result" && b.loadedTools !== undefined ? withoutLoadedTools(b) : b)), icons);
|
|
24215
|
+
}
|
|
24216
|
+
function mcpNamespaceFor(advertisedName, server) {
|
|
24217
|
+
if (server === undefined)
|
|
24218
|
+
return;
|
|
24219
|
+
const namespace = `mcp__${server}`;
|
|
24220
|
+
return advertisedName.startsWith(`${namespace}__`) && advertisedName.length > namespace.length + 2 ? namespace : undefined;
|
|
24221
|
+
}
|
|
24222
|
+
var NAMED_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
|
|
24223
|
+
function isNamedEffort(value) {
|
|
24224
|
+
return typeof value === "string" && NAMED_EFFORTS.includes(value);
|
|
24225
|
+
}
|
|
24226
|
+
|
|
22493
24227
|
class ProviderTurnError extends Error {
|
|
22494
24228
|
winterProviderFailure = true;
|
|
22495
24229
|
providerCode;
|
|
@@ -22647,7 +24381,7 @@ function raceInterrupt(p, interrupted) {
|
|
|
22647
24381
|
}
|
|
22648
24382
|
function buildBaselineDenyRules(resolvedWinterHome, brand, resolvedStoreHome) {
|
|
22649
24383
|
const absolute = [];
|
|
22650
|
-
const defaultHome = resolve12(
|
|
24384
|
+
const defaultHome = resolve12(join26(homedir(), (brand ?? WINTER_BRAND24).homeDirName));
|
|
22651
24385
|
if (resolvedWinterHome !== undefined && resolve12(resolvedWinterHome) !== defaultHome) {
|
|
22652
24386
|
const root = `/${escapeFileRulePathSegment(resolve12(resolvedWinterHome))}`;
|
|
22653
24387
|
for (const dir of ["run"]) {
|
|
@@ -22796,6 +24530,7 @@ async function runEngine2(opts) {
|
|
|
22796
24530
|
}
|
|
22797
24531
|
}
|
|
22798
24532
|
async function runEngineBody(opts, facetDisposers, registerBackgroundShellSweep) {
|
|
24533
|
+
const sessionLaneTails = opts.toolLaneTails ?? createToolLaneTails();
|
|
22799
24534
|
const {
|
|
22800
24535
|
config,
|
|
22801
24536
|
input,
|
|
@@ -23233,6 +24968,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
23233
24968
|
opts.onChildRosterReady?.(() => childRoster);
|
|
23234
24969
|
opts.onForegroundChildrenReady?.(() => [...foregroundChildren]);
|
|
23235
24970
|
const removeChildRosterSource = ensureDefaultMessagingRuntimeRegistered().addChildRosterSource(() => childRoster);
|
|
24971
|
+
const removeHostMessagingPort = config.hostMessaging === true && config.agentId === undefined ? getDefaultMessagingRuntime()?.registerHostMessagingPort?.(config.sessionId, createHostMessagingPort(bridge)) : undefined;
|
|
23236
24972
|
const sandboxSettingsForSession = config.sandbox ?? DEFAULT_SANDBOX_SETTINGS;
|
|
23237
24973
|
const bashPostureFor = (call, dangerouslyDisableSandbox) => {
|
|
23238
24974
|
const command = call.input["command"];
|
|
@@ -23782,16 +25518,17 @@ ${escapeAttributionText(msg.body)}
|
|
|
23782
25518
|
continue;
|
|
23783
25519
|
const toolDefs = serverCfg.tools;
|
|
23784
25520
|
const toolNames = serverCfg.toolNames;
|
|
23785
|
-
|
|
25521
|
+
const toolLanes = serverCfg.toolLanes;
|
|
25522
|
+
registerMcpServerTools(serverName, toolDefs, { deferredDefault: true, ...toolNames !== undefined ? { toolNames } : {}, ...toolLanes !== undefined ? { toolLanes } : {} });
|
|
23786
25523
|
sdkMcpServerNames.push(serverName);
|
|
23787
25524
|
const perServerTimeoutMs = serverCfg.timeout;
|
|
23788
25525
|
for (const tool of toolDefs) {
|
|
23789
25526
|
const canonicalName = toolNames !== undefined && Object.hasOwn(toolNames, tool.name) ? toolNames[tool.name] : `mcp__${serverName}__${tool.name}`;
|
|
23790
25527
|
replaceExecutor(canonicalName, {
|
|
23791
|
-
async execute(input) {
|
|
25528
|
+
async execute(input, ctx) {
|
|
23792
25529
|
const timeoutMs = perServerTimeoutMs ?? mcpEnvConfig.toolTimeoutMs ?? 120000;
|
|
23793
25530
|
try {
|
|
23794
|
-
const result = await bridge.request("sdk_mcp_call", { server: serverName, tool: tool.name, arguments: input && typeof input === "object" ? input : {} }, { timeoutMs });
|
|
25531
|
+
const result = await bridge.request("sdk_mcp_call", { server: serverName, tool: tool.name, arguments: input && typeof input === "object" ? input : {} }, { timeoutMs, ...ctx?.signal !== undefined ? { signal: ctx.signal, cancelGraceMs: SDK_MCP_CANCEL_GRACE_MS } : {} });
|
|
23795
25532
|
const text = (result.content ?? []).map((block) => typeof block.text === "string" ? block.text : JSON.stringify(block)).join(`
|
|
23796
25533
|
`);
|
|
23797
25534
|
return { output: text, ...result.isError === true ? { isError: true } : {} };
|
|
@@ -26077,34 +27814,77 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26077
27814
|
...callRecord.moved !== undefined ? { nativeState: callRecord.moved.nativeState } : {},
|
|
26078
27815
|
...generationEffort ?? {}
|
|
26079
27816
|
});
|
|
26080
|
-
const
|
|
27817
|
+
const slots = turn.calls.map(() => {
|
|
27818
|
+
return;
|
|
27819
|
+
});
|
|
26081
27820
|
const hostSiteIcons = new Map;
|
|
26082
27821
|
let toolThrowText = null;
|
|
27822
|
+
const throwTextByIndex = new Map;
|
|
27823
|
+
let lateFailure;
|
|
26083
27824
|
let structuredTerminated = false;
|
|
26084
27825
|
const truncatedByOutputLimit = turn.stopReason === "max_tokens";
|
|
26085
|
-
|
|
27826
|
+
const sentToHost = new Set;
|
|
27827
|
+
const sendResultToHost = (index) => {
|
|
27828
|
+
const block = slots[index];
|
|
27829
|
+
if (block === undefined || sentToHost.has(index))
|
|
27830
|
+
return;
|
|
27831
|
+
sentToHost.add(index);
|
|
27832
|
+
output.write({ type: "data", message: { type: "user", message: { content: hostToolResultContent([block], hostSiteIcons) } } });
|
|
27833
|
+
};
|
|
27834
|
+
const inFlight = new Set;
|
|
27835
|
+
const deferredPostToolEffects = new Map;
|
|
27836
|
+
const applyFinishedStops = () => {
|
|
27837
|
+
if (turnStop.request !== undefined)
|
|
27838
|
+
return;
|
|
27839
|
+
for (const index of [...deferredPostToolEffects.keys()].sort((a, b) => a - b)) {
|
|
27840
|
+
const stop = deferredPostToolEffects.get(index).stop;
|
|
27841
|
+
if (stop !== undefined) {
|
|
27842
|
+
turnStop.request = stop;
|
|
27843
|
+
return;
|
|
27844
|
+
}
|
|
27845
|
+
}
|
|
27846
|
+
};
|
|
27847
|
+
const settleConcurrent = async () => {
|
|
27848
|
+
while (inFlight.size > 0)
|
|
27849
|
+
await Promise.all([...inFlight]);
|
|
27850
|
+
for (const index of [...deferredPostToolEffects.keys()].sort((a, b) => a - b))
|
|
27851
|
+
deferredPostToolEffects.get(index).apply();
|
|
27852
|
+
deferredPostToolEffects.clear();
|
|
27853
|
+
};
|
|
27854
|
+
const deferPostToolEffects = (index, composite, hookName, apply) => {
|
|
27855
|
+
const prevent = composite.preventContinuation;
|
|
27856
|
+
deferredPostToolEffects.set(index, {
|
|
27857
|
+
apply,
|
|
27858
|
+
...prevent !== undefined ? { stop: { hookName, ...prevent.reason !== undefined ? { reason: prevent.reason } : {} } } : {}
|
|
27859
|
+
});
|
|
27860
|
+
};
|
|
27861
|
+
const processCall = async (emitted, index, mode) => {
|
|
27862
|
+
const record = (block) => {
|
|
27863
|
+
slots[index] = block;
|
|
27864
|
+
};
|
|
26086
27865
|
const resolvedName = currentToolName(emitted.name);
|
|
26087
27866
|
const call = resolvedName !== emitted.name ? { ...emitted, name: resolvedName } : emitted;
|
|
27867
|
+
applyFinishedStops();
|
|
26088
27868
|
const stoppedBy = currentTurnStop();
|
|
26089
27869
|
if (stoppedBy !== undefined) {
|
|
26090
|
-
|
|
26091
|
-
|
|
27870
|
+
record({ type: "tool_result", tool_use_id: call.id, content: `[not executed: the ${stoppedBy.hookName} hook stopped the turn]`, error: true });
|
|
27871
|
+
return "next";
|
|
26092
27872
|
}
|
|
26093
27873
|
if (truncatedByOutputLimit) {
|
|
26094
|
-
|
|
26095
|
-
|
|
27874
|
+
record({ type: "tool_result", tool_use_id: call.id, content: OUTPUT_LIMIT_TRUNCATED_CALL_TEXT, is_error: true });
|
|
27875
|
+
return "next";
|
|
26096
27876
|
}
|
|
26097
27877
|
if (structuredOutputActive && structuredOutput !== undefined && call.name === STRUCTURED_OUTPUT_TOOL_NAME) {
|
|
26098
27878
|
structuredOutputAttempts++;
|
|
26099
27879
|
const validation = structuredOutput.validate(outputFormatSchema, call.input);
|
|
26100
27880
|
if (validation.ok) {
|
|
26101
|
-
|
|
27881
|
+
record({ type: "tool_result", tool_use_id: call.id, content: "Structured output accepted." });
|
|
26102
27882
|
finalResult = { type: "result", subtype: "success", is_error: false, structured_output: validation.value };
|
|
26103
27883
|
structuredTerminated = true;
|
|
26104
|
-
|
|
27884
|
+
return "stop";
|
|
26105
27885
|
}
|
|
26106
27886
|
if (structuredOutputAttempts >= maxStructuredOutputAttempts) {
|
|
26107
|
-
|
|
27887
|
+
record({ type: "tool_result", tool_use_id: call.id, content: `Structured output validation failed: ${validation.errors.join("; ")}`, error: true });
|
|
26108
27888
|
finalResult = {
|
|
26109
27889
|
type: "result",
|
|
26110
27890
|
subtype: "error_max_structured_output_retries",
|
|
@@ -26113,26 +27893,26 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26113
27893
|
terminal_reason: "structured_output_retry_exhausted"
|
|
26114
27894
|
};
|
|
26115
27895
|
structuredTerminated = true;
|
|
26116
|
-
|
|
27896
|
+
return "stop";
|
|
26117
27897
|
}
|
|
26118
|
-
|
|
26119
|
-
|
|
27898
|
+
record({ type: "tool_result", tool_use_id: call.id, content: `Structured output validation failed: ${validation.errors.join("; ")}. Call ${STRUCTURED_OUTPUT_TOOL_NAME} again with a corrected value.`, error: true });
|
|
27899
|
+
return "next";
|
|
26120
27900
|
}
|
|
26121
27901
|
const callMcpServer = mcpServerIdentityForCall(call.name);
|
|
26122
27902
|
try {
|
|
26123
27903
|
const availabilityDescriptor = getRegisteredTool(call.name)?.descriptor;
|
|
26124
27904
|
if (availabilityDescriptor !== undefined && !isToolAvailable(availabilityDescriptor, { ...advertisedCfg, mode: policyStateStore.getState().mode })) {
|
|
26125
|
-
|
|
27905
|
+
record({
|
|
26126
27906
|
type: "tool_result",
|
|
26127
27907
|
tool_use_id: call.id,
|
|
26128
27908
|
content: `'${call.name}' is not available in this session's current configuration (WS-06 §1.5 availability) -- it is registered but excluded here, so it was not executed`,
|
|
26129
27909
|
error: true
|
|
26130
27910
|
});
|
|
26131
|
-
|
|
27911
|
+
return "next";
|
|
26132
27912
|
}
|
|
26133
27913
|
if (isDeferredAndUnloaded(call.name)) {
|
|
26134
27914
|
const excludedReason = aliasExclusionLive(call.name);
|
|
26135
|
-
|
|
27915
|
+
record(excludedReason !== undefined ? {
|
|
26136
27916
|
type: "tool_result",
|
|
26137
27917
|
tool_use_id: call.id,
|
|
26138
27918
|
content: `'${call.name}' is not available in this session: ${excludedReason}. It cannot be loaded with ToolSearch either -- the same exclusion removes it from the searchable pool.`,
|
|
@@ -26143,7 +27923,7 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26143
27923
|
content: `'${call.name}' is a deferred tool that has not been loaded this session yet -- use ToolSearch to select it before calling it (WS-09 §8.5)`,
|
|
26144
27924
|
loadFirst: true
|
|
26145
27925
|
});
|
|
26146
|
-
|
|
27926
|
+
return "next";
|
|
26147
27927
|
}
|
|
26148
27928
|
const permissionInput = typeof call.input === "object" && call.input !== null ? call.input : {};
|
|
26149
27929
|
const evalCtxForIdentity = makeEvalCtx();
|
|
@@ -26159,8 +27939,8 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26159
27939
|
const declaredIdentity = declaredMcpIdentity(call.name);
|
|
26160
27940
|
const notOffered = [...aliasPermissionIdentities(call.name, config.toolAliases, sessionBrand), ...declaredIdentity !== undefined ? [declaredIdentity] : []].some(probeRule("deny")) ? undefined : toolNotOfferedRefusal(call.name);
|
|
26161
27941
|
if (notOffered !== undefined) {
|
|
26162
|
-
|
|
26163
|
-
|
|
27942
|
+
record({ type: "tool_result", tool_use_id: call.id, content: notOffered, is_error: true });
|
|
27943
|
+
return "next";
|
|
26164
27944
|
}
|
|
26165
27945
|
const identityProbes = {
|
|
26166
27946
|
deniedByRule: probeRule("deny"),
|
|
@@ -26179,12 +27959,12 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26179
27959
|
const decisionRaced = await raceInterrupt(evaluateWithFreshPolicy(permissionCall), interruptSignal);
|
|
26180
27960
|
if (decisionRaced.kind === "interrupted") {
|
|
26181
27961
|
interrupted = true;
|
|
26182
|
-
|
|
27962
|
+
return "stop";
|
|
26183
27963
|
}
|
|
26184
27964
|
const decision = decisionRaced.value;
|
|
26185
27965
|
takeGatingHookComposites(call.id, call.name);
|
|
26186
27966
|
const denyCall = async (message, mechanism, ruleRef) => {
|
|
26187
|
-
|
|
27967
|
+
record({ type: "tool_result", tool_use_id: call.id, content: message, denied: true });
|
|
26188
27968
|
turnPermissionDenials.push({ tool_name: call.name, tool_use_id: call.id, tool_input: permissionCall.input });
|
|
26189
27969
|
output.write({
|
|
26190
27970
|
type: "data",
|
|
@@ -26212,7 +27992,7 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26212
27992
|
const deferMessage = decision.message ?? "a PreToolUse hook deferred this call for durable approval";
|
|
26213
27993
|
if (!approvalStore) {
|
|
26214
27994
|
await denyCall(`Denied: cannot defer -- no durable approval store is available for this session (${deferMessage})`, "hook");
|
|
26215
|
-
|
|
27995
|
+
return "next";
|
|
26216
27996
|
}
|
|
26217
27997
|
const requestId = randomUUID18();
|
|
26218
27998
|
const executedInput = decision.transformedInput ?? permissionCall.input;
|
|
@@ -26239,9 +28019,9 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26239
28019
|
} catch (err) {
|
|
26240
28020
|
const text = err instanceof Error ? err.message : String(err);
|
|
26241
28021
|
await denyCall(`Denied: failed to persist the durable approval record (${text})`, "hook");
|
|
26242
|
-
|
|
28022
|
+
return "next";
|
|
26243
28023
|
}
|
|
26244
|
-
|
|
28024
|
+
record({ type: "tool_result", tool_use_id: call.id, content: "[deferred]", deferred: true });
|
|
26245
28025
|
output.write({
|
|
26246
28026
|
type: "data",
|
|
26247
28027
|
message: {
|
|
@@ -26256,7 +28036,7 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26256
28036
|
uuid: randomUUID18()
|
|
26257
28037
|
}
|
|
26258
28038
|
});
|
|
26259
|
-
|
|
28039
|
+
return "next";
|
|
26260
28040
|
}
|
|
26261
28041
|
if (decision.decision !== "allow") {
|
|
26262
28042
|
const deniedMessage = decision.message ?? "Permission denied";
|
|
@@ -26264,9 +28044,9 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26264
28044
|
if (decision.interrupt === true) {
|
|
26265
28045
|
interrupted = true;
|
|
26266
28046
|
interruptCurrentTurn.current?.();
|
|
26267
|
-
|
|
28047
|
+
return "stop";
|
|
26268
28048
|
}
|
|
26269
|
-
|
|
28049
|
+
return "next";
|
|
26270
28050
|
}
|
|
26271
28051
|
if (decision.updatedPermissions) {
|
|
26272
28052
|
for (const update of decision.updatedPermissions) {
|
|
@@ -26297,18 +28077,47 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26297
28077
|
}
|
|
26298
28078
|
}
|
|
26299
28079
|
}
|
|
26300
|
-
|
|
26301
|
-
|
|
26302
|
-
|
|
26303
|
-
|
|
28080
|
+
if (interrupted || toolThrowText !== null || lateFailure !== undefined)
|
|
28081
|
+
return "stop";
|
|
28082
|
+
applyFinishedStops();
|
|
28083
|
+
const stoppedBeforeStart = currentTurnStop();
|
|
28084
|
+
if (stoppedBeforeStart !== undefined) {
|
|
28085
|
+
record({ type: "tool_result", tool_use_id: call.id, content: `[not executed: the ${stoppedBeforeStart.hookName} hook stopped the turn]`, error: true });
|
|
28086
|
+
return "next";
|
|
28087
|
+
}
|
|
28088
|
+
const laneTurn = mode.enterLane?.();
|
|
28089
|
+
mode.readyToExecute?.();
|
|
28090
|
+
if (laneTurn !== undefined) {
|
|
28091
|
+
const turnOfLane = await raceInterrupt(laneTurn, interruptSignal);
|
|
28092
|
+
if (turnOfLane.kind === "interrupted") {
|
|
28093
|
+
interrupted = true;
|
|
28094
|
+
return "stop";
|
|
28095
|
+
}
|
|
28096
|
+
if (interrupted || toolThrowText !== null || lateFailure !== undefined)
|
|
28097
|
+
return "stop";
|
|
28098
|
+
applyFinishedStops();
|
|
28099
|
+
const stoppedInLane = currentTurnStop();
|
|
28100
|
+
if (stoppedInLane !== undefined) {
|
|
28101
|
+
record({ type: "tool_result", tool_use_id: call.id, content: `[not executed: the ${stoppedInLane.hookName} hook stopped the turn]`, error: true });
|
|
28102
|
+
return "next";
|
|
28103
|
+
}
|
|
28104
|
+
}
|
|
28105
|
+
if (!mode.concurrent)
|
|
28106
|
+
toolReferenceCollector = [];
|
|
28107
|
+
const execution = tools.execute(executedCall, { signal: turnAbort.signal, ...decision.explicitApproval !== undefined ? { explicitApproval: decision.explicitApproval } : {} });
|
|
28108
|
+
mode.onExecution?.(execution);
|
|
28109
|
+
const raced = await raceInterrupt(execution, interruptSignal);
|
|
28110
|
+
const loadedTools = mode.concurrent ? [] : advertisedNamesFor(toolReferenceCollector);
|
|
28111
|
+
if (!mode.concurrent)
|
|
28112
|
+
toolReferenceCollector = undefined;
|
|
26304
28113
|
const loadedToolDefinitions = loadedDefinitionsFor(loadedTools);
|
|
26305
28114
|
const forkDefinitionsText = exactRequestLayout !== undefined ? forkLoadedDefinitionsText(loadedToolDefinitions) : "";
|
|
26306
28115
|
if (raced.kind === "interrupted") {
|
|
26307
28116
|
interrupted = true;
|
|
26308
|
-
|
|
28117
|
+
return "stop";
|
|
26309
28118
|
}
|
|
26310
28119
|
const resultBlocksOfCall = raced.value.blocks;
|
|
26311
|
-
|
|
28120
|
+
record({
|
|
26312
28121
|
type: "tool_result",
|
|
26313
28122
|
tool_use_id: call.id,
|
|
26314
28123
|
content: resultBlocksOfCall !== undefined ? forkDefinitionsText.length > 0 ? [...resultBlocksOfCall, { type: "text", text: forkDefinitionsText.trimStart() }] : [...resultBlocksOfCall] : forkDefinitionsText.length > 0 ? `${raced.value.output}${forkDefinitionsText}` : raced.value.output,
|
|
@@ -26331,57 +28140,154 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26331
28140
|
input: executedCall.input,
|
|
26332
28141
|
payload: { tool_response: raced.value.output }
|
|
26333
28142
|
});
|
|
26334
|
-
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
28143
|
+
const postHookName = `${raced.value.isError === true ? "PostToolUseFailure" : "PostToolUse"}:${call.name}`;
|
|
28144
|
+
const applyPostToolEffects = () => {
|
|
28145
|
+
if (postToolUseHookOutcome.classifierContext !== undefined) {
|
|
28146
|
+
accumulatedClassifierContext.push(...postToolUseHookOutcome.classifierContext);
|
|
28147
|
+
}
|
|
28148
|
+
absorbHookComposite(postToolUseHookOutcome, { hookName: postHookName, context: true, toolUseID: call.id });
|
|
28149
|
+
};
|
|
28150
|
+
if (mode.concurrent)
|
|
28151
|
+
deferPostToolEffects(index, postToolUseHookOutcome, postHookName, applyPostToolEffects);
|
|
28152
|
+
else
|
|
28153
|
+
applyPostToolEffects();
|
|
26338
28154
|
if (postToolUseHookOutcome.transformedOutput !== undefined) {
|
|
26339
|
-
const
|
|
26340
|
-
if (
|
|
26341
|
-
|
|
28155
|
+
const own = slots[index];
|
|
28156
|
+
if (own !== undefined && own.type === "tool_result" && own.tool_use_id === call.id) {
|
|
28157
|
+
slots[index] = { ...own, content: toolOutputReplacement(postToolUseHookOutcome.transformedOutput) };
|
|
26342
28158
|
}
|
|
26343
28159
|
}
|
|
26344
28160
|
} catch (err) {
|
|
26345
28161
|
const text = err instanceof Error ? err.message : String(err);
|
|
26346
|
-
|
|
26347
|
-
toolThrowText
|
|
26348
|
-
|
|
28162
|
+
throwTextByIndex.set(index, text);
|
|
28163
|
+
if (toolThrowText === null) {
|
|
28164
|
+
finalResult = { type: "result", subtype: "error_during_execution", is_error: true, result: text };
|
|
28165
|
+
toolThrowText = text;
|
|
28166
|
+
}
|
|
28167
|
+
const failureOutcome = await fireObservationalHook("PostToolUseFailure", {
|
|
26349
28168
|
toolUseID: call.id,
|
|
26350
28169
|
toolName: postToolHookSubject("PostToolUseFailure", call.name),
|
|
26351
28170
|
...callMcpServer !== undefined ? { mcpServer: callMcpServer } : {},
|
|
26352
28171
|
input: typeof call.input === "object" && call.input !== null ? call.input : {},
|
|
26353
28172
|
payload: { error: text }
|
|
26354
|
-
})
|
|
28173
|
+
});
|
|
28174
|
+
const failureHookName = `PostToolUseFailure:${call.name}`;
|
|
28175
|
+
const applyFailureEffects = () => absorbHookComposite(failureOutcome, { hookName: failureHookName, context: true, toolUseID: call.id });
|
|
28176
|
+
if (mode.concurrent)
|
|
28177
|
+
deferPostToolEffects(index, failureOutcome, failureHookName, applyFailureEffects);
|
|
28178
|
+
else
|
|
28179
|
+
applyFailureEffects();
|
|
28180
|
+
return "stop";
|
|
28181
|
+
}
|
|
28182
|
+
return "next";
|
|
28183
|
+
};
|
|
28184
|
+
const bashReadOnlyContext = () => ({
|
|
28185
|
+
cwd: currentCwd,
|
|
28186
|
+
originalCwd: config.cwd,
|
|
28187
|
+
sandboxEnabled: sandboxSettingsForSession.enabled !== false && isSandboxAvailable()
|
|
28188
|
+
});
|
|
28189
|
+
for (let index = 0;index < turn.calls.length; index++) {
|
|
28190
|
+
const emitted = turn.calls[index];
|
|
28191
|
+
const scheduling = schedulingForCall(currentToolName(emitted.name), emitted.input, bashReadOnlyContext());
|
|
28192
|
+
if (scheduling.kind === "serial") {
|
|
28193
|
+
await settleConcurrent();
|
|
28194
|
+
} else {
|
|
28195
|
+
while (inFlight.size >= MAX_TOOL_CONCURRENCY)
|
|
28196
|
+
await Promise.race(inFlight);
|
|
28197
|
+
}
|
|
28198
|
+
if (interrupted || toolThrowText !== null || lateFailure !== undefined)
|
|
26355
28199
|
break;
|
|
28200
|
+
if (scheduling.kind === "serial") {
|
|
28201
|
+
try {
|
|
28202
|
+
const outcome = await processCall(emitted, index, { concurrent: false });
|
|
28203
|
+
sendResultToHost(index);
|
|
28204
|
+
if (outcome === "stop")
|
|
28205
|
+
break;
|
|
28206
|
+
} catch (error) {
|
|
28207
|
+
lateFailure = { error };
|
|
28208
|
+
break;
|
|
28209
|
+
}
|
|
28210
|
+
continue;
|
|
26356
28211
|
}
|
|
28212
|
+
let releaseLane;
|
|
28213
|
+
let laneExecution;
|
|
28214
|
+
const lane = scheduling.kind === "lane" ? scheduling.lane : undefined;
|
|
28215
|
+
const enterLane = lane !== undefined ? () => enterToolLane(sessionLaneTails, lane, new Promise((resolve) => releaseLane = resolve)) : undefined;
|
|
28216
|
+
let markReady;
|
|
28217
|
+
const ready = new Promise((resolve) => markReady = () => resolve("ready"));
|
|
28218
|
+
const settled = processCall(emitted, index, {
|
|
28219
|
+
concurrent: true,
|
|
28220
|
+
readyToExecute: markReady,
|
|
28221
|
+
...enterLane !== undefined ? { enterLane, onExecution: (execution) => {
|
|
28222
|
+
laneExecution = execution;
|
|
28223
|
+
} } : {}
|
|
28224
|
+
}).then((outcome) => {
|
|
28225
|
+
sendResultToHost(index);
|
|
28226
|
+
return outcome;
|
|
28227
|
+
}).catch((error) => {
|
|
28228
|
+
if (lateFailure === undefined)
|
|
28229
|
+
lateFailure = { error };
|
|
28230
|
+
return "stop";
|
|
28231
|
+
});
|
|
28232
|
+
const tracked = settled.finally(() => {
|
|
28233
|
+
inFlight.delete(tracked);
|
|
28234
|
+
const release = () => releaseLane?.();
|
|
28235
|
+
if (laneExecution !== undefined)
|
|
28236
|
+
laneExecution.then(release, release);
|
|
28237
|
+
else
|
|
28238
|
+
release();
|
|
28239
|
+
});
|
|
28240
|
+
inFlight.add(tracked);
|
|
28241
|
+
const first = await Promise.race([ready, settled]);
|
|
28242
|
+
if (first === "stop")
|
|
28243
|
+
break;
|
|
26357
28244
|
}
|
|
28245
|
+
await settleConcurrent();
|
|
26358
28246
|
if (toolThrowText !== null) {
|
|
26359
|
-
|
|
26360
|
-
|
|
26361
|
-
|
|
26362
|
-
|
|
26363
|
-
|
|
26364
|
-
|
|
28247
|
+
turn.calls.forEach((call, index) => {
|
|
28248
|
+
if (slots[index] === undefined)
|
|
28249
|
+
slots[index] = { type: "tool_result", tool_use_id: call.id, content: `[error: ${throwTextByIndex.get(index) ?? toolThrowText}]`, error: true };
|
|
28250
|
+
});
|
|
28251
|
+
}
|
|
28252
|
+
if (lateFailure !== undefined) {
|
|
28253
|
+
const failure = lateFailure.error;
|
|
28254
|
+
const text = failure instanceof Error ? failure.message : String(failure);
|
|
28255
|
+
turn.calls.forEach((call, index) => {
|
|
28256
|
+
if (slots[index] === undefined)
|
|
28257
|
+
slots[index] = { type: "tool_result", tool_use_id: call.id, content: `[error: ${text}]`, error: true };
|
|
28258
|
+
});
|
|
26365
28259
|
}
|
|
26366
28260
|
if (structuredTerminated) {
|
|
26367
|
-
|
|
26368
|
-
|
|
26369
|
-
|
|
26370
|
-
|
|
26371
|
-
}
|
|
26372
|
-
}
|
|
28261
|
+
turn.calls.forEach((call, index) => {
|
|
28262
|
+
if (slots[index] === undefined)
|
|
28263
|
+
slots[index] = { type: "tool_result", tool_use_id: call.id, content: "[not executed: the turn ended on a structured-output result]", error: true };
|
|
28264
|
+
});
|
|
26373
28265
|
}
|
|
26374
28266
|
if (interrupted) {
|
|
26375
|
-
|
|
26376
|
-
|
|
26377
|
-
|
|
26378
|
-
|
|
26379
|
-
|
|
28267
|
+
turn.calls.forEach((call, index) => {
|
|
28268
|
+
if (slots[index] === undefined)
|
|
28269
|
+
slots[index] = { type: "tool_result", tool_use_id: call.id, content: "[interrupted]", interrupted: true };
|
|
28270
|
+
});
|
|
28271
|
+
}
|
|
28272
|
+
turn.calls.forEach((call, index) => {
|
|
28273
|
+
if (slots[index] === undefined)
|
|
28274
|
+
slots[index] = { type: "tool_result", tool_use_id: call.id, content: "[not executed]", error: true };
|
|
28275
|
+
});
|
|
28276
|
+
const resultBlocks = slots;
|
|
28277
|
+
const unsent = resultBlocks.filter((_, index) => !sentToHost.has(index));
|
|
28278
|
+
if (unsent.length > 0) {
|
|
28279
|
+
try {
|
|
28280
|
+
output.write({ type: "data", message: { type: "user", message: { content: hostToolResultContent(unsent, hostSiteIcons) } } });
|
|
28281
|
+
} catch (error) {
|
|
28282
|
+
if (lateFailure === undefined)
|
|
28283
|
+
lateFailure = { error };
|
|
26380
28284
|
}
|
|
28285
|
+
resultBlocks.forEach((_, index) => sentToHost.add(index));
|
|
26381
28286
|
}
|
|
26382
|
-
output.write({ type: "data", message: { type: "user", message: { content: withHostSiteIcons(toolResultsForHost(resultBlocks.map((b) => b.type === "tool_result" && b.loadedTools !== undefined ? withoutLoadedTools(b) : b)), hostSiteIcons) } } });
|
|
26383
28287
|
messages.push({ role: "tool", content: resultBlocks });
|
|
26384
28288
|
await recordUser(resultBlocks);
|
|
28289
|
+
if (lateFailure !== undefined)
|
|
28290
|
+
throw lateFailure.error;
|
|
26385
28291
|
await flushPendingHookAttachments();
|
|
26386
28292
|
gatingHookComposites.clear();
|
|
26387
28293
|
if (finalResult)
|
|
@@ -26439,6 +28345,7 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26439
28345
|
if (config.agentId === undefined)
|
|
26440
28346
|
clearNotificationQueue(config.sessionId);
|
|
26441
28347
|
removeChildRosterSource();
|
|
28348
|
+
removeHostMessagingPort?.();
|
|
26442
28349
|
facetSelfPeer?.();
|
|
26443
28350
|
facetNoticeForwarder?.();
|
|
26444
28351
|
disposeSessionMcpLifecycle?.();
|
|
@@ -26457,7 +28364,7 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
26457
28364
|
}
|
|
26458
28365
|
|
|
26459
28366
|
// src/provider/mock.ts
|
|
26460
|
-
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
28367
|
+
import { createHash as createHash2, randomUUID as randomUUID19 } from "node:crypto";
|
|
26461
28368
|
|
|
26462
28369
|
// src/compaction/summarizer.ts
|
|
26463
28370
|
var WINTER_SUMMARY_INSTRUCTION = "You are compacting a conversation so that it can continue with less context. " + "Summarize the conversation above as clear declarative statements of what is true and what was decided -- " + "not as a paraphrase of the most recent message and not as an acknowledgement. " + "Preserve every specific verbatim: numbers, names, file paths, identifiers, exact values, and any decision that was made or reversed. " + "Record work still outstanding as well as work completed. " + "Be concise but complete; never drop a fact to save words. " + "Output the summary text and nothing else.";
|
|
@@ -26642,7 +28549,7 @@ var TEST_PROVIDER_NAMES = new Set([
|
|
|
26642
28549
|
"readimage",
|
|
26643
28550
|
"calls"
|
|
26644
28551
|
]);
|
|
26645
|
-
var CALL_LINE = /^CALL\s+(\S+)(?:\s+(.*))?$/;
|
|
28552
|
+
var CALL_LINE = /^(\+?)CALL\s+(\S+)(?:\s+(.*))?$/;
|
|
26646
28553
|
function promptedCallsProvider() {
|
|
26647
28554
|
return {
|
|
26648
28555
|
async generate({ messages }) {
|
|
@@ -26657,13 +28564,21 @@ function promptedCallsProvider() {
|
|
|
26657
28564
|
}
|
|
26658
28565
|
if (at < 0)
|
|
26659
28566
|
return { kind: "text", text: "[]" };
|
|
26660
|
-
const
|
|
26661
|
-
|
|
28567
|
+
const rounds = [];
|
|
28568
|
+
for (const m of userMessageText(messages[at]).split(`
|
|
28569
|
+
`).map((line) => CALL_LINE.exec(line.trim())).filter((m) => m !== null)) {
|
|
28570
|
+
const call = { name: m[2], input: m[3] !== undefined && m[3].trim() !== "" ? JSON.parse(m[3]) : {} };
|
|
28571
|
+
if (m[1] === "+" && rounds.length > 0)
|
|
28572
|
+
rounds[rounds.length - 1].push(call);
|
|
28573
|
+
else
|
|
28574
|
+
rounds.push([call]);
|
|
28575
|
+
}
|
|
26662
28576
|
const after = messages.slice(at + 1);
|
|
26663
28577
|
const made = after.filter((m) => m.role === "assistant" && Array.isArray(m.content) && m.content.some((b) => b.type === "tool_use")).length;
|
|
26664
|
-
if (made <
|
|
26665
|
-
const
|
|
26666
|
-
|
|
28578
|
+
if (made < rounds.length) {
|
|
28579
|
+
const script = createHash2("sha256").update(userMessageText(messages[at])).digest("hex").slice(0, 6);
|
|
28580
|
+
const round = rounds[made];
|
|
28581
|
+
return { kind: "tool_use", calls: round.map((next, i) => ({ id: `calls-${script}-${at + 1}-${made + 1}${i === 0 ? "" : `-${i + 1}`}`, name: next.name, input: next.input })) };
|
|
26667
28582
|
}
|
|
26668
28583
|
const results = after.flatMap((m) => Array.isArray(m.content) ? m.content : []).filter((b) => b.type === "tool_result");
|
|
26669
28584
|
const named = new Map(after.flatMap((m) => m.role === "assistant" && Array.isArray(m.content) ? m.content : []).flatMap((b) => b.type === "tool_use" ? [[b.id, b.name]] : []));
|
|
@@ -27292,7 +29207,7 @@ function rebuildProviderMessages2(entries) {
|
|
|
27292
29207
|
|
|
27293
29208
|
// src/store/dialect.ts
|
|
27294
29209
|
import { randomUUID as randomUUID20 } from "node:crypto";
|
|
27295
|
-
import { join as
|
|
29210
|
+
import { join as join27 } from "node:path";
|
|
27296
29211
|
import {
|
|
27297
29212
|
WinterCompatibilitySessionStore,
|
|
27298
29213
|
DIALECT_RECORD_ENTRY_TYPE,
|
|
@@ -27598,10 +29513,10 @@ class TranscriptWriter2 {
|
|
|
27598
29513
|
return entries;
|
|
27599
29514
|
}
|
|
27600
29515
|
}
|
|
27601
|
-
var RUNTIME_ENGINE_VERSION2 = "0.0.
|
|
29516
|
+
var RUNTIME_ENGINE_VERSION2 = "0.0.40";
|
|
27602
29517
|
function withPermissionJournal(writer, location) {
|
|
27603
29518
|
return {
|
|
27604
|
-
transcriptPath:
|
|
29519
|
+
transcriptPath: join27(location.winterHome, "projects", location.projectKey, `${location.sessionId}.jsonl`),
|
|
27605
29520
|
recordUserEntry: (content, opts) => writer.recordUserEntry(content, opts),
|
|
27606
29521
|
recordAssistantEntry: (content, opts) => writer.recordAssistantEntry(content, opts),
|
|
27607
29522
|
recordAttachmentEntry: (attachment) => writer.recordAttachmentEntry(attachment),
|
|
@@ -27629,7 +29544,7 @@ function buildWriter(opts) {
|
|
|
27629
29544
|
ctx: { sessionId: opts.sessionId, cwd: opts.cwd, version: RUNTIME_ENGINE_VERSION2, projectDirName: opts.projectKey },
|
|
27630
29545
|
initialParentUuid: opts.initialParentUuid,
|
|
27631
29546
|
...opts.initialConversationalUuids !== undefined ? { initialConversationalUuids: opts.initialConversationalUuids } : {},
|
|
27632
|
-
providerStateSink: createFileProviderStateSink(providerStateSidecarPath(
|
|
29547
|
+
providerStateSink: createFileProviderStateSink(providerStateSidecarPath(join27(opts.winterHome, "projects", opts.projectKey, `${opts.sessionId}.jsonl`)))
|
|
27633
29548
|
});
|
|
27634
29549
|
return withPermissionJournal(writer, { winterHome: opts.winterHome, projectKey: opts.projectKey, sessionId: opts.sessionId });
|
|
27635
29550
|
}
|
|
@@ -27642,7 +29557,7 @@ function buildChildTranscriptWriter(opts) {
|
|
|
27642
29557
|
key: { projectKey: opts.projectKey, sessionId: opts.parentSessionId, subpath: childTranscriptSubpath(opts.agentId) },
|
|
27643
29558
|
ctx: { sessionId: opts.parentSessionId, cwd: opts.cwd, version: RUNTIME_ENGINE_VERSION2, projectDirName: opts.projectKey },
|
|
27644
29559
|
sidechain: { agentId: opts.agentId, parentToolUseId: opts.parentToolUseId },
|
|
27645
|
-
...opts.winterHome !== undefined ? { providerStateSink: createFileProviderStateSink(providerStateSidecarPath(
|
|
29560
|
+
...opts.winterHome !== undefined ? { providerStateSink: createFileProviderStateSink(providerStateSidecarPath(join27(opts.winterHome, "projects", opts.projectKey, opts.parentSessionId, `${childTranscriptSubpath(opts.agentId)}.jsonl`))) } : {}
|
|
27646
29561
|
});
|
|
27647
29562
|
}
|
|
27648
29563
|
var CHILD_SUBKEY_PATTERN = /^subagents\/agent-(.+)$/;
|
|
@@ -28324,4 +30239,4 @@ function selectClassifierRoute2(config, descriptorOf) {
|
|
|
28324
30239
|
return { kind: "worker-eligible" };
|
|
28325
30240
|
}
|
|
28326
30241
|
|
|
28327
|
-
export { ChildResumeModeIncomparableError, resolveChildResumeMode, ensureDefaultMessagingRuntimeRegistered, buildContinuationChain, AGENT_TOOL_CANONICAL_NAME, computeActiveSlotSet, resolveSlotToProvider, createRpcBridge2, shellWords, escapeRegExpLiteral, validatePermissionRuleString, registerTool2, getRegisteredTool, listRegisteredTools, rebrandStandingServerTools, mcpServerHasRegistrations, onCompaction2, STRUCTURED_OUTPUT_TOOL_NAME, looksLikeCommand, matchFileRule, resolveFileRuleAbsolutePath, resolveFileRuleAbsoluteGlobText, resolvesWithinPluginRoot, buildWorkflowWorkerSeatbeltProfile2, capBytes, neutralizeReminderTags, readWhole, readCappedLinesAndBytes, registerAttachmentRenderer, attachmentsIn, registerChildEngineFactory, rawToRuleValue, sourceRule, parseFrontmatter, parseAgentDefinitionFile, allowedAgentTypesFromTools, validateAgentDefinition, WinterPathsError2, sessionTempDir2, ensureTasksDir2, resolveProjectDirName2, buildForkDirectiveText, buildForkInitialMessages, gitInstructionsEnabled, isUserInvocable, buildSkillListing, registerPluginAgents, clearPluginAgents, registerSkillSessionRuntime, clearSkillSessionRuntime, buildHookEntriesFromSettings, defaultTrustSource, formatValidationErrors, createSchemaValidatorCache, killAllTaskProcessGroups, runGit, listWorktrees, canonicalAliases, WINTER_CANONICAL_ALIASES, DEFAULT_SKILL_BODY_BYTES, skillNameError, pluginNameError, capBytes2, parseSkillFile, validateSkillsOption, isSkillEnabled, autoSkillPermissionEntries, setHostWorkflowWorkerCommand, listWorkflowsForListing, buildWorkflowSkillPrompt, WORKFLOW_SANDBOX_REFUSED_EXIT_CODE2, DEFAULT_CONTEXT_WINDOW_TOKENS2, ProviderTurnError, isProviderTurnError, createContextAccountant2, runEngine2, WINTER_SUMMARY_INSTRUCTION, WINTER_PREFIX_SUMMARY_INSTRUCTION, CARRIED_SUMMARY_NOTE, retainedExchangesNote, buildSummaryInstruction, redactForSummary, summarize, summarizeOverPrefix, recordedProviderSystems2, resetRecordedProviderSystems2, echoProvider2, scriptedProvider2, stubExecutor2, P5_FIXTURE_SKILL_NAME2, P5_WORKFLOW_SCRIPT2, isTestProviderName2, testProviderForNamespace, testProviderByName2, SUBAGENT_CHILD_PROBE_TEXT2, MCP_SDK_TEST_SERVER_NAME2, MCP_SDK_TEST_TOOL_NAME2, BGTASK_TEST_TOOL_NAME2, registerBgTaskTestTool2, ResumeTargetError2, ResumeTruncationError2, findContinueTarget2, findResumeTarget2, toDialectEntries2, truncateAt2, rebuildProviderMessages2, userEntry2, winterMessageIdFor2, assistantEntry2, CLAUDE_COMPACT_SUMMARY_PREAMBLE2, claudeCompactBoundaryEntry2, claudeCompactSummaryEntry2, TranscriptWriterError2, TranscriptWriter2, RUNTIME_ENGINE_VERSION2, childTranscriptSubpath, buildChildTranscriptWriter, listChildAgentIds, resolveEngineSession2, resolveProductionWinterHome2, resolveProductionStoreHome, resolveSessionProvider, redactCredentialRef, createProviderContext, providerCredentialRef2, storeProviderCredential2, deleteProviderCredential2, validateProviderCredential2, CLASSIFIER_TOOL_NAME2, CLASSIFIER_VERDICT_SCHEMA2, CLASSIFIER_NO_VERDICT_REASONS2, DEFAULT_MAX_CONTEXT_CHARS2, CLASSIFIER_SYSTEM_PROMPT2, DEFAULT_CLASSIFIER_TIMEOUT_MS2, createModelClassifier2, selectClassifierRoute2 };
|
|
30242
|
+
export { ChildResumeModeIncomparableError, resolveChildResumeMode, ensureDefaultMessagingRuntimeRegistered, buildContinuationChain, AGENT_TOOL_CANONICAL_NAME, computeActiveSlotSet, resolveSlotToProvider, createRpcBridge2, shellWords, escapeRegExpLiteral, validatePermissionRuleString, registerTool2, getRegisteredTool, listRegisteredTools, rebrandStandingServerTools, mcpServerHasRegistrations, onCompaction2, STRUCTURED_OUTPUT_TOOL_NAME, looksLikeCommand, matchFileRule, resolveFileRuleAbsolutePath, resolveFileRuleAbsoluteGlobText, resolvesWithinPluginRoot, buildWorkflowWorkerSeatbeltProfile2, capBytes, neutralizeReminderTags, readWhole, readCappedLinesAndBytes, registerAttachmentRenderer, attachmentsIn, registerChildEngineFactory, rawToRuleValue, sourceRule, parseFrontmatter, parseAgentDefinitionFile, allowedAgentTypesFromTools, validateAgentDefinition, WinterPathsError2, sessionTempDir2, ensureTasksDir2, resolveProjectDirName2, buildForkDirectiveText, buildForkInitialMessages, gitInstructionsEnabled, isUserInvocable, buildSkillListing, registerPluginAgents, clearPluginAgents, registerSkillSessionRuntime, clearSkillSessionRuntime, buildHookEntriesFromSettings, defaultTrustSource, formatValidationErrors, createSchemaValidatorCache, killAllTaskProcessGroups, runGit, listWorktrees, canonicalAliases, WINTER_CANONICAL_ALIASES, DEFAULT_SKILL_BODY_BYTES, skillNameError, pluginNameError, capBytes2, parseSkillFile, validateSkillsOption, isSkillEnabled, autoSkillPermissionEntries, setHostWorkflowWorkerCommand, listWorkflowsForListing, buildWorkflowSkillPrompt, WORKFLOW_SANDBOX_REFUSED_EXIT_CODE2, createToolLaneTails, DEFAULT_CONTEXT_WINDOW_TOKENS2, ProviderTurnError, isProviderTurnError, createContextAccountant2, runEngine2, WINTER_SUMMARY_INSTRUCTION, WINTER_PREFIX_SUMMARY_INSTRUCTION, CARRIED_SUMMARY_NOTE, retainedExchangesNote, buildSummaryInstruction, redactForSummary, summarize, summarizeOverPrefix, recordedProviderSystems2, resetRecordedProviderSystems2, echoProvider2, scriptedProvider2, stubExecutor2, P5_FIXTURE_SKILL_NAME2, P5_WORKFLOW_SCRIPT2, isTestProviderName2, testProviderForNamespace, testProviderByName2, SUBAGENT_CHILD_PROBE_TEXT2, MCP_SDK_TEST_SERVER_NAME2, MCP_SDK_TEST_TOOL_NAME2, BGTASK_TEST_TOOL_NAME2, registerBgTaskTestTool2, ResumeTargetError2, ResumeTruncationError2, findContinueTarget2, findResumeTarget2, toDialectEntries2, truncateAt2, rebuildProviderMessages2, userEntry2, winterMessageIdFor2, assistantEntry2, CLAUDE_COMPACT_SUMMARY_PREAMBLE2, claudeCompactBoundaryEntry2, claudeCompactSummaryEntry2, TranscriptWriterError2, TranscriptWriter2, RUNTIME_ENGINE_VERSION2, childTranscriptSubpath, buildChildTranscriptWriter, listChildAgentIds, resolveEngineSession2, resolveProductionWinterHome2, resolveProductionStoreHome, resolveSessionProvider, redactCredentialRef, createProviderContext, providerCredentialRef2, storeProviderCredential2, deleteProviderCredential2, validateProviderCredential2, CLASSIFIER_TOOL_NAME2, CLASSIFIER_VERDICT_SCHEMA2, CLASSIFIER_NO_VERDICT_REASONS2, DEFAULT_MAX_CONTEXT_CHARS2, CLASSIFIER_SYSTEM_PROMPT2, DEFAULT_CLASSIFIER_TIMEOUT_MS2, createModelClassifier2, selectClassifierRoute2 };
|