Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit dafdf66

Browse files
authored
Simplify always-on skill prompt inclusion
Generated-By: PostHog Code Task-Id: b0e948e2-b733-4d51-be6a-efd68d54216e
1 parent 34aa0d6 commit dafdf66

23 files changed

Lines changed: 201 additions & 438 deletions

packages/agent/src/server/agent-server.test.ts

Lines changed: 20 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2320,22 +2320,6 @@ describe("AgentServer HTTP Mode", () => {
23202320
serverInternals.posthogAPI.downloadArtifact = downloadArtifact;
23212321

23222322
const token = createToken();
2323-
const artifact = {
2324-
id: "skill-artifact-1",
2325-
name: "local-test-skill.zip",
2326-
type: "skill_bundle",
2327-
source: "posthog_code_skill",
2328-
storage_path: "tasks/artifacts/local-test-skill.zip",
2329-
content_type: "application/zip",
2330-
metadata: {
2331-
skill_name: "local-test-skill",
2332-
skill_source: "user",
2333-
content_sha256: checksum,
2334-
bundle_format: "zip",
2335-
schema_version: 1,
2336-
always_on: true,
2337-
},
2338-
};
23392323
const response = await fetch(`http://localhost:${port}/command`, {
23402324
method: "POST",
23412325
headers: {
@@ -2348,7 +2332,23 @@ describe("AgentServer HTTP Mode", () => {
23482332
method: "user_message",
23492333
params: {
23502334
content: "/local-test-skill with context",
2351-
artifacts: [artifact],
2335+
artifacts: [
2336+
{
2337+
id: "skill-artifact-1",
2338+
name: "local-test-skill.zip",
2339+
type: "skill_bundle",
2340+
source: "posthog_code_skill",
2341+
storage_path: "tasks/artifacts/local-test-skill.zip",
2342+
content_type: "application/zip",
2343+
metadata: {
2344+
skill_name: "local-test-skill",
2345+
skill_source: "user",
2346+
content_sha256: checksum,
2347+
bundle_format: "zip",
2348+
schema_version: 1,
2349+
},
2350+
},
2351+
],
23522352
},
23532353
}),
23542354
});
@@ -2374,38 +2374,11 @@ describe("AgentServer HTTP Mode", () => {
23742374

23752375
expect(sentText).toBe("/local-test-skill with context");
23762376
expect(sentMeta?.localSkillContext).toContain(
2377-
"Always-on skills apply for the entire session",
2378-
);
2379-
expect(sentMeta?.localSkillContext).toContain(
2380-
"/skills/local-test-skill/SKILL.md",
2381-
);
2382-
expect(sentMeta?.localSkillContext).toContain(
2383-
"User request:\nwith context",
2377+
'local skill "/local-test-skill"',
23842378
);
2385-
expect(sentMeta?.localSkillContext).not.toContain("LOCAL_SKILL_MARKER");
2379+
expect(sentMeta?.localSkillContext).toContain("LOCAL_SKILL_MARKER");
2380+
expect(sentMeta?.localSkillContext).toContain("with context");
23862381
expect(sentMeta?.localSkillName).toBe("local-test-skill");
2387-
2388-
await fetch(`http://localhost:${port}/command`, {
2389-
method: "POST",
2390-
headers: {
2391-
Authorization: `Bearer ${token}`,
2392-
"Content-Type": "application/json",
2393-
},
2394-
body: JSON.stringify({
2395-
jsonrpc: "2.0",
2396-
id: "skill-followup",
2397-
method: "user_message",
2398-
params: {
2399-
content: "Follow up",
2400-
artifacts: [artifact],
2401-
},
2402-
}),
2403-
});
2404-
2405-
expect(prompt).toHaveBeenCalledTimes(2);
2406-
expect(
2407-
prompt.mock.calls[1]?.[0]._meta?.localSkillContext,
2408-
).toBeUndefined();
24092382
}, 20000);
24102383

24112384
it("lists co-installed dependency skills with their paths in the skill context", async () => {

packages/agent/src/server/agent-server.ts

Lines changed: 11 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,6 @@ export class AgentServer {
395395
private warmAutoPublishResolved = false;
396396
private installedSkillBundles = new Set<string>();
397397
private installedSkillBundleInfo = new Map<string, InstalledSkillBundle>();
398-
private alwaysOnSkillsActivatedSessionId: string | null = null;
399398
private installingSkillBundles = new Map<string, Promise<void>>();
400399
// Guards against concurrent session initialization. autoInitializeSession() and
401400
// the GET /events SSE handler can both call initializeSession() — the SSE connection
@@ -2839,44 +2838,8 @@ export class AgentServer {
28392838
runId: string,
28402839
artifacts: TaskRunArtifact[],
28412840
): LocalSkillPromptContext | null {
2842-
const alwaysOnSkills = artifacts
2843-
.filter(
2844-
(artifact) =>
2845-
artifact.type === "skill_bundle" &&
2846-
artifact.metadata?.always_on === true,
2847-
)
2848-
.sort((left, right) =>
2849-
`${left.metadata?.skill_source}:${left.metadata?.skill_name}`.localeCompare(
2850-
`${right.metadata?.skill_source}:${right.metadata?.skill_name}`,
2851-
),
2852-
)
2853-
.map((artifact) =>
2854-
this.installedSkillBundleInfo.get(
2855-
this.getInstalledSkillBundleInfoKey(
2856-
runId,
2857-
artifact.metadata?.skill_name ?? "",
2858-
),
2859-
),
2860-
)
2861-
.filter((skill): skill is InstalledSkillBundle => !!skill);
2862-
const sessionId = this.session?.acpSessionId;
2863-
const shouldActivateAlwaysOnSkills =
2864-
alwaysOnSkills.length > 0 &&
2865-
!!sessionId &&
2866-
this.alwaysOnSkillsActivatedSessionId !== sessionId;
2867-
const alwaysOnContext = shouldActivateAlwaysOnSkills
2868-
? [
2869-
"Always-on skills apply for the entire session. Before proceeding, read each SKILL.md below from the filesystem in the listed order and follow its instructions for the entire session.",
2870-
...alwaysOnSkills.map(
2871-
(skill) => `- /${skill.skillName}: ${skill.skillRoot}/SKILL.md`,
2872-
),
2873-
].join("\n")
2874-
: null;
2875-
if (shouldActivateAlwaysOnSkills && sessionId) {
2876-
this.alwaysOnSkillsActivatedSessionId = sessionId;
2877-
}
28782841
if (contentBlocks.length === 0) {
2879-
return alwaysOnContext ? { context: alwaysOnContext } : null;
2842+
return null;
28802843
}
28812844

28822845
const textBlockIndex = contentBlocks.findIndex(
@@ -2891,23 +2854,6 @@ export class AgentServer {
28912854
: null;
28922855

28932856
if (invocation) {
2894-
const invokedAlwaysOnSkill = alwaysOnSkills.find(
2895-
(skill) => skill.skillName === invocation.skillName,
2896-
);
2897-
if (invokedAlwaysOnSkill) {
2898-
const invokedContext = this.buildInstalledSkillPrompt(
2899-
invokedAlwaysOnSkill,
2900-
invocation.args,
2901-
this.getCoInstalledSkillBundles(runId, invocation.skillName),
2902-
false,
2903-
);
2904-
return {
2905-
skillName: invocation.skillName,
2906-
context: alwaysOnContext
2907-
? `${alwaysOnContext}\n\n${invokedContext}`
2908-
: invokedContext,
2909-
};
2910-
}
29112857
const hasMatchingArtifact = artifacts.some(
29122858
(artifact) =>
29132859
artifact.type === "skill_bundle" &&
@@ -2919,16 +2865,13 @@ export class AgentServer {
29192865
)
29202866
: undefined;
29212867
if (installedSkill) {
2922-
const invokedContext = this.buildInstalledSkillPrompt(
2923-
installedSkill,
2924-
invocation.args,
2925-
this.getCoInstalledSkillBundles(runId, invocation.skillName),
2926-
);
29272868
return {
29282869
skillName: invocation.skillName,
2929-
context: alwaysOnContext
2930-
? `${alwaysOnContext}\n\n${invokedContext}`
2931-
: invokedContext,
2870+
context: this.buildInstalledSkillPrompt(
2871+
installedSkill,
2872+
invocation.args,
2873+
this.getCoInstalledSkillBundles(runId, invocation.skillName),
2874+
),
29322875
};
29332876
}
29342877
}
@@ -2940,17 +2883,7 @@ export class AgentServer {
29402883
)
29412884
.map((block) => block.text)
29422885
.join("\n");
2943-
const attachedContext = this.buildAttachedSkillsPromptContext(
2944-
runId,
2945-
artifacts.filter((artifact) => artifact.metadata?.always_on !== true),
2946-
messageText,
2947-
);
2948-
if (!alwaysOnContext) return attachedContext;
2949-
return {
2950-
context: attachedContext
2951-
? `${alwaysOnContext}\n\n${attachedContext.context}`
2952-
: alwaysOnContext,
2953-
};
2886+
return this.buildAttachedSkillsPromptContext(runId, artifacts, messageText);
29542887
}
29552888

29562889
/**
@@ -3049,19 +2982,14 @@ export class AgentServer {
30492982
skill: InstalledSkillBundle,
30502983
args: string | undefined,
30512984
coInstalledSkills: InstalledSkillBundle[] = [],
3052-
includeDefinition = true,
30532985
): string {
30542986
return [
30552987
`The user invoked the local skill "/${skill.skillName}". Apply these skill instructions for this turn.`,
30562988
"",
3057-
...(includeDefinition
3058-
? [
3059-
`--- BEGIN LOCAL SKILL ${skill.skillName} ---`,
3060-
skill.skillDefinition.trim(),
3061-
`--- END LOCAL SKILL ${skill.skillName} ---`,
3062-
"",
3063-
]
3064-
: []),
2989+
`--- BEGIN LOCAL SKILL ${skill.skillName} ---`,
2990+
skill.skillDefinition.trim(),
2991+
`--- END LOCAL SKILL ${skill.skillName} ---`,
2992+
"",
30652993
`Installed skill path: ${skill.skillRoot}`,
30662994
...(coInstalledSkills.length > 0
30672995
? [

packages/core/src/sessions/cloudArtifactIdentifiers.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ export interface CloudSkillBundleRef {
5151
name: string;
5252
source: UploadableSkillSource;
5353
path: string;
54-
alwaysOn?: boolean;
5554
}
5655

5756
export interface LocalSkillBundle {

packages/core/src/sessions/cloudArtifactService.test.ts

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ describe("CloudArtifactService", () => {
170170
name: "local-skill",
171171
source: "user",
172172
path: "/tmp/local-skill",
173-
alwaysOn: true,
174173
},
175174
],
176175
);
@@ -189,57 +188,13 @@ describe("CloudArtifactService", () => {
189188
skill_source: "user",
190189
bundle_format: "zip",
191190
schema_version: 1,
192-
always_on: true,
193191
}),
194192
}),
195193
],
196194
);
197195
fetchMock.mockRestore();
198196
});
199197

200-
it("skips an unavailable always-on skill without failing the upload", async () => {
201-
const service = new CloudArtifactService(
202-
vi.fn(),
203-
vi.fn().mockRejectedValue(new Error("missing skill")),
204-
passthroughDeps,
205-
);
206-
207-
await expect(
208-
service.uploadRunAttachments(
209-
makeClient(),
210-
"task-1",
211-
"run-1",
212-
[],
213-
[
214-
{
215-
name: "missing",
216-
source: "user",
217-
path: "/tmp/missing",
218-
alwaysOn: true,
219-
},
220-
],
221-
),
222-
).resolves.toEqual([]);
223-
});
224-
225-
it("fails when an explicitly requested skill is unavailable", async () => {
226-
const service = new CloudArtifactService(
227-
vi.fn(),
228-
vi.fn().mockRejectedValue(new Error("missing skill")),
229-
passthroughDeps,
230-
);
231-
232-
await expect(
233-
service.uploadRunAttachments(
234-
makeClient(),
235-
"task-1",
236-
"run-1",
237-
[],
238-
[{ name: "missing", source: "user", path: "/tmp/missing" }],
239-
),
240-
).rejects.toThrow("missing skill");
241-
});
242-
243198
it("uploads dependency skills the resolver adds to a tagged skill", async () => {
244199
const fetchMock = vi
245200
.spyOn(globalThis, "fetch")

packages/core/src/sessions/cloudArtifactService.ts

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,6 @@ export class CloudArtifactService {
173173
...(await this.loadCloudAttachments(filePaths)),
174174
...(await this.loadCloudSkillBundles(skillBundles)),
175175
];
176-
if (attachments.length === 0) return [];
177176
const preparedArtifacts = await client.prepareTaskRunArtifactUploads(
178177
taskId,
179178
runId,
@@ -233,30 +232,8 @@ export class CloudArtifactService {
233232
if (skillBundleRefs.length === 0) {
234233
return [];
235234
}
236-
const explicitRefs = skillBundleRefs.filter((ref) => !ref.alwaysOn);
237-
const alwaysOnRefs = skillBundleRefs.filter((ref) => ref.alwaysOn);
238-
const explicit = await this.loadCloudSkillBundleRefs(explicitRefs);
239-
const alwaysOn = (
240-
await Promise.all(
241-
alwaysOnRefs.map((ref) =>
242-
this.loadCloudSkillBundleRefs([ref]).catch(() => []),
243-
),
244-
)
245-
).flat();
246-
const deduplicated = new Map(
247-
[...explicit, ...alwaysOn].map((attachment) => [
248-
attachment.filePath,
249-
attachment,
250-
]),
251-
);
252-
return [...deduplicated.values()];
253-
}
254-
255-
private async loadCloudSkillBundleRefs(
256-
refs: CloudSkillBundleRef[],
257-
): Promise<LoadedCloudAttachment[]> {
258-
if (refs.length === 0) return [];
259-
const expandedRefs = await this.resolveSkillBundleDependencies(refs);
235+
const expandedRefs =
236+
await this.resolveSkillBundleDependencies(skillBundleRefs);
260237
return Promise.all(
261238
expandedRefs.map(async (skillBundleRef) => {
262239
const bundle = await this.bundleLocalSkill(skillBundleRef);
@@ -287,7 +264,6 @@ export class CloudArtifactService {
287264
content_sha256: bundle.contentSha256,
288265
bundle_format: "zip",
289266
schema_version: 1,
290-
...(skillBundleRef.alwaysOn ? { always_on: true } : {}),
291267
},
292268
},
293269
};

0 commit comments

Comments
 (0)