@compilr-dev/sdk 0.10.17 → 0.10.19

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/dist/index.d.ts CHANGED
@@ -35,9 +35,9 @@
35
35
  */
36
36
  export { createCompilrAgent } from './agent.js';
37
37
  export type { CompilrAgentConfig, CompilrAgent, RunOptions, RunResult, ToolCallRecord, ToolConfig, UsageInfo, ProviderType, PermissionCallback, GuardrailConfig, ContextConfig, CapabilitiesConfig, } from './config.js';
38
- export { AgentTeam, TeamAgent, SharedContextManager, ArtifactStore, DelegationTracker, ContextResolver, createDelegationStatusTool, createHandoffTool, } from './team/index.js';
38
+ export { AgentTeam, TeamAgent, SharedContextManager, ArtifactStore, DelegationTracker, ContextResolver, createDelegationStatusTool, createHandoffTool, buildHandoffTaskMessage, validateHandoffIntent, HandoffStash, ROLE_NAME_ALIASES, normalizeRoleName, } from './team/index.js';
39
39
  export type { AgentTeamConfig, TeamAgentConfig, ITeamPersistence, IArtifactStorage, ISessionRegistry, CustomAgentDefinition, AgentTemplate, AgentWorkshopData, WorkshopRoleDef, WorkshopToolProfile, WorkshopModelTier, WorkshopSkillDef, PlanSubmitInfo, PlanSubmitResult, PlanModeExitInfo, PlanModeCallbacks, ToolConfig as TeamToolConfig, ToolTier, ToolGroup, ProfileInfo, } from './team/index.js';
40
- export type { AgentRole, RoleMetadata, ToolProfile, MascotExpression, BackgroundSessionInfo, SerializedTeam, SerializedTeamAgent, TeamMetadata, TeamEvent, TeamEventType, TeamEventHandler, Artifact, ArtifactType as TeamArtifactType, ArtifactSummary as TeamArtifactSummary, CreateArtifactOptions, UpdateArtifactOptions, SerializedArtifact, SharedContext, SharedProjectInfo, SharedTeamInfo, TeamRosterEntry, TeamActivity, TeamActivityType, SharedDecision, TokenBudget, SerializedSharedContext, ParsedMention, ParsedInput, ResolvedMention, ResolveOptions, ResolutionSource, Delegation, DelegationStatus, DelegationResult, CompletionEvent, CreateDelegationOptions, DelegationStats, DelegationTrackerEvents, HandoffResult, HandoffToolConfig, SkillToolRequirement, } from './team/index.js';
40
+ export type { AgentRole, RoleMetadata, ToolProfile, MascotExpression, BackgroundSessionInfo, SerializedTeam, SerializedTeamAgent, TeamMetadata, TeamEvent, TeamEventType, TeamEventHandler, Artifact, ArtifactType as TeamArtifactType, ArtifactSummary as TeamArtifactSummary, CreateArtifactOptions, UpdateArtifactOptions, SerializedArtifact, SharedContext, SharedProjectInfo, SharedTeamInfo, TeamRosterEntry, TeamActivity, TeamActivityType, SharedDecision, TokenBudget, SerializedSharedContext, ParsedMention, ParsedInput, ResolvedMention, ResolveOptions, ResolutionSource, Delegation, DelegationStatus, DelegationResult, CompletionEvent, CreateDelegationOptions, DelegationStats, DelegationTrackerEvents, HandoffResult, HandoffToolConfig, HandoffIntent, HandoffValidationResult, NormalizedRole, SkillToolRequirement, } from './team/index.js';
41
41
  export { ROLE_METADATA, ROLE_EXPERTISE, ROLE_GROUPS, PREDEFINED_ROLE_IDS, TOOL_GROUPS, TOOL_PROFILES, PROFILE_INFO, SKILL_REQUIREMENTS, CUSTOM_MASCOTS, buildAgentWorkshopData, buildSuggestedRolesMap, PLAN_MODE_BLOCKED_TOOLS, PLAN_MODE_DENIAL_MESSAGE, PLAN_MODE_PROMPT, isToolAllowedInPlanMode, getPlanModePrompt, } from './team/index.js';
42
42
  export { getToolsForProfile, detectProfileFromTools, isProfileReadOnly, generateToolAwarenessPrompt, generateCoordinatorGuidance, generateSpecialistGuidance, createDefaultToolConfig, validateToolConfig, getAllGroupIds, getGroupInfo, getGroupsByTier, getGroupsForProfile, assignMascot, generateCustomAgentSystemPrompt, getCustomAgentToolFilter, getCustomAgentProfileLabel, validateAgentId, isAgentIdTaken, createCustomAgentDefinition, listTemplates, getTemplate, saveTemplate, updateTemplate, deleteTemplate, createAgentFromTemplate, parseInputForMentions, getReferencedAgents, hasReferences, buildMessageWithContext, buildContextMap, findAgentForRole, findAgentById, getAvailableSpecialists, getSpecialistsSummary, hasSpecialists, suggestOwner, suggestOwners, matchesAgentExpertise, wouldCreateLoop, recordAssignment, getAssignmentHistory, clearAssignmentHistory, clearAllAssignmentHistory, canReassign, resolveAgentIdCollision, setActiveSharedContext, getActiveSharedContext, recordTeamActivity, getDefinedSkillNames, getSkillRequirements, checkSkillCompatibility, getCompatibleSkills, getAllRequiredTools, getSkillsByCategory, } from './team/index.js';
43
43
  export { codingPreset, readOnlyPreset, resolvePreset } from './presets/index.js';
package/dist/index.js CHANGED
@@ -41,7 +41,7 @@ export { createCompilrAgent } from './agent.js';
41
41
  // Multi-Agent Team Orchestration
42
42
  // =============================================================================
43
43
  // Core classes
44
- export { AgentTeam, TeamAgent, SharedContextManager, ArtifactStore, DelegationTracker, ContextResolver, createDelegationStatusTool, createHandoffTool, } from './team/index.js';
44
+ export { AgentTeam, TeamAgent, SharedContextManager, ArtifactStore, DelegationTracker, ContextResolver, createDelegationStatusTool, createHandoffTool, buildHandoffTaskMessage, validateHandoffIntent, HandoffStash, ROLE_NAME_ALIASES, normalizeRoleName, } from './team/index.js';
45
45
  // Constants
46
46
  export { ROLE_METADATA, ROLE_EXPERTISE, ROLE_GROUPS, PREDEFINED_ROLE_IDS, TOOL_GROUPS, TOOL_PROFILES, PROFILE_INFO, SKILL_REQUIREMENTS, CUSTOM_MASCOTS, buildAgentWorkshopData, buildSuggestedRolesMap,
47
47
  // Plan mode
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Handoff orchestration helpers.
3
+ *
4
+ * The `createHandoffTool` factory (delegation-tools.ts) covers the
5
+ * tool-side of handoffs: validation, one-hop enforcement, `team.recordHandoff`.
6
+ * What it does NOT cover is the post-turn orchestration pattern shared by
7
+ * all hosts that surface this tool — stashing the dispatched intent during
8
+ * the source agent's turn, picking it back up once the turn ends, and
9
+ * dispatching the task to the target agent.
10
+ *
11
+ * Hosts (CLI, Desktop) used to implement that pattern independently,
12
+ * which is exactly the kind of drift that produced the desktop's
13
+ * silent-stub bug. This module centralises:
14
+ *
15
+ * - **HandoffIntent** — the shape both hosts pass between "tool fired"
16
+ * and "source turn ended."
17
+ * - **buildHandoffTaskMessage** — canonical `[Handoff from $arch]\n\nTask:`
18
+ * format so the synthesised message the target receives is identical
19
+ * across hosts.
20
+ * - **validateHandoffIntent** — re-checks team state at dispatch time
21
+ * (target could have been removed between stash and pickup).
22
+ * - **HandoffStash** — optional convenience for hosts that have multiple
23
+ * independent conversations (Desktop). CLI's single-REPL pattern is
24
+ * simpler than this class warrants; it can use the type + helpers
25
+ * directly without the stash.
26
+ *
27
+ * Storage location is intentionally platform-shaped (CLI: one global,
28
+ * Desktop: per-conversation map) — only the *what* gets standardised,
29
+ * not the *where*.
30
+ */
31
+ import type { AgentTeam } from './team.js';
32
+ /**
33
+ * The intent recorded when a specialist agent calls `handoff(...)`.
34
+ * Stashed during the source agent's turn, consumed when the turn ends so
35
+ * the target agent can be invoked with the task.
36
+ *
37
+ * `conversationId` is opaque to the SDK — hosts use it to scope stashed
38
+ * intents (Desktop's per-conversation map; CLI can use a constant like
39
+ * `'default'` because there's only one REPL session).
40
+ */
41
+ export interface HandoffIntent {
42
+ /** Host-defined conversation key. Opaque to the SDK. */
43
+ conversationId: string;
44
+ /** Agent that called `handoff`. */
45
+ sourceAgentId: string;
46
+ /** Agent receiving the handoff. */
47
+ targetAgentId: string;
48
+ /** Task description passed verbatim to the target. */
49
+ task: string;
50
+ /** Optional reason text the source provided. */
51
+ reason?: string;
52
+ }
53
+ /**
54
+ * Build the synthesised user-style message handed to the target agent
55
+ * after a successful handoff. Wrapping the body with a `[Handoff from $X]`
56
+ * marker keeps the target oriented and gives future tooling a way to
57
+ * distinguish handoff-injected messages from direct user input.
58
+ *
59
+ * Format:
60
+ *
61
+ * [Handoff from $arch]
62
+ *
63
+ * Task: <verbatim task>
64
+ *
65
+ * Reason: <verbatim reason — omitted when absent>
66
+ */
67
+ export declare function buildHandoffTaskMessage(intent: HandoffIntent): string;
68
+ /**
69
+ * Result of `validateHandoffIntent`. A discriminated union so call sites
70
+ * can `if (!result.valid) ... result.reason`.
71
+ */
72
+ export type HandoffValidationResult = {
73
+ valid: true;
74
+ } | {
75
+ valid: false;
76
+ reason: string;
77
+ };
78
+ /**
79
+ * Re-validate a stashed `HandoffIntent` against the current team state.
80
+ *
81
+ * The intent was validated by `createHandoffTool` at the moment the tool
82
+ * fired, but team state can change between stash and dispatch — an agent
83
+ * may have been removed, the team may have been swapped out, etc. Hosts
84
+ * should call this before invoking the target agent.
85
+ *
86
+ * Returns `{ valid: false, reason }` with a user-readable message when
87
+ * the intent is no longer dispatchable.
88
+ */
89
+ export declare function validateHandoffIntent(team: AgentTeam, intent: HandoffIntent): HandoffValidationResult;
90
+ /**
91
+ * Per-conversation stash for `HandoffIntent`s. Hosts that manage multiple
92
+ * independent conversations (the Desktop app) can use this directly; the
93
+ * CLI's single-REPL pattern is simpler than this class warrants and can
94
+ * use plain fields on its shared-state object.
95
+ *
96
+ * Usage:
97
+ *
98
+ * // Inside the handoff tool's `onHandoff` adapter callback:
99
+ * stash.set({ conversationId, sourceAgentId, targetAgentId, task, reason });
100
+ *
101
+ * // After the source agent's turn ends:
102
+ * const intent = stash.consume(conversationId);
103
+ * if (intent) {
104
+ * const check = validateHandoffIntent(team, intent);
105
+ * if (check.valid) {
106
+ * await runTargetAgent(intent.targetAgentId, buildHandoffTaskMessage(intent));
107
+ * }
108
+ * }
109
+ */
110
+ export declare class HandoffStash {
111
+ private readonly intents;
112
+ /** Stash an intent. Replaces any prior intent for the same conversation
113
+ * — only one handoff can be in flight per conversation at a time. */
114
+ set(intent: HandoffIntent): void;
115
+ /** Read without removing. Useful for "is there a handoff pending?" UI checks. */
116
+ peek(conversationId: string): HandoffIntent | undefined;
117
+ /** Read and remove. The normal post-turn pickup pattern. */
118
+ consume(conversationId: string): HandoffIntent | undefined;
119
+ /** Explicitly clear without consuming — used by error paths so a failed
120
+ * source turn doesn't leave a phantom intent for the next turn. */
121
+ clear(conversationId: string): void;
122
+ /** Whether a stashed intent exists for this conversation. */
123
+ has(conversationId: string): boolean;
124
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Handoff orchestration helpers.
3
+ *
4
+ * The `createHandoffTool` factory (delegation-tools.ts) covers the
5
+ * tool-side of handoffs: validation, one-hop enforcement, `team.recordHandoff`.
6
+ * What it does NOT cover is the post-turn orchestration pattern shared by
7
+ * all hosts that surface this tool — stashing the dispatched intent during
8
+ * the source agent's turn, picking it back up once the turn ends, and
9
+ * dispatching the task to the target agent.
10
+ *
11
+ * Hosts (CLI, Desktop) used to implement that pattern independently,
12
+ * which is exactly the kind of drift that produced the desktop's
13
+ * silent-stub bug. This module centralises:
14
+ *
15
+ * - **HandoffIntent** — the shape both hosts pass between "tool fired"
16
+ * and "source turn ended."
17
+ * - **buildHandoffTaskMessage** — canonical `[Handoff from $arch]\n\nTask:`
18
+ * format so the synthesised message the target receives is identical
19
+ * across hosts.
20
+ * - **validateHandoffIntent** — re-checks team state at dispatch time
21
+ * (target could have been removed between stash and pickup).
22
+ * - **HandoffStash** — optional convenience for hosts that have multiple
23
+ * independent conversations (Desktop). CLI's single-REPL pattern is
24
+ * simpler than this class warrants; it can use the type + helpers
25
+ * directly without the stash.
26
+ *
27
+ * Storage location is intentionally platform-shaped (CLI: one global,
28
+ * Desktop: per-conversation map) — only the *what* gets standardised,
29
+ * not the *where*.
30
+ */
31
+ // =============================================================================
32
+ // Canonical task-message format
33
+ // =============================================================================
34
+ /**
35
+ * Build the synthesised user-style message handed to the target agent
36
+ * after a successful handoff. Wrapping the body with a `[Handoff from $X]`
37
+ * marker keeps the target oriented and gives future tooling a way to
38
+ * distinguish handoff-injected messages from direct user input.
39
+ *
40
+ * Format:
41
+ *
42
+ * [Handoff from $arch]
43
+ *
44
+ * Task: <verbatim task>
45
+ *
46
+ * Reason: <verbatim reason — omitted when absent>
47
+ */
48
+ export function buildHandoffTaskMessage(intent) {
49
+ return intent.reason
50
+ ? `[Handoff from $${intent.sourceAgentId}]\n\nTask: ${intent.task}\n\nReason: ${intent.reason}`
51
+ : `[Handoff from $${intent.sourceAgentId}]\n\nTask: ${intent.task}`;
52
+ }
53
+ /**
54
+ * Re-validate a stashed `HandoffIntent` against the current team state.
55
+ *
56
+ * The intent was validated by `createHandoffTool` at the moment the tool
57
+ * fired, but team state can change between stash and dispatch — an agent
58
+ * may have been removed, the team may have been swapped out, etc. Hosts
59
+ * should call this before invoking the target agent.
60
+ *
61
+ * Returns `{ valid: false, reason }` with a user-readable message when
62
+ * the intent is no longer dispatchable.
63
+ */
64
+ export function validateHandoffIntent(team, intent) {
65
+ if (!team.has(intent.targetAgentId)) {
66
+ return {
67
+ valid: false,
68
+ reason: `Agent '${intent.targetAgentId}' no longer exists in team.`,
69
+ };
70
+ }
71
+ if (!team.has(intent.sourceAgentId)) {
72
+ return {
73
+ valid: false,
74
+ reason: `Source agent '${intent.sourceAgentId}' no longer exists in team.`,
75
+ };
76
+ }
77
+ return { valid: true };
78
+ }
79
+ // =============================================================================
80
+ // Optional stash for multi-conversation hosts (Desktop)
81
+ // =============================================================================
82
+ /**
83
+ * Per-conversation stash for `HandoffIntent`s. Hosts that manage multiple
84
+ * independent conversations (the Desktop app) can use this directly; the
85
+ * CLI's single-REPL pattern is simpler than this class warrants and can
86
+ * use plain fields on its shared-state object.
87
+ *
88
+ * Usage:
89
+ *
90
+ * // Inside the handoff tool's `onHandoff` adapter callback:
91
+ * stash.set({ conversationId, sourceAgentId, targetAgentId, task, reason });
92
+ *
93
+ * // After the source agent's turn ends:
94
+ * const intent = stash.consume(conversationId);
95
+ * if (intent) {
96
+ * const check = validateHandoffIntent(team, intent);
97
+ * if (check.valid) {
98
+ * await runTargetAgent(intent.targetAgentId, buildHandoffTaskMessage(intent));
99
+ * }
100
+ * }
101
+ */
102
+ export class HandoffStash {
103
+ intents = new Map();
104
+ /** Stash an intent. Replaces any prior intent for the same conversation
105
+ * — only one handoff can be in flight per conversation at a time. */
106
+ set(intent) {
107
+ this.intents.set(intent.conversationId, intent);
108
+ }
109
+ /** Read without removing. Useful for "is there a handoff pending?" UI checks. */
110
+ peek(conversationId) {
111
+ return this.intents.get(conversationId);
112
+ }
113
+ /** Read and remove. The normal post-turn pickup pattern. */
114
+ consume(conversationId) {
115
+ const intent = this.intents.get(conversationId);
116
+ this.intents.delete(conversationId);
117
+ return intent;
118
+ }
119
+ /** Explicitly clear without consuming — used by error paths so a failed
120
+ * source turn doesn't leave a phantom intent for the next turn. */
121
+ clear(conversationId) {
122
+ this.intents.delete(conversationId);
123
+ }
124
+ /** Whether a stashed intent exists for this conversation. */
125
+ has(conversationId) {
126
+ return this.intents.has(conversationId);
127
+ }
128
+ }
@@ -40,3 +40,7 @@ export { SKILL_REQUIREMENTS, getDefinedSkillNames, getSkillRequirements, checkSk
40
40
  export { resolveAgentIdCollision } from './collision-utils.js';
41
41
  export { createDelegationStatusTool, createHandoffTool } from './delegation-tools.js';
42
42
  export type { HandoffResult, HandoffToolConfig } from './delegation-tools.js';
43
+ export { buildHandoffTaskMessage, validateHandoffIntent, HandoffStash, } from './handoff-orchestration.js';
44
+ export type { HandoffIntent, HandoffValidationResult } from './handoff-orchestration.js';
45
+ export { ROLE_NAME_ALIASES, normalizeRoleName } from './role-aliases.js';
46
+ export type { NormalizedRole } from './role-aliases.js';
@@ -33,3 +33,10 @@ export { SKILL_REQUIREMENTS, getDefinedSkillNames, getSkillRequirements, checkSk
33
33
  export { resolveAgentIdCollision } from './collision-utils.js';
34
34
  // Delegation & Handoff tools (factory functions)
35
35
  export { createDelegationStatusTool, createHandoffTool } from './delegation-tools.js';
36
+ // Handoff orchestration helpers — shared between CLI and Desktop for the
37
+ // post-turn stash/dispatch pattern. See handoff-orchestration.ts header.
38
+ export { buildHandoffTaskMessage, validateHandoffIntent, HandoffStash, } from './handoff-orchestration.js';
39
+ // Role-name aliases — longform → canonical AgentRole short key. Shared
40
+ // between CLI and Desktop for `suggestedAgents` translation when batch-
41
+ // adding a project type's default team. See role-aliases.ts header.
42
+ export { ROLE_NAME_ALIASES, normalizeRoleName } from './role-aliases.js';
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Role-name aliases — longform → canonical AgentRole short key.
3
+ *
4
+ * The project-type configs (`src/project-types/configs.ts`) use a mix of
5
+ * longform role names (`developer`, `architect`, `technical-writer`) and
6
+ * canonical short keys (`qa`, `pm`, `writer`, `researcher`) in their
7
+ * `suggestedAgents` lists. `TeamAgent.addAgentFromRole` only accepts the
8
+ * canonical short keys (`AgentRole`).
9
+ *
10
+ * Hosts (CLI, Desktop) that auto-add `suggestedAgents` into a team need
11
+ * to translate the mixed input to the canonical form before calling
12
+ * `addAgentFromRole`. This module is the single source of truth for that
13
+ * mapping — previously duplicated between the desktop's
14
+ * `lib/role-mascots.ts:toSdkRole` and the CLI's inline `LONG_TO_SHORT`
15
+ * map in `commands-v2/handlers/project.ts`.
16
+ *
17
+ * The alias targets are best-fit mappings, not exact equivalents:
18
+ * security → reviewer (security review work, no dedicated agent yet)
19
+ * designer → default (no design agent yet)
20
+ * plotter / scene-writer / character-dev / content-designer → writer
21
+ * (writing-adjacent roles that share the writer agent)
22
+ *
23
+ * When a new TeamAgent role is added to `AgentRole`, prefer that role
24
+ * over the alias here. E.g., when a real `security` TeamAgent ships,
25
+ * update the alias to `security → security` (or remove the alias and
26
+ * add `security` to `AgentRole` so it's pass-through).
27
+ */
28
+ import type { AgentRole } from './types.js';
29
+ /** Longform role names mapped to canonical AgentRole short keys. */
30
+ export declare const ROLE_NAME_ALIASES: Record<string, AgentRole>;
31
+ /** Result of `normalizeRoleName`. Three states: pass-through (the input was
32
+ * already canonical), mapped (an alias was applied), or unknown (input is
33
+ * neither and we fell back to `'default'`). */
34
+ export interface NormalizedRole {
35
+ /** The canonical short AgentRole. */
36
+ role: AgentRole;
37
+ /** True if `ROLE_NAME_ALIASES` rewrote the input. */
38
+ mapped: boolean;
39
+ /** True if the input was neither a canonical role nor a known alias.
40
+ * Callers can either treat this as an error or accept the `'default'`
41
+ * fallback that's been set on `role`. */
42
+ unknown: boolean;
43
+ }
44
+ /**
45
+ * Normalise a role name to the canonical `AgentRole`.
46
+ *
47
+ * Behaviour:
48
+ * - Input is already a canonical short key → pass through, `mapped: false`,
49
+ * `unknown: false`.
50
+ * - Input matches a known longform alias → mapped to the canonical key,
51
+ * `mapped: true`, `unknown: false`.
52
+ * - Input is neither → returns `'default'`, `mapped: false`, `unknown: true`.
53
+ * Callers typically log/warn and skip rather than silently add the
54
+ * default agent.
55
+ *
56
+ * @example
57
+ * normalizeRoleName('developer') // { role: 'dev', mapped: true, unknown: false }
58
+ * normalizeRoleName('qa') // { role: 'qa', mapped: false, unknown: false }
59
+ * normalizeRoleName('astronaut') // { role: 'default', mapped: false, unknown: true }
60
+ */
61
+ export declare function normalizeRoleName(role: string): NormalizedRole;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Role-name aliases — longform → canonical AgentRole short key.
3
+ *
4
+ * The project-type configs (`src/project-types/configs.ts`) use a mix of
5
+ * longform role names (`developer`, `architect`, `technical-writer`) and
6
+ * canonical short keys (`qa`, `pm`, `writer`, `researcher`) in their
7
+ * `suggestedAgents` lists. `TeamAgent.addAgentFromRole` only accepts the
8
+ * canonical short keys (`AgentRole`).
9
+ *
10
+ * Hosts (CLI, Desktop) that auto-add `suggestedAgents` into a team need
11
+ * to translate the mixed input to the canonical form before calling
12
+ * `addAgentFromRole`. This module is the single source of truth for that
13
+ * mapping — previously duplicated between the desktop's
14
+ * `lib/role-mascots.ts:toSdkRole` and the CLI's inline `LONG_TO_SHORT`
15
+ * map in `commands-v2/handlers/project.ts`.
16
+ *
17
+ * The alias targets are best-fit mappings, not exact equivalents:
18
+ * security → reviewer (security review work, no dedicated agent yet)
19
+ * designer → default (no design agent yet)
20
+ * plotter / scene-writer / character-dev / content-designer → writer
21
+ * (writing-adjacent roles that share the writer agent)
22
+ *
23
+ * When a new TeamAgent role is added to `AgentRole`, prefer that role
24
+ * over the alias here. E.g., when a real `security` TeamAgent ships,
25
+ * update the alias to `security → security` (or remove the alias and
26
+ * add `security` to `AgentRole` so it's pass-through).
27
+ */
28
+ import { PREDEFINED_ROLE_IDS } from './types.js';
29
+ /** Longform role names mapped to canonical AgentRole short keys. */
30
+ export const ROLE_NAME_ALIASES = {
31
+ developer: 'dev',
32
+ architect: 'arch',
33
+ 'technical-writer': 'docs',
34
+ security: 'reviewer',
35
+ designer: 'default',
36
+ 'character-dev': 'writer',
37
+ 'content-designer': 'writer',
38
+ plotter: 'writer',
39
+ 'scene-writer': 'writer',
40
+ };
41
+ const SDK_ROLE_PASSTHROUGH = new Set(PREDEFINED_ROLE_IDS);
42
+ /**
43
+ * Normalise a role name to the canonical `AgentRole`.
44
+ *
45
+ * Behaviour:
46
+ * - Input is already a canonical short key → pass through, `mapped: false`,
47
+ * `unknown: false`.
48
+ * - Input matches a known longform alias → mapped to the canonical key,
49
+ * `mapped: true`, `unknown: false`.
50
+ * - Input is neither → returns `'default'`, `mapped: false`, `unknown: true`.
51
+ * Callers typically log/warn and skip rather than silently add the
52
+ * default agent.
53
+ *
54
+ * @example
55
+ * normalizeRoleName('developer') // { role: 'dev', mapped: true, unknown: false }
56
+ * normalizeRoleName('qa') // { role: 'qa', mapped: false, unknown: false }
57
+ * normalizeRoleName('astronaut') // { role: 'default', mapped: false, unknown: true }
58
+ */
59
+ export function normalizeRoleName(role) {
60
+ if (SDK_ROLE_PASSTHROUGH.has(role)) {
61
+ return { role: role, mapped: false, unknown: false };
62
+ }
63
+ // hasOwn is needed because Record<string, T> reports every string key as
64
+ // present at the type level even when it isn't — we need the runtime check.
65
+ if (Object.prototype.hasOwnProperty.call(ROLE_NAME_ALIASES, role)) {
66
+ return { role: ROLE_NAME_ALIASES[role], mapped: true, unknown: false };
67
+ }
68
+ return { role: 'default', mapped: false, unknown: true };
69
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.10.17",
3
+ "version": "0.10.19",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",