agent-lyceum 0.7.0 → 0.8.0

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 CHANGED
@@ -107,9 +107,10 @@ agents:
107
107
  lead: { resume: true, can_message: all, can_edit_agent_md: true }
108
108
  fe-member: { runtime: codex, can_message: [lead], owns: ["src/web/**"] }
109
109
  qa-member: { can_message: [lead], owns: ["tests/**"] }
110
+ pm: { can_message: [lead], allow_web: fetch } # pm may search and read pages; the others have no web tools
110
111
  ```
111
112
 
112
- Agent fields: `runtime` (`claude-code`|`codex`; optional when `model` is recognizable: `opus`/`sonnet`/`haiku`/`claude-*` → Claude Code, `gpt-*`/`o3`/`*codex*` → Codex; precedence: project runtime > project model > global runtime > global model), `model`, `effort` (Claude Code: `low`|`medium`|`high`|`xhigh`|`max` via `--effort`; Codex: `minimal`|`low`|`medium`|`high`|`xhigh` via `model_reasoning_effort`; unset = CLI default), `agent_md`, `memory.global` / `memory.project`, `resume`, `can_message` (`all` or list; default `[lead]`, lead default `all`), `can_edit_agent_md` (default only the lead), `can_ask_user` (may send `type: ask` mail to you and pause the run for an answer; default only the lead), `owns` (repo globs).
113
+ Agent fields: `runtime` (`claude-code`|`codex`; optional when `model` is recognizable: `opus`/`sonnet`/`haiku`/`claude-*` → Claude Code, `gpt-*`/`o3`/`*codex*` → Codex; precedence: project runtime > project model > global runtime > global model), `model`, `effort` (Claude Code: `low`|`medium`|`high`|`xhigh`|`max` via `--effort`; Codex: `minimal`|`low`|`medium`|`high`|`xhigh` via `model_reasoning_effort`; unset = CLI default), `agent_md`, `memory.global` / `memory.project`, `resume`, `can_message` (`all` or list; default `[lead]`, lead default `all`), `can_edit_agent_md` (default only the lead), `can_ask_user` (may send `type: ask` mail to you and pause the run for an answer; default only the lead), `allow_web` (`none`|`search`|`fetch`: what the agent may use of the web; unset means Claude Code gets no web tools and Codex keeps its own default; `search` = `WebSearch` / Codex `web_search="cached"`, `fetch` = `WebSearch` + `WebFetch` / Codex `web_search="live"`, `none` = no web tools / Codex `disabled`; `fetch` pulls whole pages into the agent's context, so give it only to agents that do little editing, such as a requirements or research role), `owns` (repo globs).
113
114
 
114
115
  Rules checked by `validate`: ≥ 2 agents (the lead and one member; the template has three), the lead is a listed agent, every agent has a runtime (explicit or inferred from `model`) and an existing `AGENT.md`, `effort` is valid for the agent's runtime (warning if `runtime` contradicts a recognizable `model`), `can_message` targets exist, memory dirs don't overlap, and with `max_parallel > 1` every non-lead agent needs non-overlapping `owns` and the repo must be a git repository.
115
116
 
package/README.zh-TW.md CHANGED
@@ -108,9 +108,10 @@ agents:
108
108
  lead: { resume: true, can_message: all, can_edit_agent_md: true }
109
109
  fe-member: { runtime: codex, can_message: [lead], owns: ["src/web/**"] }
110
110
  qa-member: { can_message: [lead], owns: ["tests/**"] }
111
+ pm: { can_message: [lead], allow_web: fetch } # pm 可以搜尋並讀網頁;其他人沒有網路工具
111
112
  ```
112
113
 
113
- Agent 欄位:`runtime`(`claude-code`|`codex`;若 `model` 可辨識則可省略:`opus`/`sonnet`/`haiku`/`claude-*` → Claude Code,`gpt-*`/`o3`/`*codex*` → Codex;優先順序:專案 runtime > 專案 model > 全域 runtime > 全域 model)、`model`、`effort`(Claude Code:`low`|`medium`|`high`|`xhigh`|`max`,經 `--effort`;Codex:`minimal`|`low`|`medium`|`high`|`xhigh`,經 `model_reasoning_effort`;未設則用 CLI 預設)、`agent_md`、`memory.global` / `memory.project`、`resume`、`can_message`(`all` 或清單;預設 `[lead]`,lead 預設 `all`)、`can_edit_agent_md`(預設只有 lead)、`can_ask_user`(可用 `type: ask` 信件向你提問並暫停 run 等待回答;預設只有 lead)、`owns`(repo glob)。
114
+ Agent 欄位:`runtime`(`claude-code`|`codex`;若 `model` 可辨識則可省略:`opus`/`sonnet`/`haiku`/`claude-*` → Claude Code,`gpt-*`/`o3`/`*codex*` → Codex;優先順序:專案 runtime > 專案 model > 全域 runtime > 全域 model)、`model`、`effort`(Claude Code:`low`|`medium`|`high`|`xhigh`|`max`,經 `--effort`;Codex:`minimal`|`low`|`medium`|`high`|`xhigh`,經 `model_reasoning_effort`;未設則用 CLI 預設)、`agent_md`、`memory.global` / `memory.project`、`resume`、`can_message`(`all` 或清單;預設 `[lead]`,lead 預設 `all`)、`can_edit_agent_md`(預設只有 lead)、`can_ask_user`(可用 `type: ask` 信件向你提問並暫停 run 等待回答;預設只有 lead)、`allow_web`(`none`|`search`|`fetch`:agent 可以使用多少網路;不設時 Claude Code 沒有網路工具、Codex 維持它自己的預設;`search` = `WebSearch`/Codex `web_search="cached"`,`fetch` = `WebSearch` 加 `WebFetch`/Codex `web_search="live"`,`none` = 不給網路工具/Codex `disabled`;`fetch` 會把整個網頁放進 agent 的上下文,只建議給很少改檔的角色,例如需求或研究角色)、`owns`(repo glob)。
114
115
 
115
116
  `validate` 檢查的規則:至少 2 個 agent(lead 加一位成員;範本預設是三位)、lead 在 agent 清單內、每個 agent 都有 runtime(明設或由 `model` 推斷)且 `AGENT.md` 存在、`effort` 對該 runtime 合法(若 `runtime` 與可辨識的 `model` 矛盾則警告)、`can_message` 的目標存在、記憶資料夾互不重疊,且當 `max_parallel > 1` 時,每個非 lead 的 agent 都必須有互不重疊的 `owns`,repo 也必須是 git repository。
116
117
 
@@ -22,6 +22,11 @@ export function buildClaudeSettings(project, agent) {
22
22
  const ws = project.workspaces?.[agent.name];
23
23
  const repo = repoDirFor(project, agent.name);
24
24
  const allow = ["Bash", "Read", "Glob", "Grep"];
25
+ // Web tools are opt-in: `search` is the lighter one (snippets only), `fetch` also pulls whole pages into the agent's context.
26
+ if (agent.allowWeb === "search" || agent.allowWeb === "fetch")
27
+ allow.push("WebSearch");
28
+ if (agent.allowWeb === "fetch")
29
+ allow.push("WebFetch");
25
30
  if (pol.restrictRepoToOwns) {
26
31
  for (const g of pol.owns)
27
32
  allow.push(rule("Edit", path.join(repo, g)));
@@ -48,6 +48,8 @@ export function codexOutputTokens(jsonl) {
48
48
  }
49
49
  return total;
50
50
  }
51
+ /** Codex has one `web_search` setting: `cached` answers from an index, `live` browses. */
52
+ const CODEX_WEB_SEARCH = { none: "disabled", search: "cached", fetch: "live" };
51
53
  export function buildCodexInvocation(input) {
52
54
  const { project, agent, workDir } = input;
53
55
  const repo = repoDirFor(project, agent.name);
@@ -67,6 +69,8 @@ export function buildCodexInvocation(input) {
67
69
  common.push("-m", agent.model);
68
70
  if (agent.effort)
69
71
  common.push("-c", `model_reasoning_effort="${agent.effort}"`);
72
+ if (agent.allowWeb)
73
+ common.push("-c", `web_search="${CODEX_WEB_SEARCH[agent.allowWeb]}"`);
70
74
  const args = input.sessionId
71
75
  ? ["exec", "resume", input.sessionId, "-c", 'sandbox_mode="workspace-write"', ...common, "-"]
72
76
  : ["exec", "-C", repo, "-s", "workspace-write", "--skip-git-repo-check", ...common, "-"];
package/dist/config.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type ProjectPaths } from "./paths.js";
2
2
  import type { AgentWorkspace } from "./worktree.js";
3
- import { type DispatcherSettings, type Effort, type Runtime } from "./schema.js";
3
+ import { type DispatcherSettings, type Effort, type Runtime, type WebLevel } from "./schema.js";
4
4
  export declare class ConfigError extends Error {
5
5
  }
6
6
  export type Source = "global" | "project" | "default" | "model";
@@ -20,6 +20,8 @@ export interface ResolvedAgent {
20
20
  canEditAgentMd: boolean;
21
21
  /** May send `type: ask` mail to the user (the run then waits for the answer). */
22
22
  canAskUser: boolean;
23
+ /** Web access for this agent; undefined = not configured, so the runtime keeps its own default. */
24
+ allowWeb?: WebLevel;
23
25
  owns: string[];
24
26
  /** Where each field's value came from (for `validate` output). */
25
27
  sources: Record<string, Source>;
@@ -58,6 +60,7 @@ export declare function loadGlobal(home: string): {
58
60
  can_message?: "all" | string[] | undefined;
59
61
  can_edit_agent_md?: boolean | undefined;
60
62
  can_ask_user?: boolean | undefined;
63
+ allow_web?: "fetch" | "none" | "search" | undefined;
61
64
  owns?: string[] | undefined;
62
65
  }>;
63
66
  };
@@ -88,6 +91,7 @@ export declare function loadProjectRaw(home: string, name: string): {
88
91
  can_message?: "all" | string[] | undefined;
89
92
  can_edit_agent_md?: boolean | undefined;
90
93
  can_ask_user?: boolean | undefined;
94
+ allow_web?: "fetch" | "none" | "search" | undefined;
91
95
  owns?: string[] | undefined;
92
96
  }>;
93
97
  };
package/dist/config.js CHANGED
@@ -133,6 +133,7 @@ export function resolveProjectFrom(home, name, g, p) {
133
133
  const canMessage = pick("can_message");
134
134
  const canEdit = pick("can_edit_agent_md");
135
135
  const canAsk = pick("can_ask_user");
136
+ const allowWeb = pick("allow_web");
136
137
  const owns = pick("owns");
137
138
  for (const [k, v] of [
138
139
  ["resume", resume],
@@ -155,6 +156,7 @@ export function resolveProjectFrom(home, name, g, p) {
155
156
  canMessage: canMessage ?? (isLead ? "all" : [lead]),
156
157
  canEditAgentMd: canEdit ?? isLead,
157
158
  canAskUser: canAsk ?? isLead,
159
+ allowWeb,
158
160
  owns: owns ?? [],
159
161
  sources,
160
162
  };
@@ -203,7 +205,7 @@ export function findProjectForCwd(home, cwd) {
203
205
  }
204
206
  return best?.name;
205
207
  }
206
- const AGENT_FIELDS = ["runtime", "model", "effort", "agent_md", "memory.global", "memory.project", "resume", "can_message", "can_edit_agent_md", "can_ask_user", "owns"];
208
+ const AGENT_FIELDS = ["runtime", "model", "effort", "agent_md", "memory.global", "memory.project", "resume", "can_message", "can_edit_agent_md", "can_ask_user", "allow_web", "owns"];
207
209
  /** The resolved project plus, for every setting, the file and key it was taken from (or "default"). */
208
210
  export function resolveProjectWithSources(home, name) {
209
211
  const project = resolveProject(home, name);
@@ -261,6 +263,7 @@ export function flattenResolved(project) {
261
263
  v[`${p}.can_message`] = a.canMessage;
262
264
  v[`${p}.can_edit_agent_md`] = a.canEditAgentMd;
263
265
  v[`${p}.can_ask_user`] = a.canAskUser;
266
+ v[`${p}.allow_web`] = a.allowWeb;
264
267
  v[`${p}.owns`] = a.owns;
265
268
  }
266
269
  return v;
package/dist/schema.d.ts CHANGED
@@ -8,6 +8,9 @@ export type Effort = (typeof EFFORTS)[number];
8
8
  export declare const RUNTIME_EFFORTS: Record<Runtime, readonly Effort[]>;
9
9
  /** Guess the runtime from a model name: Claude aliases/IDs -> claude-code, GPT/o-series/codex names -> codex. */
10
10
  export declare function inferRuntime(model: string | undefined): Runtime | undefined;
11
+ /** How much of the web an agent may use: nothing, search only, or search plus fetching pages. Unset leaves each runtime's own default alone. */
12
+ export declare const WEB_LEVELS: readonly ["none", "search", "fetch"];
13
+ export type WebLevel = (typeof WEB_LEVELS)[number];
11
14
  export declare const AgentPartial: z.ZodObject<{
12
15
  runtime: z.ZodOptional<z.ZodEnum<{
13
16
  "claude-code": "claude-code";
@@ -31,6 +34,11 @@ export declare const AgentPartial: z.ZodObject<{
31
34
  can_message: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodArray<z.ZodString>]>>;
32
35
  can_edit_agent_md: z.ZodOptional<z.ZodBoolean>;
33
36
  can_ask_user: z.ZodOptional<z.ZodBoolean>;
37
+ allow_web: z.ZodOptional<z.ZodEnum<{
38
+ fetch: "fetch";
39
+ none: "none";
40
+ search: "search";
41
+ }>>;
34
42
  owns: z.ZodOptional<z.ZodArray<z.ZodString>>;
35
43
  }, z.core.$strict>;
36
44
  export type AgentPartialT = z.infer<typeof AgentPartial>;
@@ -71,6 +79,11 @@ export declare const GlobalConfig: z.ZodObject<{
71
79
  can_message: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodArray<z.ZodString>]>>;
72
80
  can_edit_agent_md: z.ZodOptional<z.ZodBoolean>;
73
81
  can_ask_user: z.ZodOptional<z.ZodBoolean>;
82
+ allow_web: z.ZodOptional<z.ZodEnum<{
83
+ fetch: "fetch";
84
+ none: "none";
85
+ search: "search";
86
+ }>>;
74
87
  owns: z.ZodOptional<z.ZodArray<z.ZodString>>;
75
88
  }, z.core.$strict>>>;
76
89
  }, z.core.$strict>;
@@ -115,6 +128,11 @@ export declare const ProjectConfig: z.ZodObject<{
115
128
  can_message: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodArray<z.ZodString>]>>;
116
129
  can_edit_agent_md: z.ZodOptional<z.ZodBoolean>;
117
130
  can_ask_user: z.ZodOptional<z.ZodBoolean>;
131
+ allow_web: z.ZodOptional<z.ZodEnum<{
132
+ fetch: "fetch";
133
+ none: "none";
134
+ search: "search";
135
+ }>>;
118
136
  owns: z.ZodOptional<z.ZodArray<z.ZodString>>;
119
137
  }, z.core.$strict>>>;
120
138
  }, z.core.$strict>;
package/dist/schema.js CHANGED
@@ -18,6 +18,8 @@ export function inferRuntime(model) {
18
18
  return "codex";
19
19
  return undefined;
20
20
  }
21
+ /** How much of the web an agent may use: nothing, search only, or search plus fetching pages. Unset leaves each runtime's own default alone. */
22
+ export const WEB_LEVELS = ["none", "search", "fetch"];
21
23
  const Memory = z
22
24
  .object({
23
25
  global: z.string().optional(),
@@ -35,6 +37,7 @@ export const AgentPartial = z
35
37
  can_message: z.union([z.literal("all"), z.array(z.string())]).optional(),
36
38
  can_edit_agent_md: z.boolean().optional(),
37
39
  can_ask_user: z.boolean().optional(),
40
+ allow_web: z.enum(WEB_LEVELS).optional(),
38
41
  owns: z.array(z.string()).optional(),
39
42
  })
40
43
  .strict();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-lyceum",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Config + dispatcher for teams of Claude Code / Codex agents with scoped context and file-based mailboxes",
5
5
  "keywords": [
6
6
  "ai",