scream-code 0.18.1 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/{app-BQW2gDgm.mjs → app-DuRqozgp.mjs} +43 -10
- package/dist/{dispatch-FYZpdI9u.mjs → dispatch-BbGcQ7_2.mjs} +750 -28
- package/dist/{dispatch-Byc9hdXF.mjs → dispatch-Cj1IEGCC.mjs} +1 -1
- package/dist/main.mjs +1 -1
- package/dist/public/assets/index-BgrNzOAz.css +1 -0
- package/dist/public/assets/{index-DOnRKkt2.js → index-BsmGEP9G.js} +33 -33
- package/dist/public/assets/index-BsmGEP9G.js.map +1 -0
- package/dist/public/index.html +2 -2
- package/dist/{text-input-dialog-b-TqTP2z.mjs → text-input-dialog-DCMeCMoC.mjs} +1 -1
- package/dist/{text-input-dialog-Bu8GPguU.mjs → text-input-dialog-Dw-Cl3DW.mjs} +18 -0
- package/package.json +1 -1
- package/dist/public/assets/index-DOnRKkt2.js.map +0 -1
- package/dist/public/assets/index-m8QsxSTy.css +0 -1
|
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
|
|
|
6
6
|
import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } from "./chunk-D90kvbyJ.mjs";
|
|
7
7
|
import { A as sharedMemoStore, B as buildProjectTagCloud, C as applyConsolidation, D as parseMemoryMemos, E as buildExitExtractionPrompt, F as probeLocalEmbeddingSupport, G as join$1, H as basename$1, I as createMemoryMemo, J as relative$1, K as normalize, L as toSummary, M as clearEmbeddingModelCache, N as createFastEmbedEngine, O as stripMemoryTags, P as hasEmbeddingModelCache$1, R as generateTags, S as DreamTracker, T as EXIT_EXTRACTION_SYSTEM_PROMPT, U as dirname$2, V as rankMemos, W as isAbsolute$1, Y as resolve$2, a as isSupportedFile, b as sharedKnowledgeStoreReady, i as ingestFile, j as classifyEmbeddingFailure, k as MemoryMemoStore, q as parse$7, r as ingestDirectory, t as multiSearch, w as buildConsolidationPlan, y as sharedKnowledgeStore, z as normalizeTags } from "./src-C7C9e5Ds.mjs";
|
|
8
8
|
import { n as require_base64_js, t as XMLParser } from "./XMLParser-DFAnGqkc.mjs";
|
|
9
|
-
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-
|
|
9
|
+
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-Dw-Cl3DW.mjs";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
12
12
|
import * as fs$1 from "node:fs/promises";
|
|
@@ -68001,7 +68001,10 @@ const PermissionRuleSchema = z.object({
|
|
|
68001
68001
|
pattern: z.string().min(1).refine(isValidPermissionPattern, { message: "Invalid permission rule pattern" }),
|
|
68002
68002
|
reason: z.string().optional()
|
|
68003
68003
|
});
|
|
68004
|
-
const PermissionConfigSchema = z.object({
|
|
68004
|
+
const PermissionConfigSchema = z.object({
|
|
68005
|
+
rules: z.array(PermissionRuleSchema).optional(),
|
|
68006
|
+
egressAllowlist: z.array(z.string()).optional()
|
|
68007
|
+
});
|
|
68005
68008
|
const LoopControlSchema = z.object({
|
|
68006
68009
|
maxStepsPerTurn: z.number().int().min(0).optional(),
|
|
68007
68010
|
maxRetriesPerStep: z.number().int().min(0).optional(),
|
|
@@ -87483,6 +87486,216 @@ var AutoModeAskUserQuestionDenyPermissionPolicy = class {
|
|
|
87483
87486
|
}
|
|
87484
87487
|
};
|
|
87485
87488
|
//#endregion
|
|
87489
|
+
//#region ../../packages/agent-core/src/agent/permission/policies/private-read-egress-ask.ts
|
|
87490
|
+
/**
|
|
87491
|
+
* Hosts that may receive data even after the session read private data from
|
|
87492
|
+
* outside the workspace. Merged (union) with the user's configured
|
|
87493
|
+
* `permission.egressAllowlist`.
|
|
87494
|
+
*/
|
|
87495
|
+
const DEFAULT_EGRESS_ALLOWLIST = [
|
|
87496
|
+
"github.com",
|
|
87497
|
+
"api.github.com",
|
|
87498
|
+
"raw.githubusercontent.com",
|
|
87499
|
+
"registry.npmjs.org",
|
|
87500
|
+
"pypi.org",
|
|
87501
|
+
"files.pythonhosted.org",
|
|
87502
|
+
"wikipedia.org",
|
|
87503
|
+
"localhost",
|
|
87504
|
+
"127.0.0.1",
|
|
87505
|
+
"[::1]"
|
|
87506
|
+
];
|
|
87507
|
+
/**
|
|
87508
|
+
* Shared guard condition: returns an assessment when this call would move
|
|
87509
|
+
* private-read data to a destination that is not allowlisted, undefined when
|
|
87510
|
+
* the call is clean (or not an egress call at all).
|
|
87511
|
+
*
|
|
87512
|
+
* Two callers keep the same semantics:
|
|
87513
|
+
* - `PrivateReadEgressAskPermissionPolicy` — prompt the user (once or
|
|
87514
|
+
* session), even in auto/yolo mode;
|
|
87515
|
+
* - bot mode — deny instead, because no human is there to answer a prompt.
|
|
87516
|
+
*
|
|
87517
|
+
* Fail-closed details:
|
|
87518
|
+
* - a candidate private read that has not settled yet counts as taint: every
|
|
87519
|
+
* call in one tool batch is authorized before any result is finalized, so
|
|
87520
|
+
* a same-batch `[Read /tmp/secret, Bash(curl …)]` must not slip through;
|
|
87521
|
+
* - every destination found in the call must be allowlisted — one unknown
|
|
87522
|
+
* host in a command is enough to ask;
|
|
87523
|
+
* - a command whose destination cannot be extracted counts as unrecognized,
|
|
87524
|
+
* never as allowlisted.
|
|
87525
|
+
*/
|
|
87526
|
+
function assessPrivateReadEgress(agent, context) {
|
|
87527
|
+
const permission = agent.permission;
|
|
87528
|
+
if (permission.taintedPrivateReads.length === 0 && !permission.hasPendingPrivateReads()) return;
|
|
87529
|
+
const hosts = egressHosts(context);
|
|
87530
|
+
if (hosts === void 0) return void 0;
|
|
87531
|
+
if (hasMatchingSessionGrant(agent, context)) return void 0;
|
|
87532
|
+
if (hosts.length === 0) return {
|
|
87533
|
+
reason: egressReason(void 0),
|
|
87534
|
+
targetKnown: false
|
|
87535
|
+
};
|
|
87536
|
+
const allowlist = egressAllowlist(agent);
|
|
87537
|
+
const blocked = hosts.find((host) => !isAllowedEgressHost(host, allowlist));
|
|
87538
|
+
if (blocked === void 0) return void 0;
|
|
87539
|
+
return {
|
|
87540
|
+
reason: egressReason(blocked),
|
|
87541
|
+
targetKnown: true
|
|
87542
|
+
};
|
|
87543
|
+
}
|
|
87544
|
+
/**
|
|
87545
|
+
* Once the session has successfully read data from outside the workspace,
|
|
87546
|
+
* sending anything to a host outside the allowlist asks first — the data may
|
|
87547
|
+
* have come from a private file, and an unrecognized destination cannot be
|
|
87548
|
+
* assumed safe.
|
|
87549
|
+
*
|
|
87550
|
+
* The chain position is the mechanism (see `createPermissionDecisionPolicies`):
|
|
87551
|
+
* the policy sits after `collaboration-auto-approve` and before
|
|
87552
|
+
* `auto-mode-approve`, so it fires even in unattended auto mode; the allowlist,
|
|
87553
|
+
* not the chain, is what keeps ordinary auto-mode work quiet. It sits before
|
|
87554
|
+
* `session-approval-history` as well, so only a memorized grant that matches
|
|
87555
|
+
* this exact call can silence it.
|
|
87556
|
+
*
|
|
87557
|
+
* Egress detection is deliberately narrow (MVP): `FetchURL` and the shell
|
|
87558
|
+
* download/remote verbs in `Bash`. Known blind spots, kept here so the next
|
|
87559
|
+
* batch starts from the list: egress through python, RunScript or MCP tools;
|
|
87560
|
+
* shell reads without a read verb or without an absolute path — `cd / && cat
|
|
87561
|
+
* .env`, `curl -d @file`, `-T`, `dd`; `WebSearch`/`PaperSearch` are not judged
|
|
87562
|
+
* as egress either, so a tainted session still reaches the network through
|
|
87563
|
+
* them (consistent with the allowlist being about hosts, registered as an
|
|
87564
|
+
* observation); and encoding tricks that hide a host or a path from the
|
|
87565
|
+
* command text.
|
|
87566
|
+
*/
|
|
87567
|
+
var PrivateReadEgressAskPermissionPolicy = class {
|
|
87568
|
+
agent;
|
|
87569
|
+
name = "private-read-egress-ask";
|
|
87570
|
+
constructor(agent) {
|
|
87571
|
+
this.agent = agent;
|
|
87572
|
+
}
|
|
87573
|
+
evaluate(context) {
|
|
87574
|
+
const assessment = assessPrivateReadEgress(this.agent, context);
|
|
87575
|
+
if (assessment === void 0) return;
|
|
87576
|
+
return {
|
|
87577
|
+
kind: "ask",
|
|
87578
|
+
reason: {
|
|
87579
|
+
private_read_egress: true,
|
|
87580
|
+
target_known: assessment.targetKnown
|
|
87581
|
+
},
|
|
87582
|
+
reasons: [assessment.reason],
|
|
87583
|
+
grantOptions: ["once", "session"]
|
|
87584
|
+
};
|
|
87585
|
+
}
|
|
87586
|
+
};
|
|
87587
|
+
/** Built-in hosts plus the configured ones; configuration only widens. */
|
|
87588
|
+
function egressAllowlist(agent) {
|
|
87589
|
+
const configured = agent.screamConfig?.permission?.egressAllowlist;
|
|
87590
|
+
if (configured === void 0 || configured.length === 0) return DEFAULT_EGRESS_ALLOWLIST;
|
|
87591
|
+
return [...DEFAULT_EGRESS_ALLOWLIST, ...configured];
|
|
87592
|
+
}
|
|
87593
|
+
/** A memorized approve-for-session grant that matches this call wins. */
|
|
87594
|
+
function hasMatchingSessionGrant(agent, context) {
|
|
87595
|
+
return agent.permission.sessionApprovalRulePatterns.some((pattern) => matchPermissionRule({
|
|
87596
|
+
rule: {
|
|
87597
|
+
decision: "allow",
|
|
87598
|
+
scope: "session-runtime",
|
|
87599
|
+
pattern,
|
|
87600
|
+
reason: "approve for session"
|
|
87601
|
+
},
|
|
87602
|
+
toolName: context.toolCall.name,
|
|
87603
|
+
execution: context.execution
|
|
87604
|
+
}) !== void 0);
|
|
87605
|
+
}
|
|
87606
|
+
/**
|
|
87607
|
+
* Destinations this call sends to, or undefined when the call is not egress:
|
|
87608
|
+
*
|
|
87609
|
+
* - `FetchURL` — the URL argument; an unparsable URL yields no destination
|
|
87610
|
+
* (unrecognized).
|
|
87611
|
+
* - `Bash` — a download/remote verb (curl, wget, nc, netcat, ssh, scp,
|
|
87612
|
+
* rsync, telnet) marks the call as egress; every host the command spells
|
|
87613
|
+
* out is collected, so a second URL cannot hide behind an allowlisted one.
|
|
87614
|
+
*/
|
|
87615
|
+
function egressHosts(context) {
|
|
87616
|
+
if (context.toolCall.name === "FetchURL") {
|
|
87617
|
+
const url = context.args?.url;
|
|
87618
|
+
if (typeof url !== "string") return void 0;
|
|
87619
|
+
const host = hostFromUrl(url);
|
|
87620
|
+
return host === void 0 ? [] : [host];
|
|
87621
|
+
}
|
|
87622
|
+
if (context.toolCall.name === "Bash") {
|
|
87623
|
+
const command = context.args?.command;
|
|
87624
|
+
if (typeof command !== "string") return void 0;
|
|
87625
|
+
if (!EGRESS_COMMAND_PATTERN.test(command)) return void 0;
|
|
87626
|
+
return hostsFromCommand(command);
|
|
87627
|
+
}
|
|
87628
|
+
}
|
|
87629
|
+
/**
|
|
87630
|
+
* Shell download/remote verbs. Shared with the private-read taint scan
|
|
87631
|
+
* (`permission/index.ts`): an egress command reads local files through upload
|
|
87632
|
+
* flags (`-d @file`, `-T`) without any read verb, so it must not be filtered
|
|
87633
|
+
* out by that scan's read-verb gate.
|
|
87634
|
+
*/
|
|
87635
|
+
const EGRESS_COMMAND_PATTERN = /\b(curl|wget|nc|netcat|ssh(?!-)|scp|rsync|telnet)\b/;
|
|
87636
|
+
/**
|
|
87637
|
+
* Authority of a URL. The character class stops at `/`, `#`, `?`, quotes,
|
|
87638
|
+
* backticks and whitespace, so a fragment or a path cannot extend the
|
|
87639
|
+
* authority into a different host.
|
|
87640
|
+
*/
|
|
87641
|
+
const URL_AUTHORITY = /https?:\/\/([^\s/'"`#?]+)/gi;
|
|
87642
|
+
/** `user@host` (ssh/scp style). */
|
|
87643
|
+
const USER_HOST = /[\w.-]+@([A-Za-z0-9][A-Za-z0-9.-]*)/g;
|
|
87644
|
+
/** `host:path` remote targets (scp/rsync style). */
|
|
87645
|
+
const HOST_PATH = /(?:^|\s)([A-Za-z0-9][A-Za-z0-9.-]*\.[A-Za-z]{2,}):\S/g;
|
|
87646
|
+
function hostsFromCommand(command) {
|
|
87647
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
87648
|
+
for (const match of command.matchAll(URL_AUTHORITY)) {
|
|
87649
|
+
const host = hostFromAuthority(match[1] ?? "");
|
|
87650
|
+
if (host !== void 0) hosts.add(host);
|
|
87651
|
+
}
|
|
87652
|
+
for (const match of command.matchAll(USER_HOST)) {
|
|
87653
|
+
const host = match[1]?.toLowerCase();
|
|
87654
|
+
if (host !== void 0 && host.length > 0) hosts.add(host);
|
|
87655
|
+
}
|
|
87656
|
+
for (const match of command.matchAll(HOST_PATH)) {
|
|
87657
|
+
const host = match[1]?.toLowerCase();
|
|
87658
|
+
if (host !== void 0 && host.length > 0) hosts.add(host);
|
|
87659
|
+
}
|
|
87660
|
+
return [...hosts];
|
|
87661
|
+
}
|
|
87662
|
+
function hostFromUrl(url) {
|
|
87663
|
+
try {
|
|
87664
|
+
const hostname = new URL(url).hostname.toLowerCase();
|
|
87665
|
+
return hostname.length > 0 ? hostname : void 0;
|
|
87666
|
+
} catch {
|
|
87667
|
+
return;
|
|
87668
|
+
}
|
|
87669
|
+
}
|
|
87670
|
+
function hostFromAuthority(authority) {
|
|
87671
|
+
const withoutUser = authority.slice(authority.indexOf("@") + 1);
|
|
87672
|
+
if (withoutUser.startsWith("[")) {
|
|
87673
|
+
const end = withoutUser.indexOf("]");
|
|
87674
|
+
return end === -1 ? void 0 : withoutUser.slice(0, end + 1).toLowerCase();
|
|
87675
|
+
}
|
|
87676
|
+
const host = withoutUser.split(":")[0]?.toLowerCase();
|
|
87677
|
+
return host !== void 0 && host.length > 0 ? host : void 0;
|
|
87678
|
+
}
|
|
87679
|
+
/**
|
|
87680
|
+
* Case-insensitive exact match; a `*.` entry also covers its apex
|
|
87681
|
+
* (`*.example.com` matches `example.com` and `a.example.com`).
|
|
87682
|
+
*/
|
|
87683
|
+
function isAllowedEgressHost(host, allowlist) {
|
|
87684
|
+
const normalized = host.toLowerCase();
|
|
87685
|
+
return allowlist.some((entry) => {
|
|
87686
|
+
const candidate = entry.trim().toLowerCase();
|
|
87687
|
+
if (candidate.length === 0) return false;
|
|
87688
|
+
if (candidate.startsWith("*.")) {
|
|
87689
|
+
const suffix = candidate.slice(2);
|
|
87690
|
+
return normalized === suffix || normalized.endsWith(`.${suffix}`);
|
|
87691
|
+
}
|
|
87692
|
+
return normalized === candidate;
|
|
87693
|
+
});
|
|
87694
|
+
}
|
|
87695
|
+
function egressReason(host) {
|
|
87696
|
+
return `private data was read outside the workspace in this session; sending it to ${host ?? "an unrecognized destination"} needs your confirmation`;
|
|
87697
|
+
}
|
|
87698
|
+
//#endregion
|
|
87486
87699
|
//#region ../../packages/agent-core/src/agent/permission/policies/bot-mode-permission.ts
|
|
87487
87700
|
/**
|
|
87488
87701
|
* Tools whose effects are reversible within the workspace (safe to run
|
|
@@ -87585,10 +87798,16 @@ var BotModePermissionPolicy = class BotModePermissionPolicy {
|
|
|
87585
87798
|
reason: { reason: "bot: in-workspace reversible edit" }
|
|
87586
87799
|
};
|
|
87587
87800
|
}
|
|
87588
|
-
if (REVERSIBLE_TOOLS.has(tool))
|
|
87589
|
-
|
|
87590
|
-
|
|
87591
|
-
|
|
87801
|
+
if (REVERSIBLE_TOOLS.has(tool)) {
|
|
87802
|
+
if (assessPrivateReadEgress(this.agent, context) !== void 0) return {
|
|
87803
|
+
kind: "deny",
|
|
87804
|
+
reason: { reason: "bot: egress after private read blocked (unattended)" }
|
|
87805
|
+
};
|
|
87806
|
+
return {
|
|
87807
|
+
kind: "approve",
|
|
87808
|
+
reason: { reason: "bot: reversible tool" }
|
|
87809
|
+
};
|
|
87810
|
+
}
|
|
87592
87811
|
return {
|
|
87593
87812
|
kind: "deny",
|
|
87594
87813
|
reason: { reason: `bot: tool '${tool}' not in the reversible allowlist` }
|
|
@@ -88366,6 +88585,99 @@ function formatPermissionRuleDenyMessage(tool, reason, agentType) {
|
|
|
88366
88585
|
return `Tool "${tool}" was denied by permission rule.${suffix}`;
|
|
88367
88586
|
}
|
|
88368
88587
|
//#endregion
|
|
88588
|
+
//#region ../../packages/agent-core/src/agent/permission/warnings.ts
|
|
88589
|
+
/**
|
|
88590
|
+
* Declaration order is the display order, so the riskiest families come first.
|
|
88591
|
+
* Every warning is a standalone English sentence; consumers render them as-is.
|
|
88592
|
+
*/
|
|
88593
|
+
const DANGEROUS_COMMAND_PATTERNS = [
|
|
88594
|
+
{
|
|
88595
|
+
pattern: /\brm\s+(?=[^|;&\n]*(?<![\w])(?:-[a-zA-Z]*r[a-zA-Z]*\b|--recursive\b))(?=[^|;&\n]*(?<![\w])(?:-[a-zA-Z]*f[a-zA-Z]*\b|--force\b))/i,
|
|
88596
|
+
warning: "dangerous command: recursive force delete"
|
|
88597
|
+
},
|
|
88598
|
+
{
|
|
88599
|
+
pattern: /\bsudo\b/,
|
|
88600
|
+
warning: "dangerous command: privilege escalation"
|
|
88601
|
+
},
|
|
88602
|
+
{
|
|
88603
|
+
pattern: /\bmkfs\b/,
|
|
88604
|
+
warning: "dangerous command: filesystem format"
|
|
88605
|
+
},
|
|
88606
|
+
{
|
|
88607
|
+
pattern: /\bdd\s+if=/,
|
|
88608
|
+
warning: "dangerous command: raw disk write"
|
|
88609
|
+
},
|
|
88610
|
+
{
|
|
88611
|
+
pattern: /curl[^|]*\|\s*(ba)?sh|wget[^|]*\|\s*(ba)?sh/,
|
|
88612
|
+
warning: "dangerous command: download piped into a shell"
|
|
88613
|
+
},
|
|
88614
|
+
{
|
|
88615
|
+
pattern: /\bchmod\s+-R\s+777\b/,
|
|
88616
|
+
warning: "dangerous command: world-writable recursive chmod"
|
|
88617
|
+
},
|
|
88618
|
+
{
|
|
88619
|
+
pattern: />\s*\/dev\/sd/,
|
|
88620
|
+
warning: "dangerous command: write to a raw disk device"
|
|
88621
|
+
},
|
|
88622
|
+
{
|
|
88623
|
+
pattern: /\bshutdown\b/,
|
|
88624
|
+
warning: "dangerous command: system shutdown"
|
|
88625
|
+
},
|
|
88626
|
+
{
|
|
88627
|
+
pattern: /\breboot\b/,
|
|
88628
|
+
warning: "dangerous command: system reboot"
|
|
88629
|
+
},
|
|
88630
|
+
{
|
|
88631
|
+
pattern: /\bgit\s+(?:-C\s+\S+\s+)?push\b.*(?:\s-f\b|--force\b)/,
|
|
88632
|
+
warning: "dangerous command: force push"
|
|
88633
|
+
}
|
|
88634
|
+
];
|
|
88635
|
+
/**
|
|
88636
|
+
* Warnings triggered by `command`, in declaration order. Returns an empty
|
|
88637
|
+
* array for ordinary commands, and for input that is not a command string.
|
|
88638
|
+
*/
|
|
88639
|
+
function dangerousCommandWarnings(command) {
|
|
88640
|
+
if (command.length === 0) return [];
|
|
88641
|
+
const warnings = [];
|
|
88642
|
+
for (const { pattern, warning } of DANGEROUS_COMMAND_PATTERNS) if (pattern.test(command)) warnings.push(warning);
|
|
88643
|
+
return warnings;
|
|
88644
|
+
}
|
|
88645
|
+
//#endregion
|
|
88646
|
+
//#region ../../packages/agent-core/src/agent/permission/policies/warnings-ask.ts
|
|
88647
|
+
/**
|
|
88648
|
+
* Dangerous shell commands always ask — even in `yolo` mode and even when a
|
|
88649
|
+
* session grant already covers Bash.
|
|
88650
|
+
*
|
|
88651
|
+
* The chain position is the mechanism (see `createPermissionDecisionPolicies`):
|
|
88652
|
+
* the policy sits after `auto-mode-approve`, so unattended auto mode keeps
|
|
88653
|
+
* running without prompts, and before `session-approval-history`, so a
|
|
88654
|
+
* memorized grant cannot silence the warning. The prompt offers `once` only —
|
|
88655
|
+
* a dangerous command is never memorizable.
|
|
88656
|
+
*/
|
|
88657
|
+
var WarningsAskPermissionPolicy = class {
|
|
88658
|
+
agent;
|
|
88659
|
+
name = "dangerous-command-warnings-ask";
|
|
88660
|
+
constructor(agent) {
|
|
88661
|
+
this.agent = agent;
|
|
88662
|
+
}
|
|
88663
|
+
evaluate(context) {
|
|
88664
|
+
if (context.toolCall.name !== "Bash") return;
|
|
88665
|
+
const command = context.args?.command;
|
|
88666
|
+
if (typeof command !== "string") return;
|
|
88667
|
+
const warnings = dangerousCommandWarnings(command);
|
|
88668
|
+
if (warnings.length === 0) return;
|
|
88669
|
+
return {
|
|
88670
|
+
kind: "ask",
|
|
88671
|
+
reason: {
|
|
88672
|
+
dangerous_command: true,
|
|
88673
|
+
warning_count: warnings.length
|
|
88674
|
+
},
|
|
88675
|
+
reasons: warnings,
|
|
88676
|
+
grantOptions: ["once"]
|
|
88677
|
+
};
|
|
88678
|
+
}
|
|
88679
|
+
};
|
|
88680
|
+
//#endregion
|
|
88369
88681
|
//#region ../../packages/agent-core/src/agent/permission/policies/collaboration-auto-approve.ts
|
|
88370
88682
|
/**
|
|
88371
88683
|
* Coordination tools are conversation, not mutation: a subagent asking its
|
|
@@ -88435,7 +88747,9 @@ function createPermissionDecisionPolicies(agent) {
|
|
|
88435
88747
|
new UserConfiguredDenyPermissionPolicy(agent),
|
|
88436
88748
|
new BotModePermissionPolicy(agent),
|
|
88437
88749
|
new CollaborationAutoApprovePermissionPolicy(),
|
|
88750
|
+
new PrivateReadEgressAskPermissionPolicy(agent),
|
|
88438
88751
|
new AutoModeApprovePermissionPolicy(agent),
|
|
88752
|
+
new WarningsAskPermissionPolicy(agent),
|
|
88439
88753
|
new SessionApprovalHistoryPermissionPolicy(agent),
|
|
88440
88754
|
new UserConfiguredAskPermissionPolicy(agent),
|
|
88441
88755
|
new UserConfiguredAllowPermissionPolicy(agent),
|
|
@@ -88464,6 +88778,15 @@ var PermissionManager = class {
|
|
|
88464
88778
|
localSessionApprovalRulePatterns = /* @__PURE__ */ new Set();
|
|
88465
88779
|
policies;
|
|
88466
88780
|
pendingApprovals = /* @__PURE__ */ new Map();
|
|
88781
|
+
/**
|
|
88782
|
+
* Two-phase private-read tracking (see `trackPendingPrivateRead` /
|
|
88783
|
+
* `settlePrivateRead`): the first phase records a candidate read of
|
|
88784
|
+
* outside-workspace data keyed by tool call id, the second promotes it to
|
|
88785
|
+
* `localTaintedPrivateReads` only after the call is known to have
|
|
88786
|
+
* succeeded. Both maps are in-memory session state — never persisted.
|
|
88787
|
+
*/
|
|
88788
|
+
localPendingPrivateReads = /* @__PURE__ */ new Map();
|
|
88789
|
+
localTaintedPrivateReads = /* @__PURE__ */ new Set();
|
|
88467
88790
|
nextApprovalId = 0;
|
|
88468
88791
|
constructor(agent, options = {}) {
|
|
88469
88792
|
this.agent = agent;
|
|
@@ -88547,10 +88870,89 @@ var PermissionManager = class {
|
|
|
88547
88870
|
get sessionApprovalRulePatterns() {
|
|
88548
88871
|
return [...this.localSessionApprovalRulePatterns, ...this.parent?.sessionApprovalRulePatterns ?? []];
|
|
88549
88872
|
}
|
|
88873
|
+
/**
|
|
88874
|
+
* Forget one memorized approve-for-session pattern. Revocation is local
|
|
88875
|
+
* only: `sessionApprovalRulePatterns` merges the parent chain read-only, so
|
|
88876
|
+
* this manager cannot drop a grant its parent holds (and a child's grant is
|
|
88877
|
+
* invisible to the parent). Returns true when a local grant was removed.
|
|
88878
|
+
*
|
|
88879
|
+
* A real removal is recorded on the wire, otherwise a resumed session would
|
|
88880
|
+
* re-add the pattern from its own approval-result record and prompt again
|
|
88881
|
+
* for a grant the user took back. The same method serves the replay path:
|
|
88882
|
+
* `AgentRecords.logRecord` is a no-op while restoring, so restore cannot
|
|
88883
|
+
* re-write the record it is replaying.
|
|
88884
|
+
*/
|
|
88885
|
+
revokeSessionGrant(pattern) {
|
|
88886
|
+
const removed = this.localSessionApprovalRulePatterns.delete(pattern);
|
|
88887
|
+
if (removed) this.agent.records.logRecord({
|
|
88888
|
+
type: "permission.record_grant_revocation",
|
|
88889
|
+
pattern
|
|
88890
|
+
});
|
|
88891
|
+
return removed;
|
|
88892
|
+
}
|
|
88893
|
+
/**
|
|
88894
|
+
* Descriptions of the successful reads of outside-workspace data this
|
|
88895
|
+
* session has performed, merged with the parent chain read-only (same
|
|
88896
|
+
* shape as `sessionApprovalRulePatterns`). A subagent sees its parent's
|
|
88897
|
+
* taint but cannot add to it, and the taint dies with the agent: it is
|
|
88898
|
+
* never persisted.
|
|
88899
|
+
*/
|
|
88900
|
+
get taintedPrivateReads() {
|
|
88901
|
+
return [...this.localTaintedPrivateReads, ...this.parent?.taintedPrivateReads ?? []];
|
|
88902
|
+
}
|
|
88903
|
+
/**
|
|
88904
|
+
* True while a candidate private read has not settled yet, on this agent or
|
|
88905
|
+
* anywhere up the parent chain. Every call in one tool batch is authorized
|
|
88906
|
+
* before any of their results is finalized, so a same-batch
|
|
88907
|
+
* `[Read /tmp/secret, Bash(curl …)]` never sees the taint — the egress
|
|
88908
|
+
* guard reads this flag too and fails closed during that window.
|
|
88909
|
+
*/
|
|
88910
|
+
hasPendingPrivateReads() {
|
|
88911
|
+
return this.localPendingPrivateReads.size > 0 || (this.parent?.hasPendingPrivateReads() ?? false);
|
|
88912
|
+
}
|
|
88913
|
+
/**
|
|
88914
|
+
* Second half of two-phase private-read tracking. A pending candidate read
|
|
88915
|
+
* is promoted to the tainted set only when the call succeeded; a failed
|
|
88916
|
+
* call never delivered its content to the model, so it is dropped. Both
|
|
88917
|
+
* outcomes clear the pending entry, so the map cannot grow unbounded.
|
|
88918
|
+
*/
|
|
88919
|
+
settlePrivateRead(toolCallId, failed) {
|
|
88920
|
+
const description = this.localPendingPrivateReads.get(toolCallId);
|
|
88921
|
+
if (description === void 0) return;
|
|
88922
|
+
this.localPendingPrivateReads.delete(toolCallId);
|
|
88923
|
+
if (failed) return;
|
|
88924
|
+
this.localTaintedPrivateReads.add(description);
|
|
88925
|
+
}
|
|
88926
|
+
/**
|
|
88927
|
+
* First half of two-phase private-read tracking: a call that reads,
|
|
88928
|
+
* searches, or read-writes a path outside the workspace is recorded as a
|
|
88929
|
+
* pending candidate here. Promotion happens in `settlePrivateRead` once the
|
|
88930
|
+
* result is known — denied, blocked, and failed calls must not taint the
|
|
88931
|
+
* session. The description never carries file content, only the path.
|
|
88932
|
+
*/
|
|
88933
|
+
trackPendingPrivateRead(context) {
|
|
88934
|
+
const cwd = this.agent.config.cwd;
|
|
88935
|
+
if (cwd.length === 0) return;
|
|
88936
|
+
const pathClass = this.agent.jian.pathClass();
|
|
88937
|
+
const accesses = context.execution.accesses;
|
|
88938
|
+
if (accesses === void 0 || accesses.some((access) => access.kind === "all")) {
|
|
88939
|
+
const path = outsidePathInShellCommand(context, cwd, pathClass, this.agent.jian.gethome());
|
|
88940
|
+
if (path !== void 0) this.localPendingPrivateReads.set(context.toolCall.id, formatPrivateReadDescription(path));
|
|
88941
|
+
return;
|
|
88942
|
+
}
|
|
88943
|
+
const privateReads = accesses.filter(isPrivateReadAccess);
|
|
88944
|
+
if (privateReads.length === 0) return;
|
|
88945
|
+
const access = privateReads.find((fileAccess) => !isWithinDirectory$1(fileAccess.path, cwd, pathClass));
|
|
88946
|
+
if (access === void 0) return;
|
|
88947
|
+
this.localPendingPrivateReads.set(context.toolCall.id, formatPrivateReadDescription(access.path));
|
|
88948
|
+
}
|
|
88550
88949
|
async beforeToolCall(context) {
|
|
88950
|
+
this.trackPendingPrivateRead(context);
|
|
88551
88951
|
const evaluation = await this.evaluatePolicies(context);
|
|
88552
88952
|
if (evaluation === void 0) return void 0;
|
|
88553
|
-
|
|
88953
|
+
const prepared = await this.permissionPolicyResolutionToPrepare(evaluation.result, context, evaluation.policyName);
|
|
88954
|
+
if (prepared?.block === true || prepared?.syntheticResult !== void 0) this.localPendingPrivateReads.delete(context.toolCall.id);
|
|
88955
|
+
return prepared;
|
|
88554
88956
|
}
|
|
88555
88957
|
async requestToolApproval(context, result, policyName) {
|
|
88556
88958
|
const { signal } = context;
|
|
@@ -88572,10 +88974,12 @@ var PermissionManager = class {
|
|
|
88572
88974
|
...sourceCapabilityMode !== "all" ? { sourceCapabilityMode } : {},
|
|
88573
88975
|
sourceToolName: name
|
|
88574
88976
|
};
|
|
88977
|
+
const requestSummary = this.agent.turn.getLastPromptSummary();
|
|
88575
88978
|
let response;
|
|
88576
88979
|
if (this.agent.rpc?.requestApproval) {
|
|
88577
88980
|
const approvalId = `approval-${String(++this.nextApprovalId)}`;
|
|
88578
88981
|
let timeoutHandle;
|
|
88982
|
+
let timedOut = false;
|
|
88579
88983
|
try {
|
|
88580
88984
|
const customPromise = new Promise((resolve, reject) => {
|
|
88581
88985
|
this.pendingApprovals.set(approvalId, {
|
|
@@ -88595,27 +88999,42 @@ var PermissionManager = class {
|
|
|
88595
88999
|
toolName: name,
|
|
88596
89000
|
action,
|
|
88597
89001
|
display,
|
|
89002
|
+
reasons: result.reasons,
|
|
89003
|
+
grantOptions: result.grantOptions,
|
|
89004
|
+
...requestSummary !== void 0 ? { requestSummary } : {},
|
|
88598
89005
|
...source
|
|
88599
89006
|
}, { signal }).then(resolve, reject);
|
|
88600
89007
|
});
|
|
88601
89008
|
const timeoutPromise = new Promise((_, reject) => {
|
|
88602
89009
|
timeoutHandle = setTimeout(() => {
|
|
89010
|
+
timedOut = true;
|
|
88603
89011
|
reject(/* @__PURE__ */ new Error(`Approval request timed out after ${String(APPROVAL_TIMEOUT_MS)}ms`));
|
|
88604
89012
|
}, APPROVAL_TIMEOUT_MS);
|
|
88605
89013
|
});
|
|
88606
89014
|
response = await Promise.race([customPromise, timeoutPromise]);
|
|
88607
89015
|
} catch (error) {
|
|
88608
89016
|
this.pendingApprovals.delete(approvalId);
|
|
89017
|
+
this.recordDecision(context, {
|
|
89018
|
+
decision: timedOut ? "timeout" : "cancelled",
|
|
89019
|
+
reason: errorText(error)
|
|
89020
|
+
});
|
|
88609
89021
|
const resolved = result.resolveError?.(error);
|
|
88610
89022
|
return await (resolved === void 0 ? Promise.reject(error) : this.permissionPolicyResolutionToPrepare(resolved, context, policyName));
|
|
88611
89023
|
} finally {
|
|
88612
89024
|
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
88613
89025
|
this.pendingApprovals.delete(approvalId);
|
|
88614
89026
|
}
|
|
88615
|
-
} else
|
|
88616
|
-
|
|
88617
|
-
|
|
88618
|
-
|
|
89027
|
+
} else {
|
|
89028
|
+
response = {
|
|
89029
|
+
decision: "cancelled",
|
|
89030
|
+
feedback: "Approval handler is unavailable."
|
|
89031
|
+
};
|
|
89032
|
+
this.recordDecision(context, {
|
|
89033
|
+
decision: "cancelled",
|
|
89034
|
+
reason: "Approval handler is unavailable."
|
|
89035
|
+
});
|
|
89036
|
+
}
|
|
89037
|
+
response = this.downgradeUnofferedSessionScope(response, result.grantOptions, name);
|
|
88619
89038
|
const sessionApprovalRule = response.decision === "approved" && response.scope === "session" ? context.execution.approvalRule : void 0;
|
|
88620
89039
|
this.recordApprovalResult({
|
|
88621
89040
|
turnId: Number(context.turnId),
|
|
@@ -88633,6 +89052,23 @@ var PermissionManager = class {
|
|
|
88633
89052
|
reason: this.formatApprovalRejectionMessage(name, response)
|
|
88634
89053
|
};
|
|
88635
89054
|
}
|
|
89055
|
+
/**
|
|
89056
|
+
* Drop a session scope the policy never offered. Panels filter their buttons
|
|
89057
|
+
* by `grantOptions`, but the response is wire input — the core is the only
|
|
89058
|
+
* place that can guarantee a once-only prompt never memorizes a session
|
|
89059
|
+
* grant. Runs before the session rule is derived, so a downgraded response
|
|
89060
|
+
* leaves no `session-runtime` rule behind.
|
|
89061
|
+
*/
|
|
89062
|
+
downgradeUnofferedSessionScope(response, grantOptions, toolName) {
|
|
89063
|
+
if (response.decision !== "approved" || response.scope !== "session") return response;
|
|
89064
|
+
if (grantOptions === void 0 || grantOptions.includes("session")) return response;
|
|
89065
|
+
this.agent.log.warn("Approval response requested a session grant that was not offered; downgrading to a one-time grant.", {
|
|
89066
|
+
toolName,
|
|
89067
|
+
grantOptions: [...grantOptions]
|
|
89068
|
+
});
|
|
89069
|
+
const { scope: _scope, ...rest } = response;
|
|
89070
|
+
return rest;
|
|
89071
|
+
}
|
|
88636
89072
|
async evaluatePolicies(context) {
|
|
88637
89073
|
for (const policy of this.policies) {
|
|
88638
89074
|
const result = await policy.evaluate(context);
|
|
@@ -88645,13 +89081,37 @@ var PermissionManager = class {
|
|
|
88645
89081
|
get effectiveRules() {
|
|
88646
89082
|
return [...this.rules, ...this.parent?.effectiveRules ?? []];
|
|
88647
89083
|
}
|
|
89084
|
+
/**
|
|
89085
|
+
* Append a stop decision to the wire, so a session's denials and aborted
|
|
89086
|
+
* approvals stay auditable. Uses the low-level record API on purpose: the
|
|
89087
|
+
* record is an audit trail, and replaying a session must not re-run a
|
|
89088
|
+
* decision (the live UI state already comes from the approval-result
|
|
89089
|
+
* records and the blocked tool's own error result).
|
|
89090
|
+
*/
|
|
89091
|
+
recordDecision(context, decision) {
|
|
89092
|
+
this.agent.records.logRecord({
|
|
89093
|
+
type: "permission.record_decision",
|
|
89094
|
+
turnId: Number(context.turnId),
|
|
89095
|
+
toolCallId: context.toolCall.id,
|
|
89096
|
+
toolName: context.toolCall.name,
|
|
89097
|
+
...decision
|
|
89098
|
+
});
|
|
89099
|
+
}
|
|
88648
89100
|
permissionPolicyResolutionToPrepare(result, context, policyName) {
|
|
88649
89101
|
switch (result.kind) {
|
|
88650
89102
|
case "approve": return result.executionMetadata === void 0 ? void 0 : { executionMetadata: result.executionMetadata };
|
|
88651
|
-
case "deny":
|
|
88652
|
-
|
|
88653
|
-
|
|
88654
|
-
|
|
89103
|
+
case "deny": {
|
|
89104
|
+
const reason = result.message ?? (typeof result.reason === "object" && result.reason !== null && typeof result.reason.reason === "string" ? `Tool "${context.toolCall.name}" was denied by permission policy: ${result.reason.reason}` : this.formatPolicyDenyMessage(context.toolCall.name));
|
|
89105
|
+
this.recordDecision(context, {
|
|
89106
|
+
...policyName !== void 0 ? { policyName } : {},
|
|
89107
|
+
decision: "deny",
|
|
89108
|
+
reason
|
|
89109
|
+
});
|
|
89110
|
+
return {
|
|
89111
|
+
block: true,
|
|
89112
|
+
reason
|
|
89113
|
+
};
|
|
89114
|
+
}
|
|
88655
89115
|
case "ask": return this.requestToolApproval(context, result, policyName);
|
|
88656
89116
|
case "result": {
|
|
88657
89117
|
const { kind: _kind, ...prepareResult } = result;
|
|
@@ -88672,6 +89132,96 @@ var PermissionManager = class {
|
|
|
88672
89132
|
return prefix;
|
|
88673
89133
|
}
|
|
88674
89134
|
};
|
|
89135
|
+
function errorText(error) {
|
|
89136
|
+
return error instanceof Error ? error.message : String(error);
|
|
89137
|
+
}
|
|
89138
|
+
/** Operations that pull outside-workspace data into the session. */
|
|
89139
|
+
const PRIVATE_READ_OPERATIONS = new Set([
|
|
89140
|
+
"read",
|
|
89141
|
+
"search",
|
|
89142
|
+
"readwrite"
|
|
89143
|
+
]);
|
|
89144
|
+
function isPrivateReadAccess(access) {
|
|
89145
|
+
return access.kind === "file" && PRIVATE_READ_OPERATIONS.has(access.operation);
|
|
89146
|
+
}
|
|
89147
|
+
/** Longest path kept in a taint description; the tail is what identifies the file. */
|
|
89148
|
+
const PRIVATE_READ_PATH_LIMIT = 120;
|
|
89149
|
+
function formatPrivateReadDescription(path) {
|
|
89150
|
+
return `read outside the workspace: ${path.length > PRIVATE_READ_PATH_LIMIT ? `…${path.slice(-120)}` : path}`;
|
|
89151
|
+
}
|
|
89152
|
+
/** Absolute paths, `~/…` and `$HOME/…` references in shell command text. */
|
|
89153
|
+
const SHELL_PATH_REFERENCE = /(?<!:)(?:~|\$\{?HOME\}?)?\/[^\s'"`;|&<>()]*/g;
|
|
89154
|
+
/** URLs are stripped before the scan: their host part is not a file path. */
|
|
89155
|
+
const SHELL_URL_REFERENCE = /[a-z][a-z0-9+.-]*:\/\/[^\s'"`]+/gi;
|
|
89156
|
+
/**
|
|
89157
|
+
* Read-class commands: only these pull file *content* into the session. A
|
|
89158
|
+
* command that merely names a path — `ls /tmp`, `echo /etc`, a redirect
|
|
89159
|
+
* target, an env assignment — must not taint it, or ordinary build/test
|
|
89160
|
+
* commands would leave a permanent taint (and a permanent bot-mode denial).
|
|
89161
|
+
*/
|
|
89162
|
+
const SHELL_READ_VERB = /\b(cat|head|tail|grep|rg|less|more|xxd|od|strings|base64|awk|sed|sort|uniq|cut)\b/;
|
|
89163
|
+
/** Tokens that introduce a write target. */
|
|
89164
|
+
const SHELL_WRITE_OPERATORS = new Set([
|
|
89165
|
+
">",
|
|
89166
|
+
">>",
|
|
89167
|
+
"-o",
|
|
89168
|
+
"-O",
|
|
89169
|
+
"--output",
|
|
89170
|
+
"tee"
|
|
89171
|
+
]);
|
|
89172
|
+
/** Environment assignment (`PATH=…`), never a read. */
|
|
89173
|
+
const SHELL_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
89174
|
+
/** Glued redirects: `>/tmp/log` and fd forms like `2>/tmp/err`. */
|
|
89175
|
+
const SHELL_REDIRECT_PREFIX = /^\d*>>?/;
|
|
89176
|
+
/**
|
|
89177
|
+
* `github.com/foo` — a bare host followed by a path, as `curl`/`scp` accept
|
|
89178
|
+
* it. The leading segment makes it look like an absolute path to the scanner,
|
|
89179
|
+
* but it names a remote resource, not a local file.
|
|
89180
|
+
*/
|
|
89181
|
+
const SHELL_HOST_SHAPED_TOKEN = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)+\//;
|
|
89182
|
+
/**
|
|
89183
|
+
* First path reference in a shell command that resolves outside the workspace
|
|
89184
|
+
* (home expanded), or undefined.
|
|
89185
|
+
*
|
|
89186
|
+
* The scan only runs for read-class or egress commands and skips write targets,
|
|
89187
|
+
* assignments, and host-shaped tokens, because the guard exists to catch file
|
|
89188
|
+
* content entering the session — not every mention of a path. Known residues,
|
|
89189
|
+
* registered with the egress policy's blind-spot note: relative paths
|
|
89190
|
+
* (`cd / && cat .env`); readers or interpreters outside the verb list
|
|
89191
|
+
* (`python3 -c`, `perl -ne`, `dd`), which never taint through this scan; write
|
|
89192
|
+
* flag families that name a file without a redirect (`ssh-keygen -f`,
|
|
89193
|
+
* `openssl -i`) and commands such as `ssh-copy-id` that are not judged at all;
|
|
89194
|
+
* and paths hidden behind quoting or encoding.
|
|
89195
|
+
*/
|
|
89196
|
+
function outsidePathInShellCommand(context, cwd, pathClass, homeDir) {
|
|
89197
|
+
if (context.toolCall.name !== "Bash") return void 0;
|
|
89198
|
+
const command = context.args?.command;
|
|
89199
|
+
if (typeof command !== "string") return void 0;
|
|
89200
|
+
if (!EGRESS_COMMAND_PATTERN.test(command) && !SHELL_READ_VERB.test(command)) return void 0;
|
|
89201
|
+
const tokens = command.replace(SHELL_URL_REFERENCE, " ").split(/\s+/);
|
|
89202
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
89203
|
+
const token = tokens[index] ?? "";
|
|
89204
|
+
if (isShellWriteTarget(token, tokens[index - 1])) continue;
|
|
89205
|
+
if (SHELL_HOST_SHAPED_TOKEN.test(token)) continue;
|
|
89206
|
+
for (const match of token.matchAll(SHELL_PATH_REFERENCE)) {
|
|
89207
|
+
const path = expandHomeReference(match[0], homeDir);
|
|
89208
|
+
if (!isWithinDirectory$1(path, cwd, pathClass)) return path;
|
|
89209
|
+
}
|
|
89210
|
+
}
|
|
89211
|
+
}
|
|
89212
|
+
function isShellWriteTarget(token, previous) {
|
|
89213
|
+
if (token.length === 0) return false;
|
|
89214
|
+
if (SHELL_ASSIGNMENT.test(token)) return true;
|
|
89215
|
+
if (SHELL_REDIRECT_PREFIX.test(token)) return true;
|
|
89216
|
+
if (SHELL_WRITE_OPERATORS.has(token)) return true;
|
|
89217
|
+
return previous !== void 0 && SHELL_WRITE_OPERATORS.has(previous);
|
|
89218
|
+
}
|
|
89219
|
+
function expandHomeReference(path, homeDir) {
|
|
89220
|
+
if (path.startsWith("~/")) return `${homeDir}${path.slice(1)}`;
|
|
89221
|
+
if (path.startsWith("${HOME}/")) return `${homeDir}${path.slice(7)}`;
|
|
89222
|
+
if (path.startsWith("$HOME/")) return `${homeDir}${path.slice(5)}`;
|
|
89223
|
+
return path;
|
|
89224
|
+
}
|
|
88675
89225
|
//#endregion
|
|
88676
89226
|
//#region ../../packages/agent-core/src/utils/hero-slug.ts
|
|
88677
89227
|
/** Hero-name slug generator: produces "hero-hero-hero" with collision fallback. */
|
|
@@ -89767,16 +90317,30 @@ const MIGRATIONS = [
|
|
|
89767
90317
|
migrateRecord(record) {
|
|
89768
90318
|
return record;
|
|
89769
90319
|
}
|
|
90320
|
+
},
|
|
90321
|
+
{
|
|
90322
|
+
sourceVersion: "1.6",
|
|
90323
|
+
targetVersion: "1.7",
|
|
90324
|
+
migrateRecord(record) {
|
|
90325
|
+
return record;
|
|
90326
|
+
}
|
|
90327
|
+
},
|
|
90328
|
+
{
|
|
90329
|
+
sourceVersion: "1.7",
|
|
90330
|
+
targetVersion: "1.8",
|
|
90331
|
+
migrateRecord(record) {
|
|
90332
|
+
return record;
|
|
90333
|
+
}
|
|
89770
90334
|
}
|
|
89771
90335
|
];
|
|
89772
90336
|
function isNewerWireVersion(readVersion) {
|
|
89773
|
-
return compareWireVersions(readVersion, "1.
|
|
90337
|
+
return compareWireVersions(readVersion, "1.8") > 0;
|
|
89774
90338
|
}
|
|
89775
90339
|
function resolveWireMigrations(readVersion) {
|
|
89776
|
-
if (compareWireVersions(readVersion, "1.
|
|
90340
|
+
if (compareWireVersions(readVersion, "1.8") >= 0) return [];
|
|
89777
90341
|
const migrations = [];
|
|
89778
90342
|
let version = readVersion;
|
|
89779
|
-
while (compareWireVersions(version, "1.
|
|
90343
|
+
while (compareWireVersions(version, "1.8") < 0) {
|
|
89780
90344
|
const migration = findMigration(version);
|
|
89781
90345
|
if (migration === void 0) throw new Error(`Missing wire migration for version ${version}`);
|
|
89782
90346
|
migrations.push(migration);
|
|
@@ -89884,7 +90448,7 @@ function headStartsWithAnyPrefix(head, headLength, prefixes) {
|
|
|
89884
90448
|
*/
|
|
89885
90449
|
function readSkipFoldedFlag(headerText) {
|
|
89886
90450
|
try {
|
|
89887
|
-
return JSON.parse(headerText).protocol_version === "1.
|
|
90451
|
+
return JSON.parse(headerText).protocol_version === "1.8";
|
|
89888
90452
|
} catch {
|
|
89889
90453
|
return false;
|
|
89890
90454
|
}
|
|
@@ -90623,6 +91187,10 @@ function restoreAgentRecord(agent, input) {
|
|
|
90623
91187
|
case "permission.record_approval_result":
|
|
90624
91188
|
agent.permission.recordApprovalResult(input);
|
|
90625
91189
|
return;
|
|
91190
|
+
case "permission.record_grant_revocation":
|
|
91191
|
+
agent.permission.revokeSessionGrant(input.pattern);
|
|
91192
|
+
return;
|
|
91193
|
+
case "permission.record_decision": return;
|
|
90626
91194
|
case "usage.record":
|
|
90627
91195
|
agent.usage.record(input.model, input.usage, input.usageScope ?? "session", { skipCurrentTurn: true });
|
|
90628
91196
|
return;
|
|
@@ -90743,7 +91311,7 @@ var AgentRecords = class {
|
|
|
90743
91311
|
if (this.persistence !== void 0 && !this.metadataInitialized && stamped.type !== "metadata") {
|
|
90744
91312
|
this.persistence.append({
|
|
90745
91313
|
type: "metadata",
|
|
90746
|
-
protocol_version: "1.
|
|
91314
|
+
protocol_version: "1.8",
|
|
90747
91315
|
created_at: Date.now()
|
|
90748
91316
|
});
|
|
90749
91317
|
this.metadataInitialized = true;
|
|
@@ -90774,18 +91342,18 @@ var AgentRecords = class {
|
|
|
90774
91342
|
this.metadataInitialized = true;
|
|
90775
91343
|
const readVersion = record.protocol_version;
|
|
90776
91344
|
if (isNewerWireVersion(readVersion)) {
|
|
90777
|
-
warning = `Session wire protocol version ${readVersion} is newer than the current version 1.
|
|
91345
|
+
warning = `Session wire protocol version ${readVersion} is newer than the current version 1.8. Records will be replayed without migration.`;
|
|
90778
91346
|
shouldRewrite = false;
|
|
90779
91347
|
} else {
|
|
90780
91348
|
migrations = resolveWireMigrations(readVersion);
|
|
90781
|
-
shouldRewrite = readVersion !== "1.
|
|
91349
|
+
shouldRewrite = readVersion !== "1.8";
|
|
90782
91350
|
}
|
|
90783
91351
|
buffered = shouldRewrite || warning !== void 0;
|
|
90784
91352
|
}
|
|
90785
91353
|
let migratedRecord = migrateWireRecord(record, migrations);
|
|
90786
91354
|
if (migratedRecord.type === "metadata") migratedRecord = {
|
|
90787
91355
|
...migratedRecord,
|
|
90788
|
-
protocol_version: "1.
|
|
91356
|
+
protocol_version: "1.8"
|
|
90789
91357
|
};
|
|
90790
91358
|
if (buffered) {
|
|
90791
91359
|
replayedRecords.push(migratedRecord);
|
|
@@ -103281,6 +103849,11 @@ const SCREAM_MCP_CLIENT_VERSION = getCoreVersion();
|
|
|
103281
103849
|
* including either the configured tool-call timeout, an in-flight abort
|
|
103282
103850
|
* signal, both, or neither. Returns `undefined` when nothing needs to be
|
|
103283
103851
|
* passed so the SDK falls back to its defaults.
|
|
103852
|
+
*
|
|
103853
|
+
* `McpServerConfig.toolTimeoutMs` resolves to `DEFAULT_TOOL_TIMEOUT_MS` when
|
|
103854
|
+
* unset, which equals the SDK's own request timeout — a config-derived call
|
|
103855
|
+
* therefore arrives here with the same budget the SDK would have applied on
|
|
103856
|
+
* its own.
|
|
103284
103857
|
*/
|
|
103285
103858
|
function buildRequestOptions(toolCallTimeoutMs, signal) {
|
|
103286
103859
|
if (toolCallTimeoutMs === void 0 && signal === void 0) return void 0;
|
|
@@ -105262,10 +105835,21 @@ var TurnFlow = class {
|
|
|
105262
105835
|
turnStartWorkingSetPathCount = 0;
|
|
105263
105836
|
turnStartVerificationCount = 0;
|
|
105264
105837
|
verificationFailureInjected = false;
|
|
105838
|
+
/**
|
|
105839
|
+
* Plain-text summary of the most recent prompt, carried onto approval
|
|
105840
|
+
* requests so the panel can attribute a prompt to what the user asked for.
|
|
105841
|
+
* In-memory only: it is UI context, not session state.
|
|
105842
|
+
*/
|
|
105843
|
+
lastPromptSummary;
|
|
105265
105844
|
constructor(agent) {
|
|
105266
105845
|
this.agent = agent;
|
|
105267
105846
|
}
|
|
105847
|
+
/** See {@link lastPromptSummary}. Undefined until a text prompt arrives. */
|
|
105848
|
+
getLastPromptSummary() {
|
|
105849
|
+
return this.lastPromptSummary;
|
|
105850
|
+
}
|
|
105268
105851
|
prompt(input, origin = USER_PROMPT_ORIGIN) {
|
|
105852
|
+
this.lastPromptSummary = summarizePrompt(input);
|
|
105269
105853
|
this.agent.records.logRecord({
|
|
105270
105854
|
type: "turn.prompt",
|
|
105271
105855
|
input,
|
|
@@ -105795,6 +106379,7 @@ var TurnFlow = class {
|
|
|
105795
106379
|
finalizeToolResult: async (ctx) => {
|
|
105796
106380
|
const finalResult = ctx.nested === true ? ctx.result : await deduper.finalizeResult(ctx.toolCall.id, ctx.toolCall.name, ctx.args, ctx.result);
|
|
105797
106381
|
const { isError, output } = finalResult;
|
|
106382
|
+
this.agent.permission.settlePrivateRead(ctx.toolCall.id, isError === true);
|
|
105798
106383
|
this.agent.sessionMemory.recordToolExecution(ctx.toolCall.name, summarizeToolArgs(ctx.args), isError === true, ctx.stepNumber);
|
|
105799
106384
|
this.recordWorkingSetPaths(ctx.toolCall.name, ctx.args, Number(ctx.turnId));
|
|
105800
106385
|
if (ctx.toolCall.name === "Bash" && typeof ctx.args.command === "string") {
|
|
@@ -105924,6 +106509,18 @@ var TurnFlow = class {
|
|
|
105924
106509
|
return false;
|
|
105925
106510
|
}
|
|
105926
106511
|
};
|
|
106512
|
+
/** Longest prompt summary kept for the approval card. */
|
|
106513
|
+
const PROMPT_SUMMARY_LIMIT = 120;
|
|
106514
|
+
/**
|
|
106515
|
+
* Plain text of a prompt, whitespace-collapsed and truncated. Returns
|
|
106516
|
+
* undefined for input without text (attachments only), so consumers can omit
|
|
106517
|
+
* the field instead of rendering an empty row.
|
|
106518
|
+
*/
|
|
106519
|
+
function summarizePrompt(input) {
|
|
106520
|
+
const text = input.filter((part) => part.type === "text").map((part) => part.text).join(" ").replaceAll(/\s+/g, " ").trim();
|
|
106521
|
+
if (text.length === 0) return void 0;
|
|
106522
|
+
return text.length > PROMPT_SUMMARY_LIMIT ? `${text.slice(0, PROMPT_SUMMARY_LIMIT)}…` : text;
|
|
106523
|
+
}
|
|
105927
106524
|
//#endregion
|
|
105928
106525
|
//#region ../../packages/agent-core/src/utils/completion-budget.ts
|
|
105929
106526
|
const MIN_FLOOR = 1;
|
|
@@ -107442,6 +108039,7 @@ function transformPermissionData(data) {
|
|
|
107442
108039
|
appendPermissionRules(rules, raw["allow"], "allow");
|
|
107443
108040
|
appendPermissionRules(rules, raw["ask"], "ask");
|
|
107444
108041
|
if (rules.length > 0) out["rules"] = rules;
|
|
108042
|
+
if (raw["egressAllowlist"] !== void 0) out["egressAllowlist"] = raw["egressAllowlist"];
|
|
107445
108043
|
return out;
|
|
107446
108044
|
}
|
|
107447
108045
|
function appendPermissionRules(target, value, decision) {
|
|
@@ -107561,6 +108159,8 @@ function permissionToToml(permission, rawPermission) {
|
|
|
107561
108159
|
delete out["ask"];
|
|
107562
108160
|
if (permission.rules !== void 0) out["rules"] = permission.rules.map(permissionRuleToToml);
|
|
107563
108161
|
else delete out["rules"];
|
|
108162
|
+
if (permission.egressAllowlist !== void 0) out["egress_allowlist"] = [...permission.egressAllowlist];
|
|
108163
|
+
else delete out["egress_allowlist"];
|
|
107564
108164
|
return out;
|
|
107565
108165
|
}
|
|
107566
108166
|
function permissionRuleToToml(rule) {
|
|
@@ -110863,6 +111463,10 @@ function assertMcpInputSchema(toolName, inputSchema) {
|
|
|
110863
111463
|
//#endregion
|
|
110864
111464
|
//#region ../../packages/agent-core/src/mcp/connection-manager.ts
|
|
110865
111465
|
const DEFAULT_STARTUP_TIMEOUT_MS = 6e4;
|
|
111466
|
+
/** Effective tool-call budget for a server: configured value or the default. */
|
|
111467
|
+
function resolveToolCallTimeoutMs(config) {
|
|
111468
|
+
return config.toolTimeoutMs ?? 6e4;
|
|
111469
|
+
}
|
|
110866
111470
|
/**
|
|
110867
111471
|
* Owns the lifecycle of every configured MCP server for a Session.
|
|
110868
111472
|
*
|
|
@@ -111088,7 +111692,7 @@ var McpConnectionManager = class {
|
|
|
111088
111692
|
return entry.attemptId;
|
|
111089
111693
|
}
|
|
111090
111694
|
createClient(config, name) {
|
|
111091
|
-
const toolCallTimeoutMs = config
|
|
111695
|
+
const toolCallTimeoutMs = resolveToolCallTimeoutMs(config);
|
|
111092
111696
|
if (config.transport === "stdio") return new StdioMcpClient(config, { toolCallTimeoutMs });
|
|
111093
111697
|
return new HttpMcpClient(config, {
|
|
111094
111698
|
toolCallTimeoutMs,
|
|
@@ -112628,6 +113232,22 @@ var Session$1 = class {
|
|
|
112628
113232
|
return this.skills.listSkills().map(summarizeSkill);
|
|
112629
113233
|
}
|
|
112630
113234
|
/**
|
|
113235
|
+
* Approval patterns with an active approve-for-session grant in this
|
|
113236
|
+
* session, in insertion order. Empty when the main agent has not been
|
|
113237
|
+
* instantiated (or has no grants).
|
|
113238
|
+
*/
|
|
113239
|
+
getSessionApprovalGrants() {
|
|
113240
|
+
return this.agents.get("main")?.permission.sessionApprovalRulePatterns.slice() ?? [];
|
|
113241
|
+
}
|
|
113242
|
+
/**
|
|
113243
|
+
* Revoke one approve-for-session grant held by the main agent. Returns true
|
|
113244
|
+
* when a grant was removed; false when the pattern was not granted (or the
|
|
113245
|
+
* main agent does not exist yet).
|
|
113246
|
+
*/
|
|
113247
|
+
revokeSessionApprovalGrant(pattern) {
|
|
113248
|
+
return this.agents.get("main")?.permission.revokeSessionGrant(pattern) ?? false;
|
|
113249
|
+
}
|
|
113250
|
+
/**
|
|
112631
113251
|
* Dynamically load additional skill roots into the running session.
|
|
112632
113252
|
* Used after a plugin is installed so its skills become available
|
|
112633
113253
|
* without requiring the user to create a new session.
|
|
@@ -127257,7 +127877,7 @@ var OpenAlexPaperSource = class {
|
|
|
127257
127877
|
};
|
|
127258
127878
|
//#endregion
|
|
127259
127879
|
//#region ../../packages/agent-core/src/session/export/manifest.ts
|
|
127260
|
-
const WIRE_PROTOCOL_VERSION = "1.
|
|
127880
|
+
const WIRE_PROTOCOL_VERSION = "1.8";
|
|
127261
127881
|
function buildExportManifest(args) {
|
|
127262
127882
|
return {
|
|
127263
127883
|
sessionId: args.summary.id,
|
|
@@ -128628,6 +129248,12 @@ var SessionAPIImpl = class {
|
|
|
128628
129248
|
async removeSkill(payload) {
|
|
128629
129249
|
return this.session.removeSkill(payload.skillName);
|
|
128630
129250
|
}
|
|
129251
|
+
getSessionApprovalGrants(_payload) {
|
|
129252
|
+
return this.session.getSessionApprovalGrants();
|
|
129253
|
+
}
|
|
129254
|
+
revokeSessionApprovalGrant(payload) {
|
|
129255
|
+
return this.session.revokeSessionApprovalGrant(payload.pattern);
|
|
129256
|
+
}
|
|
128631
129257
|
listMcpServers(_payload) {
|
|
128632
129258
|
return this.session.mcp.list();
|
|
128633
129259
|
}
|
|
@@ -129852,6 +130478,12 @@ var ScreamCore = class {
|
|
|
129852
130478
|
listMcpServers({ sessionId, ...payload }) {
|
|
129853
130479
|
return this.sessionApi(sessionId).listMcpServers(payload);
|
|
129854
130480
|
}
|
|
130481
|
+
getSessionApprovalGrants({ sessionId, ...payload }) {
|
|
130482
|
+
return this.sessionApi(sessionId).getSessionApprovalGrants(payload);
|
|
130483
|
+
}
|
|
130484
|
+
revokeSessionApprovalGrant({ sessionId, ...payload }) {
|
|
130485
|
+
return this.sessionApi(sessionId).revokeSessionApprovalGrant(payload);
|
|
130486
|
+
}
|
|
129855
130487
|
getMcpStartupMetrics({ sessionId, ...payload }) {
|
|
129856
130488
|
return this.sessionApi(sessionId).getMcpStartupMetrics(payload);
|
|
129857
130489
|
}
|
|
@@ -130777,6 +131409,15 @@ var SDKRpcClient = class {
|
|
|
130777
131409
|
skillName: input.skillName
|
|
130778
131410
|
});
|
|
130779
131411
|
}
|
|
131412
|
+
async getSessionApprovalGrants(input) {
|
|
131413
|
+
return (await this.getRpc()).getSessionApprovalGrants({ sessionId: input.sessionId });
|
|
131414
|
+
}
|
|
131415
|
+
async revokeSessionApprovalGrant(input) {
|
|
131416
|
+
return (await this.getRpc()).revokeSessionApprovalGrant({
|
|
131417
|
+
sessionId: input.sessionId,
|
|
131418
|
+
pattern: input.pattern
|
|
131419
|
+
});
|
|
131420
|
+
}
|
|
130780
131421
|
/**
|
|
130781
131422
|
* Background tasks are registered per agent: a subagent's Bash/Agent task
|
|
130782
131423
|
* lives in that subagent's own registry, so `agentId` selects whose registry
|
|
@@ -131342,6 +131983,19 @@ var Session = class {
|
|
|
131342
131983
|
this.ensureOpen();
|
|
131343
131984
|
return this.rpc.listMcpServers({ sessionId: this.id });
|
|
131344
131985
|
}
|
|
131986
|
+
/** Approval patterns with an active approve-for-session grant. */
|
|
131987
|
+
async getSessionApprovalGrants() {
|
|
131988
|
+
this.ensureOpen();
|
|
131989
|
+
return this.rpc.getSessionApprovalGrants({ sessionId: this.id });
|
|
131990
|
+
}
|
|
131991
|
+
/** Revoke one approve-for-session grant; true when a grant was removed. */
|
|
131992
|
+
async revokeSessionApprovalGrant(pattern) {
|
|
131993
|
+
this.ensureOpen();
|
|
131994
|
+
return this.rpc.revokeSessionApprovalGrant({
|
|
131995
|
+
sessionId: this.id,
|
|
131996
|
+
pattern
|
|
131997
|
+
});
|
|
131998
|
+
}
|
|
131345
131999
|
async getMcpStartupMetrics() {
|
|
131346
132000
|
this.ensureOpen();
|
|
131347
132001
|
return this.rpc.getMcpStartupMetrics({ sessionId: this.id });
|
|
@@ -132724,6 +133378,13 @@ const BUILTIN_SLASH_COMMANDS = [
|
|
|
132724
133378
|
priority: 188,
|
|
132725
133379
|
availability: "always"
|
|
132726
133380
|
},
|
|
133381
|
+
{
|
|
133382
|
+
name: "grants",
|
|
133383
|
+
aliases: [],
|
|
133384
|
+
description: "registry.grants_desc",
|
|
133385
|
+
priority: 188,
|
|
133386
|
+
availability: "always"
|
|
133387
|
+
},
|
|
132727
133388
|
{
|
|
132728
133389
|
name: "editor",
|
|
132729
133390
|
aliases: [],
|
|
@@ -134658,6 +135319,17 @@ function buildTraceCells({ wirePath }) {
|
|
|
134658
135319
|
}, time);
|
|
134659
135320
|
break;
|
|
134660
135321
|
}
|
|
135322
|
+
case "permission.record_decision": {
|
|
135323
|
+
const decision = asString(record["decision"]) ?? "";
|
|
135324
|
+
const toolName = asString(record["toolName"]) ?? "";
|
|
135325
|
+
const policyName = asString(record["policyName"]);
|
|
135326
|
+
const reason = asString(record["reason"]);
|
|
135327
|
+
pushCell("context", `${decision === "timeout" ? "审批超时" : decision === "cancelled" ? "审批取消" : "审批拒绝"}: ${toolName}${policyName !== void 0 ? ` (${policyName})` : ""}`, {
|
|
135328
|
+
inputDetail: reason,
|
|
135329
|
+
sourceSeq: seq
|
|
135330
|
+
}, time);
|
|
135331
|
+
break;
|
|
135332
|
+
}
|
|
134661
135333
|
case "request.header": {
|
|
134662
135334
|
const provider = asString(record["provider"]) ?? "";
|
|
134663
135335
|
const model = asString(record["model"]) ?? "";
|
|
@@ -139300,7 +139972,7 @@ async function guidedGoalSetup(host) {
|
|
|
139300
139972
|
host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
|
|
139301
139973
|
return;
|
|
139302
139974
|
}
|
|
139303
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
139975
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-DCMeCMoC.mjs");
|
|
139304
139976
|
const initialDesc = await promptText(host, TextInputDialogComponent, {
|
|
139305
139977
|
title: t("goal.setup_title_initial"),
|
|
139306
139978
|
subtitle: t("goal.setup_desc_hint"),
|
|
@@ -139321,7 +139993,7 @@ async function guidedGoalSetup(host) {
|
|
|
139321
139993
|
await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
|
|
139322
139994
|
}
|
|
139323
139995
|
async function showGoalConfigWizard(host, session, objective, replace) {
|
|
139324
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
139996
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-DCMeCMoC.mjs");
|
|
139325
139997
|
const turnInput = await promptNumber(host, TextInputDialogComponent, {
|
|
139326
139998
|
title: t("goal.wizard_title", { objective }),
|
|
139327
139999
|
subtitle: t("goal.budget_turns_hint"),
|
|
@@ -150747,6 +151419,53 @@ async function handleKnowledgeCommand(host, _args) {
|
|
|
150747
151419
|
showMenu();
|
|
150748
151420
|
}
|
|
150749
151421
|
//#endregion
|
|
151422
|
+
//#region src/tui/commands/grants.ts
|
|
151423
|
+
async function handleGrantsCommand(host) {
|
|
151424
|
+
const session = host.session;
|
|
151425
|
+
if (session === void 0) {
|
|
151426
|
+
host.showError(getNoActiveSessionMessage());
|
|
151427
|
+
return;
|
|
151428
|
+
}
|
|
151429
|
+
let grants;
|
|
151430
|
+
try {
|
|
151431
|
+
grants = await session.getSessionApprovalGrants();
|
|
151432
|
+
} catch (error) {
|
|
151433
|
+
host.showError(t("grants.load_failed", { msg: formatErrorMessage(error) }));
|
|
151434
|
+
return;
|
|
151435
|
+
}
|
|
151436
|
+
if (grants.length === 0) {
|
|
151437
|
+
host.showStatus(t("grants.empty"), host.state.theme.colors.textDim);
|
|
151438
|
+
return;
|
|
151439
|
+
}
|
|
151440
|
+
host.mountEditorReplacement(new ChoicePickerComponent({
|
|
151441
|
+
title: t("grants.title", { count: String(grants.length) }),
|
|
151442
|
+
hint: t("grants.hint"),
|
|
151443
|
+
colors: host.state.theme.colors,
|
|
151444
|
+
options: grants.map((pattern) => ({
|
|
151445
|
+
value: pattern,
|
|
151446
|
+
label: pattern,
|
|
151447
|
+
tone: "danger"
|
|
151448
|
+
})),
|
|
151449
|
+
onSelect: (pattern) => {
|
|
151450
|
+
revokeGrant(host, session, pattern);
|
|
151451
|
+
},
|
|
151452
|
+
onCancel: () => {
|
|
151453
|
+
host.restoreEditor();
|
|
151454
|
+
}
|
|
151455
|
+
}));
|
|
151456
|
+
}
|
|
151457
|
+
async function revokeGrant(host, session, pattern) {
|
|
151458
|
+
try {
|
|
151459
|
+
const revoked = await session.revokeSessionApprovalGrant(pattern);
|
|
151460
|
+
host.restoreEditor();
|
|
151461
|
+
if (revoked) host.showStatus(t("grants.revoked", { pattern }), host.state.theme.colors.success);
|
|
151462
|
+
else host.showError(t("grants.revoke_failed", { pattern }));
|
|
151463
|
+
} catch {
|
|
151464
|
+
host.restoreEditor();
|
|
151465
|
+
host.showError(t("grants.revoke_failed", { pattern }));
|
|
151466
|
+
}
|
|
151467
|
+
}
|
|
151468
|
+
//#endregion
|
|
150750
151469
|
//#region ../../packages/evals/src/judge.ts
|
|
150751
151470
|
/** Runs every rule against the input; a rule passes when its check is true. */
|
|
150752
151471
|
function judgeOutput(input, rules) {
|
|
@@ -151287,6 +152006,9 @@ async function handleBuiltInSlashCommand(host, name, args) {
|
|
|
151287
152006
|
case "permission":
|
|
151288
152007
|
showPermissionPicker(host);
|
|
151289
152008
|
return;
|
|
152009
|
+
case "grants":
|
|
152010
|
+
await handleGrantsCommand(host);
|
|
152011
|
+
return;
|
|
151290
152012
|
case "settings":
|
|
151291
152013
|
showSettingsSelector(host);
|
|
151292
152014
|
return;
|
|
@@ -151405,4 +152127,4 @@ async function handleBuiltInSlashCommand(host, name, args) {
|
|
|
151405
152127
|
}
|
|
151406
152128
|
}
|
|
151407
152129
|
//#endregion
|
|
151408
|
-
export {
|
|
152130
|
+
export { refineGoal as $, ScreamHarness as $n, FooterComponent as $t, renderDiffLinesClustered as A, setExperimentalFlags as An, isEmptySessionHintDismissed as At, AssistantMessageComponent as B, saveTuiConfig as Bn, ENABLE_TERMINAL_FOCUS_REPORTING as Bt, readJsonlFile as C, getCtrlDHint as Cn, supportsBalance as Ct, UserMessageComponent as D, BUILTIN_SLASH_COMMANDS as Dn, resolveThemeSync as Dt, handleRevokeCommand as E, buildSkillSlashCommands as En, showUsage as Et, NoticeMessageComponent as F, PIXEL_PULSE_FRAMES as Fn, getColorPalette as Ft, BackgroundAgentStatusComponent as G, detectInstallSource as Gn, OSC11_RESPONSE_PREFIX_NO_ESC as Gt, charsForTokenBudget as H, getDataDir as Hn, OSC11_QUERY as Ht, StatusMessageComponent as I, PULSE_WAVE_FRAMES as In, detectTerminalTheme as It, BREATHE_CYCLE_MS as J, CLI_USER_AGENT_PRODUCT as Jn, TERMINAL_FOCUS_OUT as Jt, AgentGroupComponent as K, CLI_COMMAND_NAME as Kn, QUERY_TERMINAL_THEME as Kt, replaceTabs as L, TuiConfigParseError as Ln, parseOsc11BackgroundTheme as Lt, langFromPath as M, getKnowledgeStore as Mn, toggleEmptySessionHint as Mt, CachedContainer as N, hasEmbeddingModelCache as Nn, createThemeStyles as Nt, ToolCallComponent as O, sortSlashCommands as On, createEditorTheme as Ot, ThinkingComponent as P, startManualEmbeddingDownload as Pn, contrastTextHex as Pt, clearGoalState as Q, saveCatalogCache as Qn, isStreaming as Qt, sanitizeShellOutput as R, TuiLikePreferencesSchema as Rn, DISABLE_TERMINAL_FOCUS_REPORTING as Rt, appendJsonlLine as S, getCtrlCHint as Sn, refreshProviderBalance as St, getDaemonInstructions as T, getNoActiveSessionMessage as Tn, showStatusReport as Tt, estimateTokens as U, getInputHistoryFile as Un, OSC11_RESPONSE as Ut, ActivityGroupComponent as V, detectShellEnvironment as Vn, ENABLE_TERMINAL_THEME_REPORTING as Vt, getSharedSpeedTracker as W, getLogDir as Wn, OSC11_RESPONSE_PREFIX as Wt, resetBreathingClock as X, DEFAULT_CATALOG_URL as Xn, TERMINAL_THEME_LIGHT as Xt, getBreathingFrame as Y, PRODUCT_NAME as Yn, TERMINAL_THEME_DARK as Yt, CompactionComponent as Z, fetchCatalog as Zn, isBusy as Zt, handleMcpCommand as _, EMPTY_SESSION_HINT_URL as _n, handleWolfpackCommand as _t, buildRoleAdditionalText as a, handleLogoutCommand as an, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE as ar, isInternalMessage as at, refreshUpdateCache as b, SESSION_TIPS as bn, showPermissionPicker as bt, handleSkillCommand as c, appendSessionHeaderHint as cn, resolveScreamHome as cr, getModelCycleLevel as ct, isExpandable as d, formatErrorMessage as dn, resolveGlobalLogPath as dr, handleCompactCommand as dt, lerpGradient as en, encodeWorkDirKey as er, handleExportDebugZipCommand as et, isPlanExpandable as f, isTodoItemShape as fn, isScreamError as fr, handleEditorCommand as ft, handleChannelCommand as g, truncateErrorMessage as gn, handleThemeCommand as gt, handleMemoryCommand as h, stringValue as hn, SCREAM_ERROR_INFO as hr, handlePlanCommand as ht, openUrl as i, handleConnectCommand as in, removeSessionIndexEntry as ir, handleTitleCommand as it, highlightLines as j, embeddingFailureDetail as jn, isTurnElapsedEnabled as jt, renderDiffLines as k, isExperimentalFlagEnabled as kn, createMarkdownTheme as kt, disposeChildren as l, appendStreamingArgsPreview as ln, flushDiagnosticLogs as lr, handleAutoCommand as lt, formatMemoryMemoForInjection as m, serializeToolResultOutput as mn, ErrorCodes as mr, handleModelCommand as mt, clearEvalPanelState as n, handleTraceCommand as nn, appendSessionIndexEntry as nr, handleForkCommand as nt, syncDiagramCapabilityPrompt as o, printableChar as on, isRealUserPrompt as or, toTerminalHyperlink as ot, MoonLoader as p, parseStreamingArgs as pn, isOrphanedToolCallError as pr, handleFusionPlanCommand as pt, WelcomeComponent as q, CLI_UI_MODE as qn, TERMINAL_FOCUS_IN as qt, handleGrantsCommand as r, handleSearchCommand as rn, readSessionIndex as rr, handleInitCommand as rt, handleExtensionCommand as s, STATUS_BULLET as sn, parseRlmMaxDepthArg as sr, changeThinkingLevel as st, dispatchInput as t, SubagentSlots as tn, normalizeWorkDir as tr, handleExportMdCommand as tt, hasDispose as u, argsRecord as un, log as ur, handleBotCommand as ut, handleUpdateCommand as v, EXIT_CONFIRM_WINDOW_MS as vn, handleYoloCommand as vt, handleCcCommand as w, getLlmNotSetMessage as wn, clearInfoPanelState as wt, readUpdateCache as x, TIP_ROTATION_INTERVAL_MS as xn, showSettingsSelector as xt, selectUpdateTarget as y, MAIN_AGENT_ID$1 as yn, showModelPicker as yt, SkillActivationComponent as z, loadTuiConfig as zn, DISABLE_TERMINAL_THEME_REPORTING as zt };
|