@agent-workspace/utils 0.5.0
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/README.md +80 -0
- package/dist/frontmatter.d.ts +30 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +46 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/graph.d.ts +109 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +252 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/reputation.d.ts +37 -0
- package/dist/reputation.d.ts.map +1 -0
- package/dist/reputation.js +78 -0
- package/dist/reputation.js.map +1 -0
- package/dist/swarm.d.ts +78 -0
- package/dist/swarm.d.ts.map +1 -0
- package/dist/swarm.js +182 -0
- package/dist/swarm.js.map +1 -0
- package/dist/validation.d.ts +40 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +73 -0
- package/dist/validation.js.map +1 -0
- package/dist/workspace.d.ts +47 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +93 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { REPUTATION_EWMA_ALPHA, REPUTATION_DECAY_RATE, REPUTATION_BASELINE, MS_PER_MONTH, } from "@agent-workspace/core";
|
|
2
|
+
/**
|
|
3
|
+
* Compute confidence from sample size.
|
|
4
|
+
* Formula: confidence = 1 - 1/(1 + sampleSize * 0.1)
|
|
5
|
+
*
|
|
6
|
+
* @param sampleSize - Number of samples
|
|
7
|
+
* @returns Confidence value (0.0 to ~1.0)
|
|
8
|
+
*/
|
|
9
|
+
export function computeConfidence(sampleSize) {
|
|
10
|
+
return Math.round((1 - 1 / (1 + sampleSize * 0.1)) * 100) / 100;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Apply time-based decay to a reputation score.
|
|
14
|
+
* Scores decay toward REPUTATION_BASELINE (0.5) over time.
|
|
15
|
+
*
|
|
16
|
+
* @param dim - The reputation dimension to decay
|
|
17
|
+
* @param now - Current date (defaults to now)
|
|
18
|
+
* @param decayRate - Monthly decay rate (defaults to REPUTATION_DECAY_RATE)
|
|
19
|
+
* @returns The decayed score
|
|
20
|
+
*/
|
|
21
|
+
export function computeDecayedScore(dim, now = new Date(), decayRate = REPUTATION_DECAY_RATE) {
|
|
22
|
+
const lastSignalDate = new Date(dim.lastSignal);
|
|
23
|
+
const monthsElapsed = (now.getTime() - lastSignalDate.getTime()) / MS_PER_MONTH;
|
|
24
|
+
if (monthsElapsed <= 0)
|
|
25
|
+
return dim.score;
|
|
26
|
+
const decayFactor = Math.exp(-decayRate * monthsElapsed);
|
|
27
|
+
// Decay toward baseline (0.5)
|
|
28
|
+
const decayed = REPUTATION_BASELINE + (dim.score - REPUTATION_BASELINE) * decayFactor;
|
|
29
|
+
return Math.round(decayed * 1000) / 1000;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Update a reputation dimension with a new signal using EWMA.
|
|
33
|
+
*
|
|
34
|
+
* @param existing - Existing dimension (undefined for first signal)
|
|
35
|
+
* @param signalScore - The new signal score (0.0 to 1.0)
|
|
36
|
+
* @param now - Current date (defaults to now)
|
|
37
|
+
* @param alpha - EWMA learning rate (defaults to REPUTATION_EWMA_ALPHA)
|
|
38
|
+
* @returns Updated reputation dimension
|
|
39
|
+
*/
|
|
40
|
+
export function updateDimension(existing, signalScore, now = new Date(), alpha = REPUTATION_EWMA_ALPHA) {
|
|
41
|
+
const timestamp = now.toISOString();
|
|
42
|
+
if (!existing) {
|
|
43
|
+
return {
|
|
44
|
+
score: signalScore,
|
|
45
|
+
confidence: computeConfidence(1),
|
|
46
|
+
sampleSize: 1,
|
|
47
|
+
lastSignal: timestamp,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Apply decay to old score before EWMA
|
|
51
|
+
const decayed = computeDecayedScore(existing, now);
|
|
52
|
+
const newScore = alpha * signalScore + (1 - alpha) * decayed;
|
|
53
|
+
const newSampleSize = existing.sampleSize + 1;
|
|
54
|
+
return {
|
|
55
|
+
score: Math.round(newScore * 1000) / 1000,
|
|
56
|
+
confidence: computeConfidence(newSampleSize),
|
|
57
|
+
sampleSize: newSampleSize,
|
|
58
|
+
lastSignal: timestamp,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Compute a weighted average of multiple dimension scores.
|
|
63
|
+
*
|
|
64
|
+
* @param scores - Array of [score, weight] tuples
|
|
65
|
+
* @returns Weighted average score
|
|
66
|
+
*/
|
|
67
|
+
export function computeWeightedScore(scores) {
|
|
68
|
+
let totalWeight = 0;
|
|
69
|
+
let weightedSum = 0;
|
|
70
|
+
for (const [score, weight] of scores) {
|
|
71
|
+
weightedSum += score * weight;
|
|
72
|
+
totalWeight += weight;
|
|
73
|
+
}
|
|
74
|
+
if (totalWeight === 0)
|
|
75
|
+
return REPUTATION_BASELINE;
|
|
76
|
+
return Math.round((weightedSum / totalWeight) * 1000) / 1000;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=reputation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reputation.js","sourceRoot":"","sources":["../src/reputation.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,GAEb,MAAM,uBAAuB,CAAC;AAE/B;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB;IAClD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AAClE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAwB,EACxB,MAAY,IAAI,IAAI,EAAE,EACtB,YAAoB,qBAAqB;IAEzC,MAAM,cAAc,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAChD,MAAM,aAAa,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,CAAC,GAAG,YAAY,CAAC;IAEhF,IAAI,aAAa,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC;IAEzC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,SAAS,GAAG,aAAa,CAAC,CAAC;IAEzD,8BAA8B;IAC9B,MAAM,OAAO,GAAG,mBAAmB,GAAG,CAAC,GAAG,CAAC,KAAK,GAAG,mBAAmB,CAAC,GAAG,WAAW,CAAC;IAEtF,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;AAC3C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAyC,EACzC,WAAmB,EACnB,MAAY,IAAI,IAAI,EAAE,EACtB,QAAgB,qBAAqB;IAErC,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAEpC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,KAAK,EAAE,WAAW;YAClB,UAAU,EAAE,iBAAiB,CAAC,CAAC,CAAC;YAChC,UAAU,EAAE,CAAC;YACb,UAAU,EAAE,SAAS;SACtB,CAAC;IACJ,CAAC;IAED,uCAAuC;IACvC,MAAM,OAAO,GAAG,mBAAmB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,KAAK,GAAG,WAAW,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC;IAC7D,MAAM,aAAa,GAAG,QAAQ,CAAC,UAAU,GAAG,CAAC,CAAC;IAE9C,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,IAAI;QACzC,UAAU,EAAE,iBAAiB,CAAC,aAAa,CAAC;QAC5C,UAAU,EAAE,aAAa;QACzB,UAAU,EAAE,SAAS;KACtB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAA+B;IAClE,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACrC,WAAW,IAAI,KAAK,GAAG,MAAM,CAAC;QAC9B,WAAW,IAAI,MAAM,CAAC;IACxB,CAAC;IAED,IAAI,WAAW,KAAK,CAAC;QAAE,OAAO,mBAAmB,CAAC;IAElD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,GAAG,WAAW,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;AAC/D,CAAC"}
|
package/dist/swarm.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swarm recruitment logic for finding and assigning qualified agents to roles.
|
|
3
|
+
*/
|
|
4
|
+
import type { SwarmRole, SwarmFrontmatter, ReputationProfileFrontmatter } from "@agent-workspace/core";
|
|
5
|
+
/**
|
|
6
|
+
* A candidate agent for a swarm role with qualification status
|
|
7
|
+
*/
|
|
8
|
+
export interface RecruitmentCandidate {
|
|
9
|
+
/** Reputation profile slug */
|
|
10
|
+
slug: string;
|
|
11
|
+
/** Agent DID */
|
|
12
|
+
did: string;
|
|
13
|
+
/** Agent name */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Decayed scores for relevant dimensions */
|
|
16
|
+
scores: Record<string, number>;
|
|
17
|
+
/** Whether the agent meets all minimum requirements */
|
|
18
|
+
qualifies: boolean;
|
|
19
|
+
/** Dimensions/domains where the agent is below threshold */
|
|
20
|
+
gaps: string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Result of auto-recruitment for a swarm
|
|
24
|
+
*/
|
|
25
|
+
export interface RecruitmentResult {
|
|
26
|
+
/** Role name */
|
|
27
|
+
role: string;
|
|
28
|
+
/** DIDs of agents assigned */
|
|
29
|
+
assigned: string[];
|
|
30
|
+
/** Slugs of agents assigned */
|
|
31
|
+
assignedSlugs: string[];
|
|
32
|
+
/** Number of slots still unfilled */
|
|
33
|
+
unfilled: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Find candidates for a swarm role from available reputation profiles.
|
|
37
|
+
* Returns all candidates sorted by qualification (qualified first, then by average score).
|
|
38
|
+
*
|
|
39
|
+
* @param role - The swarm role to find candidates for
|
|
40
|
+
* @param profiles - Available reputation profiles
|
|
41
|
+
* @param now - Current date for decay calculation
|
|
42
|
+
* @returns Array of candidates with qualification status
|
|
43
|
+
*/
|
|
44
|
+
export declare function findCandidatesForRole(role: SwarmRole, profiles: ReputationProfileFrontmatter[], now?: Date): RecruitmentCandidate[];
|
|
45
|
+
/**
|
|
46
|
+
* Auto-recruit agents to unfilled roles in a swarm.
|
|
47
|
+
* Assigns the best qualified candidates to each role until filled or no more candidates.
|
|
48
|
+
*
|
|
49
|
+
* @param swarm - The swarm to recruit for
|
|
50
|
+
* @param profiles - Available reputation profiles
|
|
51
|
+
* @param now - Current date for decay calculation
|
|
52
|
+
* @returns Array of recruitment results for each role
|
|
53
|
+
*/
|
|
54
|
+
export declare function autoRecruitSwarm(swarm: SwarmFrontmatter, profiles: ReputationProfileFrontmatter[], now?: Date): RecruitmentResult[];
|
|
55
|
+
/**
|
|
56
|
+
* Check if a swarm has all roles filled.
|
|
57
|
+
*
|
|
58
|
+
* @param swarm - The swarm to check
|
|
59
|
+
* @returns True if all roles are fully staffed
|
|
60
|
+
*/
|
|
61
|
+
export declare function isSwarmFullyStaffed(swarm: SwarmFrontmatter): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Get swarm staffing summary.
|
|
64
|
+
*
|
|
65
|
+
* @param swarm - The swarm to summarize
|
|
66
|
+
* @returns Object with filled, needed, and total counts
|
|
67
|
+
*/
|
|
68
|
+
export declare function getSwarmStaffingSummary(swarm: SwarmFrontmatter): {
|
|
69
|
+
filled: number;
|
|
70
|
+
needed: number;
|
|
71
|
+
total: number;
|
|
72
|
+
byRole: Array<{
|
|
73
|
+
role: string;
|
|
74
|
+
filled: number;
|
|
75
|
+
needed: number;
|
|
76
|
+
}>;
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=swarm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swarm.d.ts","sourceRoot":"","sources":["../src/swarm.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,4BAA4B,EAE7B,MAAM,uBAAuB,CAAC;AAG/B;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,uDAAuD;IACvD,SAAS,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,4BAA4B,EAAE,EACxC,GAAG,GAAE,IAAiB,GACrB,oBAAoB,EAAE,CA4DxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,gBAAgB,EACvB,QAAQ,EAAE,4BAA4B,EAAE,EACxC,GAAG,GAAE,IAAiB,GACrB,iBAAiB,EAAE,CAkDrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAOpE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,gBAAgB,GAAG;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE,CAsBA"}
|
package/dist/swarm.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Swarm recruitment logic for finding and assigning qualified agents to roles.
|
|
3
|
+
*/
|
|
4
|
+
import { computeDecayedScore } from "./reputation.js";
|
|
5
|
+
/**
|
|
6
|
+
* Get the decayed score for a dimension from a reputation profile.
|
|
7
|
+
*
|
|
8
|
+
* @param profile - Reputation profile frontmatter
|
|
9
|
+
* @param dimension - Dimension name or "domain-competence:<domain>"
|
|
10
|
+
* @param now - Current date for decay calculation
|
|
11
|
+
* @returns Decayed score or null if dimension not found
|
|
12
|
+
*/
|
|
13
|
+
function getDimensionScore(profile, dimension, now) {
|
|
14
|
+
// Handle domain-competence format
|
|
15
|
+
if (dimension.startsWith("domain-competence:")) {
|
|
16
|
+
const domain = dimension.slice("domain-competence:".length);
|
|
17
|
+
const dim = profile.domainCompetence?.[domain];
|
|
18
|
+
if (!dim)
|
|
19
|
+
return null;
|
|
20
|
+
return computeDecayedScore(dim, now);
|
|
21
|
+
}
|
|
22
|
+
// Standard dimension
|
|
23
|
+
const dim = profile.dimensions?.[dimension];
|
|
24
|
+
if (!dim)
|
|
25
|
+
return null;
|
|
26
|
+
return computeDecayedScore(dim, now);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Find candidates for a swarm role from available reputation profiles.
|
|
30
|
+
* Returns all candidates sorted by qualification (qualified first, then by average score).
|
|
31
|
+
*
|
|
32
|
+
* @param role - The swarm role to find candidates for
|
|
33
|
+
* @param profiles - Available reputation profiles
|
|
34
|
+
* @param now - Current date for decay calculation
|
|
35
|
+
* @returns Array of candidates with qualification status
|
|
36
|
+
*/
|
|
37
|
+
export function findCandidatesForRole(role, profiles, now = new Date()) {
|
|
38
|
+
const candidates = [];
|
|
39
|
+
for (const profile of profiles) {
|
|
40
|
+
// Skip if already assigned to this role
|
|
41
|
+
if (role.assigned.includes(profile.agentDid)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const scores = {};
|
|
45
|
+
const gaps = [];
|
|
46
|
+
let qualifies = true;
|
|
47
|
+
// Check each minimum reputation requirement
|
|
48
|
+
if (role.minReputation) {
|
|
49
|
+
for (const [dimension, minScore] of Object.entries(role.minReputation)) {
|
|
50
|
+
const score = getDimensionScore(profile, dimension, now);
|
|
51
|
+
if (score !== null) {
|
|
52
|
+
scores[dimension] = Math.round(score * 100) / 100;
|
|
53
|
+
if (score < minScore) {
|
|
54
|
+
gaps.push(dimension);
|
|
55
|
+
qualifies = false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// Missing dimension means not qualified
|
|
60
|
+
scores[dimension] = 0;
|
|
61
|
+
gaps.push(dimension);
|
|
62
|
+
qualifies = false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
candidates.push({
|
|
67
|
+
slug: profile.id.replace("reputation:", ""),
|
|
68
|
+
did: profile.agentDid,
|
|
69
|
+
name: profile.agentName,
|
|
70
|
+
scores,
|
|
71
|
+
qualifies,
|
|
72
|
+
gaps,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Sort: qualified first, then by average score descending
|
|
76
|
+
candidates.sort((a, b) => {
|
|
77
|
+
if (a.qualifies !== b.qualifies) {
|
|
78
|
+
return a.qualifies ? -1 : 1;
|
|
79
|
+
}
|
|
80
|
+
const avgA = Object.values(a.scores).length > 0
|
|
81
|
+
? Object.values(a.scores).reduce((sum, s) => sum + s, 0) / Object.values(a.scores).length
|
|
82
|
+
: 0;
|
|
83
|
+
const avgB = Object.values(b.scores).length > 0
|
|
84
|
+
? Object.values(b.scores).reduce((sum, s) => sum + s, 0) / Object.values(b.scores).length
|
|
85
|
+
: 0;
|
|
86
|
+
return avgB - avgA;
|
|
87
|
+
});
|
|
88
|
+
return candidates;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Auto-recruit agents to unfilled roles in a swarm.
|
|
92
|
+
* Assigns the best qualified candidates to each role until filled or no more candidates.
|
|
93
|
+
*
|
|
94
|
+
* @param swarm - The swarm to recruit for
|
|
95
|
+
* @param profiles - Available reputation profiles
|
|
96
|
+
* @param now - Current date for decay calculation
|
|
97
|
+
* @returns Array of recruitment results for each role
|
|
98
|
+
*/
|
|
99
|
+
export function autoRecruitSwarm(swarm, profiles, now = new Date()) {
|
|
100
|
+
const results = [];
|
|
101
|
+
const usedDids = new Set();
|
|
102
|
+
// Collect already assigned agents
|
|
103
|
+
for (const role of swarm.roles) {
|
|
104
|
+
for (const did of role.assigned) {
|
|
105
|
+
usedDids.add(did);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const role of swarm.roles) {
|
|
109
|
+
const currentCount = role.assigned.length;
|
|
110
|
+
const neededCount = role.count - currentCount;
|
|
111
|
+
if (neededCount <= 0) {
|
|
112
|
+
results.push({
|
|
113
|
+
role: role.name,
|
|
114
|
+
assigned: [],
|
|
115
|
+
assignedSlugs: [],
|
|
116
|
+
unfilled: 0,
|
|
117
|
+
});
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
// Filter profiles to exclude already used agents
|
|
121
|
+
const availableProfiles = profiles.filter((p) => !usedDids.has(p.agentDid));
|
|
122
|
+
const candidates = findCandidatesForRole(role, availableProfiles, now);
|
|
123
|
+
// Take qualified candidates up to needed count
|
|
124
|
+
const qualified = candidates.filter((c) => c.qualifies);
|
|
125
|
+
const toAssign = qualified.slice(0, neededCount);
|
|
126
|
+
const assignedDids = toAssign.map((c) => c.did);
|
|
127
|
+
const assignedSlugs = toAssign.map((c) => c.slug);
|
|
128
|
+
// Mark these agents as used
|
|
129
|
+
for (const did of assignedDids) {
|
|
130
|
+
usedDids.add(did);
|
|
131
|
+
}
|
|
132
|
+
results.push({
|
|
133
|
+
role: role.name,
|
|
134
|
+
assigned: assignedDids,
|
|
135
|
+
assignedSlugs,
|
|
136
|
+
unfilled: neededCount - toAssign.length,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return results;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Check if a swarm has all roles filled.
|
|
143
|
+
*
|
|
144
|
+
* @param swarm - The swarm to check
|
|
145
|
+
* @returns True if all roles are fully staffed
|
|
146
|
+
*/
|
|
147
|
+
export function isSwarmFullyStaffed(swarm) {
|
|
148
|
+
for (const role of swarm.roles) {
|
|
149
|
+
if (role.assigned.length < role.count) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get swarm staffing summary.
|
|
157
|
+
*
|
|
158
|
+
* @param swarm - The swarm to summarize
|
|
159
|
+
* @returns Object with filled, needed, and total counts
|
|
160
|
+
*/
|
|
161
|
+
export function getSwarmStaffingSummary(swarm) {
|
|
162
|
+
let filled = 0;
|
|
163
|
+
let total = 0;
|
|
164
|
+
const byRole = [];
|
|
165
|
+
for (const role of swarm.roles) {
|
|
166
|
+
const roleFilled = role.assigned.length;
|
|
167
|
+
filled += roleFilled;
|
|
168
|
+
total += role.count;
|
|
169
|
+
byRole.push({
|
|
170
|
+
role: role.name,
|
|
171
|
+
filled: roleFilled,
|
|
172
|
+
needed: role.count,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
filled,
|
|
177
|
+
needed: total - filled,
|
|
178
|
+
total,
|
|
179
|
+
byRole,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=swarm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swarm.js","sourceRoot":"","sources":["../src/swarm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate an artifact or profile slug.
|
|
3
|
+
* Slugs must be lowercase alphanumeric with hyphens, not starting with hyphen.
|
|
4
|
+
*
|
|
5
|
+
* @param slug - The slug to validate
|
|
6
|
+
* @returns true if valid, false otherwise
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateSlug(slug: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Validate and sanitize a slug, throwing an error if invalid.
|
|
11
|
+
*
|
|
12
|
+
* @param slug - The slug to validate
|
|
13
|
+
* @returns The sanitized slug (trimmed, lowercased)
|
|
14
|
+
* @throws Error if slug is invalid
|
|
15
|
+
*/
|
|
16
|
+
export declare function sanitizeSlug(slug: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Validate that a path is within a root directory (prevents directory traversal).
|
|
19
|
+
*
|
|
20
|
+
* @param root - The root directory path
|
|
21
|
+
* @param targetPath - The path to validate (relative or absolute)
|
|
22
|
+
* @returns The normalized absolute path
|
|
23
|
+
* @throws Error if path traversal is detected
|
|
24
|
+
*/
|
|
25
|
+
export declare function validatePath(root: string, targetPath: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a string is a valid ISO 8601 date (YYYY-MM-DD).
|
|
28
|
+
*
|
|
29
|
+
* @param dateStr - The date string to validate
|
|
30
|
+
* @returns true if valid, false otherwise
|
|
31
|
+
*/
|
|
32
|
+
export declare function isValidDate(dateStr: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Check if a string is a valid ISO 8601 timestamp.
|
|
35
|
+
*
|
|
36
|
+
* @param timestamp - The timestamp string to validate
|
|
37
|
+
* @returns true if valid, false otherwise
|
|
38
|
+
*/
|
|
39
|
+
export declare function isValidTimestamp(timestamp: string): boolean;
|
|
40
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWjD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAUrE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAMpD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAG3D"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { resolve, relative, isAbsolute } from "node:path";
|
|
2
|
+
/** Pattern for valid slugs (lowercase alphanumeric with hyphens, not starting with hyphen) */
|
|
3
|
+
const SLUG_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
|
|
4
|
+
/** Maximum slug length */
|
|
5
|
+
const MAX_SLUG_LENGTH = 100;
|
|
6
|
+
/**
|
|
7
|
+
* Validate an artifact or profile slug.
|
|
8
|
+
* Slugs must be lowercase alphanumeric with hyphens, not starting with hyphen.
|
|
9
|
+
*
|
|
10
|
+
* @param slug - The slug to validate
|
|
11
|
+
* @returns true if valid, false otherwise
|
|
12
|
+
*/
|
|
13
|
+
export function validateSlug(slug) {
|
|
14
|
+
return SLUG_PATTERN.test(slug) && slug.length <= MAX_SLUG_LENGTH;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate and sanitize a slug, throwing an error if invalid.
|
|
18
|
+
*
|
|
19
|
+
* @param slug - The slug to validate
|
|
20
|
+
* @returns The sanitized slug (trimmed, lowercased)
|
|
21
|
+
* @throws Error if slug is invalid
|
|
22
|
+
*/
|
|
23
|
+
export function sanitizeSlug(slug) {
|
|
24
|
+
const trimmed = slug.trim().toLowerCase();
|
|
25
|
+
if (!SLUG_PATTERN.test(trimmed)) {
|
|
26
|
+
throw new Error(`Invalid slug: "${slug}". Must be lowercase alphanumeric with hyphens, not starting with hyphen.`);
|
|
27
|
+
}
|
|
28
|
+
if (trimmed.length > MAX_SLUG_LENGTH) {
|
|
29
|
+
throw new Error(`Slug too long: max ${MAX_SLUG_LENGTH} characters`);
|
|
30
|
+
}
|
|
31
|
+
return trimmed;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Validate that a path is within a root directory (prevents directory traversal).
|
|
35
|
+
*
|
|
36
|
+
* @param root - The root directory path
|
|
37
|
+
* @param targetPath - The path to validate (relative or absolute)
|
|
38
|
+
* @returns The normalized absolute path
|
|
39
|
+
* @throws Error if path traversal is detected
|
|
40
|
+
*/
|
|
41
|
+
export function validatePath(root, targetPath) {
|
|
42
|
+
const normalized = resolve(root, targetPath);
|
|
43
|
+
const rel = relative(root, normalized);
|
|
44
|
+
// Prevent directory traversal
|
|
45
|
+
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
46
|
+
throw new Error(`Path traversal detected: ${targetPath}`);
|
|
47
|
+
}
|
|
48
|
+
return normalized;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if a string is a valid ISO 8601 date (YYYY-MM-DD).
|
|
52
|
+
*
|
|
53
|
+
* @param dateStr - The date string to validate
|
|
54
|
+
* @returns true if valid, false otherwise
|
|
55
|
+
*/
|
|
56
|
+
export function isValidDate(dateStr) {
|
|
57
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const date = new Date(dateStr);
|
|
61
|
+
return !isNaN(date.getTime());
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if a string is a valid ISO 8601 timestamp.
|
|
65
|
+
*
|
|
66
|
+
* @param timestamp - The timestamp string to validate
|
|
67
|
+
* @returns true if valid, false otherwise
|
|
68
|
+
*/
|
|
69
|
+
export function isValidTimestamp(timestamp) {
|
|
70
|
+
const date = new Date(timestamp);
|
|
71
|
+
return !isNaN(date.getTime());
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE1D,8FAA8F;AAC9F,MAAM,YAAY,GAAG,sBAAsB,CAAC;AAE5C,0BAA0B;AAC1B,MAAM,eAAe,GAAG,GAAG,CAAC;AAE5B;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC;AACnE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,kBAAkB,IAAI,2EAA2E,CAClG,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,sBAAsB,eAAe,aAAa,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,UAAkB;IAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAEvC,8BAA8B;IAC9B,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,4BAA4B,UAAU,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;IACjC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type WorkspaceManifest } from "@agent-workspace/core";
|
|
2
|
+
/**
|
|
3
|
+
* Find workspace root by walking up from startDir looking for .awp/workspace.json.
|
|
4
|
+
*
|
|
5
|
+
* @param startDir - Directory to start searching from (defaults to cwd)
|
|
6
|
+
* @returns The workspace root path, or null if not found
|
|
7
|
+
*/
|
|
8
|
+
export declare function findWorkspaceRoot(startDir?: string): Promise<string | null>;
|
|
9
|
+
/**
|
|
10
|
+
* Load workspace manifest from .awp/workspace.json.
|
|
11
|
+
*
|
|
12
|
+
* @param workspaceRoot - The workspace root directory
|
|
13
|
+
* @returns The parsed workspace manifest
|
|
14
|
+
* @throws Error if manifest cannot be read or parsed
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadManifest(workspaceRoot: string): Promise<WorkspaceManifest>;
|
|
17
|
+
/**
|
|
18
|
+
* Check if a file exists at the given path.
|
|
19
|
+
*
|
|
20
|
+
* @param path - The file path to check
|
|
21
|
+
* @returns true if file exists, false otherwise
|
|
22
|
+
*/
|
|
23
|
+
export declare function fileExists(path: string): Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Get the agent DID from the workspace manifest, or "anonymous" if not set.
|
|
26
|
+
*
|
|
27
|
+
* @param workspaceRoot - The workspace root directory
|
|
28
|
+
* @returns The agent DID or "anonymous"
|
|
29
|
+
*/
|
|
30
|
+
export declare function getAgentDid(workspaceRoot: string): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Read a file with size limit check to prevent memory issues.
|
|
33
|
+
*
|
|
34
|
+
* @param path - The file path to read
|
|
35
|
+
* @param maxSize - Maximum allowed file size in bytes (default: 1MB)
|
|
36
|
+
* @returns The file contents as a string
|
|
37
|
+
* @throws Error if file is too large
|
|
38
|
+
*/
|
|
39
|
+
export declare function safeReadFile(path: string, maxSize?: number): Promise<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve workspace root from the AWP_WORKSPACE env var or cwd.
|
|
42
|
+
* This is a simpler version that doesn't walk up directories.
|
|
43
|
+
*
|
|
44
|
+
* @returns The workspace root path
|
|
45
|
+
*/
|
|
46
|
+
export declare function getWorkspaceRoot(): string;
|
|
47
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAK9E;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcjF;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAIpF;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOxE;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAMjG;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { readFile, access, stat } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { MANIFEST_PATH } from "@agent-workspace/core";
|
|
4
|
+
/** Maximum file size allowed for safe reads (1MB) */
|
|
5
|
+
const MAX_FILE_SIZE = 1024 * 1024;
|
|
6
|
+
/**
|
|
7
|
+
* Find workspace root by walking up from startDir looking for .awp/workspace.json.
|
|
8
|
+
*
|
|
9
|
+
* @param startDir - Directory to start searching from (defaults to cwd)
|
|
10
|
+
* @returns The workspace root path, or null if not found
|
|
11
|
+
*/
|
|
12
|
+
export async function findWorkspaceRoot(startDir) {
|
|
13
|
+
let dir = resolve(startDir || process.cwd());
|
|
14
|
+
const root = resolve("/");
|
|
15
|
+
while (dir !== root) {
|
|
16
|
+
const manifestPath = join(dir, MANIFEST_PATH);
|
|
17
|
+
try {
|
|
18
|
+
await access(manifestPath);
|
|
19
|
+
return dir;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
dir = resolve(dir, "..");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Load workspace manifest from .awp/workspace.json.
|
|
29
|
+
*
|
|
30
|
+
* @param workspaceRoot - The workspace root directory
|
|
31
|
+
* @returns The parsed workspace manifest
|
|
32
|
+
* @throws Error if manifest cannot be read or parsed
|
|
33
|
+
*/
|
|
34
|
+
export async function loadManifest(workspaceRoot) {
|
|
35
|
+
const manifestPath = join(workspaceRoot, MANIFEST_PATH);
|
|
36
|
+
const raw = await readFile(manifestPath, "utf-8");
|
|
37
|
+
return JSON.parse(raw);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check if a file exists at the given path.
|
|
41
|
+
*
|
|
42
|
+
* @param path - The file path to check
|
|
43
|
+
* @returns true if file exists, false otherwise
|
|
44
|
+
*/
|
|
45
|
+
export async function fileExists(path) {
|
|
46
|
+
try {
|
|
47
|
+
await access(path);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the agent DID from the workspace manifest, or "anonymous" if not set.
|
|
56
|
+
*
|
|
57
|
+
* @param workspaceRoot - The workspace root directory
|
|
58
|
+
* @returns The agent DID or "anonymous"
|
|
59
|
+
*/
|
|
60
|
+
export async function getAgentDid(workspaceRoot) {
|
|
61
|
+
try {
|
|
62
|
+
const manifest = await loadManifest(workspaceRoot);
|
|
63
|
+
return manifest.agent?.did || "anonymous";
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return "anonymous";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read a file with size limit check to prevent memory issues.
|
|
71
|
+
*
|
|
72
|
+
* @param path - The file path to read
|
|
73
|
+
* @param maxSize - Maximum allowed file size in bytes (default: 1MB)
|
|
74
|
+
* @returns The file contents as a string
|
|
75
|
+
* @throws Error if file is too large
|
|
76
|
+
*/
|
|
77
|
+
export async function safeReadFile(path, maxSize = MAX_FILE_SIZE) {
|
|
78
|
+
const stats = await stat(path);
|
|
79
|
+
if (stats.size > maxSize) {
|
|
80
|
+
throw new Error(`File too large: ${stats.size} bytes (max: ${maxSize})`);
|
|
81
|
+
}
|
|
82
|
+
return readFile(path, "utf-8");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve workspace root from the AWP_WORKSPACE env var or cwd.
|
|
86
|
+
* This is a simpler version that doesn't walk up directories.
|
|
87
|
+
*
|
|
88
|
+
* @returns The workspace root path
|
|
89
|
+
*/
|
|
90
|
+
export function getWorkspaceRoot() {
|
|
91
|
+
return process.env.AWP_WORKSPACE || process.cwd();
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAA0B,MAAM,uBAAuB,CAAC;AAE9E,qDAAqD;AACrD,MAAM,aAAa,GAAG,IAAI,GAAG,IAAI,CAAC;AAElC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAiB;IACvD,IAAI,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAE1B,OAAO,GAAG,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YAC3B,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,aAAqB;IACtD,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,aAAqB;IACrD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,aAAa,CAAC,CAAC;QACnD,OAAO,QAAQ,CAAC,KAAK,EAAE,GAAG,IAAI,WAAW,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,CAAC;IACrB,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,UAAkB,aAAa;IAC9E,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,IAAI,GAAG,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,CAAC,IAAI,gBAAgB,OAAO,GAAG,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;AACpD,CAAC"}
|