mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-26 19:22:51 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abf9a6ff77 | ||
|
|
ae153721c8 |
@@ -135,6 +135,7 @@ export async function ensureAgentLoaded(
|
||||
labels: record.labels,
|
||||
workspaceId: record.workspaceId,
|
||||
owner: record.owner,
|
||||
env: record.launchEnv,
|
||||
});
|
||||
deps.logger.info({ agentId, provider: record.provider }, "Agent created from stored config");
|
||||
}
|
||||
|
||||
@@ -2869,6 +2869,134 @@ test("createAgent passes daemon launch env through the provider launch context",
|
||||
});
|
||||
});
|
||||
|
||||
test.each([
|
||||
{ provider: "codex", hasHandle: true },
|
||||
{ provider: "opencode", hasHandle: true },
|
||||
{ provider: "codex", hasHandle: false },
|
||||
{ provider: "opencode", hasHandle: false },
|
||||
])(
|
||||
"$provider preserves explicit launch env across reloads and storage-backed resumes (handle: $hasHandle)",
|
||||
async ({ provider, hasHandle }) => {
|
||||
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-launch-env-"));
|
||||
const storagePath = join(workdir, "agents");
|
||||
class LaunchEnvClient extends TestAgentClient {
|
||||
lastLaunchContext: AgentLaunchContext | undefined;
|
||||
|
||||
override async createSession(
|
||||
config: AgentSessionConfig,
|
||||
context?: AgentLaunchContext,
|
||||
): Promise<AgentSession> {
|
||||
this.lastLaunchContext = context;
|
||||
const session = await super.createSession(config);
|
||||
session.describePersistence = () => ({ provider, sessionId: session.id });
|
||||
return session;
|
||||
}
|
||||
|
||||
override async resumeSession(
|
||||
handle: AgentPersistenceHandle,
|
||||
config?: Partial<AgentSessionConfig>,
|
||||
context?: AgentLaunchContext,
|
||||
): Promise<AgentSession> {
|
||||
this.lastLaunchContext = context;
|
||||
const session = await super.resumeSession(handle, config, context);
|
||||
session.describePersistence = () => ({ provider, sessionId: handle.sessionId });
|
||||
return session;
|
||||
}
|
||||
}
|
||||
|
||||
const client = new LaunchEnvClient(provider);
|
||||
let storage = new AgentStorage(storagePath, logger);
|
||||
let manager = new AgentManager({ clients: { [provider]: client }, registry: storage, logger });
|
||||
const env = { XDG_DATA_HOME: join(workdir, "agent-data"), CUSTOM_EMPTY: "" };
|
||||
const expectedEnv = { ...env };
|
||||
const agent = await manager.createAgent({ provider, cwd: workdir }, undefined, {
|
||||
workspaceId: undefined,
|
||||
env,
|
||||
});
|
||||
onTestFinished(async () => {
|
||||
await manager.closeAgent(agent.id);
|
||||
await manager.flush();
|
||||
rmSync(workdir, { recursive: true, force: true });
|
||||
});
|
||||
env.XDG_DATA_HOME = "caller-mutated";
|
||||
|
||||
await manager.closeAgent(agent.id);
|
||||
await manager.flush();
|
||||
|
||||
if (!hasHandle) {
|
||||
const record = await storage.get(agent.id);
|
||||
if (!record) throw new Error("Created agent was not persisted");
|
||||
await storage.upsert({ ...record, persistence: null });
|
||||
}
|
||||
|
||||
// Fresh storage and manager instances must read the environment from disk.
|
||||
for (let restart = 0; restart < 2; restart += 1) {
|
||||
storage = new AgentStorage(storagePath, logger);
|
||||
manager = new AgentManager({ clients: { [provider]: client }, registry: storage, logger });
|
||||
const resumed = await ensureAgentLoaded(agent.id, {
|
||||
agentManager: manager,
|
||||
agentStorage: storage,
|
||||
logger,
|
||||
});
|
||||
expect(client.lastLaunchContext).toEqual({
|
||||
agentId: agent.id,
|
||||
env: { ...expectedEnv, PASEO_AGENT_ID: agent.id, PASEO_AGENT_CWD: workdir },
|
||||
});
|
||||
await manager.reloadAgentSession(agent.id);
|
||||
expect(client.lastLaunchContext).toEqual({
|
||||
agentId: agent.id,
|
||||
env: { ...expectedEnv, PASEO_AGENT_ID: agent.id, PASEO_AGENT_CWD: workdir },
|
||||
});
|
||||
expect(toAgentPayload(resumed)).not.toHaveProperty("launchEnv");
|
||||
expect(toAgentPayload(resumed).persistence?.metadata).not.toHaveProperty("launchEnv");
|
||||
await manager.closeAgent(agent.id);
|
||||
await manager.flush();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test("session-open hooks override persisted launch env without persisting per-opening values", async () => {
|
||||
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-launch-env-hook-"));
|
||||
const storage = new AgentStorage(join(workdir, "agents"), logger);
|
||||
const observedEnv: Array<Record<string, string> | undefined> = [];
|
||||
const manager = new AgentManager({
|
||||
clients: { codex: new TestAgentClient() },
|
||||
registry: storage,
|
||||
logger,
|
||||
pluginLifecycle: {
|
||||
emit() {},
|
||||
async before(name, request) {
|
||||
if (name === "agent.create" && "env" in request) {
|
||||
request.env = { ...request.env, FROM_CREATE_HOOK: "saved" };
|
||||
}
|
||||
if (name === "agent.session_open" && "env" in request) {
|
||||
observedEnv.push({ ...request.env });
|
||||
request.env = { ...request.env, XDG_DATA_HOME: "current-hook-data" };
|
||||
}
|
||||
return request;
|
||||
},
|
||||
},
|
||||
});
|
||||
const agent = await manager.createAgent({ provider: "codex", cwd: workdir }, undefined, {
|
||||
workspaceId: undefined,
|
||||
env: { XDG_DATA_HOME: "original-data" },
|
||||
});
|
||||
onTestFinished(async () => {
|
||||
await manager.closeAgent(agent.id);
|
||||
await manager.flush();
|
||||
rmSync(workdir, { recursive: true, force: true });
|
||||
});
|
||||
await manager.reloadAgentSession(agent.id);
|
||||
expect(observedEnv).toEqual([
|
||||
{ XDG_DATA_HOME: "original-data", FROM_CREATE_HOOK: "saved" },
|
||||
{ XDG_DATA_HOME: "original-data", FROM_CREATE_HOOK: "saved" },
|
||||
]);
|
||||
expect((await storage.get(agent.id))?.launchEnv).toEqual({
|
||||
XDG_DATA_HOME: "original-data",
|
||||
FROM_CREATE_HOOK: "saved",
|
||||
});
|
||||
});
|
||||
|
||||
test("createAgent passes persistSession to provider create options", async () => {
|
||||
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-test-"));
|
||||
const storagePath = join(workdir, "agents");
|
||||
|
||||
@@ -414,6 +414,8 @@ interface ManagedAgentBase {
|
||||
owner?: AgentOwner;
|
||||
capabilities: AgentCapabilityFlags;
|
||||
config: AgentSessionConfig;
|
||||
/** Explicit launch overrides, before per-opening hooks and daemon-owned variables. */
|
||||
launchEnv?: Record<string, string>;
|
||||
runtimeInfo?: AgentRuntimeInfo;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
@@ -1323,6 +1325,7 @@ export class AgentManager {
|
||||
initialTitle: options.initialTitle,
|
||||
workspaceId: options.workspaceId,
|
||||
owner: options.owner,
|
||||
launchEnv: options.env ? { ...options.env } : undefined,
|
||||
historyPrimed: true,
|
||||
});
|
||||
if (!agent.internal) {
|
||||
@@ -1414,7 +1417,7 @@ export class AgentManager {
|
||||
const { storedConfig, launchConfig, paseoToolPolicy } = await this.prepareSessionConfig(
|
||||
mergedConfig,
|
||||
resolvedAgentId,
|
||||
{ purpose },
|
||||
{ purpose, env: record?.launchEnv },
|
||||
);
|
||||
const client = this.requireClient(handle.provider);
|
||||
const available = await client.isAvailable();
|
||||
@@ -1429,7 +1432,7 @@ export class AgentManager {
|
||||
client,
|
||||
storedConfig.cwd,
|
||||
paseoToolPolicy,
|
||||
undefined,
|
||||
record?.launchEnv,
|
||||
{
|
||||
reason: "resume",
|
||||
purpose,
|
||||
@@ -1446,6 +1449,7 @@ export class AgentManager {
|
||||
await this.requireExternalMcpSupport(session, storedConfig);
|
||||
return this.registerSession(session, storedConfig, resolvedAgentId, {
|
||||
...options,
|
||||
launchEnv: record?.launchEnv,
|
||||
persistence: handle,
|
||||
restoring: true,
|
||||
});
|
||||
@@ -1577,6 +1581,7 @@ export class AgentManager {
|
||||
const { storedConfig, launchConfig, paseoToolPolicy } = await this.prepareSessionConfig(
|
||||
refreshConfig,
|
||||
agentId,
|
||||
{ env: existing.launchEnv },
|
||||
);
|
||||
const hadPreviousPaseoToolPolicy = this.paseoToolPolicies.has(agentId);
|
||||
const previousPaseoToolPolicy = this.paseoToolPolicies.get(agentId);
|
||||
@@ -1585,7 +1590,7 @@ export class AgentManager {
|
||||
client,
|
||||
storedConfig.cwd,
|
||||
paseoToolPolicy,
|
||||
undefined,
|
||||
existing.launchEnv,
|
||||
{ reason: "refresh", purpose: "interactive", workspaceId: existing.workspaceId },
|
||||
);
|
||||
const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
|
||||
@@ -1630,6 +1635,7 @@ export class AgentManager {
|
||||
labels: existing.labels,
|
||||
workspaceId: existing.workspaceId,
|
||||
owner: existing.owner,
|
||||
launchEnv: existing.launchEnv,
|
||||
createdAt: existing.createdAt,
|
||||
updatedAt: existing.updatedAt,
|
||||
lastUserMessageAt: existing.lastUserMessageAt,
|
||||
@@ -3668,6 +3674,7 @@ export class AgentManager {
|
||||
publishWhenReady?: boolean;
|
||||
workspaceId?: string;
|
||||
owner?: AgentOwner;
|
||||
launchEnv?: Record<string, string>;
|
||||
},
|
||||
): Promise<ManagedAgent> {
|
||||
let registered = false;
|
||||
@@ -3846,6 +3853,7 @@ export class AgentManager {
|
||||
persistence?: AgentPersistenceHandle;
|
||||
workspaceId?: string;
|
||||
owner?: AgentOwner;
|
||||
launchEnv?: Record<string, string>;
|
||||
}
|
||||
| undefined;
|
||||
}): ActiveManagedAgent {
|
||||
@@ -3856,6 +3864,7 @@ export class AgentManager {
|
||||
cwd: config.cwd,
|
||||
workspaceId: options?.workspaceId,
|
||||
owner: options?.owner,
|
||||
launchEnv: options?.launchEnv,
|
||||
session,
|
||||
capabilities: session.capabilities,
|
||||
config,
|
||||
|
||||
@@ -95,6 +95,7 @@ export function toStoredAgentRecord(
|
||||
lastStatus: agent.lifecycle,
|
||||
lastModeId: agent.currentModeId ?? config?.modeId ?? null,
|
||||
config: config ?? null,
|
||||
launchEnv: agent.launchEnv ? { ...agent.launchEnv } : undefined,
|
||||
runtimeInfo,
|
||||
features: normalizeFeatures(agent.features),
|
||||
persistence,
|
||||
|
||||
@@ -56,6 +56,7 @@ const STORED_AGENT_SCHEMA = z.object({
|
||||
lastStatus: AgentStatusSchema.default("closed"),
|
||||
lastModeId: z.string().nullable().optional(),
|
||||
config: SERIALIZABLE_CONFIG_SCHEMA,
|
||||
launchEnv: z.record(z.string(), z.string()).optional(),
|
||||
runtimeInfo: z
|
||||
.object({
|
||||
provider: z.string(),
|
||||
|
||||
Reference in New Issue
Block a user