@sema-agent/core 7.9.0 → 7.9.1

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/core/ask-origin.d.ts +7 -3
  3. package/dist/core/checkpoint-store.d.ts +2 -7
  4. package/dist/core/effective-path-target.d.ts +43 -0
  5. package/dist/core/effective-path-target.js +56 -0
  6. package/dist/core/engine-notice.d.ts +8 -0
  7. package/dist/core/fs-write-gate-policy.js +2 -1
  8. package/dist/core/gate-lanes.d.ts +1 -0
  9. package/dist/core/gate-lanes.js +11 -5
  10. package/dist/core/governance-codes.d.ts +1 -1
  11. package/dist/core/governance-codes.js +2 -0
  12. package/dist/core/hooks.d.ts +10 -1
  13. package/dist/core/mcp-injection-drop.d.ts +74 -0
  14. package/dist/core/mcp-injection-drop.js +27 -0
  15. package/dist/core/permission-rule-model.d.ts +91 -75
  16. package/dist/core/permission-rule-model.js +90 -111
  17. package/dist/core/permission-rule-org.d.ts +12 -6
  18. package/dist/core/permission-rule-org.js +9 -3
  19. package/dist/core/permission-rules.d.ts +3 -2
  20. package/dist/core/permission-rules.js +38 -15
  21. package/dist/core/persisted-rule-arms.d.ts +7 -2
  22. package/dist/core/persisted-rule-arms.js +3 -1
  23. package/dist/core/runner/active-skill-scope.js +2 -1
  24. package/dist/core/runner/permission-rule-lanes.d.ts +12 -4
  25. package/dist/core/runner/permission-rule-lanes.js +13 -15
  26. package/dist/core/runner/session-rule-policy.js +2 -1
  27. package/dist/core/runner/tool-face-overlay.js +22 -3
  28. package/dist/core/sensitive-path-policy.js +5 -3
  29. package/dist/core/shell-lexer.d.ts +47 -0
  30. package/dist/core/shell-lexer.js +478 -0
  31. package/dist/core/shell-scan.d.ts +60 -0
  32. package/dist/core/shell-scan.js +183 -0
  33. package/dist/core/shell-wrapper-table.d.ts +297 -0
  34. package/dist/core/shell-wrapper-table.js +58 -0
  35. package/dist/core/tool-catalog-entries.js +6 -6
  36. package/dist/core/tool-face.d.ts +80 -4
  37. package/dist/core/tool-face.js +10 -0
  38. package/dist/core/tool-policy.d.ts +1 -6
  39. package/dist/core/tool-registry.d.ts +8 -11
  40. package/dist/core/tool-registry.js +5 -2
  41. package/dist/core/tool-roster.d.ts +11 -2
  42. package/dist/core/tool-roster.js +21 -3
  43. package/dist/index.d.ts +6 -3
  44. package/dist/index.js +5 -2
  45. package/dist/tools/fs/fs-search-tools.d.ts +3 -2
  46. package/dist/tools/fs/fs-search-tools.js +17 -9
  47. package/dist/tools/fs/index.js +1 -1
  48. package/dist/tools/fs/search.d.ts +0 -8
  49. package/dist/tools/fs/search.js +0 -23
  50. package/package.json +1 -1
  51. package/test/export-surface.snapshot.json +57 -1
@@ -46,18 +46,94 @@ export type ToolApprovalCard = (typeof TOOL_APPROVAL_CARDS)[number];
46
46
  export declare const TOOL_PATH_ACCESSES: readonly ["read", "create", "edit"];
47
47
  export type ToolPathAccess = (typeof TOOL_PATH_ACCESSES)[number];
48
48
  /**
49
- * The parameter that names this tool's filesystem target, and how the target is touched. Present the
50
- * tool can be confirmed BY PATH (write confinement, sensitive-path guards, edited-file tracking, the
51
- * transcript-directory gate). `param` outranks every alias (NotebookEdit's `notebook_path` beats a decoy
49
+ * The BASE a RELATIVE spelling in the target slot resolves against the tool's OWN resolution base,
50
+ * DECLARED so every fence resolves the very path the tool will open instead of guessing one:
51
+ *
52
+ * - `cwd` — the live tracked working directory, falling back to the task root when no tracker moved
53
+ * (Read / Edit / Write / NotebookEdit: each hands `cwdRef.current` to `canonicalizeTarget` as its base);
54
+ * - `root` — the task root, always (Grep / Glob: their `path` describes say "relative to root", and the
55
+ * tools hand `rootCanonical` to the same resolver whatever the shell's `cd` did).
56
+ *
57
+ * Absent ⇒ `cwd`: the pre-declaration reading every fence took, kept as the floor for a face (a caller's,
58
+ * an MCP server's) that declares no base. This one word is the whole difference the fences had to model,
59
+ * and getting it wrong is SILENT — a rule about the path the tool really opens simply never fires.
60
+ */
61
+ export declare const TOOL_PATH_BASES: readonly ["cwd", "root"];
62
+ export type ToolPathBase = (typeof TOOL_PATH_BASES)[number];
63
+ /**
64
+ * What the call targets when the target slot carries NO usable spelling:
65
+ *
66
+ * - `none` — nothing. A file tool with no path names no file, so no path rule speaks about the call;
67
+ * - `base` — the resolution base ITSELF. A search tool with no `path` searches the WHOLE base, so a rule
68
+ * naming the base (or anything containing it) does reach the call.
69
+ *
70
+ * Absent ⇒ `none` (the pre-declaration reading: an absent slot was "no resolvable path" for every tool).
71
+ */
72
+ export declare const TOOL_PATH_ABSENCES: readonly ["none", "base"];
73
+ export type ToolPathAbsence = (typeof TOOL_PATH_ABSENCES)[number];
74
+ /**
75
+ * The parameter that names a tool's filesystem target, how the target is touched, and how a spelling in that
76
+ * slot RESOLVES — as a READER sees it, whichever side it came from. Present ⇔ the tool can be confirmed BY
77
+ * PATH (write confinement, sensitive-path guards, edited-file tracking, the transcript-directory gate, the
78
+ * permission-rule path family). `param` outranks every alias (NotebookEdit's `notebook_path` beats a decoy
52
79
  * `file_path`). `skillScopeEligible` (default false) is the SEPARATE authorization domain of the
53
80
  * active-skill `allowPaths` face — a confinable write tool is not automatically eligible there.
81
+ *
82
+ * ONE shape, two spellings: a DECLARATION ({@link ToolPathTarget}, what a catalog row / a `ToolSpec` / an
83
+ * MCP overlay writes) and the roster row's NORMALIZED form (`aliases` always an array, `skillScopeEligible`
84
+ * always a boolean) that the wire carries and the gate stamps on a call. Both are readable through this
85
+ * type, which is what every fence reads — the shape used to be re-declared at each of them, and a member
86
+ * added to one spelling silently never reached the others.
87
+ *
88
+ * The three RESOLUTION members are read by one function (`effectivePathTargetOf`, effective-path-target.ts)
89
+ * that every fence and the tools themselves go through: the declaration is the single source, so a fence
90
+ * and the tool it fences cannot resolve one call to two different files.
54
91
  */
55
- export interface ToolPathTarget {
92
+ export interface ToolCallPathTarget {
56
93
  param: string;
57
94
  aliases?: readonly string[];
58
95
  access: ToolPathAccess;
96
+ skillScopeEligible?: boolean;
97
+ /** How a RELATIVE spelling in `param` (or an alias) resolves. Absent ⇒ `cwd`. */
98
+ base?: ToolPathBase;
99
+ /** What the call targets when the slot is absent. Absent ⇒ `none`. */
100
+ absent?: ToolPathAbsence;
101
+ /**
102
+ * A top-level parameter whose ABSOLUTE glob pattern carries its own base directory (Glob's `pattern`:
103
+ * `/proj/secret/**` searches `/proj/secret` with no `path` at all). Consulted ONLY when the target slot
104
+ * is absent and `absent` is `base` — the tool's own precedence, in which an explicit `path` wins and a
105
+ * relative pattern leaves the base alone.
106
+ */
107
+ patternParam?: string;
108
+ }
109
+ /** The DECLARATION spelling of {@link ToolCallPathTarget}: what a catalog row, a `ToolSpec` or an MCP
110
+ * overlay writes. `skillScopeEligible` is narrowed to `true` — a declaration says "eligible" or says
111
+ * nothing; "eligible: false" is the absence, and spelling it would be a second way to say one thing. */
112
+ export interface ToolPathTarget extends ToolCallPathTarget {
59
113
  skillScopeEligible?: true;
60
114
  }
115
+ /**
116
+ * The NORMALIZED spelling of {@link ToolCallPathTarget}: what the roster MINTS, what the wire carries, and
117
+ * what the gate stamps on a call. `aliases` and `skillScopeEligible` are always present because the mint
118
+ * fills them — a guarantee a consumer reading a stamped face has always had, and one the shared reader type
119
+ * deliberately does not weaken (an optional member here would break `face.pathTarget.aliases.includes(…)`
120
+ * at compile time for every downstream that already reads it).
121
+ */
122
+ export interface ToolRosterPathTarget extends ToolCallPathTarget {
123
+ aliases: readonly string[];
124
+ skillScopeEligible: boolean;
125
+ }
126
+ /**
127
+ * The SLOT half of a target — which argument names the path — with the RESOLUTION half (`base` / `absent` /
128
+ * `patternParam`) dropped, so the reader falls back to the documented defaults.
129
+ *
130
+ * #635, from the adversarial round: the protective floor (design/388 B6 — a face that declares no target of
131
+ * its own is judged against the CATALOG's declaration for the name) rests on "naming a slot can only TIGHTEN
132
+ * a deny". Naming a slot does. Claiming how a FOREIGN tool of the same name RESOLVES that slot does not: a
133
+ * wrong claim moves the judged path to a different file, and the deny the floor exists to keep then misses.
134
+ * So the floor hands over the slot and keeps its mouth shut about the resolution.
135
+ */
136
+ export declare function pathTargetSlotOnly(pt: ToolCallPathTarget): ToolCallPathTarget;
61
137
  /**
62
138
  * The parameter vocabulary a permission rule (`Tool(param:pattern)`) may name. DERIVED by default from
63
139
  * the tool's schema: the SCALAR top-level parameters (string/number/integer/boolean and their enum/const/
@@ -2,6 +2,16 @@ export const TOOL_FAMILIES = ["shell", "file-read", "file-write", "search", "web
2
2
  export const TOOL_MOUNT_TAGS = ["hands", "backgroundTasks", "workflow", "delegation", "hostInternals", "peerLane", "scheduler", "memory", "scenario", "always", "optional"];
3
3
  export const TOOL_APPROVAL_CARDS = ["file-edit", "file-write", "notebook-edit", "shell", "fetch", "plan", "question", "generic"];
4
4
  export const TOOL_PATH_ACCESSES = ["read", "create", "edit"];
5
+ export const TOOL_PATH_BASES = ["cwd", "root"];
6
+ export const TOOL_PATH_ABSENCES = ["none", "base"];
7
+ export function pathTargetSlotOnly(pt) {
8
+ return {
9
+ param: pt.param,
10
+ access: pt.access,
11
+ ...(pt.aliases !== undefined ? { aliases: pt.aliases } : {}),
12
+ ...(pt.skillScopeEligible !== undefined ? { skillScopeEligible: pt.skillScopeEligible } : {}),
13
+ };
14
+ }
5
15
  export const TOOL_WIRE_NAME_MAX_CHARS = 128;
6
16
  export const TOOL_CONTRACT_MAX_CHARS = 64;
7
17
  export const TOOL_KEY_MAX_CHARS = 256;
@@ -4,12 +4,7 @@ import type { Settlement } from "./gate-outcome.js";
4
4
  /** design/388 B6 — the per-call face (see {@link ToolCallRequest.face}). */
5
5
  export interface ToolCallFace {
6
6
  family: import("./tool-face.js").ToolFamily;
7
- pathTarget?: {
8
- param: string;
9
- aliases: readonly string[];
10
- access: "read" | "create" | "edit";
11
- skillScopeEligible: boolean;
12
- };
7
+ pathTarget?: import("./tool-face.js").ToolRosterPathTarget;
13
8
  capabilityId: string;
14
9
  contractId: string;
15
10
  shapeDigest: string;
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * Layer-0 vocabulary: depends on the leaf catalog and the face vocabulary only.
12
12
  */
13
- import { type ToolCatalogEntry, type ToolMountTag, type ToolPathAccess } from "./tool-face.js";
13
+ import { type ToolCallPathTarget, type ToolCatalogEntry, type ToolMountTag, type ToolPathAccess } from "./tool-face.js";
14
14
  /** The whole static catalog, one frozen row per definition (design/388 §2.1 `describeToolCatalog()`).
15
15
  * Replaces the retired name-keyed exports `HAND_TOOL_EFFECTS` / `DEFAULT_BUDGET_EXEMPT_TOOLS` /
16
16
  * `COMPACTABLE_TOOLS`: filter this instead (`e.mountedBy.includes("hands")`, `e.face.budgetExempt`,
@@ -45,24 +45,21 @@ export interface ToolCallLike {
45
45
  toolName: string;
46
46
  args: unknown;
47
47
  face?: {
48
- pathTarget?: {
49
- param: string;
50
- aliases?: readonly string[];
51
- access: ToolPathAccess;
52
- };
48
+ pathTarget?: ToolCallPathTarget;
53
49
  } | undefined;
54
50
  }
55
- type CallPathTarget = {
56
- param: string;
57
- aliases?: readonly string[];
58
- access: ToolPathAccess;
59
- };
51
+ type CallPathTarget = ToolCallPathTarget;
60
52
  /**
61
53
  * design/388 B6/B17 — the path target a PROTECTIVE fence reads (a deny-list judging a write's target: sensitive paths,
62
54
  * frozen paths, write protection, the transcript gate; edit tracking): the object face's own declaration first, the
63
55
  * catalog's declaration for the NAME as the FLOOR — for a bare request (a policy invoked outside a Runner) and for a
64
56
  * face that declares none. Reading the catalog for an undeclared same-name tool can only TIGHTEN a deny-list (it adds
65
57
  * a slot to judge — the pre-388 posture, never wider), which is exactly why this floor is wrong for a grant.
58
+ *
59
+ * #635: "can only tighten" is true of the SLOT and false of the RESOLUTION, so the floor hands over the first and
60
+ * defaults the second (`pathTargetSlotOnly`). Telling a fence that a FOREIGN tool of the same name resolves its slot
61
+ * against the project root is a claim about someone else's implementation: get it wrong and the judged path moves to
62
+ * a different file, and the deny this floor exists to preserve simply misses.
66
63
  */
67
64
  export declare function protectivePathTargetOf(call: ToolCallLike): CallPathTarget | undefined;
68
65
  /**
@@ -1,4 +1,4 @@
1
- import { TOOL_MOUNT_TAGS } from "./tool-face.js";
1
+ import { TOOL_MOUNT_TAGS, pathTargetSlotOnly } from "./tool-face.js";
2
2
  import { ENGINE_CARDS, TOOL_CATALOG_ENTRIES } from "./tool-catalog-entries.js";
3
3
  export function describeToolCatalog() {
4
4
  return TOOL_CATALOG_ENTRIES;
@@ -49,7 +49,10 @@ export function pathTargetValue(args, pt) {
49
49
  return undefined;
50
50
  }
51
51
  export function protectivePathTargetOf(call) {
52
- return call.face?.pathTarget ?? pathTargetOf(call.toolName);
52
+ if (call.face?.pathTarget !== undefined)
53
+ return call.face.pathTarget;
54
+ const floor = pathTargetOf(call.toolName);
55
+ return floor === undefined || call.face === undefined ? floor : pathTargetSlotOnly(floor);
53
56
  }
54
57
  export function declaredPathTargetOf(call) {
55
58
  return call.face !== undefined ? call.face.pathTarget : pathTargetOf(call.toolName);
@@ -1,6 +1,6 @@
1
1
  import { Type, type Static, type TSchema } from "typebox";
2
2
  import type { AgentTool } from "../internal/harness-types.js";
3
- import { type ToolFamily, type ToolMountTag, type ToolPathTarget, type ToolRenderHints, type ToolRuleFace } from "./tool-face.js";
3
+ import { type ToolFamily, type ToolRosterPathTarget, type ToolMountTag, type ToolPathTarget, type ToolRenderHints, type ToolRuleFace } from "./tool-face.js";
4
4
  import type { ToolContentOrigin, ToolEffect } from "./tool-spec.js";
5
5
  export declare const ROSTER_SOURCES: readonly ["builtin", "caller", "host", "mcp", "a2a", "external", "synthetic"];
6
6
  export type RosterSource = (typeof ROSTER_SOURCES)[number];
@@ -55,6 +55,9 @@ export declare const ToolRosterEntry: Type.TObject<{
55
55
  aliases: Type.TArray<Type.TString>;
56
56
  access: Type.TUnion<[Type.TLiteral<"read">, Type.TLiteral<"create">, Type.TLiteral<"edit">]>;
57
57
  skillScopeEligible: Type.TBoolean;
58
+ base: Type.TOptional<Type.TUnion<[Type.TLiteral<"cwd">, Type.TLiteral<"root">]>>;
59
+ absent: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"base">]>>;
60
+ patternParam: Type.TOptional<Type.TString>;
58
61
  }>>;
59
62
  ruleFace: Type.TObject<{
60
63
  primaryParams: Type.TArray<Type.TString>;
@@ -126,6 +129,9 @@ export declare const ToolRoster: Type.TObject<{
126
129
  aliases: Type.TArray<Type.TString>;
127
130
  access: Type.TUnion<[Type.TLiteral<"read">, Type.TLiteral<"create">, Type.TLiteral<"edit">]>;
128
131
  skillScopeEligible: Type.TBoolean;
132
+ base: Type.TOptional<Type.TUnion<[Type.TLiteral<"cwd">, Type.TLiteral<"root">]>>;
133
+ absent: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"base">]>>;
134
+ patternParam: Type.TOptional<Type.TString>;
129
135
  }>>;
130
136
  ruleFace: Type.TObject<{
131
137
  primaryParams: Type.TArray<Type.TString>;
@@ -205,6 +211,9 @@ export declare const ToolRosterDelta: Type.TObject<{
205
211
  aliases: Type.TArray<Type.TString>;
206
212
  access: Type.TUnion<[Type.TLiteral<"read">, Type.TLiteral<"create">, Type.TLiteral<"edit">]>;
207
213
  skillScopeEligible: Type.TBoolean;
214
+ base: Type.TOptional<Type.TUnion<[Type.TLiteral<"cwd">, Type.TLiteral<"root">]>>;
215
+ absent: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"base">]>>;
216
+ patternParam: Type.TOptional<Type.TString>;
208
217
  }>>;
209
218
  ruleFace: Type.TObject<{
210
219
  primaryParams: Type.TArray<Type.TString>;
@@ -265,7 +274,7 @@ export declare function parkedToolFace(roster: ToolRoster | undefined, toolName:
265
274
  * partial face). This is the face every recheck of the row judges under, roster or no roster. */
266
275
  export declare function parkedCallFaceOf(pending: {
267
276
  family?: ToolFamily;
268
- pathTarget?: ToolRosterEntry["pathTarget"];
277
+ pathTarget?: ToolRosterPathTarget;
269
278
  capabilityId?: string;
270
279
  contractId?: string;
271
280
  shapeDigest?: string;
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { Type } from "typebox";
3
3
  import { getToolContract } from "../prompt-assembly/tool-catalog.js";
4
4
  import { protocolOf } from "./protocol-table.js";
5
- import { RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_CHARS, RENDER_HINT_MAX_LIST, TOOL_APPROVAL_CARDS, TOOL_FAMILIES, TOOL_KEYS_MAX, TOOL_KEY_MAX_CHARS, TOOL_MOUNT_TAGS, } from "./tool-face.js";
5
+ import { RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_CHARS, RENDER_HINT_MAX_LIST, TOOL_APPROVAL_CARDS, TOOL_FAMILIES, TOOL_KEYS_MAX, TOOL_KEY_MAX_CHARS, TOOL_MOUNT_TAGS, TOOL_PATH_ABSENCES, TOOL_PATH_BASES, } from "./tool-face.js";
6
6
  function lit(values) {
7
7
  return Type.Union(values.map((v) => Type.Literal(v)));
8
8
  }
@@ -42,7 +42,15 @@ export const ToolRosterEntry = Type.Object({
42
42
  contentOrigin: lit(["local", "execution", "external"]),
43
43
  contentOriginProvenance: lit(["declared", "server", "exempted", "default"]),
44
44
  family: lit(TOOL_FAMILIES),
45
- pathTarget: Type.Optional(Type.Object({ param: Key, aliases: Type.Array(Key), access: lit(["read", "create", "edit"]), skillScopeEligible: Type.Boolean() }, { additionalProperties: false })),
45
+ pathTarget: Type.Optional(Type.Object({
46
+ param: Key,
47
+ aliases: Type.Array(Key),
48
+ access: lit(["read", "create", "edit"]),
49
+ skillScopeEligible: Type.Boolean(),
50
+ base: Type.Optional(lit(TOOL_PATH_BASES)),
51
+ absent: Type.Optional(lit(TOOL_PATH_ABSENCES)),
52
+ patternParam: Type.Optional(Key),
53
+ }, { additionalProperties: false })),
46
54
  ruleFace: Type.Object({ primaryParams: Type.Array(Key), params: Type.Array(Key) }, { additionalProperties: false }),
47
55
  approvalPreview: Type.Boolean(),
48
56
  validateInput: Type.Boolean(),
@@ -392,7 +400,17 @@ export function mintRosterEntry(tool, meta, facts) {
392
400
  contentOriginProvenance,
393
401
  family: face.family !== undefined && TOOL_FAMILIES.includes(face.family) ? face.family : "other",
394
402
  ...(pathTarget !== undefined
395
- ? { pathTarget: { param: pathTarget.param, aliases: [...(pathTarget.aliases ?? [])], access: pathTarget.access, skillScopeEligible: pathTarget.skillScopeEligible === true } }
403
+ ? {
404
+ pathTarget: {
405
+ param: pathTarget.param,
406
+ aliases: [...(pathTarget.aliases ?? [])],
407
+ access: pathTarget.access,
408
+ skillScopeEligible: pathTarget.skillScopeEligible === true,
409
+ ...(pathTarget.base !== undefined ? { base: pathTarget.base } : {}),
410
+ ...(pathTarget.absent !== undefined ? { absent: pathTarget.absent } : {}),
411
+ ...(pathTarget.patternParam !== undefined ? { patternParam: pathTarget.patternParam } : {}),
412
+ },
413
+ }
396
414
  : {}),
397
415
  ruleFace: rf,
398
416
  approvalPreview: typeof tool.approvalPreview === "function",
package/dist/index.d.ts CHANGED
@@ -53,6 +53,7 @@ export { runCascade, type CascadeRung, type CascadeConfig, type CascadeAttempt,
53
53
  export { pgQuery, mysqlQuery, sqliteQuery } from "./tools/sql-adapters.js";
54
54
  export { materializeMcpTools, MCP_PREFIX, type MaterializedMcp, type McpServerStatus, type McpRefreshResult } from "./core/mcp.js";
55
55
  export { MCP_FAILURE_KINDS, MCP_DELIVERY_VERDICTS, classifyMcpFailure, type McpFailure, type McpFailureKind, type McpDelivered } from "./core/mcp-failure.js";
56
+ export { MCP_INJECTION_DROP_REASONS, MCP_INJECTION_DROP_TEXT, mcpInjectionDroppedNotice, type McpInjectionDropReason, type McpInjectionDropFacts } from "./core/mcp-injection-drop.js";
56
57
  export { materializeA2aTools, A2aRpcError, type MaterializedA2a, type A2aPeerStatus, type A2aRefreshResult, type A2aToolAxis } from "./core/a2a.js";
57
58
  export { PROTOCOL_TABLE, MCP_NAMESPACE, A2A_NAMESPACE, protocolOf, type ProtocolNamespace, type ProtocolId } from "./core/protocol-table.js";
58
59
  export { InMemorySessionPolicyStore, SessionPolicyError, loosenReasons, normalizeRules, stripRev, type SessionPolicyStore, type SessionPermissionRules, type StoredSessionRules, type SessionRulesRecord, type PutRulesOptions, } from "./core/session-policy-store.js";
@@ -124,10 +125,11 @@ export { createFsWriteGatePolicy, type FsWriteGatePolicyOptions } from "./core/f
124
125
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
125
126
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
126
127
  export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, isTerminalTaskNotification, SystemInjectionQueue, type TaskNotificationPayload, type TaskNotificationStatus, type ExternalNotificationInput, type SystemInjection, type SystemInjectionPriority, SYSTEM_INJECTION_PRIORITIES, isSystemInjectionPriority, AGENT_MESSAGE_TAG, renderAgentMessageFrame, type SemaProvenance, } from "./core/task-notification.js";
127
- export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, type ToolFamily, type ToolMountTag, type ToolApprovalCard, type ToolPathAccess, type ToolPathTarget, type ToolRuleFace, type ToolRenderHints, type ToolFaceDeclaration, type ToolCatalogEntry, } from "./core/tool-face.js";
128
+ export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_PATH_BASES, TOOL_PATH_ABSENCES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, type ToolFamily, type ToolMountTag, type ToolApprovalCard, type ToolPathAccess, type ToolPathBase, type ToolPathAbsence, type ToolPathTarget, type ToolCallPathTarget, type ToolRosterPathTarget, pathTargetSlotOnly, type ToolRuleFace, type ToolRenderHints, type ToolFaceDeclaration, type ToolCatalogEntry, } from "./core/tool-face.js";
128
129
  export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRuleFaceOf, pathTargetOf, pathTargetValue, protectivePathTargetOf, declaredPathTargetOf, isProtectedWrite, isDeclaredWrite, fileOpAccessOf, type ToolCallLike, pathConfinableWriteToolNames, skillScopeWriteToolNames, pathTargetingToolNames, compactableToolNames, budgetExemptToolNames, handBandEffects, engineCardTypes, predictMountedToolNames, HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND, } from "./core/tool-registry.js";
129
130
  export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, type RosterSource, type ToolIdentity, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, inputKeysOf, ruleFaceOfInstance, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
130
131
  export { type ToolCallFace } from "./core/tool-policy.js";
132
+ export { effectivePathTargetOf, pathTargetBaseOf, type ToolPathBases } from "./core/effective-path-target.js";
131
133
  export { TOOL_CONFORMANCE_VECTORS, describeToolConformanceVectors, type ToolConformanceVector } from "./core/tool-conformance.js";
132
134
  export { applyMcpToolFaces, toolFaceProblem, APPROVAL_CARD_REQUIRED_KEYS, type McpToolFace } from "./core/runner/tool-face-overlay.js";
133
135
  export { createStructuredProjector } from "./core/runner/tool-output-projection.js";
@@ -186,12 +188,13 @@ export { createPermissionRulePolicy, validatePermissionRules, parsePermissionRul
186
188
  * and nothing more. Removal is exported without ceremony, because narrowing on a user's behalf is
187
189
  * allowed and widening is not.
188
190
  */
189
- export { RULE_BEHAVIORS, RULE_BEHAVIOR_PRECEDENCE, RULE_BEHAVIORS_BY_PRECEDENCE, isRuleBehavior, readRowBehavior, adjudicatePersistedRules, adjudicatePersistedPathRules, ruleReachesProgramRun, pathRuleReaches, ruleToolGrammarOf, COMMAND_RULE_TOOL, READ_RULE_TOOL, type PathRuleBases, type RuleBehavior, type PersistedRuleVerdict, parseRuleText, formatRuleText, ruleAdmitsCommand, findAdmittingRule, suggestRulesForCommand, scopeCoversCwd, pathWithinRoot, isRuleLive, renderUntrustedCommandText, stripFormatCharacters, UNCOVERED_SEGMENT_REASON_BASELINE, RULE_OFFERS_ABSENCE_BASELINE, type UncoveredSegmentDetail, type EditedRuleBreadthWarning, directoryRuleAdmits, BARE_INTERPRETER_NAMES, MAX_RULE_TEXT_CHARS, type PersistedRule, type RuleTombstone, type RuleScope, type RuleDot, type RuleAdd, type RuleAddOrigin, type RuleOffer, type RuleOfferBatchMember, type SegmentRuleSuggestion, type SegmentCoverage, type RuleReject, type RuleRejectCode, type ParsedRule, type PersistedRuleTool, type PersistedRuleMatch, } from "./core/permission-rule-model.js";
191
+ export { RULE_BEHAVIORS, RULE_BEHAVIOR_PRECEDENCE, RULE_BEHAVIORS_BY_PRECEDENCE, isRuleBehavior, readRowBehavior, adjudicatePersistedRules, adjudicatePersistedPathRules, ruleReachesProgramRun, programRunReachOf, PROGRAM_RUN_REACHES, type ProgramRunReach, type ProgramRunReachOutcome, pathRuleReaches, ruleToolGrammarOf, COMMAND_RULE_TOOL, READ_RULE_TOOL, type PathRuleBases, type RuleBehavior, type PersistedRuleVerdict, parseRuleText, formatRuleText, ruleAdmitsCommand, findAdmittingRule, suggestRulesForCommand, scopeCoversCwd, pathWithinRoot, isRuleLive, renderUntrustedCommandText, stripFormatCharacters, UNCOVERED_SEGMENT_REASON_BASELINE, RULE_OFFERS_ABSENCE_BASELINE, type UncoveredSegmentDetail, type EditedRuleBreadthWarning, directoryRuleAdmits, BARE_INTERPRETER_NAMES, MAX_RULE_TEXT_CHARS, type PersistedRule, type RuleTombstone, type RuleScope, type RuleDot, type RuleAdd, type RuleAddOrigin, type RuleOffer, type RuleOfferBatchMember, type SegmentRuleSuggestion, type SegmentCoverage, type RuleReject, type RuleRejectCode, type ParsedRule, type PersistedRuleTool, type PersistedRuleMatch, } from "./core/permission-rule-model.js";
190
192
  export { removePersistedRule, applyTombstones, sameScope, sameRuleIdentity, isValidConsentScope, isValidDurableScope, InMemoryDurableRulePartition, EMPTY_DURABLE_RULE_PARTITION, type DurableRulePartition, type DurableRulePartitionProvider, type RuleWriteOutcome, type StoredRules, type RemoveResult, type PutResult, joinRuleStates, screenRuleSyncState, collectBelowFrontier, ruleSyncVector, joinFrontiers, dotAtOrBelowFrontier, sameRuleOwner, type RuleSyncState, type RuleSyncFrontier, type RuleSyncDrop, type RuleSyncLandingReport, type RuleOwner, type QuarantinedRuleAdd, PERMISSION_RULE_WRITER, writerOf, foldDelta, addDotsOf, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, type PermissionRuleWriter, type WritableDurableRulePartition, type RuleWriteDelta, type RuleAddDelta, type RuleDeleteDelta, type RuleSyncJoinDelta, type RawRuleSyncState, type RedemptionAuthorization, } from "./core/permission-rule-store.js";
191
193
  export { syncPermissionRules, parseRuleSyncResponse, PERMISSION_RULE_SYNC_PATH, LOCAL_OWNER_UNSYNCABLE_CODE, type PermissionRuleSyncTransport, type PermissionRuleSyncResult, type RuleSyncRequestBody, type RuleSyncResponseBody, } from "./core/permission-rule-sync.js";
192
194
  export { createPermissionRuleStoreProvider, effectivePermissionRules, effectiveOrThrow, ruleSourceOf, type PermissionRuleStore, type PermissionRuleStoreProvider, type PermissionRuleStoreConfig, type EffectivePermissionRules, type EffectivePermissionRule, type RemovedPermissionRule, type RuleSource, } from "./core/permission-rule-provider.js";
193
195
  export { InMemorySessionRulePartition, type SessionRulePartition, type SessionRuleAdd, type SessionRuleApplyResult, } from "./core/permission-rule-session.js";
194
- export { orgRuleVerdictFor, orgRuleShadows, unenforceableOrgRules, ORG_UNAVAILABLE_DECISION_REASON, ORG_RULE_DECISION_REASON, ORG_ADJUDICATION_TIMEOUT_MS, type OrgPermissionRule, type OrgRuleSnapshot, type OrgRuleSnapshotProvider, type OrgRuleStatePersistence, type PersistedOrgRuleState, type OrgRulePartitionConfig, type OrgRuleResolution, type OrgRuleStatus, } from "./core/permission-rule-org.js";
196
+ export { readShellCommand, isFullyReadable, MAX_SHELL_READ_CHARS, SHELL_WRAPPER_TABLE, type ShellCommandShape, type ShellSegment, type ShellWord, type ShellWrapperName, } from "./core/shell-lexer.js";
197
+ export { orgRuleVerdictFor, type OrgRuleVerdict, orgRuleShadows, unenforceableOrgRules, ORG_UNAVAILABLE_DECISION_REASON, ORG_RULE_DECISION_REASON, ORG_ADJUDICATION_TIMEOUT_MS, type OrgPermissionRule, type OrgRuleSnapshot, type OrgRuleSnapshotProvider, type OrgRuleStatePersistence, type PersistedOrgRuleState, type OrgRulePartitionConfig, type OrgRuleResolution, type OrgRuleStatus, } from "./core/permission-rule-org.js";
195
198
  export { RULE_SYNC_DROP_CODES, type RuleSyncDropReason, type RuleQuarantineReason } from "./core/governance-codes.js";
196
199
  export { prepareCardApproval, confirmRuleApproval, type ConfirmResult, type ConfirmRefusalReason, precheckEditedRuleText, type EditedRuleTextPrecheck, redeemRuleTicket, redeemRuleBatch, prepareCcImport, prepareStarterBatch, mintRuleTicket, STARTER_RULES, InMemoryRuleApprovalRecordStore, ruleOffersOfRecord, type RuleTicket, type RuleCandidate, type RuleApprovalKind, type RuleApprovalRecord, type RuleApprovalRecordStore, type RuleOffer2, type StaleRuleApprovalRecord, type RuleConsentDeps, type RedeemResult, type RedeemedBatchMember, type CcImportLayer, type ImportedSettingsLayer, type ImportPreview, } from "./core/permission-rule-consent.js";
197
200
  export { FileDurableRulePartitionProvider, ORG_STATE_FILE } from "./stores/file/permission-rule-store.js";
package/dist/index.js CHANGED
@@ -40,6 +40,7 @@ export { runCascade, } from "./agents/cascade.js";
40
40
  export { pgQuery, mysqlQuery, sqliteQuery } from "./tools/sql-adapters.js";
41
41
  export { materializeMcpTools, MCP_PREFIX } from "./core/mcp.js";
42
42
  export { MCP_FAILURE_KINDS, MCP_DELIVERY_VERDICTS, classifyMcpFailure } from "./core/mcp-failure.js";
43
+ export { MCP_INJECTION_DROP_REASONS, MCP_INJECTION_DROP_TEXT, mcpInjectionDroppedNotice } from "./core/mcp-injection-drop.js";
43
44
  export { materializeA2aTools, A2aRpcError } from "./core/a2a.js";
44
45
  export { PROTOCOL_TABLE, MCP_NAMESPACE, A2A_NAMESPACE, protocolOf } from "./core/protocol-table.js";
45
46
  export { InMemorySessionPolicyStore, SessionPolicyError, loosenReasons, normalizeRules, stripRev, } from "./core/session-policy-store.js";
@@ -100,10 +101,11 @@ export { createFsWriteGatePolicy } from "./core/fs-write-gate-policy.js";
100
101
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
101
102
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
102
103
  export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, isTerminalTaskNotification, SystemInjectionQueue, SYSTEM_INJECTION_PRIORITIES, isSystemInjectionPriority, AGENT_MESSAGE_TAG, renderAgentMessageFrame, } from "./core/task-notification.js";
103
- export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, } from "./core/tool-face.js";
104
+ export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_PATH_BASES, TOOL_PATH_ABSENCES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, pathTargetSlotOnly, } from "./core/tool-face.js";
104
105
  export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRuleFaceOf, pathTargetOf, pathTargetValue, protectivePathTargetOf, declaredPathTargetOf, isProtectedWrite, isDeclaredWrite, fileOpAccessOf, pathConfinableWriteToolNames, skillScopeWriteToolNames, pathTargetingToolNames, compactableToolNames, budgetExemptToolNames, handBandEffects, engineCardTypes, predictMountedToolNames, HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND, } from "./core/tool-registry.js";
105
106
  export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, inputKeysOf, ruleFaceOfInstance, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
106
107
  export {} from "./core/tool-policy.js";
108
+ export { effectivePathTargetOf, pathTargetBaseOf } from "./core/effective-path-target.js";
107
109
  export { TOOL_CONFORMANCE_VECTORS, describeToolConformanceVectors } from "./core/tool-conformance.js";
108
110
  export { applyMcpToolFaces, toolFaceProblem, APPROVAL_CARD_REQUIRED_KEYS } from "./core/runner/tool-face-overlay.js";
109
111
  export { createStructuredProjector } from "./core/runner/tool-output-projection.js";
@@ -145,11 +147,12 @@ export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/au
145
147
  export { AUTO_MODE_ARMING_RECIPE_VERSION, autoModeArmingRecipeOf, sanitizeAutoModeArmingRecipe, foldAutoModeArming, } from "./core/auto-mode-arming.js";
146
148
  export { rebuildAutoModeDecider, } from "./core/auto-mode-rebuild.js";
147
149
  export { createPermissionRulePolicy, validatePermissionRules, parsePermissionRule, wildcardMatch, isNamespacedCoveringRuleName, namespacedRuleNameCovers, } from "./core/permission-rules.js";
148
- export { RULE_BEHAVIORS, RULE_BEHAVIOR_PRECEDENCE, RULE_BEHAVIORS_BY_PRECEDENCE, isRuleBehavior, readRowBehavior, adjudicatePersistedRules, adjudicatePersistedPathRules, ruleReachesProgramRun, pathRuleReaches, ruleToolGrammarOf, COMMAND_RULE_TOOL, READ_RULE_TOOL, parseRuleText, formatRuleText, ruleAdmitsCommand, findAdmittingRule, suggestRulesForCommand, scopeCoversCwd, pathWithinRoot, isRuleLive, renderUntrustedCommandText, stripFormatCharacters, UNCOVERED_SEGMENT_REASON_BASELINE, RULE_OFFERS_ABSENCE_BASELINE, directoryRuleAdmits, BARE_INTERPRETER_NAMES, MAX_RULE_TEXT_CHARS, } from "./core/permission-rule-model.js";
150
+ export { RULE_BEHAVIORS, RULE_BEHAVIOR_PRECEDENCE, RULE_BEHAVIORS_BY_PRECEDENCE, isRuleBehavior, readRowBehavior, adjudicatePersistedRules, adjudicatePersistedPathRules, ruleReachesProgramRun, programRunReachOf, PROGRAM_RUN_REACHES, pathRuleReaches, ruleToolGrammarOf, COMMAND_RULE_TOOL, READ_RULE_TOOL, parseRuleText, formatRuleText, ruleAdmitsCommand, findAdmittingRule, suggestRulesForCommand, scopeCoversCwd, pathWithinRoot, isRuleLive, renderUntrustedCommandText, stripFormatCharacters, UNCOVERED_SEGMENT_REASON_BASELINE, RULE_OFFERS_ABSENCE_BASELINE, directoryRuleAdmits, BARE_INTERPRETER_NAMES, MAX_RULE_TEXT_CHARS, } from "./core/permission-rule-model.js";
149
151
  export { removePersistedRule, applyTombstones, sameScope, sameRuleIdentity, isValidConsentScope, isValidDurableScope, InMemoryDurableRulePartition, EMPTY_DURABLE_RULE_PARTITION, joinRuleStates, screenRuleSyncState, collectBelowFrontier, ruleSyncVector, joinFrontiers, dotAtOrBelowFrontier, sameRuleOwner, PERMISSION_RULE_WRITER, writerOf, foldDelta, addDotsOf, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, } from "./core/permission-rule-store.js";
150
152
  export { syncPermissionRules, parseRuleSyncResponse, PERMISSION_RULE_SYNC_PATH, LOCAL_OWNER_UNSYNCABLE_CODE, } from "./core/permission-rule-sync.js";
151
153
  export { createPermissionRuleStoreProvider, effectivePermissionRules, effectiveOrThrow, ruleSourceOf, } from "./core/permission-rule-provider.js";
152
154
  export { InMemorySessionRulePartition, } from "./core/permission-rule-session.js";
155
+ export { readShellCommand, isFullyReadable, MAX_SHELL_READ_CHARS, SHELL_WRAPPER_TABLE, } from "./core/shell-lexer.js";
153
156
  export { orgRuleVerdictFor, orgRuleShadows, unenforceableOrgRules, ORG_UNAVAILABLE_DECISION_REASON, ORG_RULE_DECISION_REASON, ORG_ADJUDICATION_TIMEOUT_MS, } from "./core/permission-rule-org.js";
154
157
  export { RULE_SYNC_DROP_CODES } from "./core/governance-codes.js";
155
158
  export { prepareCardApproval, confirmRuleApproval, precheckEditedRuleText, redeemRuleTicket, redeemRuleBatch, prepareCcImport, prepareStarterBatch, mintRuleTicket, STARTER_RULES, InMemoryRuleApprovalRecordStore, ruleOffersOfRecord, } from "./core/permission-rule-consent.js";
@@ -1,6 +1,7 @@
1
1
  import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
2
2
  import type { ReadDenyMatcher } from "./read-deny.js";
3
3
  import type { ReadFace } from "./read-face.js";
4
+ import type { CwdRef } from "./fs-shared.js";
4
5
  /**
5
6
  * The Grep card's details assembly, PURE over the engine text ({mode, offset} from the request) —
6
7
  * exported so the text→structured mapping is pinnable with synthetic texts (the byte-truncation and
@@ -10,5 +11,5 @@ import type { ReadFace } from "./read-face.js";
10
11
  * this seam the honesty boundary.
11
12
  */
12
13
  export declare function grepDetailFields(text: string, mode: "files_with_matches" | "content" | "count", offset?: number, structuredRows?: readonly import("./search.js").GrepRow[]): Record<string, unknown>;
13
- export declare function createGrepTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?: ReadFace): AgentTool;
14
- export declare function createGlobTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?: ReadFace): AgentTool;
14
+ export declare function createGrepTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?: ReadFace, cwdRef?: CwdRef): AgentTool;
15
+ export declare function createGlobTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?: ReadFace, cwdRef?: CwdRef): AgentTool;
@@ -1,8 +1,10 @@
1
1
  import { Type } from "typebox";
2
2
  import { defineTool, errorResult } from "../../core/tools.js";
3
3
  import { resolveKey, violationText, violationDetails } from "./safety.js";
4
- import { runGrepDetailed, runGlobDetailed, splitAbsoluteGlobPattern, invalidGlobTokens } from "./search.js";
4
+ import { runGrepDetailed, runGlobDetailed, invalidGlobTokens } from "./search.js";
5
5
  import { toolFace } from "../../core/tool-catalog-entries.js";
6
+ import { pathTargetBaseOf, splitAbsoluteGlobPattern } from "../../core/effective-path-target.js";
7
+ import { pathTargetValue } from "../../core/tool-registry.js";
6
8
  export function grepDetailFields(text, mode, offset, structuredRows) {
7
9
  const structuredIdentityComplete = structuredRows !== undefined && structuredRows.every((r) => r.path !== undefined || r.text === "--");
8
10
  const structuredPaths = structuredIdentityComplete && structuredRows !== undefined ? [...new Set(structuredRows.filter((r) => r.path !== undefined).map((r) => r.path))] : undefined;
@@ -72,7 +74,8 @@ export function grepDetailFields(text, mode, offset, structuredRows) {
72
74
  }
73
75
  return detailFields;
74
76
  }
75
- export function createGrepTool(env, rootCanonical, additionalRoots, readDeny, readFace) {
77
+ export function createGrepTool(env, rootCanonical, additionalRoots, readDeny, readFace, cwdRef) {
78
+ const declaredTarget = toolFace("grep").pathTarget;
76
79
  return defineTool({
77
80
  name: "Grep",
78
81
  ...toolFace("grep"),
@@ -136,9 +139,11 @@ export function createGrepTool(env, rootCanonical, additionalRoots, readDeny, re
136
139
  `Close the bracket/brace, or drop \`glob\` and filter with \`path\`/\`type\` instead.`);
137
140
  }
138
141
  }
139
- let scoped = a.path;
140
- if (a.path !== undefined) {
141
- const r = await resolveKey(env, rootCanonical, a.path, ctx.signal, rootCanonical, additionalRoots, undefined, readDeny, readFace);
142
+ const baseCwd = pathTargetBaseOf(declaredTarget, { root: rootCanonical, cwd: cwdRef?.current });
143
+ const scopeSpelling = declaredTarget === undefined ? undefined : pathTargetValue(a, declaredTarget);
144
+ let scoped = scopeSpelling;
145
+ if (scopeSpelling !== undefined) {
146
+ const r = await resolveKey(env, rootCanonical, scopeSpelling, ctx.signal, baseCwd, additionalRoots, undefined, readDeny, readFace);
142
147
  if (!r.ok)
143
148
  return errorResult(violationText("Grep", r.violation), violationDetails(r.violation));
144
149
  scoped = r.key;
@@ -146,7 +151,7 @@ export function createGrepTool(env, rootCanonical, additionalRoots, readDeny, re
146
151
  if (!info.ok) {
147
152
  const ex = await env.exists(scoped, ctx.signal);
148
153
  if (!(ex.ok && !ex.value)) {
149
- return errorResult(`Error (Grep): cannot determine the type of "${a.path.slice(0, 300)}" (stat failed: ${info.error.message.slice(0, 200)}); refusing to search it — if it is a FIFO/socket/device, reading it would hang. Retry if this was transient.`);
154
+ return errorResult(`Error (Grep): cannot determine the type of "${scopeSpelling.slice(0, 300)}" (stat failed: ${info.error.message.slice(0, 200)}); refusing to search it — if it is a FIFO/socket/device, reading it would hang. Retry if this was transient.`);
150
155
  }
151
156
  }
152
157
  }
@@ -172,7 +177,8 @@ export function createGrepTool(env, rootCanonical, additionalRoots, readDeny, re
172
177
  },
173
178
  });
174
179
  }
175
- export function createGlobTool(env, rootCanonical, additionalRoots, readDeny, readFace) {
180
+ export function createGlobTool(env, rootCanonical, additionalRoots, readDeny, readFace, cwdRef) {
181
+ const declaredTarget = toolFace("glob").pathTarget;
176
182
  return defineTool({
177
183
  name: "Glob",
178
184
  ...toolFace("glob"),
@@ -195,7 +201,8 @@ export function createGlobTool(env, rootCanonical, additionalRoots, readDeny, re
195
201
  max_results: Type.Optional(Type.Number({ description: "Cap results (default 500)." })),
196
202
  }),
197
203
  execute: async (args, ctx) => {
198
- let { pattern, path } = args;
204
+ let pattern = args.pattern;
205
+ let path = declaredTarget === undefined ? undefined : pathTargetValue(args, declaredTarget);
199
206
  const { max_results } = args;
200
207
  if (path === undefined) {
201
208
  const split = splitAbsoluteGlobPattern(pattern);
@@ -204,9 +211,10 @@ export function createGlobTool(env, rootCanonical, additionalRoots, readDeny, re
204
211
  pattern = split.relativePattern;
205
212
  }
206
213
  }
214
+ const baseCwd = pathTargetBaseOf(declaredTarget, { root: rootCanonical, cwd: cwdRef?.current });
207
215
  let scoped = path;
208
216
  if (path !== undefined) {
209
- const r = await resolveKey(env, rootCanonical, path, ctx.signal, rootCanonical, additionalRoots, undefined, readDeny, readFace);
217
+ const r = await resolveKey(env, rootCanonical, path, ctx.signal, baseCwd, additionalRoots, undefined, readDeny, readFace);
210
218
  if (!r.ok)
211
219
  return errorResult(violationText("Glob", r.violation), violationDetails(r.violation));
212
220
  scoped = r.key;
@@ -52,7 +52,7 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
52
52
  if (!readOnly) {
53
53
  tools.push(createEditFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited), createWriteFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited), createNotebookEditTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite, opts.trackFileEdit, opts.onFileEdited));
54
54
  }
55
- tools.push(createGrepTool(env, rootCanonical, readFaceRoots, readDeny, readFace), createGlobTool(env, rootCanonical, readFaceRoots, readDeny, readFace), createRepoMapTool(env, rootCanonical, readFaceRoots, readDeny, readFace));
55
+ tools.push(createGrepTool(env, rootCanonical, readFaceRoots, readDeny, readFace, cwdRef), createGlobTool(env, rootCanonical, readFaceRoots, readDeny, readFace, cwdRef), createRepoMapTool(env, rootCanonical, readFaceRoots, readDeny, readFace));
56
56
  if (includeShell) {
57
57
  tools.push(readOnly
58
58
  ? createBashReadonlyTool(env, rootCanonical, new Set(bashReadonlyAllow ?? BASH_READONLY_DEFAULT_ALLOW), {
@@ -51,14 +51,6 @@ export interface GrepParams {
51
51
  * (`"*.ts,*.tsx"` → two globs), while a brace token (`*.{ts,tsx}`) is kept whole (its commas are
52
52
  * alternation syntax, not separators). CC-verbatim splitting rules. */
53
53
  export declare function splitGlobParam(glob: string): string[];
54
- /** 批④ #8 (CC 2.1.198 RWp, bundle :332987): decompose an ABSOLUTE glob pattern into a base directory +
55
- * relative pattern — cut at the last separator before the first glob metachar (`* ? [ {`); a pattern
56
- * with no metachar splits dirname/basename. Returns undefined for a relative pattern (caller keeps it
57
- * as-is) or when no usable base can be derived. */
58
- export declare function splitAbsoluteGlobPattern(pattern: string): {
59
- baseDir: string;
60
- relativePattern: string;
61
- } | undefined;
62
54
  /**
63
55
  * POSIX-safe single-quote so an arbitrary model-supplied string can't break out of the shell command that
64
56
  * runs ripgrep. Wraps in single quotes (inside which the shell expands nothing — no `$()`, backtick, `;`, or
@@ -71,29 +71,6 @@ export function splitGlobParam(glob) {
71
71
  }
72
72
  return out;
73
73
  }
74
- export function splitAbsoluteGlobPattern(pattern) {
75
- if (!isAbsolutePathForm(pattern))
76
- return undefined;
77
- const m = /[*?[{]/.exec(pattern);
78
- if (!m) {
79
- const i = Math.max(pattern.lastIndexOf("/"), pattern.lastIndexOf("\\"));
80
- if (i <= 0)
81
- return undefined;
82
- return { baseDir: pattern.slice(0, i), relativePattern: pattern.slice(i + 1) };
83
- }
84
- const prefix = pattern.slice(0, m.index);
85
- const cut = Math.max(prefix.lastIndexOf("/"), prefix.lastIndexOf("\\"));
86
- if (cut === -1)
87
- return undefined;
88
- let baseDir = pattern.slice(0, cut);
89
- if (baseDir === "" && cut === 0)
90
- baseDir = "/";
91
- if (/^[A-Za-z]:$/.test(baseDir))
92
- baseDir = baseDir + pattern[cut];
93
- if (!baseDir)
94
- return undefined;
95
- return { baseDir, relativePattern: pattern.slice(cut + 1) };
96
- }
97
74
  export function shellQuote(s) {
98
75
  return `'${s.replace(/'/g, `'\\''`)}'`;
99
76
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.9.0",
3
+ "version": "7.9.1",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",