agent-relay-server 0.69.1 → 0.70.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.
@@ -3,6 +3,7 @@ import { cleanEnum, cleanString, cleanStringArray } from "./validation";
3
3
  import { DEFAULT_CONTEXT_THRESHOLD_CONFIG, validateContextThresholdConfig } from "./context-threshold-config";
4
4
  import { DEFAULT_SELF_RESUME_CONFIG, validateSelfResumeConfig } from "./self-resume-config";
5
5
  import { TEAM_TEMPLATE_NAMESPACE, validateTeamTemplate } from "./team-template-config";
6
+ import { PROJECT_INSTRUCTION_POLICY_NAMES } from "./project-instructions";
6
7
  import { resolveProviderSelection, type ProviderEffort } from "agent-relay-sdk/provider-catalog";
7
8
  import { errMessage, isRecord, SPAWN_PROVIDERS, VALID_WORKSPACE_MODES } from "agent-relay-sdk"; import { effectiveProviderCatalogList } from "./provider-catalog-store";
8
9
  import type {
@@ -54,13 +55,14 @@ const OPS_ASSISTANT_BRIEF = [
54
55
  "Hold and apply session-local ops conventions the coordinator gives you. Report crisp outcomes only: command/gate, pass/fail, commit/version/host evidence, and the next blocker when one exists. Do not dump logs unless asked.",
55
56
  "Do not make strategy, design, scope, or release-timing calls. Escalate those decisions to your spawn-parent. Report operational results to your spawn-parent by default and keep their context clean.",
56
57
  ].join("\n");
57
- const BUILT_IN_AGENT_PROFILE_NAMES = new Set(["default-relay", "minimal", "isolated-research", "vanilla", "coordinator", "ops-assistant", "worker", "reviewer"]);
58
+ const BUILT_IN_AGENT_PROFILE_NAMES = new Set(["default-relay", "minimal", "isolated-research", "vanilla", "lean-worker", "coordinator", "ops-assistant", "worker", "reviewer"]);
58
59
 
59
60
  const BUILT_IN_AGENT_PROFILES: AgentProfile[] = [
60
61
  agentProfileDefaults({ name: "default-relay", description: "Relay-aware spawned agent with bundled Relay context, skills, plugins, and status integration.", base: "host" }),
61
62
  agentProfileDefaults({ name: "minimal", description: "Repo-focused spawned agent with Relay affordances disabled and host/global extras avoided where supported.", base: "minimal" }),
62
63
  agentProfileDefaults({ name: "isolated-research", description: "Strict profile for autonomous research runs: repo context only, no Relay prompt surface, no global extras where supported.", base: "isolated" }),
63
64
  agentProfileDefaults({ name: "vanilla", description: "Zero host bleed (#552): isolated provider home with host plugins, skills, hooks, MCP, and the CLAUDE.md/AGENTS.md cascade (repo + global) all suppressed at launch. Relay provisions any surface explicitly per profile.", base: "vanilla" }),
65
+ agentProfileDefaults({ name: "lean-worker", description: "Lean worker profile (#570): vanilla host-dependency drop with only Relay context/MCP restored and Project knowledge injected from the spawn cwd.", base: "vanilla", relay: { context: true, skills: false, plugins: false, statusLine: false, mcp: true }, projectInstructions: { policy: "all" } }),
64
66
  agentProfileDefaults({ name: "coordinator", description: "Relay-aware coordination lead for spawning worker teams, maintaining shared state, and driving review and release.", base: "host", instructions: { append: [COORDINATION_CHARTER], repoInstructions: "allow", globalInstructions: "allow" }, maxSpawnedAgents: 8 }),
65
67
  agentProfileDefaults({ name: "ops-assistant", description: "Relay-aware ops lieutenant for a coordinator: runs tests, releases, smoke probes, gates, and host checks, then reports crisp outcomes back up.", provider: "codex", base: "host", instructions: { append: [OPS_ASSISTANT_BRIEF], repoInstructions: "allow", globalInstructions: "allow" }, permissions: { mode: "open", filesystem: "host" } }),
66
68
  agentProfileDefaults({ name: "worker", description: "Relay-aware focused implementer with open approval posture and no delegated spawn capability.", base: "host", permissions: { mode: "open", filesystem: "host" } }),
@@ -211,6 +213,15 @@ function cleanNumber(value: unknown, field: string, opts: { min: number; max: nu
211
213
  return value;
212
214
  }
213
215
 
216
+ function cleanAgentProfileProjectInstructions(value: unknown): AgentProfile["projectInstructions"] {
217
+ if (value === undefined || value === null || value === false) return undefined;
218
+ if (value === true) return {};
219
+ if (!isRecord(value)) throw new ValidationError("projectInstructions must be a boolean or { policy } object");
220
+ const policy = value.policy;
221
+ if (policy === undefined || policy === null) return {};
222
+ return { policy: cleanEnum(policy, "projectInstructions.policy", PROJECT_INSTRUCTION_POLICY_NAMES) as NonNullable<AgentProfile["projectInstructions"]>["policy"] };
223
+ }
224
+
214
225
  function agentProfileDefaults(input: Pick<AgentProfile, "name" | "base"> & Partial<AgentProfile>): AgentProfile {
215
226
  const isolated = input.base !== "host";
216
227
  // "vanilla" (#552) additionally suppresses the REPO instruction cascade
@@ -230,6 +241,7 @@ function agentProfileDefaults(input: Pick<AgentProfile, "name" | "base"> & Parti
230
241
  repoInstructions: input.instructions?.repoInstructions ?? (vanilla ? "ignore" : "allow"),
231
242
  globalInstructions: input.instructions?.globalInstructions ?? (isolated ? "ignore" : "allow"),
232
243
  },
244
+ ...(input.projectInstructions ? { projectInstructions: input.projectInstructions } : {}),
233
245
  relay: {
234
246
  context: input.relay?.context ?? !isolated,
235
247
  skills: input.relay?.skills ?? !isolated,
@@ -303,6 +315,7 @@ function validateAgentProfile(key: string, value: unknown): AgentProfile {
303
315
  ? defaults.instructions.globalInstructions
304
316
  : cleanEnum(instructions.globalInstructions, "instructions.globalInstructions", VALID_PROFILE_INSTRUCTION_POLICIES),
305
317
  },
318
+ projectInstructions: cleanAgentProfileProjectInstructions(value.projectInstructions),
306
319
  relay: {
307
320
  context: relay.context === undefined ? defaults.relay.context : cleanBoolean(relay.context, "relay.context"),
308
321
  skills: relay.skills === undefined ? defaults.relay.skills : cleanBoolean(relay.skills, "relay.skills"),
@@ -1,4 +1,4 @@
1
- import { deriveContextThresholdFromWindowTokens, resolveProviderContextThreshold, type ProviderModelCatalogEntry, type ResolvedProviderContextThreshold } from "agent-relay-sdk/provider-catalog";
1
+ import { deriveContextThresholdFromWindowTokens, findProviderCatalogModel, resolveProviderContextThreshold, type ProviderModelCatalogEntry, type ResolvedProviderContextThreshold } from "agent-relay-sdk/provider-catalog";
2
2
  import { isRecord, SPAWN_PROVIDERS } from "agent-relay-sdk";
3
3
  import { getConfig, getNotificationsConfig } from "./config-store";
4
4
  import { DEFAULT_CONTEXT_THRESHOLD_CONFIG, validateContextThresholdConfig } from "./context-threshold-config";
@@ -132,10 +132,11 @@ export function handleContextPressure(input: {
132
132
  }
133
133
 
134
134
  function effectiveContextThreshold(agent: AgentCard): EffectiveContextThreshold {
135
- const model = findAgentCatalogModel(agent);
136
- const windowTokens = model?.limits?.contextWindowTokens?.value
137
- ?? finiteNumber(agent.providerCapabilities?.context?.windowTokens)
135
+ const observedWindowTokens = finiteNumber(agent.providerCapabilities?.context?.windowTokens)
138
136
  ?? finiteNumber(agent.context?.tokensMax);
137
+ const model = findAgentCatalogModel(agent, observedWindowTokens);
138
+ const windowTokens = model?.limits?.contextWindowTokens?.value
139
+ ?? observedWindowTokens;
139
140
  if (model?.contextThreshold) {
140
141
  return { ...resolveProviderContextThreshold(model), source: "model" };
141
142
  }
@@ -157,16 +158,14 @@ function explicitContextThresholdConfig(): EffectiveContextThreshold | undefined
157
158
  };
158
159
  }
159
160
 
160
- function findAgentCatalogModel(agent: AgentCard): ProviderModelCatalogEntry | undefined {
161
+ function findAgentCatalogModel(agent: AgentCard, observedWindowTokens: number | undefined): ProviderModelCatalogEntry | undefined {
161
162
  const runtime = agent.providerCapabilities?.model;
162
163
  const provider = runtime?.provider;
163
164
  if (!provider || !(SPAWN_PROVIDERS as readonly string[]).includes(provider)) return undefined;
164
165
  const catalog = effectiveProviderCatalogList().find((entry) => entry.provider === provider);
165
166
  if (!catalog) return undefined;
166
167
  const ids = [runtime.alias, runtime.id, runtime.providerModel].filter((value): value is string => Boolean(value));
167
- const lowerIds = new Set(ids.map((id) => id.toLowerCase()));
168
- return catalog.models.find((model) => ids.includes(model.alias) || ids.includes(model.providerModel))
169
- ?? catalog.models.find((model) => lowerIds.has(model.alias.toLowerCase()) || lowerIds.has(model.providerModel.toLowerCase()));
168
+ return findProviderCatalogModel(catalog, ids, { observedWindowTokens });
170
169
  }
171
170
 
172
171
  function clearContextAdvisory(context: ContextState): ContextState {
@@ -223,9 +223,10 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
223
223
  // providers (project preamble → profile system → profile append → caller/fork context) and
224
224
  // carries the #559 scoped-knowledge seam. Spawns without profile/project material stay
225
225
  // byte-identical to the caller-provided systemPromptAppend.
226
+ const projectInstructions = input.projectInstructions ?? agentProfile?.projectInstructions;
226
227
  const systemPromptAppend = composeSpawnInstructions({
227
228
  ...(callerProject ? { callerProject } : {}),
228
- ...(input.projectInstructions ? { projectInstructions: input.projectInstructions } : {}),
229
+ ...(projectInstructions ? { projectInstructions } : {}),
229
230
  ...(agentProfile ? { profile: agentProfile } : {}),
230
231
  ...(input.systemPromptAppend ? { callerSystemPromptAppend: input.systemPromptAppend } : {}),
231
232
  });