@cjhyy/code-shell-core 0.8.6 → 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Public API exports.
5
5
  */
6
- export declare const VERSION = "0.8.6";
6
+ export declare const VERSION = "0.8.7";
7
7
  export type { Message, ContentBlock, ToolDefinition, ToolCall, ToolResult, RegisteredTool, TranscriptEvent, TranscriptEventType, SessionState, SessionKind, SessionWorkspace, SessionForkLineage, ContextUsageAnchor, SessionStatus, TokenUsage, CompiledInput, PermissionDecision, PermissionMode, PermissionRule, TurnPhase, TurnResult, TerminalReason, TurnCompletionKind, StreamEvent, StreamCallback, LLMConfig, ClientDefaults, LLMResponse, Settings, MCPServerConfig, } from "./types.js";
8
8
  export type { GoalConfig, GoalLifecycleConfig, GoalLifecyclePhase, GoalLifecycleTerminalReason, GoalLifecycleV1, } from "./goal/lifecycle.js";
9
9
  export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, SandboxUnavailableError, } from "./exceptions.js";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Public API exports.
5
5
  */
6
- export const VERSION = "0.8.6";
6
+ export const VERSION = "0.8.7";
7
7
  // ─── Exceptions ──────────────────────────────────────────────────
8
8
  export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, SandboxUnavailableError, } from "./exceptions.js";
9
9
  // ─── Engine (primary API) ────────────────────────────────────────
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  export declare const PANEL_APP_MANIFEST_FILE = ".codeshell-panel/panel.json";
3
- export declare const PANEL_APP_PERMISSIONS: readonly ["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "workspace.info", "workspace.read", "workspace.write", "notifications.send"];
3
+ export declare const PANEL_APP_PERMISSIONS: readonly ["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "workspace.info", "workspace.read", "workspace.write", "notifications.send", "credentials.cookies", "automations.manage"];
4
4
  export declare const PANEL_APP_ICONS: readonly ["panel", "activity", "bar-chart-3", "chart", "file-text", "globe", "image", "layout-dashboard", "line-chart", "palette", "pie-chart", "table", "terminal"];
5
5
  export declare const PanelAppAgentTool: z.ZodEffects<z.ZodObject<{
6
6
  name: z.ZodString;
@@ -116,12 +116,12 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
116
116
  icon: z.ZodDefault<z.ZodEnum<["panel", "activity", "bar-chart-3", "chart", "file-text", "globe", "image", "layout-dashboard", "line-chart", "palette", "pie-chart", "table", "terminal"]>>;
117
117
  placement: z.ZodDefault<z.ZodLiteral<"right-dock">>;
118
118
  singleton: z.ZodDefault<z.ZodBoolean>;
119
- permissions: z.ZodDefault<z.ZodArray<z.ZodEnum<["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "workspace.info", "workspace.read", "workspace.write", "notifications.send"]>, "many">>;
119
+ permissions: z.ZodDefault<z.ZodArray<z.ZodEnum<["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "workspace.info", "workspace.read", "workspace.write", "notifications.send", "credentials.cookies", "automations.manage"]>, "many">>;
120
120
  schemaVersion: z.ZodLiteral<1>;
121
121
  }, "strict", z.ZodTypeAny, {
122
122
  id: string;
123
123
  version: string;
124
- permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send")[];
124
+ permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "credentials.cookies" | "automations.manage")[];
125
125
  entry: string;
126
126
  title: {
127
127
  default: string;
@@ -144,7 +144,7 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
144
144
  };
145
145
  schemaVersion: 1;
146
146
  description?: string | undefined;
147
- permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send")[] | undefined;
147
+ permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "credentials.cookies" | "automations.manage")[] | undefined;
148
148
  icon?: "terminal" | "image" | "panel" | "activity" | "bar-chart-3" | "chart" | "file-text" | "globe" | "layout-dashboard" | "line-chart" | "palette" | "pie-chart" | "table" | undefined;
149
149
  placement?: "right-dock" | undefined;
150
150
  singleton?: boolean | undefined;
@@ -230,12 +230,12 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
230
230
  icon: z.ZodDefault<z.ZodEnum<["panel", "activity", "bar-chart-3", "chart", "file-text", "globe", "image", "layout-dashboard", "line-chart", "palette", "pie-chart", "table", "terminal"]>>;
231
231
  placement: z.ZodDefault<z.ZodLiteral<"right-dock">>;
232
232
  singleton: z.ZodDefault<z.ZodBoolean>;
233
- permissions: z.ZodDefault<z.ZodArray<z.ZodEnum<["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "workspace.info", "workspace.read", "workspace.write", "notifications.send"]>, "many">>;
233
+ permissions: z.ZodDefault<z.ZodArray<z.ZodEnum<["context.session", "context.workspace", "storage", "external.open", "agent.submitPrompt", "workspace.info", "workspace.read", "workspace.write", "notifications.send", "credentials.cookies", "automations.manage"]>, "many">>;
234
234
  schemaVersion: z.ZodLiteral<2>;
235
235
  }, "strict", z.ZodTypeAny, {
236
236
  id: string;
237
237
  version: string;
238
- permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send")[];
238
+ permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "credentials.cookies" | "automations.manage")[];
239
239
  entry: string;
240
240
  title: {
241
241
  default: string;
@@ -276,14 +276,14 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
276
276
  }[] | undefined;
277
277
  } | undefined;
278
278
  description?: string | undefined;
279
- permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send")[] | undefined;
279
+ permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "credentials.cookies" | "automations.manage")[] | undefined;
280
280
  icon?: "terminal" | "image" | "panel" | "activity" | "bar-chart-3" | "chart" | "file-text" | "globe" | "layout-dashboard" | "line-chart" | "palette" | "pie-chart" | "table" | undefined;
281
281
  placement?: "right-dock" | undefined;
282
282
  singleton?: boolean | undefined;
283
283
  }>]>, {
284
284
  id: string;
285
285
  version: string;
286
- permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send")[];
286
+ permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "credentials.cookies" | "automations.manage")[];
287
287
  entry: string;
288
288
  title: {
289
289
  default: string;
@@ -298,7 +298,7 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
298
298
  } | {
299
299
  id: string;
300
300
  version: string;
301
- permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send")[];
301
+ permissions: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "credentials.cookies" | "automations.manage")[];
302
302
  entry: string;
303
303
  title: {
304
304
  default: string;
@@ -330,7 +330,7 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
330
330
  };
331
331
  schemaVersion: 1;
332
332
  description?: string | undefined;
333
- permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send")[] | undefined;
333
+ permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "credentials.cookies" | "automations.manage")[] | undefined;
334
334
  icon?: "terminal" | "image" | "panel" | "activity" | "bar-chart-3" | "chart" | "file-text" | "globe" | "layout-dashboard" | "line-chart" | "palette" | "pie-chart" | "table" | undefined;
335
335
  placement?: "right-dock" | undefined;
336
336
  singleton?: boolean | undefined;
@@ -354,7 +354,7 @@ export declare const PanelAppManifest: z.ZodEffects<z.ZodDiscriminatedUnion<"sch
354
354
  }[] | undefined;
355
355
  } | undefined;
356
356
  description?: string | undefined;
357
- permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send")[] | undefined;
357
+ permissions?: ("context.session" | "context.workspace" | "storage" | "external.open" | "agent.submitPrompt" | "workspace.info" | "workspace.read" | "workspace.write" | "notifications.send" | "credentials.cookies" | "automations.manage")[] | undefined;
358
358
  icon?: "terminal" | "image" | "panel" | "activity" | "bar-chart-3" | "chart" | "file-text" | "globe" | "layout-dashboard" | "line-chart" | "palette" | "pie-chart" | "table" | undefined;
359
359
  placement?: "right-dock" | undefined;
360
360
  singleton?: boolean | undefined;
@@ -11,6 +11,8 @@ export const PANEL_APP_PERMISSIONS = [
11
11
  "workspace.read",
12
12
  "workspace.write",
13
13
  "notifications.send",
14
+ "credentials.cookies",
15
+ "automations.manage",
14
16
  ];
15
17
  export const PANEL_APP_ICONS = [
16
18
  "panel",
@@ -136,7 +138,7 @@ const PanelAppManifestFields = {
136
138
  icon: z.enum(PANEL_APP_ICONS).default("panel"),
137
139
  placement: z.literal("right-dock").default("right-dock"),
138
140
  singleton: z.boolean().default(true),
139
- permissions: z.array(z.enum(PANEL_APP_PERMISSIONS)).max(8).default([]),
141
+ permissions: z.array(z.enum(PANEL_APP_PERMISSIONS)).max(12).default([]),
140
142
  };
141
143
  /**
142
144
  * Schema v2 keeps one installable Panel App identity while allowing it to
@@ -186,4 +188,13 @@ export const PanelAppManifest = z
186
188
  message: "workspace.read and workspace.write require context.workspace",
187
189
  });
188
190
  }
191
+ if (value.permissions.includes("automations.manage") &&
192
+ (!value.permissions.includes("context.session") ||
193
+ !value.permissions.includes("context.workspace"))) {
194
+ ctx.addIssue({
195
+ code: z.ZodIssueCode.custom,
196
+ path: ["permissions"],
197
+ message: "automations.manage requires context.session and context.workspace",
198
+ });
199
+ }
189
200
  });
@@ -1,11 +1,11 @@
1
1
  ## Browser automation (browser_observe / browser_act / browser_navigate)
2
2
 
3
3
  - When a task needs you to operate a real web page — search a site, open a result, read/extract an article, fill a form — use the three browser tools. They drive a task-owned background tab in CodeShell's in-app browser profile. The tab can share that profile's login state, but it never controls a user-opened tab unless the user explicitly grants that exact tab. The user's regular Chrome profile is available only through an explicit Chrome extension grant.
4
- - The Browser Runtime starts lazily in the background. Call `browser_navigate{url}`, then `browser_act{action:"wait"}` and `browser_observe`. If login, 2FA, or a high-consequence action needs the user, CodeShell reveals the exact same runtime-owned in-app target for takeover. Scheduled and explicitly isolated work may use a separate Dedicated Playwright profile instead.
4
+ - The Browser Runtime starts lazily in the background. Call `browser_navigate{url}`, then `browser_act{action:"wait"}` and `browser_observe`. If login, 2FA, CAPTCHA, or a high-consequence action needs the user—or the user explicitly asks to see the page—call `browser_act{action:"request_takeover"}`. CodeShell reveals the exact same runtime-owned in-app target; do not send a second link and claim it is the page you operated. Scheduled and explicitly isolated work may use a separate Dedicated Playwright profile instead.
5
5
  - The loop is observe → act → re-observe: ALWAYS `browser_observe` first (default mode `snapshot`) to see the page's interactive elements (each tagged `[ref=eN]`), then `browser_act` on elements BY THAT ref: `{action:"click",ref}`, `{action:"type",ref,text}`, `{action:"select",ref,value}` (native `<select>`), `{action:"hover",ref}`. Refs are only valid for the most recent snapshot — after any navigation, content-loading click, or `{action:"scroll"}`, run `{action:"wait"}` then `browser_observe` again. If an action says a ref is stale, re-observe.
6
6
  - To submit a search: `browser_act{action:"type",ref,text}` into the search box, then `browser_act{action:"press_key",key:"Enter"}`, then `{action:"wait"}` + `browser_observe`. `press_key` also does Tab / Escape / arrows / combos like `Control+a`.
7
7
  - To extract/summarize page content, navigate/click to it, `browser_act{action:"wait"}`, then `browser_observe{mode:"read"}`. If it returns `nextCursor`, continue with `browser_observe{mode:"read",cursor:"..."}` until `Read: complete`. Do NOT scroll merely to obtain the next text chunk. Use scroll only to load lazy/infinite content; stop when it reports `NO_PROGRESS` or `(end)`. For real link/image/video URLs, use `browser_observe{mode:"extract"}` (media is tagged [ref=imgN/vidN]).
8
8
  - To SEE an actual image's content (e.g. a 小红书 笔记配图 or product photo), `browser_observe{mode:"extract"}` to get image refs, then `browser_observe{mode:"image", refs:["img3"]}` — it loads the real pixels (works behind hotlink protection). A vidN ref grabs the video's current frame. To see the rendered layout/canvas/chart, `browser_observe{mode:"vision"}` (optionally `ref` for one region). Use images sparingly — they cost tokens; prefer snapshot/read. (Both need a vision-capable model; on a non-vision model they're skipped.) Do NOT repeat `vision` on the same page hoping for a clearer view — one screenshot is all you get. If snapshot/screenshot don't reveal what you need (infinite-scroll/canvas feeds), scroll + re-observe or extract the URLs and act on them — don't loop screenshots.
9
9
  - Multiple tabs: `browser_act{action:"list_tabs"}` shows open tabs (tabId/url/title/active); `browser_act{action:"switch_tab", tabId}` makes another the active one (then re-observe). Any action also accepts `tabId` to target a specific tab (it switches first). Refs are per-tab — re-observe after switching.
10
- - If `browser_observe` reports a sign-in is required (or you hit a login wall / 2FA), STOP and ask the user to log in manually in the revealed Browser Runtime window, then continue do not attempt to enter credentials yourself. Sensitive actions (payment, delete, entering card/password values) require user approval.
10
+ - If `browser_observe` reports a sign-in is required (or you hit a login wall / 2FA / CAPTCHA), call `browser_act{action:"request_takeover"}`, STOP, and ask the user to act in the revealed Browser Runtime window. Continue only after the user finishes; do not attempt to enter credentials yourself. Sensitive actions (payment, delete, entering card/password values) require user approval.
11
11
  - If the tools report that no Browser Runtime is available, the current host does not provide browser automation — say so rather than retrying.
@@ -2790,6 +2790,7 @@ export class AgentServer {
2790
2790
  makeBrowserBridge(session, sessionId) {
2791
2791
  const call = (action, payload) => this.requestBrowserActionForSession(session, sessionId, action, payload);
2792
2792
  return {
2793
+ requestHumanTakeover: () => call("requestTakeover", {}),
2793
2794
  snapshot: () => call("snapshot", {}),
2794
2795
  click: (ref) => call("click", { ref }),
2795
2796
  type: (ref, text) => call("type", { ref, text }),
@@ -145,6 +145,9 @@ export interface BrowserImageData {
145
145
  detail?: string;
146
146
  }
147
147
  export interface BrowserBridge {
148
+ /** Reveal the exact task-owned browser target so the user can complete a
149
+ * login, 2FA, CAPTCHA, or another interaction that requires human control. */
150
+ requestHumanTakeover?(): Promise<BrowserResult>;
148
151
  snapshot(): Promise<BrowserSnapshot>;
149
152
  click(ref: string): Promise<BrowserResult>;
150
153
  type(ref: string, text: string): Promise<BrowserResult>;
@@ -209,6 +209,9 @@ export const browserActToolDef = {
209
209
  "- hover {ref}: hover to reveal menus/tooltips.\n" +
210
210
  "- scroll {direction: up|down, amount?}: scroll the page, then re-observe.\n" +
211
211
  "- wait {timeout_ms?}: wait for the page to finish loading before observing.\n" +
212
+ "- request_takeover: reveal the exact task-owned Browser Runtime page so the " +
213
+ "user can see it and complete login, 2FA, CAPTCHA, or another required manual step. " +
214
+ "Use only when the user asks to see the page or human interaction is required.\n" +
212
215
  "- list_tabs: list open browser tabs (tabId, url, title, which is active).\n" +
213
216
  "- switch_tab {tabId}: make another tab the active one that actions drive.\n" +
214
217
  "Pass tabId on any action to target a specific tab (switches to it first). " +
@@ -226,6 +229,7 @@ export const browserActToolDef = {
226
229
  "hover",
227
230
  "scroll",
228
231
  "wait",
232
+ "request_takeover",
229
233
  "list_tabs",
230
234
  "switch_tab",
231
235
  ],
@@ -260,6 +264,15 @@ export async function browserActTool(args, ctx) {
260
264
  return `Error: could not switch to tab ${tabId} — ${sw.detail ?? "not found"}`;
261
265
  }
262
266
  switch (action) {
267
+ case "request_takeover": {
268
+ if (!b.requestHumanTakeover) {
269
+ return "Error: this Browser Runtime cannot reveal its page for user takeover";
270
+ }
271
+ const r = await b.requestHumanTakeover();
272
+ return r.ok
273
+ ? `Browser Runtime is visible for user takeover${r.detail ? ` — ${r.detail}` : ""}`
274
+ : `Error: ${r.detail ?? "could not reveal Browser Runtime"}`;
275
+ }
263
276
  case "list_tabs": {
264
277
  const tabs = await b.listTabs();
265
278
  if (tabs.length === 0)
@@ -432,9 +432,10 @@ function isSkillTreeResource(resolved, skillsRoot) {
432
432
  *
433
433
  * Keep the exception narrow:
434
434
  * - read-only (the caller checks the operation);
435
- * - user Skills, or an installed plugin recorded in the V2 registry;
435
+ * - user Skills, an installed plugin recorded in the V2 registry, or a
436
+ * declared Skill in the installed Panel App registry;
436
437
  * - plugin installs must realpath beneath the managed plugin cache;
437
- * - the target must remain inside a directory containing a real SKILL.md.
438
+ * - the target must remain inside the registered Skill directory.
438
439
  */
439
440
  function isRegisteredSkillResourceRead(resolved) {
440
441
  let codeShellRoot;
@@ -448,6 +449,8 @@ function isRegisteredSkillResourceRead(resolved) {
448
449
  return false;
449
450
  if (isSkillTreeResource(resolved, join(codeShellRoot, "skills")))
450
451
  return true;
452
+ if (isInstalledPanelAppSkillResourceRead(resolved, codeShellRoot))
453
+ return true;
451
454
  let cacheRoot;
452
455
  try {
453
456
  cacheRoot = realpathSync(join(codeShellRoot, "plugins", "cache"));
@@ -475,6 +478,84 @@ function isRegisteredSkillResourceRead(resolved) {
475
478
  }
476
479
  return false;
477
480
  }
481
+ /**
482
+ * Panel App Skills live under the otherwise-sensitive
483
+ * `~/.code-shell/panel-apps` tree. Installation already reviews and copies the
484
+ * package, and the Skill scanner exposes only entries declared by the app
485
+ * manifest. Mirror that exact boundary here so reading a Skill reference does
486
+ * not trigger a second approval prompt.
487
+ *
488
+ * Registry, app root, manifest, declared SKILL.md and target are all
489
+ * realpathed and containment-checked. This deliberately does not trust an
490
+ * undeclared Skill directory or a symlink escaping the installed app.
491
+ */
492
+ function isInstalledPanelAppSkillResourceRead(resolved, codeShellRoot) {
493
+ let appsRoot;
494
+ let registryPath;
495
+ try {
496
+ appsRoot = realpathSync(join(codeShellRoot, "panel-apps"));
497
+ if (!isInsideDir(appsRoot, codeShellRoot))
498
+ return false;
499
+ registryPath = realpathSync(join(appsRoot, "installed.json"));
500
+ if (!isInsideDir(registryPath, appsRoot))
501
+ return false;
502
+ }
503
+ catch {
504
+ return false;
505
+ }
506
+ let registry;
507
+ try {
508
+ registry = JSON.parse(readFileSync(registryPath, "utf-8"));
509
+ }
510
+ catch {
511
+ return false;
512
+ }
513
+ if (registry.version !== 1 || !Array.isArray(registry.apps))
514
+ return false;
515
+ for (const rawEntry of registry.apps) {
516
+ if (!rawEntry || typeof rawEntry !== "object")
517
+ continue;
518
+ const id = rawEntry.id;
519
+ if (typeof id !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(id))
520
+ continue;
521
+ try {
522
+ const appRoot = realpathSync(join(appsRoot, id));
523
+ if (appRoot === appsRoot || !isInsideDir(appRoot, appsRoot))
524
+ continue;
525
+ const manifestPath = realpathSync(join(appRoot, ".codeshell-panel", "panel.json"));
526
+ if (!isInsideDir(manifestPath, appRoot) || !statSync(manifestPath).isFile())
527
+ continue;
528
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
529
+ if (manifest.schemaVersion !== 2 ||
530
+ manifest.id !== id ||
531
+ !Array.isArray(manifest.agent?.skills)) {
532
+ continue;
533
+ }
534
+ for (const skillEntry of manifest.agent.skills) {
535
+ if (typeof skillEntry !== "string")
536
+ continue;
537
+ const segments = skillEntry.split("/");
538
+ if (segments.length !== 4 ||
539
+ segments[0] !== "agent" ||
540
+ segments[1] !== "skills" ||
541
+ !/^[a-z][a-z0-9-]{0,63}$/.test(segments[2] ?? "") ||
542
+ segments[3] !== "SKILL.md") {
543
+ continue;
544
+ }
545
+ const skillManifest = realpathSync(join(appRoot, ...segments));
546
+ if (!isInsideDir(skillManifest, appRoot) || !statSync(skillManifest).isFile())
547
+ continue;
548
+ const skillRoot = dirname(skillManifest);
549
+ if (isInsideDir(resolved, skillRoot))
550
+ return true;
551
+ }
552
+ }
553
+ catch {
554
+ // A stale, malformed, or tampered Panel App entry grants no read access.
555
+ }
556
+ }
557
+ return false;
558
+ }
478
559
  /**
479
560
  * Returns the matching sensitive-dir entry (with the user's home prefix) if
480
561
  * `resolved` lives underneath any sensitive directory, else undefined.
@@ -564,9 +645,7 @@ export function classifyPath(rawPath, opts) {
564
645
  // files through the ordinary Read tool as well. A credential-shaped basename
565
646
  // (.env, token.txt, key files, ...) deliberately keeps the sensitive-file
566
647
  // gate even inside a Skill tree.
567
- if (opts.operation === "read" &&
568
- !sensitiveFile &&
569
- isRegisteredSkillResourceRead(resolved)) {
648
+ if (opts.operation === "read" && !sensitiveFile && isRegisteredSkillResourceRead(resolved)) {
570
649
  return {
571
650
  decision: "allow",
572
651
  reason: "registered Skill resource read",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cjhyy/code-shell-core",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Core engine for code-shell — agent orchestration, tool execution, hooks, protocol. UI-agnostic.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",