@compilr-dev/sdk 0.23.0 → 0.23.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.
@@ -13,6 +13,38 @@ import type { ModelTier } from '../models/index.js';
13
13
  import type { ProviderType } from '../config.js';
14
14
  import type { SharedContextManager } from './shared-context.js';
15
15
  import type { CustomAgentDefinition } from './custom-agents.js';
16
+ /**
17
+ * Strip the composite a pre-0.23 `fromRole()` stored, so it is not composed a second time.
18
+ *
19
+ * ⚠️ WITHOUT THIS, EVERY ROLE AGENT CREATED BEFORE 0.23 SENDS ITS ROLE PROMPT TWICE. The first
20
+ * cut of this migration skipped role agents, reasoning that an edited stored prompt is
21
+ * indistinguishable from an unedited one so removing anything risks the user's text. True, and
22
+ * beside the point: the generated sections now compose on top of whatever is stored, so leaving
23
+ * the legacy copy in place duplicates it. Measured on a real `$arch`: 13,024 characters instead
24
+ * of ~6,900, with the role header and capability blurb appearing twice each.
25
+ *
26
+ * ⚠️ AND IT CANNOT WORK BY RECONSTRUCTION. The second cut rebuilt the old composite exactly and
27
+ * stripped it on an exact match. That matched 6 of 16 real agents. The capability blurb lists
28
+ * the profile's tool GROUPS as they stood on the day the agent was created — a `qa` agent saved
29
+ * before the profile gained `consult` carries a blurb nine characters shorter than today's, and
30
+ * no amount of care reproduces a list that has since changed.
31
+ *
32
+ * So this anchors on the two ends, which are stable, and ignores the drifting middle entirely:
33
+ *
34
+ * - the FIRST LINE must be the first line of generated content (the role header, or the
35
+ * guidance heading for roles with no role prompt of their own), and
36
+ * - the CURRENT guidance block must appear — it is always last, and unlike the tool blurb it
37
+ * is not derived from mutable configuration.
38
+ *
39
+ * Everything up to the end of that guidance is generated; whatever follows is the user's and is
40
+ * kept. If either anchor is missing the value is returned untouched, so hand-written text and a
41
+ * future rewording both fail safe: the agent keeps its duplication until someone clears the box,
42
+ * and nobody loses text they wrote.
43
+ *
44
+ * @returns the user's own remainder, `undefined` if the stored value was entirely generated, or
45
+ * `stored` unchanged when it is not recognisably a legacy composite.
46
+ */
47
+ export declare function splitLegacyRolePrompt(stored: string, role: AgentRole): string | undefined;
16
48
  /**
17
49
  * The editable subset of an agent.
18
50
  *
@@ -11,6 +11,58 @@ import { ROLE_METADATA } from './types.js';
11
11
  import { getModelForTier, getModelContextWindow } from '../models/index.js';
12
12
  import { generateCustomAgentScaffold, getCustomAgentToolFilter, splitLegacyCustomPrompt, } from './custom-agents.js';
13
13
  import { getToolsForProfile, PROFILE_INFO, generateToolAwarenessPrompt, generateCoordinatorGuidance, generateSpecialistGuidance, } from './tool-config.js';
14
+ /**
15
+ * Strip the composite a pre-0.23 `fromRole()` stored, so it is not composed a second time.
16
+ *
17
+ * ⚠️ WITHOUT THIS, EVERY ROLE AGENT CREATED BEFORE 0.23 SENDS ITS ROLE PROMPT TWICE. The first
18
+ * cut of this migration skipped role agents, reasoning that an edited stored prompt is
19
+ * indistinguishable from an unedited one so removing anything risks the user's text. True, and
20
+ * beside the point: the generated sections now compose on top of whatever is stored, so leaving
21
+ * the legacy copy in place duplicates it. Measured on a real `$arch`: 13,024 characters instead
22
+ * of ~6,900, with the role header and capability blurb appearing twice each.
23
+ *
24
+ * ⚠️ AND IT CANNOT WORK BY RECONSTRUCTION. The second cut rebuilt the old composite exactly and
25
+ * stripped it on an exact match. That matched 6 of 16 real agents. The capability blurb lists
26
+ * the profile's tool GROUPS as they stood on the day the agent was created — a `qa` agent saved
27
+ * before the profile gained `consult` carries a blurb nine characters shorter than today's, and
28
+ * no amount of care reproduces a list that has since changed.
29
+ *
30
+ * So this anchors on the two ends, which are stable, and ignores the drifting middle entirely:
31
+ *
32
+ * - the FIRST LINE must be the first line of generated content (the role header, or the
33
+ * guidance heading for roles with no role prompt of their own), and
34
+ * - the CURRENT guidance block must appear — it is always last, and unlike the tool blurb it
35
+ * is not derived from mutable configuration.
36
+ *
37
+ * Everything up to the end of that guidance is generated; whatever follows is the user's and is
38
+ * kept. If either anchor is missing the value is returned untouched, so hand-written text and a
39
+ * future rewording both fail safe: the agent keeps its duplication until someone clears the box,
40
+ * and nobody loses text they wrote.
41
+ *
42
+ * @returns the user's own remainder, `undefined` if the stored value was entirely generated, or
43
+ * `stored` unchanged when it is not recognisably a legacy composite.
44
+ */
45
+ export function splitLegacyRolePrompt(stored, role) {
46
+ /*
47
+ ⚠️ TYPED AS TOTAL, NOT TOTAL AT RUNTIME. `role` is `AgentRole` to the compiler but arrives
48
+ from a JSON file on disk, so it can be any string — a role removed in a later version, or one
49
+ a fixture invented. The explicit `| undefined` is what makes the guard below legitimate
50
+ rather than "always falsy": lint reasons from the type, and the type is a claim about data
51
+ we do not control.
52
+ */
53
+ const byRole = ROLE_METADATA;
54
+ const metadata = byRole[role];
55
+ if (!metadata)
56
+ return stored;
57
+ const guidance = role === 'default' ? generateCoordinatorGuidance() : generateSpecialistGuidance(role);
58
+ const generatedFirstLine = (metadata.defaultSystemPromptAddition || guidance).split('\n')[0];
59
+ if (stored.split('\n')[0] !== generatedFirstLine)
60
+ return stored;
61
+ const at = stored.lastIndexOf(guidance);
62
+ if (at === -1)
63
+ return stored;
64
+ return stored.slice(at + guidance.length).trim() || undefined;
65
+ }
14
66
  /**
15
67
  * Estimate token count from text (approximate: ~4 chars per token)
16
68
  */
@@ -670,6 +722,9 @@ export class TeamAgent {
670
722
  personality = personality ?? legacy.personality;
671
723
  }
672
724
  }
725
+ else if (data.role !== 'custom' && systemPromptAddition) {
726
+ systemPromptAddition = splitLegacyRolePrompt(systemPromptAddition, data.role);
727
+ }
673
728
  const agent = new TeamAgent({
674
729
  id: data.id,
675
730
  displayName: data.displayName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.23.0",
3
+ "version": "0.23.1",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",