@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 +2 -2
- package/dist/index.js +1 -1
- package/dist/team/handoff-orchestration.d.ts +124 -0
- package/dist/team/handoff-orchestration.js +128 -0
- package/dist/team/index.d.ts +4 -0
- package/dist/team/index.js +7 -0
- package/dist/team/role-aliases.d.ts +61 -0
- package/dist/team/role-aliases.js +69 -0
- package/package.json +1 -1
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
|
+
}
|
package/dist/team/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/team/index.js
CHANGED
|
@@ -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
|
+
}
|